Spring Boot Shiro auto-configure工作流程详解
作者:Samson_bu
01-Shiro 自动配置原理
Shiro 与 Spring Boot 集成可以通过 shiro-spring-boot-stater 实现,并能完成必要类自动装配。 实现方式是通过 Spring Boot 的自动配置机制,即 WEB-INF/spring.factories
中通过 EnableAutoConfiguration
指定了 6 个自动化配置类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration = \ org.apache.shiro.spring.config.web.autoconfigure.ShiroWebAutoConfiguration,\ org.apache.shiro.spring.config.web.autoconfigure.ShiroWebFilterConfiguration,\ org.apache.shiro.spring.config.web.autoconfigure.ShiroWebMvcAutoConfiguration,\ org.apache.shiro.spring.boot.autoconfigure.ShiroBeanAutoConfiguration,\ org.apache.shiro.spring.boot.autoconfigure.ShiroAutoConfiguration,\ org.apache.shiro.spring.boot.autoconfigure.ShiroAnnotationProcessorAutoConfiguration
它们之间的关系为:
当配置项 shiro.enabled = true
且 shiro.web.enabled = false
时,ShiroWeb*Configuration 配置不生效。 当 shiro.web.enabled = true
时,上述六个皆生效,不过 ShiroWebAutoConfiguration 上有注解 @AutoConfigureBefore(ShiroAutoConfiguration.class)
, 保证能在 ShiroAutoConfiguration 之前,使用 web 配置覆盖 standalone 配置
02-自动配置类
Shiro 中的核心是 SecurityManager,它将 Authenticator、Authorizer、SessionManager 等关键模块组合在一起。 在 ShiroWebAutoConfiguration 中包含了上述几个核心模块的默认初始化过程。
对 Authenticator 来说(ShiroWebAutoConfiguration 返回的都是父类方法的内容,所以下面我直接将方法体替换为父类的):
@Bean @ConditionalOnMissingBean @Override protected AuthenticationStrategy authenticationStrategy() { return new AtLeastOneSuccessfulStrategy(); } @Bean @ConditionalOnMissingBean @Override protected Authenticator authenticator() { ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator(); authenticator.setAuthenticationStrategy(authenticationStrategy()); return authenticator; }
默认情况下,使用的是 ModularRealmAuthenticator,策略类使用的事 AtLeastOneSuccessfulStrategy,即多个 Realms 时,至少一个成功则认为是成功。
对 Authorizer 来说:
@Bean @ConditionalOnMissingBean @Override protected Authorizer authorizer() { ModularRealmAuthorizer authorizer = new ModularRealmAuthorizer(); if (permissionResolver != null) { // 负责从 permission 字符串里解析出 Permission 对象 authorizer.setPermissionResolver(permissionResolver); // 这两个都是通过 @Autowired 注入进来的 } if (rolePermissionResolver != null) { // 负责从 role 字符串里解析出 Permission 集合 authorizer.setRolePermissionResolver(rolePermissionResolver); // 这两个都是通过 @Autowired 注入进来的 } return authorizer; }
对于 SessionManager 来说:
@Bean @ConditionalOnMissingBean @Override protected SessionManager sessionManager() { if (useNativeSessionManager) { // 从环境变量 shiro.userNativeSessionManager 取,默认为 false // 省略了其他设置 return new DefaultWebSessionManager(); } return new ServletContainerSessionManager(); }
对于 SecurityManager 来说:
@Bean @ConditionalOnMissingBean @Override protected SessionsSecurityManager securityManager(List<Realm> realms) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setSubjectDAO(subjectDAO()); securityManager.setSubjectFactory(subjectFactory()); securityManager.setRememberMeManager(rememberMeManager()); securityManager.setAuthenticator(authenticator()); securityManager.setAuthorizer(authorizer()); securityManager.setRealms(realms); securityManager.setSessionManager(sessionManager()); securityManager.setEventBus(eventBus); if (cacheManager != null) { securityManager.setCacheManager(cacheManager); } return securityManager; }
对于其他对象,例如 SubjectDAO/SubjectFactory、SessionDAO/SessionFactory/SessionManager、RememberMeManager、EventBus,在系统中属于比较底层的辅助模块,一般与业务牵扯比较小,所以通过情况下不需要修改。 我简单介绍下它们的作用,以及 Shiro Web 应用中使用得默认类型:
- SubjectFactory 有两个默认实现,DefaultSubjectFactory 和 DefaultWebSubjectFactory 分别用来创建 standalone 和 Web 程序中的 Subject 对象。
- SubjectDAO 有一个默认实现,DefaultSubjectDAO 负责将 Subject 对象存储到其所属的 Session 对象中。
- RememberMeManager 负责将 Subject 的 principals 存储到 cookie 中。
- EventBus 是 Shiro 中的事件总线,负责在 Shiro 全声明周期触发特定事件或接受事件通知。
- SessionDAO/SessionFactory/SessionManager 是与 Session 管理、持久化相关的模块。
03-Filter 相关的配置类
ShiroWebFilterConfiguration 中定义了与 Servlet Filter 相关的对象。 对于 ShiroFilterFactoryBean,负责创建 shiroFilter 对象:
@Bean @ConditionalOnMissingBean @Override protected ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean(); // 从环境变量中取 filterFactoryBean.setLoginUrl(loginUrl); // shiro.loginUrl filterFactoryBean.setSuccessUrl(successUrl); // shiro.successUrl filterFactoryBean.setUnauthorizedUrl(unauthorizedUrl); // shiro.unauthorizedUrl filterFactoryBean.setSecurityManager(securityManager); // 由 @Autowired 注入 filterFactoryBean.setShiroFilterConfiguration(shiroFilterConfiguration()); // 由 @Autowired 注入或默认使用 ShiroFilterConfiguration filterFactoryBean.setGlobalFilters(globalFilters()); // 由 @Autowired 注入 filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition.getFilterChainMap()); // 由 @Autowired 注入 filterFactoryBean.setFilters(filterMap); // 由 @Autowired 注入 return filterFactoryBean; }
对于 filterShiroFilterRegistrationBean 来说,负责向 ServletContext 中注册 shiroFilter 对象:
@Bean(name = REGISTRATION_BEAN_NAME) @ConditionalOnMissingBean(name = REGISTRATION_BEAN_NAME) protected FilterRegistrationBean<AbstractShiroFilter> filterShiroFilterRegistrationBean() throws Exception { FilterRegistrationBean<AbstractShiroFilter> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ERROR); filterRegistrationBean.setFilter((AbstractShiroFilter) shiroFilterFactoryBean().getObject()); // 有前面的 FactoryBean 创建 filterRegistrationBean.setName(FILTER_NAME); // shiroFilter filterRegistrationBean.setOrder(1); return filterRegistrationBean; }
关于 globalFilters,默认只有 InvalidRequestFilter:
@Bean(name = "globalFilters") @ConditionalOnMissingBean protected List<String> globalFilters() { return Collections.singletonList(DefaultFilter.invalidRequest.name()); }
通过前面的分析,如果业务需要针对不同的 URL 使用不同的 shiro-filter chain,可以通过自定义 shiroFilterChainDefinition 并将其注入都容器中即可,例如:
@Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); chainDefinition.addPathDefinition("/manage/index", "user"); chainDefinition.addPathDefinition("/manage/logout", "logout"); chainDefinition.addPathDefinition("/manage/**", "authc"); // shiro 放行 swagger chainDefinition.addPathDefinition("/swagger-ui/**", "user"); chainDefinition.addPathDefinition("/swagger-resources/**", "user"); chainDefinition.addPathDefinition( "/v3/api-docs/**","user"); chainDefinition.addPathDefinition("/**", "anon"); return chainDefinition; }
04-总结
今天,我介绍了 shiro-spring-boot-starter 中对 Shiro 进行自动化配置的细节。 通过对这些配置的了解,能够在遇到具体的业务问题时修改特定模块的实现方式,对理解和使用 Shiro 框架是非常必要的事情。 希望今天的内容能对你有所帮助,更多关于Spring Boot Shiro auto-configure的资料请关注脚本之家其它相关文章!