本文我们将构建一个简单但完整的小型 Web 应用程序,以演示 Spring Security 的入门教程。系统大致逻辑是:当合法用户成功登录系统之后,浏览器会跳转到一个系统主页,并展示一些个人健康档案(HealthRecord)数据。
让我们开始吧!
系统初始化
这部分工作涉及领域对象的定义、数据库初始化脚本的整理以及相关依赖组件的引入。
针对领域对象,我们重点来看如下所示的 User 类定义:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
@Enumerated(EnumType.STRING)
private PasswordEncoderType passwordEncoderType;
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
private List authorities;
…
}
public enum PasswordEncoderType {
BCRYPT, SCRYPT
}
可以看到,这里除了指定主键 id、用户名 username 和密码 password 之外,还包含了一个加密算法枚举值 EncryptionAlgorithm。在案例系统中,我们将提供 BCryptPasswordEncoder 和 SCryptPasswordEncoder 这两种可用的密码解密器,你可以通过该枚举值进行设置。
同时,我们在 User 类中还发现了一个 Authority 列表。显然,这个列表用来指定该 User 所具备的权限信息。Authority 类的定义如下所示:
@Entity
public class Authority {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@JoinColumn(name = "user")
@ManyToOne
private User user;
…
}
通过定义不难看出 User 和 Authority 之间是一对多的关系。
基于 User 和 Authority 领域对象,我们也给出创建数据库表的 SQL 定义,如下所示:
CREATE TABLE IF NOT EXISTS `spring_security`.`user` (
`id` INT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(45) NOT NULL,
`password` TEXT NOT NULL,
`password_encoder_type` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`));
CREATE TABLE IF NOT EXISTS `spring_security`.`authority` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NOT NULL,
`user` INT NOT NULL,
PRIMARY KEY (`id`));
CREATE TABLE IF NOT EXISTS `spring_security`.`health_record` (
`id` INT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(45) NOT NULL,
`name` VARCHAR(45) NOT NULL,
`value` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`));
在运行系统之前,我们同样也需要初始化数据,对应脚本如下所示:
INSERT IGNORE INTO `spring_security`.`user` (`id`, `username`, `password`, `password_encoder_type`) VALUES ('1', 'studeyang', '$2a$10$xn3LI/AjqicFYZFruSwve.681477XaVNaUQbr1gioaWPn4t1KsnmG', 'BCRYPT');
INSERT IGNORE INTO `spring_security`.`authority` (`id`, `name`, `user`) VALUES ('1', 'READ', '1');
INSERT IGNORE INTO `spring_security`.`authority` (`id`, `name`, `user`) VALUES ('2', 'WRITE', '1');
INSERT IGNORE INTO `spring_security`.`health_record` (`id`, `username`, `name`, `value`) VALUES ('1', 'studeyang', 'weight', '70');
INSERT IGNORE INTO `spring_security`.`health_record` (`id`, `username`, `name`, `value`) VALUES ('2', 'studeyang', 'height', '177');
INSERT IGNORE INTO `spring_security`.`health_record` (`id`, `username`, `name`, `value`) VALUES ('3', 'studeyang', 'bloodpressure', '70');
INSERT IGNORE INTO `spring_security`.`health_record` (`id`, `username`, `name`, `value`) VALUES ('4', 'studeyang', 'pulse', '80');
这里初始化了一个用户名为 “studeyang”的用户,同时指定了它的密码为“12345”,加密算法为“BCRYPT”。
现在,领域对象和数据层面的初始化工作已经完成了,接下来我们需要在代码工程的 pom 文件中添加如下所示的 Maven 依赖:
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
runtime
org.springframework.security
spring-security-test
test
实现用户管理
实现自定义用户认证的过程通常涉及两大部分内容,一方面需要使用 User 和 Authority 对象来完成定制化的用户管理,另一方面需要把这个定制化的用户管理嵌入整个用户认证流程中。
如果你想实现自定义的用户信息,扩展 UserDetails 这个接口即可。实现方式如下所示:
public class CustomUserDetails implements UserDetails {
private final User user;
public CustomUserDetails(User user) {
this.user = user;
}
@Override
public Collection