shiro框架基礎
1.shiro簡介
Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。
官網:shiro.apache.org
shiro作用:驗證使用者、對使用者執行訪問控制、可以使用多個數據庫、單點登入功能(SSO)
shiro框架認證流程/原理(劃重點)
Application Code:應用程式程式碼,由開發人員負責開發的(action)
Subject:框架提供的介面,代表當前使用者物件(當前的登陸物件)
SecurityManager:框架提供的介面,代表安全管理器物件(核心物件)
Realm:可以開發人員編寫,框架也提供一些,類似於DAO,用於訪問許可權資料(自己書寫的校驗物件)
2.shiro的使用
1>maven引入shiro依賴
<!-- 引入shiro框架的依賴 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.2</version>
</dependency>
2>在web.xml中配置spring框架提供的用於整合shiro框架的過濾器(位置要在struts2的過濾器前面)
<!-- 配置spring框架提供的用於整合shiro框架的過濾器 --> <filter> <filter-name></filter-name><!-- shiroFilter是指定名稱,要在applicationContext.xml中配置 --> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3>在spring中配置bean,id為shiroFilter
<!-- 配置shiro框架過濾工廠bean -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 注入安全管理器物件 -->
<property name="securityManager" ref="securityManager"></property>
<!-- 注入相關頁面訪問URL -->
<property name="loginUrl" value="/login.jsp"/><!-- 登陸頁面 -->
<property name="successUrl" value="/index.jsp"/><!-- 首頁 -->
<property name="unauthorizedUrl" value="/unauthorized.jsp"/><!-- 沒有許可權時提示頁面 -->
<!--shiro許可權控制方式1:注入URL攔截規則 -->
<property name="filterChainDefinitions">
<value>
/css/** = anon
/js/** = anon
/images/** = anon
/validatecode.jsp* = anon
/login.jsp = anon
/userAction_login.action = anon
/page_base_staff.action = perms["staff-list"]<!--訪問此路徑時必須要有“staff-list許可權” -->
/* = authc
</value>
</property>
</bean>
<!-- 註冊安全管理物件 -->
<bean name="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="bosRealm"></property>
</bean>
<!-- 註冊realm -->
<bean name="bosRealm" class="com.imwj.bos.realm.BOSRealm"></bean>
框架提供的過濾器
anon:無需認證、authc:認證過、perms:需要指定許可權
4>UserAction中的login方法,使用shiro提供的方式
public String login() throws Exception {
//得到session域中的驗證碼
String validatecode = (String) ServletActionContext.getRequest().getSession().getAttribute("key");
//先判斷驗證碼是否輸入正確
if(StringUtils.isNotBlank(checkcode) && validatecode.equals(checkcode)){
//輸入的驗證碼正確
//使用shiro框架提供的方式進行認證
Subject subject = SecurityUtils.getSubject();//獲得當前登陸的使用者物件,現在的狀態未“未認證”
//使用者名稱密碼令牌
AuthenticationToken token = new UsernamePasswordToken(model.getUsername(), MD5Utils.md5(model.getPassword()));
try {
subject.login(token)//此處會跳轉到我們所建立的realm
} catch (UnknownAccountException e) {
this.addActionError("使用者名稱不存在");
return LOGIN;
} catch (IncorrectCredentialsException e) {
this.addActionError("密碼輸入錯誤");
return LOGIN;
}
User user = (User) subject.getPrincipal();
ServletActionContext.getRequest().getSession().setAttribute("loginUser", user);
return HOME;
}else{
//驗證碼輸入不正確
this.addActionError("驗證碼輸入錯誤");
return LOGIN;
}
}
5>自定義realm,並注入給安全管理器
public class BOSRealm extends AuthorizingRealm{
@Autowired
private IUserDao userDao;
//認證方法
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken mytoken = (UsernamePasswordToken) token;
String username = mytoken.getUsername();
//根據使用者名稱查詢資料庫中的密碼
User user = userDao.findUsernameByUserName(username);
if(user == null){
//使用者名稱不存在
return null;
}
//如果能夠查詢到,再由框架比對資料中查詢到的密碼和頁面提交的密碼是否一致
AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
return info;
}
//授權方法
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("staff-list");//授予當前使用者“staff-list”許可權
//TODO 後期需要修改為根據當前登入使用者查詢資料庫,獲取實際對應的許可權
return info;
}
}
3.shiro框架提供的四種許可權控制方式
URL攔截許可權控制(基於過濾器實現)、方法註解許可權控制(基於代理技術實現)、頁面標籤許可權控制(標籤技術實現)、程式碼級別許可權控制(作了解,基於代理技術)
1>URL攔截許可權控制,前面已經實現
<value>
/css/** = anon
/js/** = anon
/images/** = anon
/validatecode.jsp* = anon
/login.jsp = anon
/userAction_login.action = anon
/page_base_staff.action = perms["staff-list"]<!--訪問此路徑時必須要有“staff-list許可權” -->
/* = authc
</value>
2>方法註解控制@RequiresPermissions("staff-delete")
<!-- 開啟shiro框架註解支援 -->
<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"/>
<!-- action中 -->
@RequiresPermissions("staff-delete")//執行這個方法需要當前的登陸使用者擁有staff-delete的許可權
public String deleteBatch(){
許可權不足時會丟擲異常,所以我們做一個全域性異常處理
<!-- 全域性結果集配置 -->
<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"/>
</global-exception-mappings>
3>使用shiro提供的頁面標籤進行許可權控制:<shiro:hasPermission name="staff-delete">
在jsp中引入shiro的標籤庫
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
將需要許可權控制的按鈕(比如刪除)放置在shiro標籤中:js程式碼也可以
<shiro:hasPermission name="staff-delete"> </shiro:hasPermission>
4>總結:使用shiro框架進行許可權控制時,使用前三種許可權控制混合的方式來達到許可權控制的目的
4.shiro中的授權方法(劃重點)
//授權方法
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//獲取當前登入使用者物件
User user = (User) SecurityUtils.getSubject().getPrincipal();
//User user2 = (User) principals.getPrimaryPrincipal();
// 根據當前登入使用者查詢資料庫,獲取實際對應的許可權
List<Function> list = null;
if(user.getUsername().equals("admin")){//超級管理員內建使用者,查詢所有許可權資料
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Function.class);
list = functionDao.findByCriteria(detachedCriteria);
}else{//普通使用者,查詢對應的許可權資料
list = functionDao.findFunctionListByUserId(user.getId());
}
for (Function function : list) {
info.addStringPermission(function.getCode());
}
return info;
}
獲取當前登陸使用者物件:User user = (User) SecurityUtils.getSubject().getPrincipal();
5.ehcache快取許可權資料(劃重點)
ehcache是專門快取外掛,可以快取Java物件,提高系統性能。
優點:ehcache是shiro的快取外掛,匯入ehcache後在授權時只需要授權一次,而不需要重複授權
1>在pom.xml檔案引入ehcache的依賴(匯入jar包)
<!-- 引入ehcache的依賴 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.6</version>
</dependency>
2>編寫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="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
3>在spring配置檔案中配置快取管理器物件,並注入給安全管理物件
<!-- 註冊安全管理器物件 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="bosRealm"/>
<!-- 注入快取管理器 -->
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 註冊快取管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<!-- 注入ehcache的配置檔案 -->
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>