目录
一、简介与配置 <— 你在这里 ( •̀ ω •́ )y
二、实现登录注销操作
三、权限认证构成与鉴权方法
四、RBAC 结构实现

Sa-Token(一)简介与配置

[!NOTE]

博主前言:Spring Security 的学习真是惊掉我下巴,就一个鉴权功能,搞那么多复杂概念,又是过滤器链又是注入的,花了我一个星期左右,实际上还很不好用(未知错误给前端返回 401 是真绷不住)。Sa-Token 作为国产鉴权,性能更强的同时简化开发的程度相比 Spring Security 起码有十倍不止。最近我还注意到对标 Spring 生态的 Solon 框架,也是国产,性能提升3倍多,内存占用减少 50%+,打包还更小,真不知道这些工作为什么老外都做的这么复杂。

一、Sa-Token 简介及作用

以下摘抄自官网:

Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证权限认证单点登录OAuth2.0分布式Session会话微服务网关鉴权 等一系列权限相关问题。

Sa-Token 旨在以简单、优雅的方式完成系统的权限认证部分,以登录认证为例,你只需要:

1
2
// 会话登录,参数填登录人的账号id 
StpUtil.login(10001);

无需实现任何接口,无需创建任何配置文件,只需要这一句静态代码的调用,便可以完成会话登录认证。

如果一个接口需要登录后才能访问,我们只需调用以下代码:

1
2
// 校验当前客户端是否已经登录,如果未登录则抛出 `NotLoginException` 异常
StpUtil.checkLogin();

在 Sa-Token 中,大多数功能都可以一行代码解决。

十分地简洁,简洁到无话可说。对比 Spring Security ,其配置工作简直抓狂,来看看 Spring Security 的配置代码(之一):

[!TIP]

下述代码还只是配置,你还得手写登录状态持久化,手写权限配置,手写异常处理……简直是暴殄天物,一相对比高下立判。

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
/**
* Spring Security 配置
*
* @author Amane64
*/
@Configuration
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
private final SecurityResultHandler securityResultHandler;

/**
* 密码加密器
*
* @return BCrypt
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

/**
* Spring Security 过滤器链配置
*
* @param http 请求体
* @return 过滤器链
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
// 关掉 csrf 用于本地调试(务必在项目正式上线时开启!!!)
.csrf(AbstractHttpConfigurer::disable)
// 准许跨域访问
.cors(Customizer.withDefaults())
// 对来自 http/https 的请求的授权保护方法
.authorizeHttpRequests(authorize -> authorize
// 配置不同终端访问权限
.requestMatchers("/manager/**").hasRole("ADMIN")
.requestMatchers("/client/**").hasRole("USER")
// 放行登录接口
.requestMatchers("/login").permitAll()
// 对所有请求均做授权保护,已认证的会自动授权
.anyRequest().authenticated())
// 禁用表单授权登录
.formLogin(AbstractHttpConfigurer::disable)
// 禁用 HTTP Basic 登录
.httpBasic(AbstractHttpConfigurer::disable)
// 自定义异常处理
.exceptionHandling(exception -> exception
// 登录异常(请求未认证)处理
.authenticationEntryPoint(securityResultHandler)
// 无权限访问处理
.accessDeniedHandler(securityResultHandler)
)
// 注册 jwt 拦截器
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}

总之,不愧为轻量级鉴权框架,近期各大 git 平台的 star 数暴涨,相信过不了多久将会成为主流鉴权框架。赶紧丢弃老旧的 Spring Security 吧,我们先进的 Sa-Token 已经完全超越了老旧的 Spring Security。


二、集成配置

首先构建一个 web 应用。

[!IMPORTANT]

博主使用 Java 17,Spring boot 3.4.1 框架,所用依赖 maven 配置参考:

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- Spring Boot Starter Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>

<!-- Spring Boot Starter Validation -->
<!-- 用于给 apifox 的插件打标签 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
</dependency>

<!-- Mysql -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
</dependency>

<!-- Mybatis-Plus Spring Boot3 引入可选模块 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>

<!-- Mybatis-Plus jdk 11+ 引入可选模块 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
</dependency>

<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.41</version>
</dependency>

</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.4.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-bom</artifactId>
<version>3.5.10.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

应用构建好之后,我们正式开始。

  1. 引入相关依赖

[!WARNING]

这一项,spring-boot后面有个3,若使用spring boot 2.x 框架,请去掉 3

1
2
3
4
5
6
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>1.40.0</version>
</dependency>
  1. 修改配置文件(application.yml)

可以零配置启动项目,但还是建议做一些配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Sa-Token 配置 (文档: https://sa-token.cc)
sa-token:
# token 名称(同时也是 cookie 名称)
token-name: sa-token
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
timeout: -1
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: -1
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
is-share: true
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
token-style: uuid
# 是否输出操作日志
is-log: true
  1. 创建测试Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
@RequestMapping("/test")
public class TestController {

@PostMapping("doLogin")
public String doLogin(String username, String password) {
// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
if("zhang".equals(username) && "123456".equals(password)) {
StpUtil.login(10001);
return "登录成功";
}
return "登录失败";
}

@GetMapping("isLogin")
public String isLogin() {
return "当前会话是否登录:" + StpUtil.isLogin();
}
}
  1. 测试运行

登录成功

验证登录