這里講shiro整合spring 實現登錄驗證 和權限攔截
首先pom文件添加依賴
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.3.2</version>
</dependency>
<!-- spring整合shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- ehcache-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
shiro要結合ehcache這個緩存框架 可以實現一些功能 比如限制用戶登錄密碼錯誤次數 然后限制多久時間內,限制登錄
接下來就來就是配置文件 首先web.xml
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>DelegatingFilterProxy</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
這是配置一個shiro過濾器 將所有的請求都交給shiro處理 至於spring的配置我這里就不寫了 和正常的配置一樣
然后ehcache緩存框架需要一個配置文件 我這里貼出來 文件名就叫 ehcache.xml
1 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> 2 3 <diskStore path="${webapp.root}"/> 4 5 <defaultCache 6 maxElementsInMemory="10000" 7 maxElementsOnDisk="0" 8 eternal="true" 9 overflowToDisk="true" 10 diskPersistent="false" 11 timeToIdleSeconds="0" 12 timeToLiveSeconds="0" 13 diskSpoolBufferSizeMB="50" 14 diskExpiryThreadIntervalSeconds="120" 15 memoryStoreEvictionPolicy="LFU" 16 /> 17 </ehcache>
接下來就是spring整合shiro的配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 5 6 7 <!-- 配置ehCache緩存支持--> 8 <bean name="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 9 <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property> 10 </bean> 11 12 13 <!-- 自定義Realm--> 14 <bean name="userRealm" class="com.newer.web.shiro.MyRealm" > 15 16 <!--注入加密算法類 --> 17 <property name="credentialsMatcher"> 18 19 <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> 20 <!--加密算法名 --> 21 <property name="hashAlgorithmName" value="MD5"/> 22 <!--加密次數 --> 23 <property name="hashIterations" value="6"/> 24 </bean> 25 26 </property> 27 28 29 </bean> 30 31 32 <!-- shiro安全管理器--> 33 <bean name="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 34 <property name="cacheManager" ref="cacheManager"></property> 35 <property name="realm" ref="userRealm"></property> 36 </bean> 37 38 39 <!-- 管理shiro bean的生命周期 --> 40 <bean name="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean> 41 42 47 48 <!--shiro核心過濾器配置--> 49 <bean name="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 50 <property name="securityManager" ref="securityManager"/> 51 <property name="loginUrl" value="/login.jsp"></property> <!-- 沒有認證 跳轉的登錄連接 --> 52 <property name="unauthorizedUrl" value="/unauthorized.jsp"></property> <!-- 沒有訪問權限 跳轉的頁面 -->
82 <!--這里使用 從數據庫查詢出來要攔截的url集合 注入到這個對象中 實現攔截 --> 83 <property name="filterChainDefinitionMap" ref="map"/> 86 </bean> 87 88 89 <!--實例工廠方法 將執行shiroFilterUtils類的build的方法 --> 90 <bean name="map" factory-bean="shiroFilterUtils" factory-method="build"/> 91 92 <!--這個類將從數據庫中查詢要攔截的url信息--> 93 <bean name="shiroFilterUtils" class="com.newer.util.ShiroFilterUtils"/> 97 </beans>
別的配置不說 shiro核心過濾器bean的 name的值 一定要與web.xml里面配置的shiro過濾器的名字一樣 否則會報錯
然后我這里是使用實例工廠方法 將一個存有要攔截的url的map集合 注入到ShiroFilterFactoryBean 中的filterChainDefinitionMap屬性
這樣當我們的請求經過shiro過濾器時 會根據我們配置的要攔截的url和使用相應的攔截器 對請求進行處理
現在把我這個工具類貼出來
/** * 這個類會在 spring容器初始化bean時 從數據庫將要攔截的url * 查詢出來 封裝成集合 注入到ShiroFilterFactoryBean類的filterChainDefinitions屬性中 * 這樣實現對需要相應權限才能訪問的url進行攔截 並由shiro驗證是否具有相應權限 */ public class ShiroFilterUtils { public Map<String,String> build(){ Map<String,String> map= new LinkedHashMap<String, String>();
//添加固定要攔截的url map.put("/user/login.do","anon"); map.put("/login.jsp","anon"); map.put("/**","authc"); return map; } }
這里講一下 url和攔截器的語法
url使用的是 Ant風格
? 匹配任何單字符
* 匹配0或者任意數量的字符
** 匹配0或者更多的目錄
攔截器常用有:
anon 匿名的 不需要認證 也可以訪問
authc 必須認證才可以訪問 沒有認證會跳轉登錄鏈接
user 登錄之后啟用了記住我功能 就可以訪問
roles 例如 roles[管理員] 表示必須具有管理員權限才可操作 如果是roles[管理員,經理] 表示要具有管理員和經理權限才可以訪問
------------------------------------------
shiro會將請求的url按照順序和map集合中的所有url匹配 如果匹配到了 不會再匹配后續url 所以要注意 map集合中要攔截的url的順序
我這里就沒有將要攔截的url 從數據庫中查出 如果需要從數據集中查出 然后將要攔截的url作為key 要使用的攔截器作為value 存入這個map集合即可
權限管理就這么多 接下來說登錄認證 貼出我的controller
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/login") public String login(String userName, String userPassword, ModelMap map){ //得到當前 Subject Subject currentSubject= SecurityUtils.getSubject(); //創建用戶名密碼 token UsernamePasswordToken token= new UsernamePasswordToken(userName,userPassword); //記住當前用戶 關掉瀏覽器 重開也能訪問 不是authc 攔截器的url token.setRememberMe(true); try { //執行登錄方法 currentSubject.login(token); }catch (IncorrectCredentialsException ie){ //將拋出的登錄異常 存儲 然后返回登錄頁面 顯示給用戶 map.put("loginMessage","用戶名或密碼錯誤!!!"); return "forward:/login.jsp"; }catch (AuthenticationException ae){ //所有異常的父類 map.put("loginMessage","未知錯誤 請聯系管理員"); return "forward:/login.jsp"; } return "success"; } @RequestMapping("/logout") public String logout(){ //得到當前 Subject Subject currentSubject= SecurityUtils.getSubject(); //注銷當前 Subject currentSubject.logout(); return "redirect:/login.jsp"; } //這里面沒有去寫注冊方法了 注冊的時候 要將密碼 通過這樣加密后存入到數據庫 public static void main(String[] args) { //生成MD5加密的密碼 參1:加密方式 ,參2:未加密的密碼 參3:鹽值 使用當前用戶名 參4:加密次數 Object str= new SimpleHash("MD5","123456","zhangsan",6); System.out.println("str = " + str); } }
當執行login()方法時 會調用我們在shiro配置文件中配置的自定義Realm類 login方法出現錯誤 會拋出異常 我們用catch可以捕獲對應異常
接下來貼出我的Realm類
@Component public class MyRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private AuthorityService authorityService; @Override //認證方法 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //將token強轉成 用戶密碼token UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken; //根據傳入的用戶名 查詢數據庫 返回用戶對象 User user=userService.queryByUserName(token.getUsername()); //沒有查到用戶 直接拋出異常 if(user==null){ throw new IncorrectCredentialsException(); } //根據當前用戶名 生成鹽值 用於結合加密算法對密碼進行加密 ByteSource salt=ByteSource.Util.bytes(token.getUsername()); //將用戶名和密碼 交由shiro認證 如果出錯會拋出對應異常 參數1:用戶名 參數2:用戶密碼 參3:鹽值 參數4:當前Realm的名字 return new SimpleAuthenticationInfo(user.getUserName(),user.getUserPassword(),salt,this.getName()); } @Override //授權方法 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //獲得用戶名 String userName= (String) principalCollection.getPrimaryPrincipal(); //根據用戶名 查詢當前用戶 擁有的權限 Set<String> roles=authorityService.queryByUserName(userName); //將當前權限集合 返回交給shiro處理 權限認證 return new SimpleAuthorizationInfo(roles); } }
第二個授權方法 就是當請求需要對應權限的攔截器的url時 shiro會調用這個方法 然后我們根據當前用戶名 查詢出當前用戶所擁有的權限
然后交給shiro 由他去比對當前用戶是否擁有可以訪問的資格 如果沒有就會跳轉到我們在配置文件中 shiro過濾器配置的unauthorizedUrl屬性的值去
shiro的基本用法就這些 網上看了很多教程 都是各不相同 不像別的框架 教程都差不多 暫時總結到這里 后續有新的用法 再來加上吧