目录

[TOC]

官方文档

https://sa-token.cc

SpringBoot前后端分离

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 版本控制 -->
<properties>
<sa-token.version>1.37.0</sa-token.version>
</properties>

<dependencies>
<!-- Sa-Token 权限认证授权 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>

<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>${sa-token.version}</version>
</dependency>
</dependencies>

配置文件

配置了sa-token.token-prefix的值,前端在请求头必须添加如下信息

headerName:sa-token.token-name的值

headerValue:sa-token.token-prefix + 空格 + 登录成功后端返回的token值

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
spring:
redis:
host: 127.0.0.1
port: 6379
#password: 123456
database: 0
# lettuce redis数据库连接池和jedis二选一配置,如果用lettuce,还需要再引入org.apache.commons下的commons-pool2
# lettuce:
# pool:
# max-active: 100 # 连接池最大连接数(使用负值表示没有限制)
# max-idle: 100 # 连接池中的最大空闲连接
# min-idle: 10 # 连接池中的最小空闲连接
# max-wait: 5000ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
#jedis数据库连接池,和lettuce二选一配置
jedis:
pool:
max-active: 100
max-idle: 100
max-wait: 50
min-idle: 10

############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token:
# token前缀
token-prefix: Bearer
# token 名称(同时也是 cookie 名称)
token-name: sa-token-study
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
timeout: 2592000
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: -1
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
is-concurrent: false
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
is-share: true
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
token-style: simple-uuid
# 是否输出操作日志
is-log: true

配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* sa-token 拦截器
*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
//拦截所有接口
.addPathPatterns("/**")
//排除接口
.excludePathPatterns("/login");
}
}

认证

登录信息接收实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import lombok.Data;

/**
* 用户登录
*/
@Data
public class LoginInfoVO {

/**
* 用户名
*/
private String username;

/**
* 密码
*/
private String password;

/**
* 登录设备型号:PC,APP 等等
*/
private String device;
}

登录控制器

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
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.xiaofei.login.entity.UserEntity;
import com.xiaofei.login.service.UserService;
import com.xiaofei.login.utils.ResponseUtils;
import com.xiaofei.login.vo.LoginInfoVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
* 登录控制器
*/
@Api(tags = "登录")
@RestController
public class LoginController {

@Autowired
private UserService userService;

@ApiOperation(value = "用户登录", httpMethod = "POST", response = ResponseUtils.class, produces = "application/json")
@PostMapping("/login")
public ResponseUtils login(@RequestBody LoginInfoVO loginInfoVO) throws IOException {

// 查询数据库
UserEntity userEntity = new UserEntity();
userEntity.setUsername(loginInfoVO.getUsername());
userEntity.setPassword(loginInfoVO.getPassword());
UserEntity respUser = userService.selectOne(userEntity);

// 判断数据库中是否有该用户信息
if (respUser != null) {
// 第1步,先登录上
StpUtil.login(respUser.getId()
, new SaLoginModel()
// 此次登录的客户端设备类型, 用于[同端互斥登录]时指定此次登录的设备类型
.setDevice(loginInfoVO.getDevice())
// 指定此次登录token的有效期, 单位:秒 (如未指定,自动取全局配置的 timeout 值)
.setTimeout(60 * 60 * 12)
);
;
// 第2步,获取 Token 相关参数
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
// 第3步,返回给前端
return ResponseUtils.success(SaResult.data(tokenInfo));
}
throw new RuntimeException("用户名或密码错误");
}

@ApiOperation(value = "检查用户是否登录", httpMethod = "POST", response = ResponseUtils.class, produces = "application/json")
@PostMapping("/login/check")
public ResponseUtils loginCheck(@RequestBody LoginInfoVO loginInfoVO) throws IOException {
return ResponseUtils.success("当前会话是否登录:" + StpUtil.isLogin());
}
}

测试

登录

登录参数

1
2
3
4
5
{
"device": "PC",
"password": "bMpiG70Fc0",
"username": "Teresa Marshall"
}

image-20231028222653293

检查用户登录状态

当用户登录成功后,会给前端返回一个token,前端请求后端的时候,需要按照下面的格式将token的值放在请求头里面将值传给后端,具体查看配置文件中的配置

image-20231028222937215

授权

当登录成功后,访问的请求设置了访问权限,才会触发该接口进行权限校验

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
/**
* 自定义权限加载接口实现类
*/
@Component // 保证此类被 SpringBoot 扫描,完成 Sa-Token 的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {

/**
* 返回一个账号所拥有的权限码集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
List<String> list = new ArrayList<String>();

// 从数据库中查询权限信息

return list;
}

/**
* 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
List<String> list = new ArrayList<String>();

// 从数据库中查询权限信息

return list;
}
}