springboot使用shiro-整合redis作為緩存的操作
說在前面
本來的整合過程是順著博客的順序來的,越往下,集成的越多,由于之前是使用ehcache緩存,現(xiàn)在改為redis,限制登錄人數(shù) 以及 限制登錄次數(shù)等 都需要改動(dòng),本篇為了簡單,目前先將這兩個(gè)功能下線,配置暫時(shí)是注銷的,原類保存,在下篇博客中改。
還有之前是使用SessionListener監(jiān)聽session創(chuàng)建來統(tǒng)計(jì)在線人數(shù),在本篇中也將改為統(tǒng)計(jì)redis中的key數(shù)目。
如果是單機(jī),使用ehcache是最快的,項(xiàng)目一般都不是單節(jié)點(diǎn),為了方便之后使用sso單點(diǎn)登錄,以及多節(jié)點(diǎn)部署,所以使用shiro整合redis。
這里有一個(gè)開源項(xiàng)目,git地址為: https://github.com/alexxiyang/shiro-redis.git 在此感謝作者無私奉獻(xiàn)。
整合過程
shiro用redis實(shí)現(xiàn)緩存需要重寫cache、cacheManager、SessionDAO和初始化redis配置。
pom添加依賴
<!-- 整合shiro框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!-- shiro-thymeleaf 2.0.0-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>1.2.1</version>
</dependency>
<!-- shiro-redis -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.1.0</version>
</dependency>
ShiroConfig.java
package com.springboot.test.shiro.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.springboot.test.shiro.config.shiro.*;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Properties;
/**
* @author: wangsaichao
* @date: 2018/5/10
* @description: Shiro配置
*/
@Configuration
public class ShiroConfig {
/**
* ShiroFilterFactoryBean 處理攔截資源文件問題。
* 注意:初始化ShiroFilterFactoryBean的時(shí)候需要注入:SecurityManager
* Web應(yīng)用中,Shiro可控制的Web請(qǐng)求必須經(jīng)過Shiro主過濾器的攔截
* @param securityManager
* @return
*/
@Bean(name = "shirFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//必須設(shè)置 SecurityManager,Shiro的核心安全接口
shiroFilterFactoryBean.setSecurityManager(securityManager);
//這里的/login是后臺(tái)的接口名,非頁面,如果不設(shè)置默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.jsp"頁面
shiroFilterFactoryBean.setLoginUrl("/");
//這里的/index是后臺(tái)的接口名,非頁面,登錄成功后要跳轉(zhuǎn)的鏈接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授權(quán)界面,該配置無效,并不會(huì)進(jìn)行頁面跳轉(zhuǎn)
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
//自定義攔截器限制并發(fā)人數(shù),參考博客:
LinkedHashMap<String, Filter> filtersMap = new LinkedHashMap<>();
//限制同一帳號(hào)同時(shí)在線的個(gè)數(shù)
//filtersMap.put("kickout", kickoutSessionControlFilter());
//統(tǒng)計(jì)登錄人數(shù)
shiroFilterFactoryBean.setFilters(filtersMap);
// 配置訪問權(quán)限 必須是LinkedHashMap,因?yàn)樗仨毐WC有序
// 過濾鏈定義,從上向下順序執(zhí)行,一般將 /**放在最為下邊 --> : 這是一個(gè)坑,一不小心代碼就不好使了
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//配置不登錄可以訪問的資源,anon 表示資源都可以匿名訪問
//配置記住我或認(rèn)證通過可以訪問的地址
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
//解鎖用戶專用 測(cè)試用的
filterChainDefinitionMap.put("/unlockAccount","anon");
filterChainDefinitionMap.put("/Captcha.jpg","anon");
//logout是shiro提供的過濾器
filterChainDefinitionMap.put("/logout", "logout");
//此時(shí)訪問/user/delete需要delete權(quán)限,在自定義Realm中為用戶授權(quán)。
//filterChainDefinitionMap.put("/user/delete", "perms[\"user:delete\"]");
//其他資源都需要認(rèn)證 authc 表示需要認(rèn)證才能進(jìn)行訪問 user表示配置記住我或認(rèn)證通過可以訪問的地址
//如果開啟限制同一賬號(hào)登錄,改為 .put("/**", "kickout,user");
filterChainDefinitionMap.put("/**", "user");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 配置核心安全事務(wù)管理器
* @return
*/
@Bean(name="securityManager")
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//設(shè)置自定義realm.
securityManager.setRealm(shiroRealm());
//配置記住我
securityManager.setRememberMeManager(rememberMeManager());
//配置redis緩存
securityManager.setCacheManager(cacheManager());
//配置自定義session管理,使用redis
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* 配置Shiro生命周期處理器
* @return
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 身份認(rèn)證realm; (這個(gè)需要自己寫,賬號(hào)密碼校驗(yàn);權(quán)限等)
* @return
*/
@Bean
public ShiroRealm shiroRealm(){
ShiroRealm shiroRealm = new ShiroRealm();
shiroRealm.setCachingEnabled(true);
//啟用身份驗(yàn)證緩存,即緩存AuthenticationInfo信息,默認(rèn)false
shiroRealm.setAuthenticationCachingEnabled(true);
//緩存AuthenticationInfo信息的緩存名稱 在ehcache-shiro.xml中有對(duì)應(yīng)緩存的配置
shiroRealm.setAuthenticationCacheName("authenticationCache");
//啟用授權(quán)緩存,即緩存AuthorizationInfo信息,默認(rèn)false
shiroRealm.setAuthorizationCachingEnabled(true);
//緩存AuthorizationInfo信息的緩存名稱 在ehcache-shiro.xml中有對(duì)應(yīng)緩存的配置
shiroRealm.setAuthorizationCacheName("authorizationCache");
//配置自定義密碼比較器
//shiroRealm.setCredentialsMatcher(retryLimitHashedCredentialsMatcher());
return shiroRealm;
}
/**
* 必須(thymeleaf頁面使用shiro標(biāo)簽控制按鈕是否顯示)
* 未引入thymeleaf包,Caused by: java.lang.ClassNotFoundException: org.thymeleaf.dialect.AbstractProcessorDialect
* @return
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
/**
* 開啟shiro 注解模式
* 可以在controller中的方法前加上注解
* 如 @RequiresPermissions("userInfo:add")
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 解決: 無權(quán)限頁面不跳轉(zhuǎn) shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized") 無效
* shiro的源代碼ShiroFilterFactoryBean.Java定義的filter必須滿足filter instanceof AuthorizationFilter,
* 只有perms,roles,ssl,rest,port才是屬于AuthorizationFilter,而anon,authcBasic,auchc,user是AuthenticationFilter,
* 所以u(píng)nauthorizedUrl設(shè)置后頁面不跳轉(zhuǎn) Shiro注解模式下,登錄失敗與沒有權(quán)限都是通過拋出異常。
* 并且默認(rèn)并沒有去處理或者捕獲這些異常。在SpringMVC下需要配置捕獲相應(yīng)異常來通知用戶信息
* @return
*/
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver simpleMappingExceptionResolver=new SimpleMappingExceptionResolver();
Properties properties=new Properties();
//這里的 /unauthorized 是頁面,不是訪問的路徑
properties.setProperty("org.apache.shiro.authz.UnauthorizedException","/unauthorized");
properties.setProperty("org.apache.shiro.authz.UnauthenticatedException","/unauthorized");
simpleMappingExceptionResolver.setExceptionMappings(properties);
return simpleMappingExceptionResolver;
}
/**
* 解決spring-boot Whitelabel Error Page
* @return
*/
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/unauthorized.html");
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");
ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html");
container.addErrorPages(error401Page, error404Page, error500Page);
}
};
}
/**
* cookie對(duì)象;會(huì)話Cookie模板 ,默認(rèn)為: JSESSIONID 問題: 與SERVLET容器名沖突,重新定義為sid或rememberMe,自定義
* @return
*/
@Bean
public SimpleCookie rememberMeCookie(){
//這個(gè)參數(shù)是cookie的名稱,對(duì)應(yīng)前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//setcookie的httponly屬性如果設(shè)為true的話,會(huì)增加對(duì)xss防護(hù)的安全系數(shù)。它有以下特點(diǎn):
//setcookie()的第七個(gè)參數(shù)
//設(shè)為true后,只能通過http訪問,javascript無法訪問
//防止xss讀取cookie
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//<!-- 記住我cookie生效時(shí)間30天 ,單位秒;-->
simpleCookie.setMaxAge(2592000);
return simpleCookie;
}
/**
* cookie管理對(duì)象;記住我功能,rememberMe管理器
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密鑰 建議每個(gè)項(xiàng)目都不一樣 默認(rèn)AES算法 密鑰長度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
/**
* FormAuthenticationFilter 過濾器 過濾記住我
* @return
*/
@Bean
public FormAuthenticationFilter formAuthenticationFilter(){
FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
//對(duì)應(yīng)前端的checkbox的name = rememberMe
formAuthenticationFilter.setRememberMeParam("rememberMe");
return formAuthenticationFilter;
}
/**
* shiro緩存管理器;
* 需要添加到securityManager中
* @return
*/
@Bean
public RedisCacheManager cacheManager(){
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
//redis中針對(duì)不同用戶緩存
redisCacheManager.setPrincipalIdFieldName("username");
//用戶權(quán)限信息緩存時(shí)間
redisCacheManager.setExpire(200000);
return redisCacheManager;
}
/**
* 讓某個(gè)實(shí)例的某個(gè)方法的返回值注入為Bean的實(shí)例
* Spring靜態(tài)注入
* @return
*/
@Bean
public MethodInvokingFactoryBean getMethodInvokingFactoryBean(){
MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
factoryBean.setArguments(new Object[]{securityManager()});
return factoryBean;
}
/**
* 配置session監(jiān)聽
* @return
*/
@Bean("sessionListener")
public ShiroSessionListener sessionListener(){
ShiroSessionListener sessionListener = new ShiroSessionListener();
return sessionListener;
}
/**
* 配置會(huì)話ID生成器
* @return
*/
@Bean
public SessionIdGenerator sessionIdGenerator() {
return new JavaUuidSessionIdGenerator();
}
@Bean
public RedisManager redisManager(){
RedisManager redisManager = new RedisManager();
redisManager.setHost("127.0.0.1");
redisManager.setPort(6379);
redisManager.setPassword("123456");
return redisManager;
}
/**
* SessionDAO的作用是為Session提供CRUD并進(jìn)行持久化的一個(gè)shiro組件
* MemorySessionDAO 直接在內(nèi)存中進(jìn)行會(huì)話維護(hù)
* EnterpriseCacheSessionDAO 提供了緩存功能的會(huì)話維護(hù),默認(rèn)情況下使用MapCache實(shí)現(xiàn),內(nèi)部使用ConcurrentHashMap保存緩存的會(huì)話。
* @return
*/
@Bean
public SessionDAO sessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
//session在redis中的保存時(shí)間,最好大于session會(huì)話超時(shí)時(shí)間
redisSessionDAO.setExpire(12000);
return redisSessionDAO;
}
/**
* 配置保存sessionId的cookie
* 注意:這里的cookie 不是上面的記住我 cookie 記住我需要一個(gè)cookie session管理 也需要自己的cookie
* 默認(rèn)為: JSESSIONID 問題: 與SERVLET容器名沖突,重新定義為sid
* @return
*/
@Bean("sessionIdCookie")
public SimpleCookie sessionIdCookie(){
//這個(gè)參數(shù)是cookie的名稱
SimpleCookie simpleCookie = new SimpleCookie("sid");
//setcookie的httponly屬性如果設(shè)為true的話,會(huì)增加對(duì)xss防護(hù)的安全系數(shù)。它有以下特點(diǎn):
//setcookie()的第七個(gè)參數(shù)
//設(shè)為true后,只能通過http訪問,javascript無法訪問
//防止xss讀取cookie
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//maxAge=-1表示瀏覽器關(guān)閉時(shí)失效此Cookie
simpleCookie.setMaxAge(-1);
return simpleCookie;
}
/**
* 配置會(huì)話管理器,設(shè)定會(huì)話超時(shí)及保存
* @return
*/
@Bean("sessionManager")
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
Collection<SessionListener> listeners = new ArrayList<SessionListener>();
//配置監(jiān)聽
listeners.add(sessionListener());
sessionManager.setSessionListeners(listeners);
sessionManager.setSessionIdCookie(sessionIdCookie());
sessionManager.setSessionDAO(sessionDAO());
sessionManager.setCacheManager(cacheManager());
//全局會(huì)話超時(shí)時(shí)間(單位毫秒),默認(rèn)30分鐘 暫時(shí)設(shè)置為10秒鐘 用來測(cè)試
sessionManager.setGlobalSessionTimeout(1800000);
//是否開啟刪除無效的session對(duì)象 默認(rèn)為true
sessionManager.setDeleteInvalidSessions(true);
//是否開啟定時(shí)調(diào)度器進(jìn)行檢測(cè)過期session 默認(rèn)為true
sessionManager.setSessionValidationSchedulerEnabled(true);
//設(shè)置session失效的掃描時(shí)間, 清理用戶直接關(guān)閉瀏覽器造成的孤立會(huì)話 默認(rèn)為 1個(gè)小時(shí)
//設(shè)置該屬性 就不需要設(shè)置 ExecutorServiceSessionValidationScheduler 底層也是默認(rèn)自動(dòng)調(diào)用ExecutorServiceSessionValidationScheduler
//暫時(shí)設(shè)置為 5秒 用來測(cè)試
sessionManager.setSessionValidationInterval(3600000);
//取消url 后面的 JSESSIONID
sessionManager.setSessionIdUrlRewritingEnabled(false);
return sessionManager;
}
/**
* 并發(fā)登錄控制
* @return
*/
// @Bean
// public KickoutSessionControlFilter kickoutSessionControlFilter(){
// KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
// //用于根據(jù)會(huì)話ID,獲取會(huì)話進(jìn)行踢出操作的;
// kickoutSessionControlFilter.setSessionManager(sessionManager());
// //使用cacheManager獲取相應(yīng)的cache來緩存用戶登錄的會(huì)話;用于保存用戶—會(huì)話之間的關(guān)系的;
// kickoutSessionControlFilter.setCacheManager(cacheManager());
// //是否踢出后來登錄的,默認(rèn)是false;即后者登錄的用戶踢出前者登錄的用戶;
// kickoutSessionControlFilter.setKickoutAfter(false);
// //同一個(gè)用戶最大的會(huì)話數(shù),默認(rèn)1;比如2的意思是同一個(gè)用戶允許最多同時(shí)兩個(gè)人登錄;
// kickoutSessionControlFilter.setMaxSession(1);
// //被踢出后重定向到的地址;
// kickoutSessionControlFilter.setKickoutUrl("/login?kickout=1");
// return kickoutSessionControlFilter;
// }
/**
* 配置密碼比較器
* @return
*/
// @Bean("credentialsMatcher")
// public RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher(){
// RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(cacheManager());
//
// //如果密碼加密,可以打開下面配置
// //加密算法的名稱
// //retryLimitHashedCredentialsMatcher.setHashAlgorithmName("MD5");
// //配置加密的次數(shù)
// //retryLimitHashedCredentialsMatcher.setHashIterations(1024);
// //是否存儲(chǔ)為16進(jìn)制
// //retryLimitHashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
//
// return retryLimitHashedCredentialsMatcher;
// }
}
ShiroRealm.java
package com.springboot.test.shiro.config.shiro;
import com.springboot.test.shiro.modules.user.dao.PermissionMapper;
import com.springboot.test.shiro.modules.user.dao.RoleMapper;
import com.springboot.test.shiro.modules.user.dao.entity.Permission;
import com.springboot.test.shiro.modules.user.dao.entity.Role;
import com.springboot.test.shiro.modules.user.dao.UserMapper;
import com.springboot.test.shiro.modules.user.dao.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author: wangsaichao
* @date: 2018/5/10
* @description: 在Shiro中,最終是通過Realm來獲取應(yīng)用程序中的用戶、角色及權(quán)限信息的
* 在Realm中會(huì)直接從我們的數(shù)據(jù)源中獲取Shiro需要的驗(yàn)證信息??梢哉f,Realm是專用于安全框架的DAO.
*/
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private PermissionMapper permissionMapper;
/**
* 驗(yàn)證用戶身份
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//獲取用戶名密碼 第一種方式
//String username = (String) authenticationToken.getPrincipal();
//String password = new String((char[]) authenticationToken.getCredentials());
//獲取用戶名 密碼 第二種方式
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String username = usernamePasswordToken.getUsername();
String password = new String(usernamePasswordToken.getPassword());
//從數(shù)據(jù)庫查詢用戶信息
User user = this.userMapper.findByUserName(username);
//可以在這里直接對(duì)用戶名校驗(yàn),或者調(diào)用 CredentialsMatcher 校驗(yàn)
if (user == null) {
throw new UnknownAccountException("用戶名或密碼錯(cuò)誤!");
}
//這里將 密碼對(duì)比 注銷掉,否則 無法鎖定 要將密碼對(duì)比 交給 密碼比較器
//if (!password.equals(user.getPassword())) {
// throw new IncorrectCredentialsException("用戶名或密碼錯(cuò)誤!");
//}
if ("1".equals(user.getState())) {
throw new LockedAccountException("賬號(hào)已被鎖定,請(qǐng)聯(lián)系管理員!");
}
//調(diào)用 CredentialsMatcher 校驗(yàn) 還需要?jiǎng)?chuàng)建一個(gè)類 繼承CredentialsMatcher 如果在上面校驗(yàn)了,這個(gè)就不需要了
//配置自定義權(quán)限登錄器 參考博客:
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
return info;
}
/**
* 授權(quán)用戶權(quán)限
* 授權(quán)的方法是在碰到<shiro:hasPermission name=''></shiro:hasPermission>標(biāo)簽的時(shí)候調(diào)用的
* 它會(huì)去檢測(cè)shiro框架中的權(quán)限(這里的permissions)是否包含有該標(biāo)簽的name值,如果有,里面的內(nèi)容顯示
* 如果沒有,里面的內(nèi)容不予顯示(這就完成了對(duì)于權(quán)限的認(rèn)證.)
*
* shiro的權(quán)限授權(quán)是通過繼承AuthorizingRealm抽象類,重載doGetAuthorizationInfo();
* 當(dāng)訪問到頁面的時(shí)候,鏈接配置了相應(yīng)的權(quán)限或者shiro標(biāo)簽才會(huì)執(zhí)行此方法否則不會(huì)執(zhí)行
* 所以如果只是簡單的身份認(rèn)證沒有權(quán)限的控制的話,那么這個(gè)方法可以不進(jìn)行實(shí)現(xiàn),直接返回null即可。
*
* 在這個(gè)方法中主要是使用類:SimpleAuthorizationInfo 進(jìn)行角色的添加和權(quán)限的添加。
* authorizationInfo.addRole(role.getRole()); authorizationInfo.addStringPermission(p.getPermission());
*
* 當(dāng)然也可以添加set集合:roles是從數(shù)據(jù)庫查詢的當(dāng)前用戶的角色,stringPermissions是從數(shù)據(jù)庫查詢的當(dāng)前用戶對(duì)應(yīng)的權(quán)限
* authorizationInfo.setRoles(roles); authorizationInfo.setStringPermissions(stringPermissions);
*
* 就是說如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "perms[權(quán)限添加]");
* 就說明訪問/add這個(gè)鏈接必須要有“權(quán)限添加”這個(gè)權(quán)限才可以訪問
*
* 如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "roles[100002],perms[權(quán)限添加]");
* 就說明訪問/add這個(gè)鏈接必須要有 "權(quán)限添加" 這個(gè)權(quán)限和具有 "100002" 這個(gè)角色才可以訪問
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("查詢權(quán)限方法調(diào)用了?。?!");
//獲取用戶
User user = (User) SecurityUtils.getSubject().getPrincipal();
//獲取用戶角色
Set<Role> roles =this.roleMapper.findRolesByUserId(user.getUid());
//添加角色
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
for (Role role : roles) {
authorizationInfo.addRole(role.getRole());
}
//獲取用戶權(quán)限
Set<Permission> permissions = this.permissionMapper.findPermissionsByRoleId(roles);
//添加權(quán)限
for (Permission permission:permissions) {
authorizationInfo.addStringPermission(permission.getPermission());
}
return authorizationInfo;
}
/**
* 重寫方法,清除當(dāng)前用戶的的 授權(quán)緩存
* @param principals
*/
@Override
public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
super.clearCachedAuthorizationInfo(principals);
}
/**
* 重寫方法,清除當(dāng)前用戶的 認(rèn)證緩存
* @param principals
*/
@Override
public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
super.clearCachedAuthenticationInfo(principals);
}
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
/**
* 自定義方法:清除所有 授權(quán)緩存
*/
public void clearAllCachedAuthorizationInfo() {
getAuthorizationCache().clear();
}
/**
* 自定義方法:清除所有 認(rèn)證緩存
*/
public void clearAllCachedAuthenticationInfo() {
getAuthenticationCache().clear();
}
/**
* 自定義方法:清除所有的 認(rèn)證緩存 和 授權(quán)緩存
*/
public void clearAllCache() {
clearAllCachedAuthenticationInfo();
clearAllCachedAuthorizationInfo();
}
}
我們可以看一下,在redis中的緩存,如下:

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java中orElse和orElseGet方法區(qū)別小結(jié)
這篇文章主要給大家介紹了關(guān)于java中orElse和orElseGet方法區(qū)別的相關(guān)資料,兩者之間的區(qū)別細(xì)微,但是卻在某些場(chǎng)景下顯的很重要,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10
Gradle構(gòu)建多模塊項(xiàng)目的方法步驟
這篇文章主要介紹了Gradle構(gòu)建多模塊項(xiàng)目的方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
Java后端Cookie實(shí)現(xiàn)(時(shí)間戳)代碼實(shí)例
這篇文章主要介紹了Java后端Cookie實(shí)現(xiàn)(時(shí)間戳)代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12

