关于SpringBoot整合Shiro并入redis缓存
最近做一个小项目加入shiro权限框架, Shiro是Apache下的一个开源项目,提供了认证、授权、加密、会话管理,与spring Security 一样都是做一个权限的安全框架,但是与Spring Security 相比,在于 Shiro 使用了比较简单易懂易于使用的授权方式。
记录一下开发过程。。。
1.开发环境:
JDK:1.8
SpringBoot:2.4.4
2. 加入依赖:
<!--springboot中的redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 使用jedis需要对新版本的redis进行屏蔽<exclusions>--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.8.0</version></dependency><!--shiro权限--><dependency><!--session持久化插件--><groupId>org.crazycake</groupId><artifactId>shiro-redis</artifactId><version>2.4.2.1-RELEASE</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.4.0</version></dependency><dependency><!--spring shiro依赖--><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.1</version></dependency>
3. ShiroConfiguration-Shiro 配置
package com.carshow.data.shior;import java.util.LinkedHashMap;import java.util.Map;import com.carshow.data.common.Common;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;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.web.mgt.DefaultWebSecurityManager;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.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import org.springframework.stereotype.Component;import org.springframework.web.filter.DelegatingFilterProxy;import javax.servlet.Filter;@ConfigurationpublicclassShiroConfiguration{privatestaticfinal Logger logger= LoggerFactory.getLogger(ShiroConfiguration.class);@Value("${spring.redis.host}")private String redisHost;@Value("${spring.redis.port}")privateint redisPort;@Value("${spring.redis.password}")private String redisPassword;@Beanpublic FilterRegistrationBeandelegatingFilterProxy(){
FilterRegistrationBean filterRegistrationBean=newFilterRegistrationBean();
DelegatingFilterProxy proxy=newDelegatingFilterProxy();
proxy.setTargetFilterLifecycle(true);
proxy.setTargetBeanName("shiroFilter");
filterRegistrationBean.setFilter(proxy);return filterRegistrationBean;}@Bean("shiroFilter")public ShiroFilterFactoryBeanshirFilter(SecurityManager securityManager){
logger.info("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean=newShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, Filter> filtersMap=newLinkedHashMap<String, Filter>();
filtersMap.put("kickout",kickoutSessionControlFilter());
shiroFilterFactoryBean.setFilters(filtersMap);
Map<String,String> filterChainDefinitionMap=newLinkedHashMap<String,String>();
filterChainDefinitionMap.put("/logout","logout");
filterChainDefinitionMap.put("/swagger-resources/**","anon");
filterChainDefinitionMap.put("/webjars/**","anon");
filterChainDefinitionMap.put("/doc.html","anon");
filterChainDefinitionMap.put("/v2/**","anon");
filterChainDefinitionMap.put("/swagger-ui.html/**","anon");
filterChainDefinitionMap.put("/auth/kickout","anon");
filterChainDefinitionMap.put(Common.PATH_OUT_URL+"**","anon");//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**","authc,kickout");
shiroFilterFactoryBean.setLoginUrl(Common.PATH_OUT_URL+"user/un_auth");// 未授权界面, 对应LoginController中 unauthorized 请求
shiroFilterFactoryBean.setUnauthorizedUrl(Common.PATH_OUT_URL+"user/unauthorized");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}@Beanpublic SecurityManagersecurityManager(){
DefaultWebSecurityManager securityManager=newDefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());// 自定义缓存实现 使用redis
securityManager.setCacheManager(cacheManager());// 自定义session管理 使用redis
securityManager.setSessionManager(sessionManager());return securityManager;}@Beanpublic MyShiroRealmmyShiroRealm(){
MyShiroRealm myShiroRealm=newMyShiroRealm();return myShiroRealm;}public RedisCacheManagercacheManager(){
RedisCacheManager redisCacheManager=newRedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
redisCacheManager.setKeyPrefix("SPRINGBOOT_CACHE:");return redisCacheManager;}public RedisManagerredisManager(){
logger.info("===============创建RedisManager,连接Redis..URL= "+ redisHost+":"+ redisPort);
RedisManager redisManager=newRedisManager();
redisManager.setHost(redisHost);
redisManager.setPort(redisPort);
redisManager.setTimeout(1800);
redisManager.setPassword(redisPassword);return redisManager;}@Beanpublic RedisSessionDAOredisSessionDAO(){
RedisSessionDAO redisSessionDAO=newRedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
redisSessionDAO.setKeyPrefix("SPRINGBOOT_SESSION:");return redisSessionDAO;}@Beanpublic JavaUuidSessionIdGeneratorsessionIdGenerator(){returnnewJavaUuidSessionIdGenerator();}@Beanpublic DefaultWebSessionManagersessionManager(){
MySessionManager sessionManager=newMySessionManager();
sessionManager.setSessionDAO(redisSessionDAO());return sessionManager;}@Beanpublic KickoutSessionControlFilterkickoutSessionControlFilter(){
KickoutSessionControlFilter kickoutSessionControlFilter=newKickoutSessionControlFilter();
kickoutSessionControlFilter.setCacheManager(cacheManager());
kickoutSessionControlFilter.setSessionManager(sessionManager());
kickoutSessionControlFilter.setKickoutAfter(false);
kickoutSessionControlFilter.setMaxSession(1);
kickoutSessionControlFilter.setKickoutUrl("/auth/kickout");return kickoutSessionControlFilter;}@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreatorgetDefaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=newDefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}@Beanpublic AuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor=newAuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}@Bean(name="lifecycleBeanPostProcessor")publicstatic LifecycleBeanPostProcessorgetLifecycleBeanPostProcessor(){returnnewLifecycleBeanPostProcessor();}}
4.身份校验核心类-MyShiroRealm
package com.carshow.data.shior;import com.carshow.data.mapper.RoleMapper;import com.carshow.data.mapper.UserMapper;import com.carshow.data.model.TPermission;import com.carshow.data.model.TRole;import com.carshow.data.model.TUser;import com.carshow.data.service.IRoleService;import com.carshow.data.service.IUserService;import com.carshow.data.service.impl.UserServiceImpl;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;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.apache.shiro.util.ByteSource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.annotation.Resource;import java.util.List;import java.util.Objects;publicclassMyShiroRealmextendsAuthorizingRealm{privatestaticfinal Logger logger= LoggerFactory.getLogger(MyShiroRealm.class);privatestatic String SALT="mySlalt";@Resourceprivate IUserService userService;@Resourceprivate IRoleService roleService;@Overrideprotected AuthenticationInfodoGetAuthenticationInfo(AuthenticationToken token)throws AuthenticationException{
logger.info("---------------- 执行 Shiro 凭证认证 ----------------------");
String username=(String)token.getPrincipal();
TUser user= userService.selectUserByName(username);if(Objects.isNull(user)){return null;}
logger.info("----->>userInfo="+ user.toString());
SimpleAuthenticationInfo authenticationInfo=newSimpleAuthenticationInfo(
username,
user.getPassword(),
ByteSource.Util.bytes(user.getUsername()+ SALT),getName());return authenticationInfo;}@Overrideprotected AuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principals){
logger.info("开始执行授权操作-->MyShiroRealm.doGetAuthorizationInfo()");
SimpleAuthorizationInfo authorizationInfo=newSimpleAuthorizationInfo();
TUser user=userService.selectUserByName(principals.getPrimaryPrincipal().toString());
List<TRole> roles= roleService.getUserRoles(user.getId());for(TRole role:roles){
authorizationInfo.addRole(role.getName());for(TPermission p:role.getPermissions()){
authorizationInfo.addStringPermission(p.getCode());}}return authorizationInfo;}}
5. 自定义session管理-MySessionManager
package com.carshow.data.shior;import org.apache.commons.lang.StringUtils;import org.apache.shiro.web.servlet.ShiroHttpServletRequest;import org.apache