I\'m trying to configure my Spring Boot application to use Apache Shiro as its security framework. I have everything working with a PropertiesRealm, now I\'m trying to get i
There are a couple problems that are happening.
The problem is due to the fact that LifecycleBeanPostProcessor
is defined in your config class. Since it is a BeanPostProcessor
it must be initialized eagerly to process all other beans. Furthermore, the rest of WebSecurityConfig
needs to be initialized eagerly since it may impact LifecycleBeanPostProcessor
.
The problem is that the autowired feature is not yet available because it is a BeanPostProcessor
(i.e. AutowiredAnnotationBeanPostProcessor
) too. This means the DataSource
is null.
Since it is null the JdbcRealm is going to throw a NullPointerException. This is in turn caught by AbstractAuthenticator and rethrown as an AuthenticationException. The DefaultWebSecurityManager
(actually its parent DefaultSecurityManager
) then catches it invokes onFailedLogin which removes the "remember me" cookie.
The easiest solution is to ensure any infrastructure related beans are defined with a static method. This informs Spring that it does not need to initialize the entire configuration class (i.e. WebSecurityConfig
). Again
@Bean
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
Alternatively, you can also isolate infrastructure related beans in their own configuration.
UPDATE
I didn't realize that ShiroFilterFactoryBean
implements BeanPostProcessor
also. It is pretty interesting case for an ObjectFactory
to also implement BeanPostProcessor
.
The problem is that this is preventing the loading of data.sql which means the application does not have any users in the table so authentication will fail.
The issue is that data.sql is loaded via a DataSourceInitializedEvent
. However, due to the eager initialization of the DataSource
(it was a dependency of a BeanPostProcessor
) the DataSourceInitializedEvent
cannot be fired. This is why you see the following in the logs:
Could not send event to complete DataSource initialization (ApplicationEventMulticaster not initialized)
There are a few options that I see to get the insert statements to load.
The easiest option is to move the contents of data.sql to schema.sql. The schema.sql is still loaded since it does not require an event to be fired to process it. The data.sql requires an event so that the same mechanism can be used to load data when JPA initializes the schema.
Unfortunately, you cannot simply make the definition for ShiroFilterFactoryBean
static since it relies on other bean definitions. Fortunately, there really is no need for the BeanPostProcessor
in this instance. This means you can change your code to return the result of the factory bean which removes the BeanPostProcessor
from the equation:
@Bean(name = "shiroFilter")
public AbstractShiroFilter shiroFilter() throws Exception {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
Map<String, String> 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<String, Filter> 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();
}
The insert statement found in data.sql is incorrect. It needs to include the enabled
column. For example:
insert into users values ('admin', '22f256eca1f336a97eef2b260773cb0d81d900c208ff26e94410d292d605fed8',true);