package cn.wisenergy.chnmuseum.party.conf; import cn.wisenergy.chnmuseum.party.auth.filter.JwtFilter; import cn.wisenergy.chnmuseum.party.auth.realm.MyShiroRealm; import cn.wisenergy.chnmuseum.party.model.PermissionInit; import cn.wisenergy.chnmuseum.party.service.PermissionInitService; import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; import org.apache.shiro.mgt.DefaultSubjectDAO; import org.apache.shiro.mgt.SecurityManager; 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.crazycake.shiro.RedisCacheManager; import org.crazycake.shiro.RedisManager; 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.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import org.springframework.core.annotation.Order; import redis.clients.jedis.JedisPoolConfig; import javax.annotation.Resource; import javax.servlet.Filter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @Configuration @Order(-2) public class ShiroConfig { private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class); private static final String CACHE_KEY = "shiro:cache:"; @Resource private PermissionInitService permissionInitService; @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.timeout}") private int timeout; @Value("${spring.redis.password}") private String password; @Value("${spring.redis.jedis.pool.min-idle}") private int minIdle; @Value("${spring.redis.jedis.pool.max-idle}") private int maxIdle; @Value("${spring.redis.jedis.pool.max-active}") private int maxActive; /** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * <p> * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在 * <p> * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager * <p> * <p> * <p> * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过 * <p> * 3、部分过滤器可指定参数,如perms,roles */ @Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 未授权界面 shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 自定义拦截器 Map<String, Filter> filtersMap = shiroFilterFactoryBean.getFilters(); filtersMap.put("jwt", new JwtFilter()); shiroFilterFactoryBean.setFilters(filtersMap); // 权限控制map Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); List<PermissionInit> list = this.permissionInitService.selectAll(); for (PermissionInit permissionInit : list) { filterChainDefinitionMap.put(permissionInit.getUrl(), permissionInit.getPermissionInit()); } //访问401和404页面不通过我们的Filter filterChainDefinitionMap.put("/logout", "anon"); filterChainDefinitionMap.put("/verifyCode", "anon"); filterChainDefinitionMap.put("/ajaxLogin1", "anon"); filterChainDefinitionMap.put("/verifyCode1", "anon"); filterChainDefinitionMap.put("/loginByQrCode", "anon"); filterChainDefinitionMap.put("/doc.html", "anon"); filterChainDefinitionMap.put("/swagger-ui.html", "anon"); filterChainDefinitionMap.put("/404", "anon"); filterChainDefinitionMap.put("/500", "anon"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean("securityManager") public DefaultWebSecurityManager securityManager() { logger.info("ShiroConfiguration.securityManager()"); DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置realm securityManager.setRealm(myShiroRealm()); // 自定义缓存实现 使用redis securityManager.setCacheManager(redisCacheManager()); // 关闭shiro自带的session DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); defaultSessionStorageEvaluator.setSessionStorageEnabled(false); subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); securityManager.setSubjectDAO(subjectDAO); return securityManager; } /** * 身份认证realm; (这个需要自己写,账号密码校验;权限等) */ @Bean public MyShiroRealm myShiroRealm() { // //开启全局缓存配置 // myShiroRealm.setCachingEnabled(true); // //启用身份验证缓存,即缓存AuthenticationInfo信息,默认false // myShiroRealm.setAuthenticationCachingEnabled(true); // //启用授权缓存,即缓存AuthorizationInfo信息,默认false // myShiroRealm.setAuthorizationCachingEnabled(true); // // //为了方便操作,我们给缓存起个名字 // myShiroRealm.setAuthenticationCacheName("authcCache"); // myShiroRealm.setAuthorizationCacheName("authzCache"); // // //注入缓存实现 // myShiroRealm.setCacheManager(redisCacheManager()); return new MyShiroRealm(); } /** * LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类, * 负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。 * 主要是AuthorizingRealm类的子类,以及EhCacheManager类。 Shiro生命周期处理器 */ @Bean public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { logger.info("ShiroConfiguration.getLifecycleBeanPostProcessor()"); return new LifecycleBeanPostProcessor(); } /** * DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。 * 实现AOP式方法级权限检查 */ @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { logger.info("ShiroConfiguration.defaultAdvisorAutoProxyCreator()"); DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); //defaultAdvisorAutoProxyCreator.setUsePrefix(true); // 强制使用cglib,防止重复代理和可能引起代理出错的问题 defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } /** * 开启shiro的aop注解支持. 使用代理方式; 所以需要开启代码支持; */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { logger.info("ShiroConfiguration.authorizationAttributeSourceAdvisor()"); AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; } /** * 配置Redis管理器 * * @return * @Attention 使用的是shiro-redis开源插件 */ @Bean public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host + ":" + port); redisManager.setTimeout(timeout); redisManager.setPassword(password); JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(maxIdle + maxActive); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMinIdle(minIdle); redisManager.setJedisPoolConfig(jedisPoolConfig); return redisManager; } /** * 用户授权信息Cache, 采用Redis */ @Bean public RedisCacheManager redisCacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); //redisCacheManager.setKeyPrefix(CACHE_KEY); // shiro-redis要求放在session里面的实体类必须有个id标识 //这是组成redis中所存储数据的key的一部分 redisCacheManager.setPrincipalIdFieldName("id"); //用户权限信息缓存时间 //redisCacheManager.setExpire(200000); return redisCacheManager; } }