Apache Shiro JdbcRealm with JavaConfig and Spring Boot

后端 未结 1 957
猫巷女王i
猫巷女王i 2021-01-03 14:26

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

相关标签:
1条回答
  • 2021-01-03 15:13

    There are a couple problems that are happening.

    LifecycleBeanPostProcessor

    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.

    Solving LifecycleBeanPostProcessor

    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

    ShiroFilterFactoryBean

    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)

    Ensuring data.sql Loads

    There are a few options that I see to get the insert statements to load.

    data.sql->schema.sql

    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.

    Fixing the Ordering

    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();
    }
    

    insert into user

    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);
    
    0 讨论(0)
提交回复
热议问题