package com.nms.swspkmas_standalone.shiro.config; import com.nms.swspkmas_standalone.filter.JwtFilter; import com.nms.swspkmas_standalone.shiro.JwtCredentialsMatcher; import com.nms.swspkmas_standalone.shiro.realm.JwtRealm; import com.nms.swspkmas_standalone.shiro.realm.ShiroRealm; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authc.pam.AuthenticationStrategy; import org.apache.shiro.authc.pam.FirstSuccessfulStrategy; import org.apache.shiro.authc.pam.ModularRealmAuthenticator; import org.apache.shiro.authz.Authorizer; import org.apache.shiro.authz.ModularRealmAuthorizer; import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; import org.apache.shiro.mgt.DefaultSubjectDAO; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.mgt.SessionStorageEvaluator; import org.apache.shiro.realm.Realm; 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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @Configuration public class ShiroConfig { /** * 交由 Spring 来自动地管理 Shiro-Bean 的生命周期 * * 注解式的权限控制需要配置两个Bean,第一个是AdvisorAutoProxyCreator,代理生成器,需要借助SpringAOP来扫描@RequiresRoles和@RequiresPermissions等注解,生成代理类实现功能增强,从而实现权限控制。需要配合AuthorizationAttributeSourceAdvisor一起使用,否则权限注解无效。 */ @Bean public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * 为 Spring-Bean 开启对 Shiro 注解的支持 */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator app = new DefaultAdvisorAutoProxyCreator(); app.setProxyTargetClass(true); return app; } /** * 不向 Spring容器中注册 JwtFilter Bean,防止 Spring 将 JwtFilter 注册为全局过滤器 * 全局过滤器会对所有请求进行拦截,而本例中只需要拦截除 /login 和 /logout 外的请求 * 另一种简单做法是:直接去掉 jwtFilter()上的 @Bean 注解 */ @Bean public FilterRegistrationBean registration(JwtFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(filter); registration.setEnabled(false); return registration; } @Bean public JwtFilter jwtFilter() { //过滤器如果加了@Compoent就没必要用这个方法了 return new JwtFilter(); } /** * 配置访问资源需要的权限 */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //给工厂bean设置web安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/api/account/login"); shiroFilterFactoryBean.setSuccessUrl("/authorized"); shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); // 添加 jwt 专用过滤器,拦截除 /login 和 /logout 外的请求 /*Map filterMap = new LinkedHashMap<>(); filterMap.put("jwtFilter", jwtFilter()); shiroFilterFactoryBean.setFilters(filterMap);*/ //配置系统受限资源以及公共资源 LinkedHashMap filterChainDefinitionMap = new LinkedHashMap(); //放行Swagger2页面,需要放行这些 filterChainDefinitionMap.put("/api/images/**","anon"); filterChainDefinitionMap.put("/api/account/login","anon"); filterChainDefinitionMap.put("/api/capture-record/addCaptureRecord","anon"); filterChainDefinitionMap.put("/api/examinee/getExaminee","anon"); filterChainDefinitionMap.put("/swagger-ui.html","anon"); filterChainDefinitionMap.put("/swagger/**","anon"); filterChainDefinitionMap.put("/webjars/**", "anon"); filterChainDefinitionMap.put("/swagger-resources/**","anon"); filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/doc.html", "anon"); filterChainDefinitionMap.put("/v2/api-docs", "anon"); filterChainDefinitionMap.put("/logout", "logout"); // 退出登录 filterChainDefinitionMap.put("/**", "jwtFilter,authc"); // 需登录才能访问 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 配置 ModularRealmAuthenticator */ @Bean public ModularRealmAuthenticator authenticator() { ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator(); // 设置多 Realm的认证策略,默认 AtLeastOneSuccessfulStrategy AuthenticationStrategy strategy = new FirstSuccessfulStrategy(); authenticator.setAuthenticationStrategy(strategy); return authenticator; } /** * 禁用session, 不保存用户登录状态。保证每次请求都重新认证 */ @Bean protected SessionStorageEvaluator sessionStorageEvaluator() { DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator(); sessionStorageEvaluator.setSessionStorageEnabled(false); return sessionStorageEvaluator; } /** * JwtRealm 配置,需实现 Realm 接口 */ @Bean JwtRealm jwtRealm() { JwtRealm jwtRealm = new JwtRealm(); // 设置加密算法 CredentialsMatcher credentialsMatcher = new JwtCredentialsMatcher(); // 设置加密次数 jwtRealm.setCredentialsMatcher(credentialsMatcher); return jwtRealm; } /** * ShiroRealm 配置,需实现 Realm 接口 */ // @Bean // ShiroRealm shiroRealm() { //这里其实是模拟的数据库的类,但是也继承了AuthorizingRealm // ShiroRealm shiroRealm = new ShiroRealm(); // // 设置加密算法 // HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher("SHA-1"); // // 设置加密次数 // credentialsMatcher.setHashIterations(16); // shiroRealm.setCredentialsMatcher(credentialsMatcher); // return shiroRealm; // } @Bean ShiroRealm shiroRealm() { //这里其实是模拟的数据库的类,但是也继承了AuthorizingRealm ShiroRealm shiroRealm = new ShiroRealm(); return shiroRealm; } /** * 配置 DefaultWebSecurityManager */ @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 1.Authenticator securityManager.setAuthenticator(authenticator()); // 2.Realm List realms = new ArrayList(16); realms.add(jwtRealm()); realms.add(shiroRealm()); securityManager.setRealms(realms); // 配置多个realm // 3.关闭shiro自带的session DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator()); securityManager.setSubjectDAO(subjectDAO); return securityManager; } @Bean public Authorizer authorizer(){ //这里是个坑,如果没有这个bean,启动会报错,所以得加上 return new ModularRealmAuthorizer(); } }