解決授權(quán)頻繁查詢數(shù)據(jù)庫問題
緩存流程
shiro 中提供了認(rèn)證信息和授權(quán)信息的緩存.
注意: shiro 默認(rèn)關(guān)閉認(rèn)證信息緩存, 但是對于授權(quán)信息的緩存默認(rèn)是開啟的.
用戶認(rèn)證通過.
該用戶第一次授權(quán): 調(diào)用 realm 查詢數(shù)據(jù)庫.
該用戶第二次授權(quán): 不調(diào)用 realm 查詢數(shù)據(jù)庫, 直接從緩存中取出授權(quán)信息(權(quán)限標(biāo)識符).
使用 ehcache
在 spring-shiro.xim 中配置 cacheManager
<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm" />
<!-- 注入緩存管理器 -->
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 緩存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
</bean>
創(chuàng)建shiro-ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!--diskStore:緩存數(shù)據(jù)持久化的目錄 地址 -->
<diskStore path="F:\develop\ehcache" />
<defaultCache
<!-- 緩存最大個數(shù) -->
maxElementsInMemory="1000"
<!-- 硬盤最大緩存?zhèn)€數(shù) -->
maxElementsOnDisk="10000000"
<!-- 對象是否永久有效,一但設(shè)置了,timeout將不起作用 -->
eternal="false"
<!-- 當(dāng)內(nèi)存中對象數(shù)量達(dá)到maxElementsInMemory時,Ehcache將會對象寫到磁盤中 -->
overflowToDisk="false"
<!-- 是否緩存虛擬機(jī)重啟期數(shù)據(jù) -->
diskPersistent="false"
<!--
設(shè)置對象在失效前的允許閑置時間(單位:秒)。
僅當(dāng)eternal=false對象不是永久有效時使用,可選屬性,默認(rèn)值是0,也就是可閑置時間無窮大
-->
timeToIdleSeconds="120"
<!--
設(shè)置對象在失效前允許存活時間(單位:秒)。
最大時間介于創(chuàng)建時間和失效時間之間。僅當(dāng)eternal=false對象不是永久有效時使用,默認(rèn)是0.,也就是對象存活時間無窮大。
-->
timeToLiveSeconds="120"
<!-- 磁盤失效線程運行時間間隔,默認(rèn)是120秒。 -->
diskExpiryThreadIntervalSeconds="120"
<!--
當(dāng)達(dá)到maxElementsInMemory限制時,Ehcache將會根據(jù)指定的策略去清理內(nèi)存。默認(rèn)策略是LRU(最近最少使用)
你可以設(shè)置為FIFO(先進(jìn)先出)或是LFU(較少使用)
-->
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
緩存清空
如果用戶正常退出, 緩存自動清空.
如果用戶非正常退出, 緩存自動清空.
如果我們修改了權(quán)限, 而且用戶不退出系統(tǒng), 修改的權(quán)限無法立即生效.
那么如何在修改了權(quán)限之后立即生效呢?
實現(xiàn)思路: 在權(quán)限修改后調(diào)用 realm 的 clearCached() 方法進(jìn)行清除緩存.
//清除緩存
public void clearCached() {
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}
驗證碼
自定義FormAuthenticationFilter
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
//原FormAuthenticationFilter的認(rèn)證方法
@Override
protected boolean onAccessDenied(ServletRequest request,
ServletResponse response) throws Exception {
//在這里進(jìn)行驗證碼的校驗
//從session獲取正確驗證碼
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpSession session =httpServletRequest.getSession();
//取出session的驗證碼(正確的驗證碼)
String validateCode = (String) session.getAttribute("validateCode");
//取出頁面的驗證碼
//輸入的驗證和session中的驗證進(jìn)行對比
String randomcode = httpServletRequest.getParameter("randomcode");
if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
//如果校驗失敗,將驗證碼錯誤失敗信息,通過shiroLoginFailure設(shè)置到request中
httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
//拒絕訪問,不再校驗賬號和密碼
return true;
}
return super.onAccessDenied(request, response);
}
}
** 配置自定FormAuthenticationFilter **
<!-- 自定義form認(rèn)證過慮器 -->
<!-- 基于Form表單的身份驗證過濾器,不配置將也會注冊此過慮器,表單中的用戶賬號、密碼及l(fā)oginurl將采用默認(rèn)值,建議配置 -->
<bean id="formAuthenticationFilter"
class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter ">
<!-- 表單中賬號的input名稱 -->
<property name="usernameParam" value="username" />
<!-- 表單中密碼的input名稱 -->
<property name="passwordParam" value="password" />
</bean>
<!-- 自定義filter配置 -->
<property name="filters">
<map>
<!-- 將自定義 的FormAuthenticationFilter注入shiroFilter中-->
<entry key="authc" value-ref="formAuthenticationFilter" />
</map>
</property>