Spring Security专栏(Spring Security 用户认证体系)

787 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

写在前面

承接上文,上文讲到配置体系,今天主要讲解认证体系。感兴趣的小伙伴可以点击头像查看我专栏和历史文章。

Spring Security 用户认证体系

回到用户认证场景。因为 Spring Security 默认提供的用户名是固定的,而密码会随着每次应用程序的启动而变化,所以很不灵活。在 Spring Boot 中。

我们可以通过在 application.yml 配置文件中添加如下所示的配置项来改变这种默认行为:

spring:
  security:
    user:
      name: spring
      password: spring_password

我们可以使用上述用户名和密码完成登录。基于配置文件的用户信息存储方案简单直接,但显然也缺乏灵活性,因为我们无法在系统运行时动态加载对应的用户名和密码。

因此,在现实中,我们主要还是通过使用 WebSecurityConfigurerAdapter 配置类来改变默认的配置行为。

前面我们说过,知道可以通过 WebSecurityConfigurerAdapter 类的 configure(HttpSecurity http) 方法来完成认证。

认证过程涉及 Spring Security 中用户信息的交互,我们可以通过继承 WebSecurityConfigurerAdapter 类并且覆写其中的configure(AuthenticationManagerBuilder auth) 的方法来完成对用户信息的配置工作。请注意这是两个不同的 configure() 方法。

两种方案

针对 WebSecurityConfigurer 配置类,我们首先需要明确配置的内容。实际上,初始化用户信息非常简单,只需要指定用户名(Username)、密码(Password)和角色(Role)这三项数据即可。在 Spring Security 中,基于 AuthenticationManagerBuilder 工具类为开发人员提供了基于内存、JDBC、LDAP 等多种验证方案。

接下来,我们就围绕 AuthenticationManagerBuilder 提供的功能来实现多种用户信息存储方案。

基于内存的用户信息存储方案

实现方法就是调用 AuthenticationManagerBuilder 的 inMemoryAuthentication 方法,示例代码如下:

@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
 
    builder.inMemoryAuthentication()
        .withUser("spring_user").password("password1").roles("USER")
        .and()
        .withUser("spring_admin").password("password2").roles("USER", "ADMIN");
}

从上面的代码中,我们可以看到系统中存在“spring_user”和“spring_admin”这两个用户,其密码分别是"password1"和"password2",在角色上也分别代表着普通用户 USER 以及管理员 ADMIN。

请注意,这里的 roles() 方法背后使用的还是authorities() 方法。通过 roles() 方法,Spring Security 会在每个角色名称前自动添加“ROLE_”前缀,所以我们也可以通过如下所示的代码实现同样的功能:

@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
 
    builder.inMemoryAuthentication()
         .withUser("spring_user").password("password1").authorities("ROLE_USER")
         .and()
         .withUser("spring_admin").password("password2").authorities("ROLE_USER", "ROLE_ADMIN");
}

可以看到,基于内存的用户信息存储方案实现也比较简单,但同样缺乏灵活性,因为用户信息是写死在代码里的。所以,我们接下来就要引出另一种更为常见的用户信息存储方案——数据库存储。

基于数据库的用户信息存储方案

既然是将用户信息存储在数据库中,势必需要创建表结构。我们可以在 Spring Security 的源文件(org/springframework/security/core/userdetails/jdbc/users.ddl)中找到对应的 SQL 语句,如下所示:

create table users(username varchar_ignorecase(50) not null primary key,password varchar_ignorecase(500) not null,enabled boolean not null);
 
create table authorities (username varchar_ignorecase(50) not null,authority varchar_ignorecase(50) not null,constraint fk_authorities_users foreign key(username) references users(username));
 
create unique index ix_auth_username on authorities (username,authority);

一旦我们在自己的数据库中创建了这两张表,并添加了相应的数据,就可以直接通过注入一个 DataSource 对象进行用户数据的查询,如下所示:

@Autowired
DataSource dataSource;
 
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
 
        auth.jdbcAuthentication().dataSource(dataSource)
               .usersByUsernameQuery("select username, password, enabled from Users " + "where username=?")
               .authoritiesByUsernameQuery("select username, authority from UserAuthorities " + "where username=?")
               .passwordEncoder(new BCryptPasswordEncoder());
}

这里使用了 AuthenticationManagerBuilder 的 jdbcAuthentication 方法来配置数据库认证方式,内部则使用了 JdbcUserDetailsManager 这个工具类。在该类中,就定义了各种用于数据库查询的 SQL 语句,以及使用 JdbcTemplate 完成数据库访问的具体实现方法。

请你注意,这里我们用到了一个passwordEncoder() 方法,这是 Spring Security 中提供的一个密码加解密器,我们会在“密码安全:Spring Security 中包含哪些加解密技术?”一讲中进行详细的讨论。

总结

我通过两篇文章介绍了如何使用 Spring Security 构建用户认证体系的系统方法。

在 Spring Security 中,认证相关的功能都是可以通过配置体系进行定制化开发和管理的。通过简单的配置方法,我们可以组合使用 HTTP 基础认证和表单登录认证,也可以分别基于内存以及基于数据库方案来存储用户信息,这些功能都是 Spring Security 内置的。

我们下篇文章接着讲深入理解,欢迎大家评论一起学习

弦外之音

感谢你的阅读,如果你感觉学到了东西,麻烦您点赞,关注。也欢迎有问题我们下面评论交流

加油! 我们下期再见!

给大家分享几个我前面写的几篇骚操作

聊聊不一样的策略模式(值得收藏)

copy对象,这个操作有点骚!