1. 概述
在这个简短的教程中,我们将探索 Spring 提供的使用现有DataSource配置执行 JDBC 身份验证的功能。
在我们的Authentication with a Database-backed UserDetailsService帖子中,我们分析了一种方法来实现这一点,即我们自己实现UserDetailService 接口。
这一次,我们将使用AuthenticationManagerBuilder#jdbcAuthentication指令来分析这种更简单方法的优缺点。
2. 使用嵌入式 H2 连接
首先,我们将分析如何使用嵌入式 H2 数据库实现身份验证。
这很容易实现,因为 Spring Boot 的大部分自动配置都是针对这种情况开箱即用的。
2.1。依赖关系和数据库配置
让我们按照我们之前的Spring Boot With H2 Database帖子的说明开始:
- 包括对应的spring-boot-starter-data-jpa 和 h2依赖
- 使用应用程序属性配置数据库连接
- 启用 H2 控制台
2.2. 配置 JDBC 身份验证
我们将使用Spring Security 的 AuthenticationManagerBuilder配置助手来配置 JDBC 身份验证:
@Autowired
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser(User.withUsername("user")
.password(passwordEncoder().encode("pass"))
.roles("USER"));
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
正如我们所见,我们使用的是自动配置的DataSource。withDefaultSchema指令添加了一个数据库脚本,该脚本将填充默认模式,允许存储用户和权限。
这个基本的用户模式记录在Spring Security 附录中。
最后,我们以编程方式在数据库中创建一个具有默认用户的条目。
2.3. 验证配置
让我们创建一个非常简单的端点来检索经过身份验证的Principal信息:
@RestController
@RequestMapping("/principal")
public class UserController {
@GetMapping
public Principal retrievePrincipal(Principal principal) {
return principal;
}
}
此外,我们将保护此端点,同时允许访问 H2 控制台:
@Configuration
public class SecurityConfiguration
extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity)
throws Exception {
httpSecurity.authorizeRequests()
.antMatchers("/h2-console/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin();
httpSecurity.csrf()
.ignoringAntMatchers("/h2-console/**");
httpSecurity.headers()
.frameOptions()
.sameOrigin();
}
}
注意:这里我们复制了Spring Boot 实现的以前的安全配置,但在现实生活中,我们可能根本不会启用 H2 控制台。
现在我们将运行应用程序并浏览 H2 控制台。我们可以验证Spring 在我们的嵌入式数据库中创建了两个表:用户和 权限。
它们的结构对应于我们之前提到的 Spring Security 附录中定义的结构。
最后,让我们验证并请求/principal端点以查看相关信息,包括用户详细信息。
2.4. 引擎盖下
在这篇文章的开头,我们提供了一个教程链接,该教程解释了如何自定义实现UserDetailsService 接口的数据库支持的身份验证;如果我们想了解事情是如何运作的,我们强烈建议您查看该帖子。
在这种情况下,我们依赖于 Spring Security 提供的相同接口的实现;JdbcDaoImpl。_
如果我们探索这个类,我们将看到它使用的UserDetails 实现,以及从数据库中检索用户信息的机制。
这对于这个简单的场景非常有效,但是如果我们想要自定义数据库模式,或者即使我们想要使用不同的数据库供应商,它也会有一些缺点。
让我们看看如果我们更改配置以使用不同的 JDBC 服务会发生什么。
3. 为不同的数据库调整模式
在本节中,我们将使用 MySQL 数据库在我们的项目上配置身份验证。
正如我们接下来将看到的,为了实现这一点,我们需要避免使用默认模式并提供我们自己的模式。
3.1。依赖关系和数据库配置
对于初学者,让我们删除 h2 依赖并将其替换为相应的 MySQL 库:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
与往常一样,我们可以在Maven Central中查找该库的最新版本。
现在让我们相应地重新设置应用程序属性:
spring.datasource.url=
jdbc:mysql://localhost:3306/jdbc_authentication
spring.datasource.username=root
spring.datasource.password=pass
3.2. 运行默认配置
当然,应该定制这些以连接到您正在运行的 MySQL 服务器。出于测试目的,这里我们将使用 Docker 启动一个新实例:
docker run -p 3306:3306
--name bael-mysql
-e MYSQL_ROOT_PASSWORD=pass
-e MYSQL_DATABASE=jdbc_authentication
mysql:latest
现在让我们运行项目,看看默认配置是否适用于 MySQL 数据库。
实际上,由于SQLSyntaxErrorException,应用程序将无法启动。这实际上是有道理的;正如我们所说,大多数默认自动配置都适用于 HSQLDB。
在这种情况下,withDefaultSchema 指令提供的 DDL 脚本使用了不适合 MySQL 的方言。
因此,我们需要避免使用这种模式并提供我们自己的模式。
3.3. 调整身份验证配置
由于我们不想使用默认模式,我们必须从AuthenticationManagerBuilder配置中删除正确的语句。
此外,由于我们将提供自己的 SQL 脚本,我们可以避免尝试以编程方式创建用户:
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource);
}
现在让我们看一下数据库初始化脚本。
首先,我们的schema.sql:
CREATE TABLE users (
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
enabled TINYINT NOT NULL DEFAULT 1,
PRIMARY KEY (username)
);
CREATE TABLE authorities (
username VARCHAR(50) NOT NULL,
authority VARCHAR(50) NOT NULL,
FOREIGN KEY (username) REFERENCES users(username)
);
CREATE UNIQUE INDEX ix_auth_username
on authorities (username,authority);
然后,我们的data.sql:
-- User user/pass
INSERT INTO users (username, password, enabled)
values ('user',
'$2a$10$8.UnVuG9HHgffUDAlk8qfOuVGkqRzgVymGe07xd00DMxs.AQubh4a',
1);
INSERT INTO authorities (username, authority)
values ('user', 'ROLE_USER');
最后,我们应该修改一些其他的应用程序属性:
- 由于我们不希望 Hibernate 现在创建模式,我们应该禁用ddl-auto属性
- 默认情况下,Spring Boot 只为嵌入式数据库初始化数据源,这里不是这样:
spring.sql.init.mode=always
spring.jpa.hibernate.ddl-auto=none
结果,我们现在应该能够正确启动我们的应用程序,从端点验证和检索Principal 数据。
另外,请注意spring.sql.init.mode属性是在 Spring Boot 2.5.0 中引入的;对于早期版本,我们需要使用spring.datasource.initialization-mode。
4. 为不同的模式调整查询
让我们更进一步。想象一下默认模式不适合我们的需求。
4.1。更改默认架构
例如,想象一下,我们已经有一个结构与默认结构略有不同的数据库:
CREATE TABLE bael_users (
name VARCHAR(50) NOT NULL,
email VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
enabled TINYINT NOT NULL DEFAULT 1,
PRIMARY KEY (email)
);
CREATE TABLE authorities (
email VARCHAR(50) NOT NULL,
authority VARCHAR(50) NOT NULL,
FOREIGN KEY (email) REFERENCES bael_users(email)
);
CREATE UNIQUE INDEX ix_auth_email on authorities (email,authority);
最后,我们的data.sql脚本也将适应这种变化:
-- User user@email.pass/pass
INSERT INTO bael_users (name, email, password, enabled)
values ('user',
'user@email.com',
'$2a$10$8.UnVuG9HHgffUDAlk8qfOuVGkqRzgVymGe07xd00DMxs.AQubh4a',
1);
INSERT INTO authorities (email, authority)
values ('user@email.com', 'ROLE_USER');
4.2. 使用新模式运行应用程序
让我们启动我们的应用程序。它正确初始化,这是有道理的,因为我们的模式是正确的。
现在,如果我们尝试登录,我们会发现在出示凭据时会提示错误。
Spring Security 仍在数据库中寻找 用户名字 段。幸运的是,JDBC 身份验证配置提供了自定义用于在身份验证过程中检索用户详细信息的查询的可能性。
4.3. 自定义搜索查询
调整查询非常容易。在配置AuthenticationManagerBuilder时,我们只需提供自己的 SQL 语句:
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select email,password,enabled "
+ "from bael_users "
+ "where email = ?")
.authoritiesByUsernameQuery("select email,authority "
+ "from authorities "
+ "where email = ?");
}
我们可以再次启动应用程序,并 使用新凭据访问/principal端点。
5. 结论
正如我们所看到的,这种方法比必须创建自己的UserDetailService 实现要简单得多,这意味着一个艰巨的过程;创建实现UserDetail 接口的实体和类 ,并将存储库添加到我们的项目中。
当然,缺点是当我们的数据库或我们的逻辑与Spring Security 解决方案提供的默认策略不同时,它提供的灵活性很小。
最后,我们可以查看GitHub 存储库中的完整示例。我们甚至包括了一个使用 PostgreSQL 的示例,我们没有在本教程中展示,只是为了让事情变得简单。