Apache Shiro是Java的一個安全框架,官網爲shiro.apache.org,主要場景爲控制登錄,判斷用戶是否有訪問某個功能的權限等等。css
認證java
受權web
會話管理spring
加密數據庫
引入Shiro對應的jar包,下面給出Mavenapache
<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的認證功能和受權功能的實現,代碼比較多。安全
Application Code:應用程序代碼, 即登陸方法(登陸方法不是直接查詢數據庫,而是調用Shiro框架提供的接口來實現)服務器
Subject:框架提供的接口,表明當前用戶對象app
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>
URL權限攔截控制
方法註解權限控制
頁面標籤權限控制(當有對應的權限就顯示對應的頁面元素,沒有權限則不顯示), 須要在對應的頁面引入Shiro的標籤庫
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
,能夠用在HTMl也能夠用在JS中。
代碼級別權限控制(在方法內添加代碼)
若是訪問一個頁面就執行一次受權,就會訪問數據庫,浪費資源,因此咱們可使用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>
<!-- 註冊ehcache --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache.xml" /> </bean>
<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>