Springboot解决 【Apache Shiro rememberMe参数秘钥可被枚举】 漏洞
- 新增AbstractRememberMeManager 实现类
package com.file.system.plugin.shiro; import org.apache.shiro.mgt.AbstractRememberMeManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.SubjectContext; /** * 中危】Apache Shiro rememberMe参数秘钥可被枚举 解决 * */ public class IgnoreRememberManager extends AbstractRememberMeManager { @Override protected void forgetIdentity(Subject subject) { } @Override protected void rememberSerializedIdentity(Subject subject, byte[] bytes) { } @Override protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) { return new byte[0]; } @Override public void forgetIdentity(SubjectContext subjectContext) { } }
- ShiroConfig文件中使用@Bean注解方式注入
@Bean(name = "rememberMeManager") public AbstractRememberMeManager rememberMeManager() { return new IgnoreRememberManager(); }
- 设置DefaultWebSecurityManager
@Bean public SecurityManager shiroSecurityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(customRealm()); //配置seesion securityManager.setSessionManager(sessionManager()); //解决漏洞 securityManager.setRememberMeManager(rememberMeManager()); return securityManager; }
- 附ShiroConfig.java完整代码
package com.file.system.plugin.shiro; import com.file.system.plugin.shiro.filter.ShiroLoginFilter; import com.file.system.service.ShiroService; import com.file.system.service.impl.ShiroServiceImpl; import com.file.system.tools.console.Read; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.mgt.AbstractRememberMeManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.SessionListener; import org.apache.shiro.session.mgt.AbstractValidatingSessionManager; import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO; 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.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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.config.MethodInvokingFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.servlet.Filter; import java.security.NoSuchAlgorithmException; import java.util.*; @Configuration public class ShiroConfig { @Bean public ShiroService shiroService() { return new ShiroServiceImpl(); } @Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiro = new ShiroFilterFactoryBean(); shiro.setSecurityManager(securityManager); shiro.setLoginUrl("/login"); shiro.setUnauthorizedUrl("/not/auth"); Map<String, Filter> filterMap = new HashMap<>(); //自定义or角色权限 filterMap.put("roleOrFilter", new RoleOrFilter()); //map里面key值要为authc才能使用自定义的过滤器 filterMap.put("authc", new ShiroLoginFilter()); shiro.setFilters(filterMap); Map<String, String> chainMap = new LinkedHashMap<>(); chainMap.put("/static/**", "anon");//放开的静态文件目录 chainMap.put("/**", "authc");//主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证 shiro.setFilterChainDefinitionMap(chainMap); Read.console("shiro"); return shiro; } /** * shiro缓存管理器; * 需要注入对应的其它的实体类中: * 1、安全管理器:securityManager * 可见securityManager是整个shiro的核心; */ @Bean public EhCacheManager ehCacheManager() { EhCacheManager cacheManager = new EhCacheManager(); cacheManager.setCacheManagerConfigFile("classpath:shiro/ehcache-shiro.xml"); return cacheManager; } @Bean public SecurityManager shiroSecurityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(customRealm()); //配置seesion securityManager.setSessionManager(sessionManager()); //记住我 securityManager.setRememberMeManager(rememberMeManager()); return securityManager; } @Bean(name = "rememberMeManager") public AbstractRememberMeManager rememberMeManager() { return new IgnoreRememberManager(); } @Bean public CustomRealm customRealm() { CustomRealm customRealm = new CustomRealm(); customRealm.setCachingEnabled(true); //启用身份验证缓存,即缓存AuthenticationInfo信息,默认false customRealm.setAuthenticationCachingEnabled(true); //缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置 customRealm.setAuthenticationCacheName("authenticationCache"); //启用授权缓存,即缓存AuthorizationInfo信息,默认false customRealm.setAuthorizationCachingEnabled(true); //缓存AuthorizationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置 customRealm.setAuthorizationCacheName("authorizationCache"); return customRealm; } /** * 配置session监听 */ @Bean("sessionListener") public ShiroSessionListener sessionListener() { ShiroSessionListener sessionListener = new ShiroSessionListener(); return sessionListener; } /** * 配置会话ID生成器 */ @Bean public SessionIdGenerator sessionIdGenerator() { return new JavaUuidSessionIdGenerator(); } /** * SessionDAO的作用是为Session提供CRUD并进行持久化的一个shiro组件 * MemorySessionDAO 直接在内存中进行会话维护 * EnterpriseCacheSessionDAO 提供了缓存功能的会话维护,默认情况下使用MapCache实现,内部使用ConcurrentHashMap保存缓存的会话。 */ @Bean public SessionDAO sessionDAO() { EnterpriseCacheSessionDAO enterpriseCacheSessionDAO = new EnterpriseCacheSessionDAO(); //使用ehCacheManager enterpriseCacheSessionDAO.setCacheManager(ehCacheManager()); //设置session缓存的名字 默认为 shiro-activeSessionCache enterpriseCacheSessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache"); //sessionId生成器 enterpriseCacheSessionDAO.setSessionIdGenerator(sessionIdGenerator()); return enterpriseCacheSessionDAO; } /** * shiro session的管理 */ @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setGlobalSessionTimeout(-1000); //配置session的监听 Collection<SessionListener> listeners = new ArrayList<>(); listeners.add(sessionListener()); sessionManager.setSessionListeners(listeners); //取消url 后面的 JSESSIONID sessionManager.setSessionIdUrlRewritingEnabled(false); SimpleCookie cookie = new SimpleCookie("system.seesion"); cookie.setHttpOnly(true); cookie.setMaxAge(Integer.MAX_VALUE); sessionManager.setSessionIdCookie(cookie); //设置检查seesion时间 sessionManager.setSessionValidationInterval(AbstractValidatingSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL); //设置在cookie中的sessionId名称 sessionManager.setSessionIdCookie(cookie); //设置sessionDao对session查询,在查询在线用户service中用到了 sessionManager.setSessionDAO(sessionDAO()); return sessionManager; } /** * 让某个实例的某个方法的返回值注入为Bean的实例 * Spring静态注入 */ @Bean public MethodInvokingFactoryBean getMethodInvokingFactoryBean() { MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean(); factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager"); factoryBean.setArguments(new Object[]{shiroSecurityManager()}); return factoryBean; } @Bean @DependsOn({"lifecycleBeanPostProcessor"}) public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { // 设置代理类 DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); return creator; } @Bean("authorizationAttributeSourceAdvisor") public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } }