目录
一、引入依赖及原理简述
二、多用户维护配置
三、基础自定义配置 <— 你在这里 ( •̀ ω •́ )y
四、前后端分离及登录结果管理
五、角色权限管理基础


从此处开始,为新的原创内容,相关数据结构代码换了一套新的,与之前的代码关系不大了。
建议新建一个项目,将配置文件复制过来,然后按照步骤走。

六、RBAC 结构实现
七、自定义响应式登录与 JWT 配置
八、集成 Redis

Spring Security(三)基础自定义配置

博主前言:本以为这个就是代替传统 jwt 的插件,没想到复杂程度如此之高。Spring Security 本身是个高度自定义化的组件,必须花时间重点学习一下。以下为个人配置学习的流程,从零到权限管理、redis嵌入等步骤。
本文基于尚硅谷的 Spring Security 教程学习,文章与原教程有不小出入,仅供参考。
B站视频链接:尚硅谷Java项目SpringSecurity+OAuth2权限管理实战教程

​接下来说说 Spring Security 的各项基础配置。


一、过滤器链相关配置

  1. 过滤器链的配置代码

​ 在Config类中创建过滤器链Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Spring Security 过滤器链配置
* @param http 请求体
* @return 过滤器链
* @throws Exception 抛出异常
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 对来自 http/https 的请求的授权保护方法
.authorizeHttpRequests(authorize -> {
// 对所有请求均做授权保护,已认证的会自动授权
authorize.anyRequest().authenticated();
})
// 使用基本授权方式(弹出一个 dialog 窗口)
// .httpBasic(Customizer.withDefaults())
// 使用网页表单授权方式(生成默认登录登出页面)
.formLogin(Customizer.withDefaults());
return http.build();
}
  1. CSRF(跨站请求伪造)攻击

CSRF跨站请求伪造详解(稀土掘金)

简单来说,就是你登录正常网站,获得了其安全凭证,然后访问了一个恶意网站,恶意网站就能借着你已获得的安全凭证偷偷地访问正常网站来干坏事。

换个角度来讲,正常网站仅仅是将安全凭证发给了你的“浏览器”,至于是你还是恶意网站使用,正常网站并不知道。

​ Spring Security 的过滤器链中自带CSRF的处理方法。但在开发过程中,我们往往会使用第三方 API 管理工具来调试程序(swagger、postman、apifox等),这些工具在CSRF流程中扮演攻击者的角色,所以会被 Spring Security 拦截,我们得手动关掉CSRF的处理方法。

不要忘记在项目上线时重新开启!!!!!

1
2
// 关掉 csrf 用于本地调试(务必在项目正式上线时开启!!!)
http.csrf(AbstractHttpConfigurer::disable)
  1. 前后端不分离,自定义登录页

​ 只需修改过滤器链相关配置即可,注意需要对相关url进行放行操作

1
2
3
4
5
6
7
// 使用网页表单授权方式
http.formLogin(form -> {
// 指定自定义登录页的 url,并放行相关 url
form.loginPage("/login").permitAll()
// 定义登录错误 url
.failureUrl("/login?error");
})

二、密码加密算法

这里仅讲解为什么要密码加密及为什么要配置加密方法

​ 对于用户的密码,我们不能明文存储,一旦数据泄露,那天都塌了,所以我们得对密码加密,然后再存入数据库。

​ 对于加密算法也有更高的要求:必须是单向加密,即只能加密,不能解密。若要比较密码是否相符,只需将输入值加密,再将加密的输入值与加密的密码进行比较即可,这个流程不会泄露密码明文。

​ 加密工具使用例:

实际上,Spring Security 支持很多加密工具,但我测试了一大堆,不是被标记已弃用,就是要引入额外依赖。BCryptPasswordEncoder是目前 Spring Security 自带并推荐使用的加密工具。

1
2
3
4
5
6
7
8
@Test
void passwordEncodersTest() {
var passwordSource = "password";

// BCrypt 加密工具
var bcryptEncoder = new BCryptPasswordEncoder();
System.out.println("bcrypt: " + bcryptEncoder.encode(passwordSource));
}

​ 我们发现,上述使用例的的加密工具并不是静态方法,原因是此类加密算法的原理是动态函数,会根据计算机的性能来动态适应,避免被暴力破解。BCryptPasswordEncoder可以接受一个参数,用来定义解析所需的时间,所以我们不仅不能静态使用,还要为其定义Bean,确保我们使用的与 Spring Security 配置的加密工具是同一个,避免差异带来的错误。

BCryptPasswordEncoder的这个参数最小4,最大31,默认10,值越大解析耗时越长。

Spring Security 推荐解析时间为1s(即默认值)。

​ 在Config类中定义Bean

1
2
3
4
5
6
7
8
/**
* 指定密码加密器
* @return BCrypt
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

​ 在用户服务层类中实现新增用户接口,并使用加密工具

注意,指定加密工具后。数据库密码字段的{bcrypt}前缀应去除,因为加密工具已指定,不需要前缀来识别密码的加密类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
private PasswordEncoder passwordEncoder;

@Override
public void addUser(User user) {
var checkUser = getByUsername(user.getUsername());
if (checkUser != null)
throw new RuntimeException("用户已存在");
user.setPassword(passwordEncoder.encode(user.getPassword()));
save(user);
}

private User getByUsername(String username) {
return this.getOne(new LambdaQueryWrapper<User>()
.eq(User::getUsername, username));
}
}