1. 程式人生 > >shiro框架基礎

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>