使用JavaConfig和Spring Boot的Apache Shiro JdbcRealm

我正在尝试配置我的Spring Boot应用程序以使用Apache Shiro作为其安全框架。 我有一切使用PropertiesRealm,现在我正在尝试使用JdbcRealm和Spring Boot的内置H2数据库。 这是我在pom.xml中的依赖项:

 org.apache.shiro shiro-core 1.2.3   org.apache.shiro shiro-spring 1.2.3   org.apache.shiro shiro-web 1.2.3   org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-thymeleaf   org.springframework.boot spring-boot-starter-jdbc   com.h2database h2  

我的schema.sql:

 create table if not exists users ( username varchar(256), password varchar(256), enabled boolean ); create table if not exists user_roles ( username varchar(256), role_name varchar(256) ); 

我的data.sql:

 insert into users (username, password, enabled) values ('user', '04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb', true); insert into user_roles (username, role_name) values ('user', 'guest'); 

我的WebSecurityConfig.java类配置了所有内容:

 package security; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.crypto.hash.Sha256Hash; import org.apache.shiro.realm.jdbc.JdbcRealm; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.filter.authc.AnonymousFilter; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; import org.apache.shiro.web.filter.authc.LogoutFilter; import org.apache.shiro.web.filter.authc.UserFilter; import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.h2.server.web.WebServlet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.embedded.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import javax.servlet.Filter; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class WebSecurityConfig { @Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilter() { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); Map filterChainDefinitionMapping = new HashMap(); filterChainDefinitionMapping.put("/api/health", "authc,roles[guest],ssl[8443]"); filterChainDefinitionMapping.put("/login", "authc"); filterChainDefinitionMapping.put("/logout", "logout"); shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMapping); shiroFilter.setSecurityManager(securityManager()); shiroFilter.setLoginUrl("/login"); Map filters = new HashMap(); filters.put("anon", new AnonymousFilter()); filters.put("authc", new FormAuthenticationFilter()); LogoutFilter logoutFilter = new LogoutFilter(); logoutFilter.setRedirectUrl("/login?logout"); filters.put("logout", logoutFilter); filters.put("roles", new RolesAuthorizationFilter()); filters.put("user", new UserFilter()); shiroFilter.setFilters(filters); return shiroFilter; } @Bean(name = "securityManager") public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(jdbcRealm()); return securityManager; } @Autowired private DataSource dataSource; @Bean(name = "realm") @DependsOn("lifecycleBeanPostProcessor") public JdbcRealm jdbcRealm() { JdbcRealm realm = new JdbcRealm(); HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME); realm.setCredentialsMatcher(credentialsMatcher); realm.setDataSource(dataSource); realm.init(); return realm; } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean public ServletRegistrationBean h2servletRegistration() { ServletRegistrationBean registration = new ServletRegistrationBean(new WebServlet()); registration.addUrlMappings("/console/*"); return registration; } } 

我在日志中没有看到任何错误。 我尝试将日志记录添加到我的application.properties中,但是它没有多大帮助。

 logging.level.org.apache.shiro=debug 

谢谢,

马特

有几个问题正在发生。

LifecycleBeanPostProcessor

问题是由于您的配置类中定义了LifecycleBeanPostProcessor 。 由于它是BeanPostProcessor因此必须急切地初始化它以处理所有其他bean。 此外, WebSecurityConfig的其余部分需要急切地初始化,因为它可能会影响LifecycleBeanPostProcessor

问题是自动assembly的function尚不可用,因为它也是BeanPostProcessor (即AutowiredAnnotationBeanPostProcessor )。 这意味着DataSource为null。

由于它为null,因此JdbcRealm将抛出NullPointerException 。 这又被AbstractAuthenticator捕获并作为AuthenticationException抛出 。 DefaultWebSecurityManager (实际上是它的父DefaultSecurityManager )然后捕获它调用onFailedLogin ,它删除了“记住我”cookie。

解决LifecycleBeanPostProcessor

最简单的解决方案是确保使用静态方法定义任何基础结构相关的bean。 这告诉Spring它不需要初始化整个配置类(即WebSecurityConfig )。 再次

 @Bean public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } 

或者,您也可以在自己的配置中隔离基础结构相关的bean。

UPDATE

ShiroFilterFactoryBean

我没有意识到ShiroFilterFactoryBean也实现了BeanPostProcessorObjectFactory也实现了BeanPostProcessor这是非常有趣的情况。

问题是这阻止了data.sql的加载,这意味着应用程序在表中没有任何用户,因此身份validation将失败。

问题是data.sql是通过DataSourceInitializedEvent加载的。 但是,由于DataSource的急切初始化(它是BeanPostProcessor的依赖项),无法触发DataSourceInitializedEvent 。 这就是您在日志中看到以下内容的原因:

无法发送事件来完成DataSource初始化(ApplicationEventMulticaster未初始化)

确保data.sql加载

我看到有几个选项可以加载insert语句。

data.sql-> schema.sql文件

最简单的选择是将data.sql的内容移动到schema.sql。 schema.sql仍然加载,因为它不需要触发事件来处理它。 data.sql需要一个事件,以便在JPA初始化模式时可以使用相同的机制来加载数据。

修复订购

不幸的是,您不能简单地为ShiroFilterFactoryBean定义静态,因为它依赖于其他bean定义。 幸运的是,在这个实例中确实不需要BeanPostProcessor 。 这意味着您可以更改代码以返回工厂bean的结果,从而从等式中删除BeanPostProcessor

 @Bean(name = "shiroFilter") public AbstractShiroFilter shiroFilter() throws Exception { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); Map filterChainDefinitionMapping = new HashMap<>(); filterChainDefinitionMapping.put("/api/health", "authc,roles[guest],ssl[8443]"); filterChainDefinitionMapping.put("/login", "authc"); filterChainDefinitionMapping.put("/logout", "logout"); shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMapping); shiroFilter.setSecurityManager(securityManager()); shiroFilter.setLoginUrl("/login"); Map filters = new HashMap<>(); filters.put("anon", new AnonymousFilter()); filters.put("authc", new FormAuthenticationFilter()); LogoutFilter logoutFilter = new LogoutFilter(); logoutFilter.setRedirectUrl("/login?logout"); filters.put("logout", logoutFilter); filters.put("roles", new RolesAuthorizationFilter()); filters.put("user", new UserFilter()); shiroFilter.setFilters(filters); return (AbstractShiroFilter) shiroFilter.getObject(); } 

插入用户

data.sql中的insert语句不正确。 它需要包含已enabled列。 例如:

 insert into users values ('admin', '22f256eca1f336a97eef2b260773cb0d81d900c208ff26e94410d292d605fed8',true);