Shiro簡介
Apache Shiro是Java的一個安全框架,官網為shiro.apache.org,主要場景為控制登陸,判斷用戶是否有訪問某個功能的權限等等。
Shiro的核心功能(入門知識,只介紹前兩個)
-
認證
-
授權
-
會話管理
-
加密
引入jar包和配置web.xml
-
引入Shiro對應的jar包,下面給出Maven
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.2.2</version> </dependency>
-
在web.xml中配置spring框架提供的用於整合shiro框架的過濾器
<!-- 配置shiro過濾器 --> <filter> <filter-name>shiroFilter</filter-name> // 需要在spring的配置文件中創建一個bean(shiroFilter) <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
下面我們將要進行Shiro的認證功能和授權功能的實現,代碼比較多。
登錄方法的實現
Shiro認證的流程
-
Application Code:應用程序代碼, 即登錄方法(登錄方法不是直接查詢數據庫,而是調用Shiro框架提供的接口來實現)
-
Subject:框架提供的接口,代表當前用戶對象
-
SecurityManager:框架提供的接口,代表安全管理器對象
-
Realm:可以開發人員編寫(即認證和授權方法)
-
我們首先將登錄方法按照Shiro指定的方式進行改進
public String login() { // 獲取驗證碼 String validateCode = (String) ServletActionContext.getRequest().getSession().getAttribute("key"); // 判斷驗證碼 if (StringUtils.isNotBlank(checkcode) && checkcode.equals(validateCode)) { // 獲取getSubject對象,Shiro中代表當前用戶對象 Subject subject = SecurityUtils.getSubject(); // 令牌 傳遞進去前台接受的賬號和密碼 AuthenticationToken token = new UsernamePasswordToken(model.getUsername(), MD5Utils.md5(model.getPassword()));// 創建用戶名密碼令牌對象 try { subject.login(token); // 調用內置的登錄方法來實現檢驗 如果登陸錯誤就會拋出異常,返回登錄頁面 } catch (Exception e) { e.printStackTrace(); return LOGIN; } User user = (User) subject.getPrincipal(); // 登錄成功后可以從subject取得登錄對象 ServletActionContext.getRequest().getSession().setAttribute("loginUser", user); return HOME; } else { this.addActionError("輸入驗證碼錯誤"); return LOGIN; } }
-
然后編寫
Realm
繼承AuthorizingRealm
,即編寫具體的認證和授權方法public class BOSRealm extends AuthorizingRealm { @Autowired private IUserDao userDao // 授權方法 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { // 可以在這里將用戶所屬的權限查詢出來,然后遍歷賦值給info對象,這樣就可以實現了授權 // 判斷訪問路徑或者方法有沒有權限的時候就是根據info中的數據來判斷 info.addStringPermission("staff-list"); return info; } // 認證方法(登陸方法) protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException { System.out.println("認證..."); UsernamePasswordToken passwordToken = (UsernamePasswordToken) arg0; // 對象轉換 String username = passwordToken.getUsername(); // 獲得username User user = userDao.findByUsername(username); // 通過username從數據庫中獲取到User對象 if (user == null) { return null; } // 內置驗證方法 (數據庫中獲取的對象,對象的密碼, this.getName()) AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName()); return authenticationInfo; } }
-
到上面以后編碼工作就完成了,剩下的就是進行配置了,首先將編寫的
Realm
注入到安全管理器,整合的是Spring,所以下面的配置都是在Spring配置文件中。<!-- 注冊realm --> <bean id="bosRealm" class="lyh.bos.realm.BOSRealm"></bean> <!-- 注冊安全管理器對象 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="bosRealm"/> </bean>
-
配置
ShiroFilterFactoryBean
,同時還可以配置一下URL攔截規則,注意這里的id要和web.xml
中<filter-name>shiroFilter</filter-name>
相同<!-- shiro 配置 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 此處為web.xml配置的攔截器 --> <!-- 注入安全管理器對象 --> <property name="securityManager" ref="securityManager"/> <!-- 注入相關頁面訪問URL --> <property name="loginUrl" value="/login.jsp"/> <!-- 登錄頁面 --> <property name="successUrl" value="/index.jsp"/> <!-- 登錄成功的主頁 --> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <!-- 權限不足轉向的錯誤頁面 --> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <!--注入URL攔截規則, --> <!-- anon 都可以訪問 perms["staff-list"] 是否有staff-list權限 authc 登錄才可以訪問 --> <property name="filterChainDefinitions"> <value> /css/** = anon /admin/logout = logout <!-- 注銷,訪問這個路徑,自動注銷不需要自己編寫方法 --> /images/** = anon /validatecode.jsp* = anon /login.jsp = anon /userAction_login.action = anon /page_base_staff.action = perms["staff-list"] <!-- 表示page_base_staff.action路徑需要有staff-list權限才可訪問 --> /* = authc </value> </property> </bean>
-
Shiro還提供了使用
注解
控制權限的方法,開啟方式如下,同時,使用的注解權限還需要配置全局異常,用來捕獲當權限不足拋出異常時轉向的頁面,這里使用的是SpringMVC
,開啟方式@RequiresPermissions("staff-list")
<!-- 開啟注解配置權限 --> <bean id="defaultAdvisorAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> <!-- 必須使用cglib方式為 Action對象創建代理對象 --> <property name="proxyTargetClass" value="true"/> </bean> <!-- 配置shiro框架提供的切面類,用於創建代理對象 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"/>
// 配置全局異常處理器 <!-- 需要進行權限控制的頁面訪問 --> <global-results> <result name="login">/login.jsp</result> <result name="unauthorized">/unauthorized.jsp</result> </global-results> <!-- 全局異常處理 --> <global-exception-mappings> <exception-mapping result="unauthorized" exception="org.apache.shiro.authz.UnauthorizedException"></exception-mapping> </global-exception-mappings>
Shiro提供的控制權限的方式
-
URL權限攔截控制
-
方法注解權限控制
-
頁面標簽權限控制(當有對應的權限就顯示對應的頁面元素,沒有權限則不顯示), 需要在對應的頁面引入Shiro的標簽庫
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
,可以用在HTMl也可以用在JS中。 -
代碼級別權限控制(在方法內添加代碼)
Shiro整合ehcache緩存權限數據
如果訪問一個頁面就執行一次授權,就會訪問數據庫,浪費資源,所以我們可以使用ehcache來進行緩存權限,只要登錄時進行一次授權,后面無需再次授權,直接使用緩存。
shiro自動整合ehcache,只需要簡單配置就能使用。
- 在根目錄下建立
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<!--
內存中最多可以存儲多少個數據
是否永久有效
空閑時間
存活時間
內存空間不夠是否存儲到磁盤
磁盤最大存儲個數
服務器重啟,磁盤數據是否需要
線程
淘汰策略(最近最少使用)
-->
</ehcache>
- 在spring配置文件中配置緩存管理器對象,並注入給安全管理器對象
<!-- 注冊ehcache -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
</bean>
- 將ehcache注入到shiro的配置管理器,shiro會自動使用緩存管理,在原來的管理器中添加
<property name="cacheManager" ref="cacheManager"/>
一條語句即可。
<!-- 注冊安全管理器對象 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="bosRealm"/>
<!-- 將ehcache注入shiro -->
<property name="cacheManager" ref="cacheManager"/>
</bean>