spring-boot-starter-security:应用安全
spring-boot-starter-security:应用安全
在 pom.xml
中添加依赖 spring-boot-starter-security
该依赖介绍:一般是企业在页面开发过程中安全管理所用的,Spring Boot权限框架,对开发者更友好的分布式权限验证框架,极大的提高验证效率
添加依赖后重新启动springboot并访问地址,会出现一个登录页面
默认的用户名:
user
在
idea
控制台可以看到动态生成的密码Using generated security password:
但是通过修改 Spring Security
,可以将该技术用于实现:更安全地判断用户登录等操作
改造Spring Security
1. 新建一个 service.impl.UserDetailsServiceImpl
类,继承自 security
的 UserDetailsService
接口
1
2
3
4
5
6
package com.lwd.backend.service.impl;
import org.springframework.security.core.userdetails.UserDetailsService;
public class UserDetailsServiceImpl implements UserDetailsService {
}写完上述代码会爆红,因为继承接口后没有实现方法,需要用
Alt + Enter
,然后选择实现方法才能补全
service.impl.UserDetailsServiceImpl
类代码
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
package com.lwd.backend.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lwd.backend.mapper.UserMapper;
import com.lwd.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 这里写User要看清楚是引用哪里的User!
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
// 前端传回一个username。从数据库中查找是否存在一条记录的username等于前端传回的username
User user = userMapper.selectOne(queryWrapper);
if (user == null) {
throw new RuntimeException("用户不存在!");
}
return null;
}
}从该段代码中,我发现每个实现类的开头,都需要加一个注解,例如这里我加了@Service用于表示service层,不加的话,使用@Autowired就会报错。Conroller注解与RestController注解、Component注解
也可以使用万能的@Component来泛指各种类
该段代码,可以将前端获取的username与后端数据库相对比,若数据库中无该username,则抛出”用户不存在”。
2. 新建一个 service.impl.utils.UserDetailsImpl
类,继承自 security
的 UserDetails
接口
1
2
3
4
5
6
package com.lwd.backend.service.impl.utils;
import org.springframework.security.core.userdetails.UserDetails;
public class UserDetailsImpl implements UserDetails {
}写完上述代码会爆红,因为继承接口后没有实现方法,需要用
Alt + Enter
,然后选择实现方法才能补全这个类可以定义用户的各种属性,在补全代码后,将以下几个属性设置为
true
isEnabled
、isCredentialsNonExpired
、isAccountNonLocked
、isAccountNonExpired
在类中添加/更改以下代码
1
2
3
4
5
6
7
8
9
10
private User user; // 这个User是pojo的
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
完成1,2两个类的初步编写后,我们需要将其连接。
也就是在
service.impl.UserDetailsServiceImpl
中,我们已经写了如果查询数据库找不到用户username时抛出异常”用户不存在”,但若用户存在的代码还没有编写完,所有更新一下UserDetailsServiceImpl
类最后return
返回的内容,而这个内容就是UserDetailsImpl
类中实现的用户属性
1
2
3
4
5
if (user == null) {
throw new RuntimeException("用户不存在!");
}
return new UserDetailsImpl(user);
//将通过username在数据库中查到的用户信息,传入UserDetailsImpl中,从而跟赋予用户属性写完这段会发现
UserDetailsImpl(user)
爆红,因为这个user
是由pojo
的User
类定义的,所以须在UserDetailsImpl
类的开头,加上和User
类一样的三个注解(小部分代码)
1
2
3
4
5
@Data
@NoArgsConstructor
@AllArgsConstructor
// 需添加上面三个注解
public class UserDetailsImpl implements UserDetails {
至此,便完成了 Spring Security
的一个改造:原先 Spring Security
是自己定义一个用户 user
,和动态生成一串密码。但是现在,Spring Security
会根据用户输入的 username
和 password
去我们的数据库中查找,若查找成功,则会通过 UserDetailsImpl
类去给这个用户赋予一些属性。
不过这个修改只是对后端进行修改,而用户输入 username
和 password
时的前端界面,还是 Spring Security
自带的,当然我们也可以对前端进行修改,写一个我们自己的前端登录界面。
但是,此时我们再次运行SpringBoot,会发现还是无法登录,控制台报错:There is no PasswordEncoder mapped for the id "null"
我们可以看一下 Spring Security
的官方文档
1 |
|
这个错误是说:现如今 Spring Security
中密码的存储格式是 {id}encodedPassword
前面的 id
是加密方式,id
可以是 bcrypt
、sha256
等,后面跟着的是加密后的密码
也就是说: Spring Security
拿到密码时,会首先查找被 {}
包起来的 id
,从而来确定后面的密码是被怎么样加密的,如果找不到就认为id是null
而我们目前还没有任何加密方式,所以可以选择官方文档中提供的 {noop}password
格式来对数据库中的用户的password直接手动修改。
实现密码加密
我们要在 controller.user.UserController
类中,添加一个简易版的注册用户功能,并在用户输入密码时,将密码通过 bcrypt
加密,并将加密后的密码存到数据库中。
1 |
|
用户直接在url中给出 userId
、username
、password
,之后,通过加密,将含有密码密文的用户信息新增到user表中,即为注册成功。
完成密码加密后,则需告诉 Spring Security
采用 bcrypt
进行密码解密
新建一个 config.SecurityConfig
类
1 |
|
完成上述代码后,就相当于告诉 Spring Security
了,采用 bcrypt
进行密码解密。
虽然上述问题已解决,但存在一个bug,那就是必须要先有一个账号,才能注册新的账号。之后会修好这个bug的
本博客所有文章除特别声明外,转载请注明出处!