Spring Security:探索 JDBC 身份验证

1. 概述

在这个简短的教程中,我们将探讨 Spring 提供的使用现有数据源配置执行 JDBC 身份验证的功能。

在我们的“使用数据库支持的用户详细信息服务进行身份验证”一文中,我们分析了一种通过自己实现用户详细信息服务接口来实现此目的的方法。

这一次,我们将使用身份验证管理器生成器#jdbc 身份验证指令来分析这种更简单方法的优缺点。

2. 使用嵌入式 H2 连接

首先,我们将分析如何使用嵌入式 H2 数据库实现身份验证。

这很容易实现,因为大多数弹簧启动的自动配置都是针对此方案准备的。

2.1. 依赖关系和数据库配置

让我们首先按照我们之前的春季启动与H2数据库帖子的说明开始:

  1. 包括相应的弹簧启动器-起始-数据-jpa 和 h2 依赖项
  2. 使用应用程序属性配置数据库连接
  3. 启用 H2 控制台

2.2. 配置 JDBC 认证

我们将使用Spring Security的身份验证管理器构建器配置帮助程序来配置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();
}

正如我们所看到的,我们正在使用自动配置的数据源。 withDefaultSchema 指令添加了一个数据库脚本,该脚本将填充默认架构,从而允许用户和权限进行存储。

此基本用户架构记录在 Spring 安全附录中

最后,我们以编程方式在数据库中使用默认用户创建一个条目。

2.3. 验证配置

让我们创建一个非常简单的终结点来检索经过身份验证的主体信息:

@RestController
@RequestMapping("/principal")
public class UserController {

    @GetMapping
    public Principal retrievePrincipal(Principal principal) {
        return principal;
    }
}

此外,我们将保护此终端节点,同时允许访问 H2 控制台:

@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity)
      throws Exception {
        httpSecurity.authorizeRequests()
          .antMatchers("/h2-console/**")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .formLogin();
        
        httpSecurity.csrf()
          .ignoringAntMatchers("/h2-console/**");
        httpSecurity.headers()
          .frameOptions()
          .sameOrigin();
      return http.build();
    }
}

注意:在这里,我们重现了Spring Boot实现的以前的安全配置但在现实场景中,我们可能根本不会启用H2控制台。

现在,我们将运行应用程序并浏览 H2 控制台。我们可以验证Spring是否正在我们的嵌入式数据库中创建两个表:用户权限。

它们的结构对应于我们之前提到的弹簧安全附录中定义的结构。

最后,让我们进行身份验证并请求 /principal 终结点以查看相关信息,包括用户详细信息。

2.4. 引擎盖下

在本文的开头,我们提供了一个教程的链接,该教程解释了如何自定义实现用户详细信息服务接口的数据库支持的身份验证;我们强烈建议看看这篇文章,如果我们想了解事情是如何运作的。

在这种情况下,我们依赖于Spring安全提供的相同接口的实现;贾德布克道因普尔

如果我们探索这个类,我们将看到它使用的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数据库。

实际上,应用程序将无法启动,因为 SQL 语法错误异常。这实际上是有道理的;正如我们所说,大多数默认的自动配置都适用于HSQLDB。

在这种情况下,随“默认程序”指令提供的 DDL 脚本使用不适合 MySQL 的方言。

因此,我们需要避免使用此架构并提供我们自己的架构。

3.3. 调整身份验证配置

由于我们不想使用默认架构,因此必须从身份验证管理器生成器配置中删除正确的语句。

此外,由于我们将提供自己的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');

最后,我们应该修改一些其他应用程序属性:

  • 由于我们不希望休眠现在创建架构,因此我们应该禁用 ddl-auto 属性
  • 默认情况下,Spring Boot 仅初始化嵌入式数据库的数据源,此处的情况并非如此:
spring.sql.init.mode=always
spring.jpa.hibernate.ddl-auto=none

因此,我们现在应该能够正确启动应用程序,从终结点对 Principal 数据进行身份验证和检索。

另外,请注意,弹簧.sql.init.mode 属性是在弹簧启动 2.5.0 中引入的。对于早期版本,我们需要使用弹簧数据源初始化模式。

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

最后,我们的数据.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. 自定义搜索查询

调整查询非常容易。在配置身份验证管理器生成器时,我们只需要提供自己的 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. 结论

正如我们所看到的,这种方法比必须创建我们自己的用户详细信息服务实现要简单得多,这意味着一个艰巨的过程;创建实现用户详细信息接口的实体和类,并将存储库添加到我们的项目中。

当然,缺点是,当我们的数据库或逻辑与Spring安全解决方案提供的默认策略不同时,它提供的灵活性很小

最后,我们可以看看 GitHub 存储库中的完整示例。我们甚至包括一个使用PostgreSQL的示例,我们在本教程中没有显示该示例,只是为了保持简单。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值