Sa-Token使用指南
1. 基础配置
1.1 添加依赖
<!-- Sa-Token 权限认证 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.34.0</version>
</dependency>
<!-- Sa-Token 整合 Redis -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis</artifactId>
<version>1.34.0</version>
</dependency>
1.2 配置文件
# Sa-Token配置
sa-token:
# token名称
token-name: Authorization
# token有效期
timeout: 2592000
# token临时有效期
activity-timeout: -1
# 是否允许同一账号并发登录
is-concurrent: true
# 在多人登录同一账号时,是否共用一个token
is-share: false
# token风格
token-style: uuid
# 是否输出操作日志
is-log: false
# Redis配置
spring:
redis:
host: localhost
port: 6379
password:
database: 0
2. 基础使用
2.1 登录认证
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/login")
public Result login(@RequestBody LoginDTO loginDTO) {
// 验证用户名密码
if("admin".equals(loginDTO.getUsername()) && "123456".equals(loginDTO.getPassword())) {
// 登录成功,记录用户id
StpUtil.login(10001);
return Result.success();
}
return Result.error("登录失败");
}
@PostMapping("/logout")
public Result logout() {
StpUtil.logout();
return Result.success();
}
@GetMapping("/check")
public Result check() {
// 检查是否登录
if(StpUtil.isLogin()) {
return Result.success("已登录");
}
return Result.error("未登录");
}
}
2.2 权限认证
@RestController
@RequestMapping("/user")
public class UserController {
@SaCheckPermission("user:add")
@PostMapping("/add")
public Result add() {
// 需要user:add权限才能访问
return Result.success();
}
@SaCheckRole("admin")
@PostMapping("/delete")
public Result delete() {
// 需要admin角色才能访问
return Result.success();
}
}
3. 权限管理
3.1 角色权限配置
@Configuration
public class SaTokenConfig {
@Bean
public StpInterface stpInterface() {
return new StpInterface() {
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 返回此 loginId 拥有的权限列表
List<String> permissionList = new ArrayList<>();
if(loginId.equals(10001)) {
permissionList.add("user:add");
permissionList.add("user:delete");
}
return permissionList;
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
// 返回此 loginId 拥有的角色列表
List<String> roleList = new ArrayList<>();
if(loginId.equals(10001)) {
roleList.add("admin");
}
return roleList;
}
};
}
}
3.2 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NotLoginException.class)
public Result handleNotLoginException(NotLoginException e) {
return Result.error("未登录");
}
@ExceptionHandler(NotPermissionException.class)
public Result handleNotPermissionException(NotPermissionException e) {
return Result.error("无权限");
}
@ExceptionHandler(NotRoleException.class)
public Result handleNotRoleException(NotRoleException e) {
return Result.error("无角色");
}
}
4. 高级功能
4.1 记住我功能
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/login")
public Result login(@RequestBody LoginDTO loginDTO) {
if("admin".equals(loginDTO.getUsername()) && "123456".equals(loginDTO.getPassword())) {
// 登录时指定是否记住我
StpUtil.login(10001, loginDTO.isRememberMe());
return Result.success();
}
return Result.error("登录失败");
}
}
4.2 会话管理
@RestController
@RequestMapping("/session")
public class SessionController {
@GetMapping("/info")
public Result getSessionInfo() {
// 获取当前会话是否登录
boolean isLogin = StpUtil.isLogin();
// 获取当前会话账号id
Object loginId = StpUtil.getLoginId();
// 获取当前会话账号id(指定类型)
Integer loginIdAsInt = StpUtil.getLoginIdAsInt();
// 获取当前会话的token值
String tokenValue = StpUtil.getTokenValue();
// 获取当前会话的token信息
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
Map<String, Object> info = new HashMap<>();
info.put("isLogin", isLogin);
info.put("loginId", loginId);
info.put("loginIdAsInt", loginIdAsInt);
info.put("tokenValue", tokenValue);
info.put("tokenInfo", tokenInfo);
return Result.success(info);
}
}
4.3 踢人下线
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/kickout")
public Result kickout(@RequestParam Long userId) {
// 将指定账号踢下线
StpUtil.kickout(userId);
return Result.success();
}
@PostMapping("/kickoutAll")
public Result kickoutAll() {
// 将当前账号踢下线
StpUtil.kickout(StpUtil.getLoginIdAsLong());
return Result.success();
}
}
5. 与Spring Security集成
5.1 配置类
@Configuration
public class SaTokenConfig {
@Bean
public SaTokenContext getSaTokenContext() {
return new SaTokenContextForSpring();
}
}
5.2 拦截器配置
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册Sa-Token拦截器
registry.addInterceptor(new SaInterceptor(handle -> {
// 指定需要拦截的路径
SaRouter.match("/**")
// 排除登录接口
.notMatch("/auth/login")
// 检查是否登录
.check(r -> StpUtil.checkLogin());
})).addPathPatterns("/**");
}
}
6. 与Redis集成
6.1 配置类
@Configuration
public class SaTokenConfig {
@Bean
public SaTokenDao getSaTokenDao() {
return new SaTokenDaoRedis();
}
}
6.2 Redis配置
spring:
redis:
host: localhost
port: 6379
password:
database: 0
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
7. 实际应用示例
7.1 用户登录注册
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void register(UserRegisterDTO dto) {
// 检查用户名是否存在
if(userMapper.existsByUsername(dto.getUsername())) {
throw new BusinessException("用户名已存在");
}
// 创建用户
User user = new User();
user.setUsername(dto.getUsername());
user.setPassword(encryptPassword(dto.getPassword()));
userMapper.insert(user);
}
public LoginVO login(LoginDTO dto) {
// 查询用户
User user = userMapper.findByUsername(dto.getUsername());
if(user == null) {
throw new BusinessException("用户不存在");
}
// 验证密码
if(!user.getPassword().equals(encryptPassword(dto.getPassword()))) {
throw new BusinessException("密码错误");
}
// 登录
StpUtil.login(user.getId());
// 返回登录信息
LoginVO vo = new LoginVO();
vo.setToken(StpUtil.getTokenValue());
vo.setUserInfo(convertToVO(user));
return vo;
}
}
7.2 权限控制
@RestController
@RequestMapping("/api")
public class ApiController {
@SaCheckPermission("user:view")
@GetMapping("/user/info")
public Result getUserInfo() {
Long userId = StpUtil.getLoginIdAsLong();
User user = userService.getById(userId);
return Result.success(user);
}
@SaCheckRole("admin")
@PostMapping("/user/delete/{id}")
public Result deleteUser(@PathVariable Long id) {
userService.deleteById(id);
return Result.success();
}
}
8. 注意事项
安全性考虑
- 使用HTTPS传输
- 设置合理的token有效期
- 定期更换token
- 敏感操作需要二次验证
性能优化
- 合理使用Redis缓存
- 避免频繁的权限检查
- 使用token续期机制
异常处理
- 统一处理登录异常
- 统一处理权限异常
- 记录关键操作日志
最佳实践
- 遵循最小权限原则
- 实现细粒度的权限控制
- 做好会话管理
- 实现单点登录