ShiroConfig
代码语言:javascript复制所有的shiro配置都在这里面配就行了,你要是喜欢xml啥的就自己对应改下
package com.ccb.web.configs;
//shiro
import com.ccb.web.shiro.*;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.SessionListener;
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.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;
//spring
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
//java
import javax.servlet.Filter;
import java.io.Serializable;
import java.util.*;
/**
* Shiro配置
* <p>
* 大部分参数都是在这里直接修改
*
* @author zhuyongsheng
* @date 2019/8/13
*/
@Configuration
public class ShiroConfig {
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
* Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截
*
* @return org.apache.shiro.spring.web.ShiroFilterFactoryBean
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean(name = "shirFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") SecurityManager securityManager) {
//定义返回对象
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//必须设置 SecurityManager,Shiro的核心安全接口
shiroFilterFactoryBean.setSecurityManager(securityManager);
//这里的/login是后台的接口名,非页面,如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/sessionInvalid");
//这里的/index是后台的接口名,非页面,登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/");
//未授权界面,该配置无效,并不会进行页面跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/sessionInvalid");
//限制同一帐号同时在线的个数
LinkedHashMap<String, Filter> filtersMap = new LinkedHashMap<>();
filtersMap.put("kickout", kickoutSessionControlFilter());
shiroFilterFactoryBean.setFilters(filtersMap);
// 配置访问权限 必须是LinkedHashMap,因为它必须保证有序
// 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 一定要注意顺序,否则就不好使了
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//logout是shiro提供的过滤器
filterChainDefinitionMap.put("/logout", "logout");
//配置不登录可以访问的资源,anon 表示资源都可以匿名访问
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/swagger-resources/**", "anon");
filterChainDefinitionMap.put("/v2/**", "anon");
filterChainDefinitionMap.put("/u/**", "anon");
filterChainDefinitionMap.put("/swagger/**", "anon");
filterChainDefinitionMap.put("/doc.html", "anon");
filterChainDefinitionMap.put("/simpleBillRepertory/getShareStockUrl", "anon");
filterChainDefinitionMap.put("/alicheck", "anon");
filterChainDefinitionMap.put("/api-docs/**", "anon");
//此时访问/userInfo/del需要del权限,在自定义Realm中为用户授权。
//filterChainDefinitionMap.put("/userInfo/del", "perms["userInfo:del"]");
//其他资源都需要认证 authc 表示需要认证才能进行访问
filterChainDefinitionMap.put("/**", "kickout,authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 身份认证realm
*
* @return com.ccb.web.shiro.ShiroRealm
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean
public ShiroRealm shiroRealm() {
ShiroRealm shiroRealm = new ShiroRealm();
shiroRealm.setCachingEnabled(true);
//启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
shiroRealm.setAuthenticationCachingEnabled(true);
//缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
shiroRealm.setAuthenticationCacheName("authenticationCache");
//启用授权缓存,即缓存AuthorizationInfo信息,默认false
shiroRealm.setAuthorizationCachingEnabled(true);
//缓存AuthorizationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
shiroRealm.setAuthorizationCacheName("authorizationCache");
return shiroRealm;
}
/**
* cookie管理对象;记住我功能,rememberMe管理器
*
* @return org.apache.shiro.web.mgt.CookieRememberMeManager
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean
public CookieRememberMeManager cookieRememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(simpleCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
/**
* shiro缓存管理器
*
* @return org.apache.shiro.cache.ehcache.EhCacheManager
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean
public EhCacheManager ehCacheManager() {
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return cacheManager;
}
/**
* redisTemplate 配置
*
* @param redisConnectionFactory
* @return
*/
@Bean("csRedisTemplate")
public RedisTemplate<String, Serializable> csRedisTemplate(LettuceConnectionFactory redisConnectionFactory) {
ObjectMapper om = new ObjectMapper();
// 4.设置可见度
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 5.启动默认的类型
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//解决实体缺少 set get 方法
om.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false);
//截取实体缺少的属性
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer(om));
template.setHashKeySerializer(new StringRedisSerializer());
template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer(om));
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean("csRedisUtil")
public CsRedisUtil csRedisUtil(@Qualifier("csRedisTemplate") RedisTemplate<String, Serializable> redisTemplate) {
CsRedisUtil redisUtil = new CsRedisUtil();
redisUtil.setRedisTemplate(redisTemplate);
return redisUtil;
}
/**
* 配置会话管理器,设定会话超时及保存
*
* @return org.apache.shiro.session.mgt.SessionManager
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean("shiroSessionManager")
public ShiroSessionManager shiroSessionManager() {
ShiroSessionManager sessionManager = new ShiroSessionManager();
Collection<SessionListener> listeners = new ArrayList<>();
//配置监听
listeners.add(shiroSessionListener());
sessionManager.setSessionListeners(listeners);
sessionManager.setSessionIdCookie(sessionIdCookie());
sessionManager.setSessionDAO(redisSessionDAO());
sessionManager.setCacheManager(ehCacheManager());
//全局会话超时时间(单位毫秒)
sessionManager.setGlobalSessionTimeout(60 * 1000 * 60);
//是否开启删除无效的session对象 默认为true
sessionManager.setDeleteInvalidSessions(true);
//是否开启定时调度器进行检测过期session 默认为true
sessionManager.setSessionValidationSchedulerEnabled(true);
//设置session失效的扫描时间, 清理用户直接关闭浏览器造成的孤立会话
//设置该属性 就不需要设置 ExecutorServiceSessionValidationScheduler 底层也是默认自动调用ExecutorServiceSessionValidationScheduler
sessionManager.setSessionValidationInterval(60 * 1000 * 10);
sessionManager.setSessionFactory(shiroSessionFactory());
//取消url 后面的 JSESSIONID
sessionManager.setSessionIdUrlRewritingEnabled(false);
return sessionManager;
}
/**
* 配置核心安全事务管理器
*
* @return org.apache.shiro.mgt.SecurityManager
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean(name = "securityManager")
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置自定义realm.
securityManager.setRealm(shiroRealm());
//配置记住我
securityManager.setRememberMeManager(cookieRememberMeManager());
//配置缓存管理器
securityManager.setCacheManager(ehCacheManager());
//配置session管理器
securityManager.setSessionManager(shiroSessionManager());
return securityManager;
}
/**
* FormAuthenticationFilter 过滤器 过滤记住我
*
* @return org.apache.shiro.web.filter.authc.FormAuthenticationFilter
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean
public FormAuthenticationFilter formAuthenticationFilter() {
FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
//对应前端的checkbox的name = rememberMe
formAuthenticationFilter.setRememberMeParam("rememberMe");
return formAuthenticationFilter;
}
/**
* 并发登录控制
*
* @return com.ccb.web.shiro.KickoutSessionControlFilter
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean
public KickoutSessionControlFilter kickoutSessionControlFilter() {
KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
//使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的;
kickoutSessionControlFilter.setCacheManager(ehCacheManager());
//是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;
kickoutSessionControlFilter.setKickoutAfter(false);
//同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;
kickoutSessionControlFilter.setMaxSession(1);
//被踢出后重定向到的地址;
kickoutSessionControlFilter.setKickoutUrl("/login?kickout=1");
return kickoutSessionControlFilter;
}
/**
* 配置Shiro生命周期处理器
*
* @return org.apache.shiro.spring.LifecycleBeanPostProcessor
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* shiro缓存管理器
*
* @return com.ccb.web.shiro.RedisCacheManager
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean
public RedisCacheManager redisCacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
//redis中针对不同用户缓存
redisCacheManager.setPrincipalIdFieldName("username");
//用户权限信息缓存时间
redisCacheManager.setExpire(60 * 1000 * 60);
return redisCacheManager;
}
/**
* redis 替代默认缓存
*
* @return com.ccb.web.shiro.RedisSessionDAO
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setExpire(60 * 60);
return redisSessionDAO;
}
/**
* SessionDAO的作用是为Session提供CRUD并进行持久化的一个shiro组件
*
* @return org.apache.shiro.session.mgt.eis.SessionDAO
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean
public SessionDAO sessionDAO() {
RedisSessionDAO redisSessionDAO = redisSessionDAO();
//使用ehCacheManager
redisSessionDAO.setCacheManager(ehCacheManager());
//设置session缓存的名字 默认为 shiro-activeSessionCache
redisSessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
//sessionId生成器
redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
return redisSessionDAO;
}
/**
* 配置保存sessionId的cookie
* 注意:这里的cookie 不是上面的记住我 cookie 记住我需要一个cookie session管理 也需要自己的cookie
*
* @return org.apache.shiro.web.servlet.SimpleCookie
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean("sessionIdCookie")
public SimpleCookie sessionIdCookie() {
//这个参数是cookie的名称
SimpleCookie simpleCookie = new SimpleCookie("sid");
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//maxAge=-1表示浏览器关闭时失效此Cookie
simpleCookie.setMaxAge(-1);
return simpleCookie;
}
/**
* 配置会话ID生成器
*
* @return org.apache.shiro.session.mgt.eis.SessionIdGenerator
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean
public SessionIdGenerator sessionIdGenerator() {
return new JavaUuidSessionIdGenerator();
}
/**
* session工厂
*
* @return com.ccb.web.shiro.ShiroSessionFactory
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean("sessionFactory")
public ShiroSessionFactory shiroSessionFactory() {
ShiroSessionFactory sessionFactory = new ShiroSessionFactory();
return sessionFactory;
}
/**
* 配置session监听
*
* @return com.ccb.web.shiro.ShiroSessionListener
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean("sessionListener")
public ShiroSessionListener shiroSessionListener() {
ShiroSessionListener sessionListener = new ShiroSessionListener();
return sessionListener;
}
/**
* cookie对象;会话Cookie模板 ,默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid或rememberMe
*
* @return org.apache.shiro.web.servlet.SimpleCookie
* @author zhuyongsheng
* @date 2019/8/15
*/
@Bean
public SimpleCookie simpleCookie() {
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
/*
* 设为true后,只能通过http访问,javascript无法访问
* 防止xss读取cookie
*/
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//<!-- 记住我cookie生效时间30天 ,单位秒;-->
simpleCookie.setMaxAge(2592000);
return simpleCookie;
}
/**
* 解决: 无权限页面不跳转
*
* @return org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
* @author zhuyongsheng
* @date 2019/8/15
* @apiNote shiroFilterFactoryBean.setUnauthorizedUrl(" / unauthorized ") 无效
* shiro的源代码ShiroFilterFactoryBean.Java定义的filter必须满足filter instanceof AuthorizationFilter,
* 只有perms,roles,ssl,rest,port才是属于AuthorizationFilter,而anon,authcBasic,auchc,user是AuthenticationFilter,
* 所以unauthorizedUrl设置后页面不跳转 Shiro注解模式下,登录失败与没有权限都是通过抛出异常。
* 并且默认并没有去处理或者捕获这些异常。在SpringMVC下需要配置捕获相应异常来通知用户信息
*/
@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;
}
/**
* 让某个实例的某个方法的返回值注入为Bean的实例
* Spring静态注入
*
* @return
*/
@Bean
public MethodInvokingFactoryBean getMethodInvokingFactoryBean() {
MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
factoryBean.setArguments(new Object[]{securityManager()});
return factoryBean;
}
}