gpt4 book ai didi

java - Apache Shiro JdbcRealm 与 JavaConfig 和 Spring Boot

转载 作者:行者123 更新时间:2023-12-02 10:31:36 24 4
gpt4 key购买 nike

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

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

我的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)
);

我的数据.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<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 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

问题是 Autowiring 功能尚不可用,因为它也是一个 BeanPostProcessor(即 AutowiredAnnotationBeanPostProcessor)。这意味着 DataSource 为空。

由于它为空,所以JdbcRealm is going to throw a NullPointerException 。这又是caught by AbstractAuthenticatorrethrown as an AuthenticationException 。然后 DefaultWebSecurityManager (实际上是其父 DefaultSecurityManager)捕获它 invokes onFailedLogin这会删除“记住我”cookie。

解决LifecycleBeanPostProcessor

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

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

或者,您也可以在其自己的配置中隔离与基础设施相关的 Bean。

更新

ShiroFilterFactoryBean

我没有意识到ShiroFilterFactoryBean也实现了BeanPostProcessor。对于 ObjectFactory 也实现 BeanPostProcessor 来说,这是非常有趣的情况。

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

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

Could not send event to complete DataSource initialization (ApplicationEventMulticaster not initialized)

确保 data.sql 加载

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

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

插入用户

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

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

关于java - Apache Shiro JdbcRealm 与 JavaConfig 和 Spring Boot,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31388445/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com