springboot使⽤shiro-整合redis作为缓存的操作
说在前⾯
本来的整合过程是顺着博客的顺序来的,越往下,集成的越多,由于之前是使⽤ehcache缓存,现在改为redis,限制登录⼈数以及限制登录次数等都需要改动,本篇为了简单,⽬前先将这两个功能下线,配置暂时是注销的,原类保存,在下篇博客中改。
还有之前是使⽤SessionListener监听ssion创建来统计在线⼈数,在本篇中也将改为统计redis中的key数⽬。
如果是单机,使⽤ehcache是最快的,项⽬⼀般都不是单节点,为了⽅便之后使⽤sso单点登录,以及多节点部署,所以使⽤shiro整合redis。
整合过程
shiro⽤redis实现缓存需要重写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&azycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.1.0</version>
</dependency>
ShiroConfig.java
package fig;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import fig.shiro.*;
import org.dec.Ba64;
import org.apache.shiro.ssion.SessionListener;
import org.apache.SessionManager;
import org.apache.is.JavaUuidSessionIdGenerator;
import org.apache.is.SessionDAO;
import org.apache.is.SessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.curity.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.SecurityManager;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.CookieRememberMeManager;
import org.apache.DefaultWebSecurityManager;
import org.apache.shiro.web.rvlet.SimpleCookie;
import org.apache.shiro.DefaultWebSessionManager;
azycake.shiro.RedisCacheManager;
azycake.shiro.RedisManager;
azycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.fig.MethodInvokingFactoryBean;猫咪文案
import org.t.embedded.ConfigurableEmbeddedServletContainer;
import org.t.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.rvlet.ErrorPage;
import t.annotation.Bean;
import t.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.rvlet.handler.SimpleMappingExceptionResolver;
import javax.rvlet.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的时候需要注⼊:SecurityManager
* Web应⽤中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截
* @param curityManager
* @return
*/
@Bean(name = "shirFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("curityManager") SecurityManager curityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//必须设置 SecurityManager,Shiro的核⼼安全接⼝
shiroFilterFactoryBean.tSecurityManager(curityManager);
//这⾥的/login是后台的接⼝名,⾮页⾯,如果不设置默认会⾃动寻找Web⼯程根⽬录下的"/login.jsp"页⾯
shiroFilterFactoryBean.tLoginUrl("/");
//这⾥的/index是后台的接⼝名,⾮页⾯,登录成功后要跳转的链接
shiroFilterFactoryBean.tSuccessUrl("/index");
/
/未授权界⾯,该配置⽆效,并不会进⾏页⾯跳转
shiroFilterFactoryBean.tUnauthorizedUrl("/unauthorized");蓝脸的道尔顿
//⾃定义拦截器限制并发⼈数,参考博客:
LinkedHashMap<String, Filter> filtersMap = new LinkedHashMap<>();
//限制同⼀帐号同时在线的个数
//filtersMap.put("kickout", kickoutSessionControlFilter());
//统计登录⼈数
shiroFilterFactoryBean.tFilters(filtersMap);
// 配置访问权限必须是LinkedHashMap,因为它必须保证有序
// 过滤链定义,从上向下顺序执⾏,⼀般将 /**放在最为下边 --> : 这是⼀个坑,⼀不⼩⼼代码就不好使了 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//配置不登录可以访问的资源,anon 表⽰资源都可以匿名访问
//配置记住我或认证通过可以访问的地址
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
//解锁⽤户专⽤测试⽤的
filterChainDefinitionMap.put("/unlockAccount","anon");
filterChainDefinitionMap.put("/Captcha.jpg","anon");
//logout是shiro提供的过滤器
filterChainDefinitionMap.put("/logout", "logout");
//此时访问/ur/delete需要delete权限,在⾃定义Realm中为⽤户授权。
//filterChainDefinitionMap.put("/ur/delete", "perms[\"ur:delete\"]");
//其他资源都需要认证 authc 表⽰需要认证才能进⾏访问 ur表⽰配置记住我或认证通过可以访问的地址 //如果开启限制同⼀账号登录,改为 .put("/**", "kickout,ur");
filterChainDefinitionMap.put("/**", "ur");
shiroFilterFactoryBean.tFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 配置核⼼安全事务管理器
* @return
*/
@Bean(name="curityManager")
public SecurityManager curityManager() {
DefaultWebSecurityManager curityManager = new DefaultWebSecurityManager();
//设置⾃定义realm.
curityManager.tRealm(shiroRealm());
//配置记住我
curityManager.tRememberMeManager(rememberMeManager());
//配置redis缓存
curityManager.tCacheManager(cacheManager());
乐开
//配置⾃定义ssion管理,使⽤redis
curityManager.tSessionManager(ssionManager());
return curityManager;
}
/**
* 配置Shiro⽣命周期处理器
* @return
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/
**
* ⾝份认证realm; (这个需要⾃⼰写,账号密码校验;权限等)
* @return
*/
@Bean
public ShiroRealm shiroRealm(){
ShiroRealm shiroRealm = new ShiroRealm();
shiroRealm.tCachingEnabled(true);
//启⽤⾝份验证缓存,即缓存AuthenticationInfo信息,默认fal
shiroRealm.tAuthenticationCachingEnabled(true);
//缓存AuthenticationInfo信息的缓存名称在l中有对应缓存的配置
shiroRealm.tAuthenticationCacheName("authenticationCache");
//启⽤授权缓存,即缓存AuthorizationInfo信息,默认fal
shiroRealm.tAuthorizationCachingEnabled(true);
//缓存AuthorizationInfo信息的缓存名称在l中有对应缓存的配置
shiroRealm.tAuthorizationCacheName("authorizationCache");
//配置⾃定义密码⽐较器
//shiroRealm.tCredentialsMatcher(retryLimitHashedCredentialsMatcher());
return shiroRealm;
}
/**
* 必须(thymeleaf页⾯使⽤shiro标签控制按钮是否显⽰)
* 未引⼊thymeleaf包,Caud by: java.lang.ClassNotFoundException: org.thymeleaf.dialect.AbstractProcessorDialect
* @return
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
/**
* 开启shiro 注解模式
* 可以在controller中的⽅法前加上注解
* 如 @RequiresPermissions("urInfo:add")
* @param curityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("curityManager") SecurityManager curityManager){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.tSecurityManager(curityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 解决:⽆权限页⾯不跳转 shiroFilterFactoryBean.tUnauthorizedUrl("/unauthorized") ⽆效
* shiro的源代码ShiroFilterFactoryBean.Java定义的filter必须满⾜filter instanceof AuthorizationFilter,
* 只有perms,roles,ssl,rest,port才是属于AuthorizationFilter,⽽anon,authcBasic,auchc,ur是AuthenticationFilter,
* 所以unauthorizedUrl设置后页⾯不跳转 Shiro注解模式下,登录失败与没有权限都是通过抛出异常。
* 并且默认并没有去处理或者捕获这些异常。在SpringMVC下需要配置捕获相应异常来通知⽤户信息
* @return
*/
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver simpleMappingExceptionResolver=new SimpleMappingExceptionResolver();
Properties properties=new Properties();
//这⾥的 /unauthorized 是页⾯,不是访问的路径
properties.tProperty("org.apache.shiro.authz.UnauthorizedException","/unauthorized");
properties.tProperty("org.apache.shiro.authz.UnauthenticatedException","/unauthorized");
simpleMappingExceptionResolver.tExceptionMappings(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对象;会话Cookie模板 ,默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid或rememberMe,⾃定义 * @return
*/
@Bean玉米粒怎么炒
public SimpleCookie rememberMeCookie(){
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//tcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:
//tcookie()的第七个参数
//设为true后,只能通过http访问,javascript⽆法访问
//防⽌xss读取cookie
simpleCookie.tHttpOnly(true);
simpleCookie.tPath("/");
//<!-- 记住我cookie⽣效时间30天 ,单位秒;-->
simpleCookie.tMaxAge(2592000);
return simpleCookie;
}
/**
* cookie管理对象;记住我功能,rememberMe管理器
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.tCookie(rememberMeCookie());
//rememberMe cookie加密的密钥建议每个项⽬都不⼀样默认AES算法密钥长度(128 256 512 位)
cookieRememberMeManager.tCipherKey(Ba64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
/**
* FormAuthenticationFilter 过滤器过滤记住我
* @return
*/
@Bean
public FormAuthenticationFilter formAuthenticationFilter(){
FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
//对应前端的checkbox的name = rememberMe
formAuthenticationFilter.tRememberMeParam("rememberMe");
return formAuthenticationFilter;
}
/**
* shiro缓存管理器;
* 需要添加到curityManager中
* @return
*/
@Bean
public RedisCacheManager cacheManager(){
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.tRedisManager(redisManager());
//redis中针对不同⽤户缓存
redisCacheManager.tPrincipalIdFieldName("urname");
//⽤户权限信息缓存时间
redisCacheManager.tExpire(200000);
return redisCacheManager;
}
/**
* 让某个实例的某个⽅法的返回值注⼊为Bean的实例
* Spring静态注⼊
* @return
*/
@Bean
public MethodInvokingFactoryBean getMethodInvokingFactoryBean(){
MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
factoryBean.tStaticMethod("org.apache.shiro.SecurityUtils.tSecurityManager");
factoryBean.tArguments(new Object[]{curityManager()});
return factoryBean;
}
/**
* 配置ssion监听
* @return
*/
@Bean("ssionListener")
public ShiroSessionListener ssionListener(){李易峰简介
ShiroSessionListener ssionListener = new ShiroSessionListener();
return ssionListener;
}
/
清白拼音**
* 配置会话ID⽣成器
* @return
*/
@Bean
public SessionIdGenerator ssionIdGenerator() {
return new JavaUuidSessionIdGenerator();
}
@Bean
public RedisManager redisManager(){
RedisManager redisManager = new RedisManager();
redisManager.tHost("127.0.0.1");
redisManager.tPort(6379);
redisManager.tPassword("123456");
return redisManager;
}
/**
* SessionDAO的作⽤是为Session提供CRUD并进⾏持久化的⼀个shiro组件
* MemorySessionDAO 直接在内存中进⾏会话维护
* EnterpriCacheSessionDAO 提供了缓存功能的会话维护,默认情况下使⽤MapCache实现,内部使⽤ConcurrentHashMap保存缓存的会话。 * @return
*/
@Bean
public SessionDAO ssionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.tRedisManager(redisManager());
//ssion在redis中的保存时间,最好⼤于ssion会话超时时间
redisSessionDAO.tExpire(12000);
return redisSessionDAO;
}
/**
* 配置保存ssionId的cookie
* 注意:这⾥的cookie 不是上⾯的记住我 cookie 记住我需要⼀个cookie ssion管理也需要⾃⼰的cookie
* 默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid
* @return
*/
@Bean("ssionIdCookie")
public SimpleCookie ssionIdCookie(){
//这个参数是cookie的名称
SimpleCookie simpleCookie = new SimpleCookie("sid");
//tcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:
//tcookie()的第七个参数
//设为true后,只能通过http访问,javascript⽆法访问
//防⽌xss读取cookie
simpleCookie.tHttpOnly(true);
simpleCookie.tPath("/");
//maxAge=-1表⽰浏览器关闭时失效此Cookie
simpleCookie.tMaxAge(-1);
return simpleCookie;
}
/**
* 配置会话管理器,设定会话超时及保存
* @return
*/
席组词
@Bean("ssionManager")
public SessionManager ssionManager() {
DefaultWebSessionManager ssionManager = new DefaultWebSessionManager();
Collection<SessionListener> listeners = new ArrayList<SessionListener>();
//配置监听
会计发展前景
listeners.add(ssionListener());
ssionManager.tSessionListeners(listeners);
ssionManager.tSessionIdCookie(ssionIdCookie());
ssionManager.tSessionDAO(ssionDAO());
ssionManager.tCacheManager(cacheManager());
//全局会话超时时间(单位毫秒),默认30分钟暂时设置为10秒钟⽤来测试
ssionManager.tGlobalSessionTimeout(1800000);