Spring Security、Apache Shiro、Sa-Token是Java Web开发中常用的主流权限认证框架。
三种框架的上手难度依次递减:Spring Security > Apache Shiro > Sa-Token
如果你使用过Apache Shiro,你会发现:
- Sa-Token和Apache Shiro有许多相似点
- 通配符权限:分隔符不同
- Sa-Token:user.*、user.login
- Apache Shiro:user:*、user:login
- 通配符权限:分隔符不同
- Apache Shiro对于前后端分离项目不友好
本篇文章将结合博主的Sa-Token框架使用经验和官方文档,通过一整篇文章详细介绍在Java开发神器(Spring Boot)中使用Sa-Token来完成权限认证功能。
创建项目
在Intellij IDEA中创建一个Maven项目,名字就叫sa-token-study。
在这里就不直接通过Spring Initializer创建Spring Boot项目了,因为默认会使用较新的Spring Boot版本,所以会触发Maven依赖包的自动下载。
添加依赖
在Maven的配置文件pom.xml中,添加Sa-Token的必要依赖。
设置当前Maven继承spring-boot-starter-parent,添加Spring Boot的启动器依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<version>2.3.4.RELEASE</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
</parent>
<version>20260601</version>
<groupId>cn.edu.sgu.www</groupId>
<artifactId>sa-token-study</artifactId>
<description>Spring Boot整合Sa-Token框架</description>
<properties>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Sa-Token -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>
</dependencies>
</project>
修改配置
创建配置文件
因为这是一个Maven项目,Spring Boot的配置文件需要手动创建。
在src/main/resources目录下创建一个application.yml文件。

添加框架配置
直接从Sa-Token官方文档复制配置文件内容,把配置sa-token.token-name的值改成了项目名。
server:
# 端口
port: 8081
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token:
# token 名称(同时也是 cookie 名称)
token-name: sa-token-study
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
timeout: 2592000
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: -1
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
is-share: false
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
token-style: uuid
# 是否输出操作日志
is-log: true
补全项目
将当前Maven项目完全变成Spring Boot项目。
创建项目根包
在src/main/java包下创建多级包cn.edu.sgu.www.study

创建启动类
在项目根包cn.edu.sgu.www.study下创建Spring Boot启动类。
package cn.edu.sgu.www.study;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author 沐雨橙风ιε
* @version 1.0
*/
@SpringBootApplication
public class SaTokenStudyApplication {
public static void main(String[] args) {
SpringApplication.run(SaTokenStudyApplication.class, args);
}
}
登录认证
Sa-Token中登录认证只需要一句代码,调用StpUtil工具类的login()方法,把账号ID作为参数。
StpUtil.login(Object userId);
案例代码:登录ID为10086的账号
StpUtil.login(10086);
保存用户信息
通常,我们希望像HttpSession一样,将用户的信息保存到当前会话中,以便在其他地方使用。
SaSession session = StpUtil.getSession();
User user = new User();
session.set("user", user);
获取用户信息
SaSession session = StpUtil.getSession();
Object obj = session.get("user");
User user = (User) obj;
// todo:访问user
登录认证案例
创建控制器类
在项目根包cn.edu.sgu.www.study下创建controller.LoginController类。
定义一个登录方法login(),该方法接收一个userId参数,并且该参数是必须的。
package cn.edu.sgu.www.study.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 沐雨橙风ιε
* @version 1.0
*/
@RestController
public class LoginController {
@GetMapping("/login")
public SaResult login(@RequestParam String userId) {
StpUtil.login(userId);
return SaResult.ok("登录成功");
}
}
测试登录接口
运行启动类SaTokenStudyApplication的main()方法,启动当前Spring Boot项目。
在浏览器地址栏输入以下网址访问登录接口
http://localhost:8081/login?userId=10086

在Intellij IDEA的控制台,可以看到Sa-Token框架登录时打印的会话日志。

权限认证
权限校验
可以使用以下方法来完成权限的校验。
// 获取:当前账号所拥有的权限集合
StpUtil.getPermissionList();
// 判断:当前账号是否含有指定权限, 返回 true 或 false
StpUtil.hasPermission("user.insert");
// 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
StpUtil.checkPermission("user.insert");
// 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
StpUtil.checkPermissionAnd("user.insert", "user.delete", "user.update");
// 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
StpUtil.checkPermissionOr("user.insert", "user.delete", "user.update");
角色校验
可以使用以下方法来完成角色的校验。
// 获取:当前账号所拥有的角色集合
StpUtil.getRoleList();
// 判断:当前账号是否拥有指定角色, 返回 true 或 false
StpUtil.hasRole("sys-admin");
// 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
StpUtil.checkRole("sys-admin");
// 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
StpUtil.checkRoleAnd("sys-admin", "sys-user");
// 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
StpUtil.checkRoleOr("sys-admin", "sys-user");
提供权限
可以通过提供一个StpInterface类型的Bean来根据账号ID和登录类型为对应账号设置权限和角色。
package cn.edu.sgu.www.study.support;
import cn.dev33.satoken.stp.StpInterface;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* 权限数据源加载类
* @author 沐雨橙风ιε
* @version 1.0
*/
@Component
public class StpInterfaceImpl implements StpInterface {
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
List<String> list = new ArrayList<>();
list.add("user.insert");
list.add("user.delete");
list.add("user.update");
return list;
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
List<String> list = new ArrayList<>();
list.add("sys-admin");
list.add("sys-user");
return list;
}
}
鉴权方式
注解鉴权
Sa-Token框架提供了以下注解用于鉴权,只需要在对应的控制器接口方法上使用这些注解。
常用的注解
@SaIgnore:忽略校验 —— 表示被修饰的方法或类无需进行注解鉴权和路由拦截器鉴权。@SaCheckLogin:登录校验 —— 只有登录之后才能进入该方法。@SaCheckRole("admin"):角色校验 —— 必须具有指定角色标识才能进入该方法。@SaCheckPermission("user:add"):权限校验 —— 必须具有指定权限才能进入该方法。
不常用的注解
@SaCheckSafe:二级认证校验 —— 必须二级认证之后才能进入该方法。@SaCheckHttpBasic:HttpBasic校验 —— 只有通过 HttpBasic 认证后才能进入该方法。@SaCheckHttpDigest:HttpDigest校验 —— 只有通过 HttpDigest 认证后才能进入该方法。@SaCheckDisable("comment"):账号服务封禁校验 —— 校验当前账号指定服务是否被封禁。@SaCheckSign:API 签名校验 —— 用于跨系统的 API 签名参数校验。
创建控制器
在项目根包cn.edu.sgu.www.study.controller下创建UserController类。
增加3个HTTP接口:/user/insert、/user/delete、/user/update
为了能够直接通过浏览器地址栏访问,将接口的HTTP请求方法都设置为GET。
package cn.edu.sgu.www.study.controller;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 沐雨橙风ιε
* @version 1.0
*/
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/insert")
public SaResult insert() {
System.out.println("添加用户...");
return SaResult.ok("添加成功");
}
@GetMapping("/delete")
public SaResult delete() {
System.out.println("删除用户...");
return SaResult.ok("删除成功");
}
@GetMapping("/update")
public SaResult update() {
System.out.println("修改用户...");
return SaResult.ok("修改成功");
}
}
注册拦截器
注解鉴权是基于AOP(拦截器)完成的,需要添加Sa-Token的拦截器到Spring MVC中。
package cn.edu.sgu.www.study.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Spring Web MVC配置类
* @author 沐雨橙风ιε
* @version 1.0
*/
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册Sa-Token拦截器,打开注解式鉴权功能
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
}
使用鉴权注解
在三个接口上使用@SaCheckPermission注解,配置需要指定的权限才能访问对应的方法。
package cn.edu.sgu.www.study.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 沐雨橙风ιε
* @version 1.0
*/
@RestController
@RequestMapping("/user")
public class UserController {
@SaCheckPermission("user.insert")
@GetMapping("/insert")
public SaResult insert() {
System.out.println("添加用户...");
return SaResult.ok("添加成功");
}
@SaCheckPermission("user.delete")
@GetMapping("/delete")
public SaResult delete() {
System.out.println("删除用户...");
return SaResult.ok("删除成功");
}
@SaCheckPermission("user.update")
@GetMapping("/update")
public SaResult update() {
System.out.println("修改用户...");
return SaResult.ok("修改成功");
}
}
测试注解鉴权
启动当前Spring Boot项目,在浏览器地址栏输入以下网址分别访问三个接口。
http://localhost:8081/user/insert
http://localhost:8081/user/delete
http://localhost:8081/user/update
访问失败
访问3个接口都触发了服务器异常。



Intellij IDEA的控制台打印了3个一样的异常的栈信息,这是因为没有登录,之前登录的token已经在项目重启之后失效了(存在了内存中,被删除了)。

完成登录
为了解决这个问题,需要在访问这些接口之前,通过登录接口登录。
http://localhost:8081/login?userId=10086
@SaCheckPermission
鉴权成功
登录完成后,在浏览器地址栏输入以下网址分别访问三个接口。
http://localhost:8081/user/insert
http://localhost:8081/user/delete
http://localhost:8081/user/update
在Intellij IDEA的控制台,可以看到3个方法里打印的信息。

鉴权失败
注释掉设置的权限,然后重启项目,再次访问三个接口。

结果显而易见,直接返回了500状态码。



从控制台打印的异常栈信息,可以看到没有对应的权限。



至此,@SaCheckPermission注解鉴权测试完成。
@SaCheckLogin
创建一个退出登录的控制器,要求在登录状态下才允许访问。
package cn.edu.sgu.www.study.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 沐雨橙风ιε
* @version 1.0
*/
@RestController
public class LogoutController {
@SaCheckLogin
@GetMapping("/logout")
public SaResult logout() {
Object userId = StpUtil.getLoginId();
System.out.printf("用户%s正在退出登录...", userId);
StpUtil.logout();
System.out.printf("用户%s退出成功。", userId);
return SaResult.ok("退出成功");
}
}
鉴权失败
重启项目,直接访问/user/logout接口。

Intellij IDEA控制台打印了未登录异常的信息,未读取到有效token,这是因为没有登录。

鉴权成功
先访问登录接口
http://localhost:8081/login?userId=10086
登录成功后,再访问退出登录的接口。
http://localhost:8081/logout
接口返回退出成功信息,@SaCheckLogin注解鉴权测试完成。

@SaCheckRole
紧接着,创建一个获取当前账号ID登录的所有终端的控制器接口。
由于是隐私数据,要求只有系统管理员才可以访问。
package cn.edu.sgu.www.study.controller;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.session.SaTerminalInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author 沐雨橙风ιε
* @version 1.0
*/
@RestController
@RequestMapping("/terminal")
public class TerminalController {
@SaCheckRole("sys-admin")
@GetMapping("/getList")
public SaResult getList(@RequestParam String userId) {
List<SaTerminalInfo> list = StpUtil.getTerminalListByLoginId(userId);
return SaResult.data(list);
}
}
鉴权成功
重新启动项目,登录成功后,再访问/terminal/getList接口。
http://localhost:8081/terminal/getList?userId=10086
由于StpInterfaceImpl类里配置了两个角色:sys-admin、sys-user,所以现在是能访问成功的。

鉴权失败
从角色列表中删除sys-admin角色,重启项目。

登录成功后,再访问/terminal/getList接口。
http://localhost:8081/terminal/getList?userId=10086

在Intellij IDEA的控制台,查看到没有sys-admin角色。

至此,@SaCheckRole注解鉴权测试完成。
@SaIgnore
对于登录接口,由于所有鉴权都是基于已登录状态下完成的,此时不能添加上述3个注解。
否则,将会导致无法登录成功,也就永远无法通过注解的鉴权,陷入无法破解的死循环。
因此,应该在这类接口上使用@SaIgnore注解,表示Sa-Token鉴权时忽视该接口。
package cn.edu.sgu.www.study.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 沐雨橙风ιε
* @version 1.0
*/
@RestController
public class LoginController {
@SaIgnore
@GetMapping("/login")
public SaResult login(@RequestParam String userId) {
StpUtil.login(userId);
return SaResult.ok("登录成功");
}
}
路由拦截鉴权
注解鉴权固然方便,但是不利于维护,更新时需要到每个控制器类中去修改。
于是,Sa-Token的另外一种鉴权方式更适合大多数Web应用:路由拦截鉴权。
可以在过滤器、拦截器中动态配置访问某个(某些)url需要哪个(哪些)权限。
为什么使用路由拦截鉴权?
- 路由拦截鉴权的最大特点就是动态,可以动态配置路由权限。
- 比起直接在代码里写死,这种方式无疑更加灵活,也更易于理解。
拦截器鉴权
由于刚学完基于拦截器的注解鉴权,就先介绍基于拦截器的路由拦截鉴权。
使用基于拦截器的路由拦截鉴权,只需要在拦截器中配置路由和需要的对应权限,并且关闭注解鉴权,避免一个url被鉴权两次的bug。
package cn.edu.sgu.www.study.config;
import cn.dev33.satoken.fun.SaParamFunction;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.router.SaRouterStaff;
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;
/**
* Spring Web MVC配置类
* @author 沐雨橙风ιε
* @version 1.0
*/
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册Sa-Token拦截器
SaInterceptor interceptor = saInterceptor();
registry.addInterceptor(interceptor).addPathPatterns("/**");
}
private SaInterceptor saInterceptor() {
return new SaInterceptor(new SaParamFunction<Object>() {
@Override
public void run(Object handler) {
SaRouter.match("/user/insert").check(new SaParamFunction<SaRouterStaff>() {
@Override
public void run(SaRouterStaff r) {
StpUtil.checkPermission("user.insert");
}
});
SaRouter.match("/user/delete").check(new SaParamFunction<SaRouterStaff>() {
@Override
public void run(SaRouterStaff r) {
StpUtil.checkPermission("user.delete");
}
});
SaRouter.match("/user/update").check(new SaParamFunction<SaRouterStaff>() {
@Override
public void run(SaRouterStaff r) {
StpUtil.checkPermission("user.update");
}
});
}
}).isAnnotation(false); // 关闭注解式鉴权功能
}
}
过滤器鉴权
路由拦截鉴权除了基于拦截器的鉴权方式以外,还可以通过配置过滤器来完成鉴权操作。
package cn.edu.sgu.www.study.config;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.filter.SaFilterAuthStrategy;
import cn.dev33.satoken.filter.SaFilterErrorStrategy;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.fun.SaParamFunction;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.router.SaRouterStaff;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Sa-Token框架配置类
* @author 沐雨橙风ιε
* @version 1.0
*/
@Configuration
public class SaTokenConfig {
/**
* 注册Sa-Token全局过滤器
*/
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter().setAuth(new SaFilterAuthStrategy() {
@Override
public void run(Object obj) {
checkPermission();
}
}).setError(new SaFilterErrorStrategy() {
@Override
public Object run(Throwable throwable) {
String message = null;
int code = 200;
// 不同类型的异常区别处理
if (throwable instanceof NotLoginException) {
message = "请登录后访问!";
code = 403;
} else if (throwable instanceof NotRoleException || throwable instanceof NotPermissionException) {
message = "正在访问未授权的资源!";
code = 401;
}
SaResponse response = SaHolder.getResponse();
response.setStatus(code);
return new SaResult(code, message, null);
}
}).addInclude("/**");
}
public static void checkPermission() {
SaRouter.match("/user/logout").check(new SaParamFunction<SaRouterStaff>() {
@Override
public void run(SaRouterStaff r) {
StpUtil.checkLogin();
}
});
SaRouter.match("/user/insert").check(new SaParamFunction<SaRouterStaff>() {
@Override
public void run(SaRouterStaff r) {
StpUtil.checkPermission("user.insert");
}
});
SaRouter.match("/user/delete").check(new SaParamFunction<SaRouterStaff>() {
@Override
public void run(SaRouterStaff r) {
StpUtil.checkPermission("user.delete");
}
});
SaRouter.match("/user/update").check(new SaParamFunction<SaRouterStaff>() {
@Override
public void run(SaRouterStaff r) {
StpUtil.checkPermission("user.update");
}
});
}
}
使用全局过滤器之后,就不需要注册Sa-Token的拦截器了。
异常处理
当使用注解鉴权或基于拦截器的路由拦截鉴权时,当鉴权失败时,会抛出对应注解的异常。
- @SaCheckPermission:NotPermissionException(无权限异常)
- @SaCheckLogin:NotLoginException(未登录异常)
- @SaCheckRole:NotRoleException(无角色异常)
由于这些异常没有被手动处理,服务器会直接返回500状态码。
这对于用户而言,体验非常不好,所以应该手动处理这些异常。
在support包下创建一个异常处理器,通过@ControllerAdvice使其在所有控制器中生效。
package cn.edu.sgu.www.study.support;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.util.SaResult;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理类
* @author 沐雨橙风ιε
* @version 1.0
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理Exception
*/
@ExceptionHandler
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public SaResult handleException(Exception e) {
e.printStackTrace();
return SaResult.error("服务器发生了不可预知的异常。");
}
/**
* 处理NotRoleException(Sa-Token的没有角色异常)
*/
@ExceptionHandler
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public SaResult handleException(NotRoleException e) {
e.printStackTrace();
return new SaResult(401, "正在访问未授权的资源!", null);
}
/**
* 处理NotLoginException(Sa-Token的未登录异常)
*/
@ExceptionHandler
@ResponseStatus(HttpStatus.FORBIDDEN)
public SaResult handleException(NotLoginException e) {
e.printStackTrace();
return new SaResult(403, "请先登录!", null);
}
/**
* 处理NotPermissionException(Sa-Token的没有权限异常)
*/
@ExceptionHandler
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public SaResult handleException(NotPermissionException e) {
e.printStackTrace();
return new SaResult(401, "正在访问未授权的资源!", null);
}
}
会话共享
Sa-Token默认将登录会话信息保存在内存中(java.util.Map),所以重启应用将导致会话丢失。
对此,Sa-Token实现了Redis的无缝集成,只需要添加一个依赖,就可以将会话存储在Redis中,实现会话的共享。
<!-- Sa-Token 整合 RedisTemplate -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template</artifactId>
<version>1.45.0</version>
</dependency>
完整的代码已经保存到码云,需要的自行拉取~
Spring Boot整合Sa-Token权限认证框架
https://gitee.com/muyu-chengfeng/sa-token-study.git
Sa-Token框架的真实项目应用(基于RBAC模型的鉴权)
Sa-Token权限认证框架项目应用
https://gitee.com/muyu-chengfeng/sa-token-service.git
同类框架Apache Shiro的相关文章:
Spring Boot整合Apache Shiro权限认证框架(基础篇)
https://blog.csdn.net/heyl163_/article/details/131504939Spring Boot整合Apache Shiro权限认证框架(应用篇)
https://blog.csdn.net/heyl163_/article/details/131518274Spring Boot整合Apache Shiro权限认证框架(实战篇)
https://blog.csdn.net/heyl163_/article/details/156282805
1万+

被折叠的 条评论
为什么被折叠?



