天天看點

Shiro架構:緩存、session會話、自定義FormAuthenticationFilter、RemenberMe

上篇的位址:https://blog.csdn.net/a745233700/article/details/81350191

一、Shiro緩存--cacheManager:

針對上一篇授權的時候頻繁查詢資料庫的問題,可以使用shiro緩存來解決。

1、緩存流程:

(1)shiro中提供了對認證資訊和授權資訊的緩存。shiro預設是關閉認證資訊緩存的,對于授權資訊的緩存shiro預設開啟的。我們主要研究授權資訊緩存,因為授權的資料量大。

(2)流程:使用者認證通過之後,該使用者第一次授權;調用realm查詢資料庫。該使用者第二次授權,不調用realm查詢資料庫,直接從緩存中取出授權資訊(權限辨別符)。

2、配置shiro緩存:

(1)導入依賴:

<dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-ehcache</artifactId>
	<version>1.2.3</version>
</dependency>
           

(2)配合cacheManager:

<!-- securityManager安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="customRealm"></property>
		<!-- 注入緩存管理器 -->
		<property name="cacheManager" ref="cacheManager"></property>
	</bean>
	
	<!-- 緩存管理器 -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"></property>
	</bean>
           

(3)編寫緩存相關資訊檔案:shiro-ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<!--diskStore:緩存資料持久化的目錄 位址  -->
	<diskStore path="D:\test\shiro\ehcache" />
	<defaultCache 
		maxElementsInMemory="1000" 
		maxElementsOnDisk="10000000"
		eternal="false" 
		overflowToDisk="false" 
		diskPersistent="false"
		timeToIdleSeconds="120"
		timeToLiveSeconds="120" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
</ehcache>
           

至此,Shiro的緩存就配置好了。

3、緩存清空:

(1)如果使用者正常退出,緩存自動清空;如果使用者非正常退出,緩存也自動清空。

如果修改了使用者的權限,而使用者不退出系統,修改的權限無法立即生效。

(2)手動進行程式設計的實作:

在權限修改後,調用realm的clearCache方法清除緩存。

下邊的代碼正常開發時要放在service中調用。

在service中,權限修改後調用realm的方法。

在realm中定義clearCached方法:

//清除緩存代碼
    public void clearCached() {
		PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
		super.clearCache(principals);
	}
           

(3)測試清除緩存的controller方法:

@Controller
public class ClearShiroCacheTest {

	//注入realm
	@Autowired
	private CustomRealm customRealm;
	
	@RequestMapping("/clearShiroCache")
	public String clearShiroCache(){
		//清除緩存,在正常開發中要在service
		customRealm.clearCached();
		
		return "success";
	}
}
           

4、shiro緩存說明:

(1)CacheManagerAware接口:

Shiro内部相關元件(DefaultSecurityManager)會自動檢測相應的對象(如Realm)是否實作了CacheManagerAware并自動注入了CacheManager。

(2)Realm緩存:Shiro提供了CachingRealm,其實作了CacheManagerAware接口,提供了一些基礎實作;并且,AuthenticationRealm和AuthorizingRealm也分别提供了AuthenticationInfo和AutuorizationInfo資訊的緩存。

二、Shiro會話--sessionManager:

SSM和Shiro整合後,使用shiro的session管理,shiro提供sessionDao操作會話資料。

1、Shiro提供了完整的企業級會話管理功能,不依賴于底層容器(如web容器tomcat),不管JavaSE還是JavaEE環境都可以使用,提供了會話管理、會話事件監聽、會話存儲/持久化、容器無關的叢集、失效/過期支援、對Web 的透明支援、SSO 單點登入的支援等特性。

2、配置sessionManager:

<!-- securityManager安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="customRealm"></property>
		<!-- 注入緩存管理器 -->
		<property name="cacheManager" ref="cacheManager"></property>
		<!-- 注入session管理器 -->
		<property name="sessionManager" ref="sessionManager"></property>
	</bean>
	
	<!-- 會話管理器 -->
	<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
		<!-- session的失效時間,機關毫秒 -->
		<property name="globalSessionTimeout" value="600000"></property>
		<!-- 删除失效的session -->
		<property name="deleteInvalidSessions" value="true"></property>
	</bean>
           

3、會話相關的API:

(1)Subject.getSession():即可擷取會話;其等價于Subject.getSession(true),即如果目前沒有建立Session 對象會建立一個;Subject.getSession(false),如果目前沒有建立Session 則傳回null。

(2)session.getId():擷取目前會話的唯一辨別。

(3)session.getHost():擷取目前Subject的主機位址

(4)session.getTimeout() & session.setTimeout(毫秒):擷取/設定目前Session的過期時間。

(5)session.getStartTimestamp() & session.getLastAccessTime():擷取會話的啟動時間及最後通路時間;如果是JavaSE應用需要自己定期調用session.touch() 去更新最後通路時間;如果是Web 應用,每次進入ShiroFilter都會自動調用session.touch() 來更新最後通路時間。

(6)session.touch() & session.stop():更新會話最後通路時間及銷毀會話;當Subject.logout()時會自動調用stop 方法來銷毀會話。如果在web中,調用HttpSession. invalidate() 也會自動調用ShiroSession.stop方法進行銷毀Shiro的會話。

(7)session.setAttribute(key, val) & session.getAttribute(key) & session.removeAttribute(key):設定/擷取/删除會話屬性;在整個會話範圍内都可以對這些屬性進行操作。

三、自定義FormAuthenticationFilter:

1、需求:使用自定義的FormAuthenticationFilter實作驗證碼功能。

(1)shiro使用FormAuthenticationFilter進行表單認證,驗證校驗的功能應該加在FormAuthenticationFilter中,在認證之前進行驗證碼校驗。

(2)需要寫FormAuthenticationFilter的子類,繼承FormAuthenticationFilter,改寫它的認證方法,在認證之前進行驗證碼校驗。

2、自定義FormAuthenticationFilter:

/**
 * Description:自定義FormAuthenticationFilter,認證之前實作 驗證碼校驗 
 */
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {

	//原FormAuthenticationFilter的認證方法
	@Override
	protected boolean onAccessDenied(ServletRequest request,
			ServletResponse response) throws Exception {
		//在這裡進行驗證碼的校驗
		//從session擷取正确驗證碼
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
		HttpSession session =httpServletRequest.getSession();
		//取出session的驗證碼(正确的驗證碼)
		String validateCode = (String) session.getAttribute("validateCode");
		
		//取出頁面的驗證碼
		//輸入的驗證和session中的驗證進行對比 
		String randomcode = httpServletRequest.getParameter("randomcode");
		if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
			//如果校驗失敗,将驗證碼錯誤失敗資訊,通過shiroLoginFailure設定到request中
			httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
			//拒絕通路,不再校驗賬号和密碼 
			return true; 
		}
		return super.onAccessDenied(request, response);
	}
}
           

3、配置自定義FormAuthenticationFilter:

<!-- 自定義form認證過濾器 -->
	<!-- 基于form表單的身份驗證過濾器,不配置也會注冊此過濾器,不過表單中的使用者賬号、密碼和loginUrl将采用預設值,建議配置 -->
	<bean id="formAuthenticationFilter" class="com.zwp.shiro.CustomFormAuthenticationFilter">
		<!-- 表單中賬戶的input名稱 -->
		<property name="usernameParam" value="username"></property>
		<!-- 表單中密碼的input名稱 -->
		<property name="passwordParam" value="password"></property>
	</bean>
           
Shiro架構:緩存、session會話、自定義FormAuthenticationFilter、RemenberMe

4、在登陸頁面添加驗證碼:

Shiro架構:緩存、session會話、自定義FormAuthenticationFilter、RemenberMe

5、在filter配置匿名通路驗證碼的jsp:

Shiro架構:緩存、session會話、自定義FormAuthenticationFilter、RemenberMe

至此,使用自定義FormAuthenticationFilter實作驗證碼功能就完成了。

四、記住我--RememberMe:

1、概述:Shiro提供了記住我(RememberMe)的功能,比如通路如淘寶等一些網站時,關閉了浏覽器,下次再打開時還是能記住你是誰,下次通路時無需再登入即可通路,基本流程如下:

(1)首先在登入頁面選中RememberMe然後登入成功;如果是浏覽器登入,一般會把RememberMe的Cookie 寫到用戶端并儲存下來;

(2)關閉浏覽器再重新打開;會發現浏覽器還是記住你的;

(3)通路一般的網頁伺服器端還是知道你是誰,且能正常通路;

(4)但是比如我們通路淘寶時,如果要檢視我的訂單或進行支付時,此時還是需要再進行身份認證的,以確定目前使用者還是你。

2、認證和記住我:

(1)subject.isAuthenticated() 表示使用者進行了身份驗證登入的,即使有Subject.login進行了登入;

(2)subject.isRemembered():表示使用者是通過記住我登入的,此時可能并不是真正的你(如你的朋友使用你的電腦,或者你的cookie 被竊取)在通路的。

(3)兩者二選一,即subject.isAuthenticated()==true,則subject.isRemembered()==false;反之一樣。

3、實作:

使用者登陸選擇“記住我",本次登陸成功會向cookie寫身份資訊,下次登陸從cookie中取出身份資訊實作自動登陸。

(1)使用者身份實作java.io.Seriializable接口:

向cookie記錄身份資訊,需要使用者身份資訊對象實作序列化接口,如下:

/**
 * 使用者身份資訊,存入session,由于tomcat将session序列化在本地硬碟上,是以使用Serializable接口
 */
public class ActiveUser implements java.io.Serializable {
	private String userid;//使用者id(主鍵)
	private String usercode;// 使用者賬号
	private String username;// 使用者名稱

	private List<SysPermission> menus;// 菜單
	private List<SysPermission> permissions;// 權限
	
	//下面省略set和get方法
}
           

(2)配置rememberMeManager:

<!-- securityManager安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="customRealm"></property>
		<!-- 注入緩存管理器 -->
		<property name="cacheManager" ref="cacheManager"></property>
		<!-- 注入session管理器 -->
		<property name="sessionManager" ref="sessionManager"></property>
		<!-- 記住我,注入 -->
		<property name="rememberMeManager" ref="rememberMeManager"></property>
	</bean>
	
	<!-- rememberMeManager管理器,取出cookie生成使用者資訊 -->
	<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
		<property name="cookie" ref="rememberMeCookie"></property>
	</bean>
	
	<!-- 記住我cookie -->
	<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
		<!-- remenberMe是cookie的名字 -->
		<constructor-arg value="rememberMe"></constructor-arg>
		<!-- remenberMe的cookie的生效時間是30天 -->
		<property name="maxAge" value="2592000"></property>
	</bean>
           

(3)登陸頁面:

Shiro架構:緩存、session會話、自定義FormAuthenticationFilter、RemenberMe

(4)配置rememberMe的名稱:

Shiro架構:緩存、session會話、自定義FormAuthenticationFilter、RemenberMe

(5)測試:

勾選“記住我”登陸後,檢視cookie是否有rememberMe:

Shiro架構:緩存、session會話、自定義FormAuthenticationFilter、RemenberMe

(6)使用UserFilter:

如果設定記住我,下次通路某些url時可以不用登陸。将記住我即可通路的位址配置讓UserFilter攔截。

Shiro架構:緩存、session會話、自定義FormAuthenticationFilter、RemenberMe

至此,Shiro的rememberMe就配置完成了。

(7)remenberMe使用建議:

①通路一般網頁:如個人在首頁之類的,我們使用user 攔截器即可,user 攔截器隻要使用者登入(isRemembered() || isAuthenticated())過即可通路成功;

②通路特殊網頁:如我的訂單,送出訂單頁面,我們使用authc攔截器即可,authc攔截器會判斷使用者是否是通過Subject.login(isAuthenticated()==true)登入的,如果是才放行,否則會跳轉到登入頁面叫你重新登入。

繼續閱讀