一、OverView

​ 在上一节中,使用了第一种方式:JDBC 连接数据库。这一节中将使用 Spring Data JPA 进行数据库操作。如果对 JPA 的环境和操作没了解过的,可以适当参考这个

在上一节也提到 Spring Security 支持多种数据源,这些不同来源的数据被共同封装成了一个 UserDetailService 接口

image-20201010091945655

​ 在这一节中,还是这个思路,这一次不用 Spring Security 提供的类, 可以自定义一个类去实现 UserDetailService

二、环境搭建

思路清晰,先看依赖

image-20201010133400947

application.properties

1
2
3
4
5
6
7
8
9
spring.datasource.username=root
spring.datasource.password=
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai

spring.jpa.database=mysql
spring.jpa.database-platform=mysql
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

实体类

User

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@Entity(name = "t_user")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private boolean enabled;

// User 和 Role 是多对多关系
@ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
private List<Role> roles;

// 返回用户权限
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : getRoles()) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}

@Override
public String getPassword() {
return password;
}

@Override
public String getUsername() {
return username;
}

// 用户是否过期
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}

// 用户是否被锁
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}

// 密码是否过期
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}

// 账户是否可用
@Override
public boolean isEnabled() {
return enabled;
}

// set get 省略
}

注:这里为什么 User 要实现 UserDetails ? 主要是由于在自定义类实现 UserDetailService,其中只有唯一的抽象方法loadUserByUsername,而该方法就是返回的 UserDetails

Role

1
2
3
4
5
6
7
8
@Entity(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String nameZh;
}

UserDao

1
2
3
4
5
6
7
8
9
10
public interface UserDao extends JpaRepository<User, Long> {

/**
* 通过用户名找到用户
*
* @param username 用户名
* @return {@link User}
*/
User findAllByUsername(String username);
}

不懂为啥这么写的可以参考一下 JPA 的 CRUD 操作

UserService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Service
public class UserService implements UserDetailsService{

private UserDao userDao;

@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userDao.findAllByUsername(s);
if (StringUtils.isEmpty(user)) {
throw new UsernameNotFoundException("用户不存在");
}
return user;
}

/**
* 保存用户
*
* @param user 用户
* @return {@link User}
*/
User save(User user) {
return userDao.save(user);
}
}

注:

  • 实现了 UserDetailsService,重写其中的 loadUserByUsername 方法:通过传入的用户名查找用户信息
  • save 方法是为了后面写入不同角色用户

UserController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
public class UserController {

private UserService userService;

@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}

@PutMapping("/save")
public User save(@RequestBody User user) {
return userService.save(user);
}
}

SecurityConfig 中 configure 方法改为:

1
2
3
4
5
6
7
8
9
10
11
private UserService userService;

@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}

三、Test

Postman

由于在未登录的情况下,直接调用 localhost:8080/save 接口会失败,所以先在数据库中手动添加一个数据:

user 表:

image-20201010155707544

role 表:

image-20201010155725922

user_role 关系表:

image-20201010155742141

使用上述增加的用户登录:

image-20201010160123372

增加不同角色用户:

user 用户

image-20201010160316590

admin用户

image-20201010160434975

看下数据库:

user表

image-20201010160555645

role表

![image-20201010160613255](/Users/wangdechao/Library/Application Support/typora-user-images/image-20201010160613255.png)

user_role 关系表

image-20201010160628692

此时在分别使用 user 用户和 admin 用户登录,看一下不同用户的权限:

user用户

image-20201010160849245

user权限接口

image-20201010160932135

admin权限接口

image-20201010161012247

评论