赞
踩
Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题。Sa-Token 旨在以简单、优雅的方式完成系统的权限认证部分
了解更多:Sa-Token官方文档
<!--如果你使用的是 SpringBoot 3.x,只需要将 sa-token-spring-boot-starter 修改为 sa-token-spring-boot3-starter -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.35.0.RC</version>
</dependency>
server: # 端口 port: 8081 ############## sa-token: # token 名称(同时也是 cookie 名称) token-name: satoken # token 有效期(单位:秒) 默认30天,-1 代表永久有效 timeout: 2592000 # 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
代码如下(示例):
document.querySelector("#but").addEventListener("click",function () {
const request = new XMLHttpRequest;
request.onreadystatechange=function () {
if (request.readyState===4){
if (request.status===200){
document.getElementById("mydiv").innerHTML=eval("("+request.responseText+")")["msg"]
}else {
alert(request.status);
}
}
}
request.open("get","http://localhost:8081/user/doLogin?username=zhang&password=123456",true);
request.send()
})
@GetMapping("doLogin")
public SaResult doLogin(@RequestParam("username")String username,@RequestParam("password")String password) {
// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
if("zhang".equals(username) && "123456".equals(password)) { StpUtil.login(10001);
//StpUtil.login(id) 方法利用了 Cookie 自动注入的特性,自动向前端返回token信息
return SaResult.ok("登陆成功");
}
return SaResult.error("登陆失败");
}
更多关于Sa-Token的登陆认证的其他用法,可以查看Sa-Token官方文档
新建一个类,实现 StpInterface接口,例如以下代码:
/** * 自定义权限加载接口实现类 */ @Component // 保证此类被 SpringBoot 扫描,完成 Sa-Token 的自定义权限验证扩展 public class StpInterfaceImpl implements StpInterface { /** * 返回一个账号所拥有的权限码集合 * @param loginId:账号id * @param loginType:账号体系标识 */ @Override public List<String> getPermissionList(Object loginId, String loginType) { // 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询权限 List<String> list = new ArrayList<String>(); list.add("101"); list.add("user.add"); list.add("user.update"); list.add("user.get"); // list.add("user.delete"); list.add("art.*"); return list; } /** * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验) */ @Override public List<String> getRoleList(Object loginId, String loginType) { // 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询角色 List<String> list = new ArrayList<String>(); list.add("admin"); list.add("super-admin"); return list; } }
// 获取:当前账号所拥有的权限集合
StpUtil.getPermissionList();
// 判断:当前账号是否含有指定权限, 返回 true 或 false
StpUtil.hasPermission("user.add");
// 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
StpUtil.checkPermission("user.add");
// 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
StpUtil.checkPermissionAnd("user.add", "user.delete", "user.get");
// 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
StpUtil.checkPermissionOr("user.add", "user.delete", "user.get");
// 获取:当前账号所拥有的角色集合
StpUtil.getRoleList();
// 判断:当前账号是否拥有指定角色, 返回 true 或 false
StpUtil.hasRole("super-admin");
// 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
StpUtil.checkRole("super-admin");
// 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
StpUtil.checkRoleAnd("super-admin", "shop-admin");
// 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
StpUtil.checkRoleOr("super-admin", "shop-admin");
@RestControllerAdvice
public class GlobalExceptionHandler {
// 全局异常拦截
@ExceptionHandler
public SaResult handlerException(Exception e) {
e.printStackTrace();
return SaResult.error(e.getMessage());
}
}
StpUtil.logout(10001); // 强制指定账号注销下线
StpUtil.logout(10001, "PC"); // 强制指定账号指定端注销下线
StpUtil.logoutByTokenValue("token"); // 强制指定 Token 注销下线
StpUtil.kickout(10001); // 将指定账号踢下线
StpUtil.kickout(10001, "PC"); // 将指定账号指定端踢下线
StpUtil.kickoutByTokenValue("token"); // 将指定 Token 踢下线
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
}
// 登录校验:只有登录之后才能进入该方法 @SaCheckLogin @RequestMapping("info") public String info() { return "查询用户信息"; } //写法一:orRole = "admin",代表需要拥有角色 admin 。 //写法二:orRole = {"admin", "manager", "staff"},代表具有三个角色其一即可。 //写法三:orRole = {"admin, manager, staff"},代表必须同时具有三个角色。 // 角色权限双重 “or校验”:具备指定权限或者指定角色即可通过校验 @SaCheckRole(value = "user.add", orRole = "admin") @RequestMapping("add") public String add() { return "用户增加"; } // 权限校验:必须具有指定权限才能进入该方法 // 注解式鉴权:只要具有其中一个权限即可通过校验 @SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR) @RequestMapping("add") public String add() { return "用户增加"; } // 二级认证校验:必须二级认证之后才能进入该方法 // 此接口加上了 @SaIgnore 可以游客访问 @SaIgnore @SaCheckSafe() @RequestMapping("add") public String add() { return "用户增加"; } // Http Basic 校验:只有通过 Basic 认证后才能进入该方法 @SaCheckBasic(account = "sa:123456") @RequestMapping("add") public String add() { return "用户增加"; } // 校验当前账号是否被封禁 comment 服务,如果已被封禁会抛出异常,无法进入方法 @SaCheckDisable("comment") @RequestMapping("send") public String send() { return "查询用户信息"; }
// 在 `@SaCheckOr` 中可以指定多个注解,只要当前会话满足其中一个注解即可通过验证,进入方法。
@SaCheckOr(
// 当前客户端只要有 [ login 账号登录] 或者 [user 账号登录] 其一,就可以通过验证进入方法。
login = { @SaCheckLogin(type = "login"), @SaCheckLogin(type = "user") }
role = @SaCheckRole("admin"),
permission = @SaCheckPermission("user.add"),
safe = @SaCheckSafe("update-password"),
basic = @SaCheckBasic(account = "sa:123456"),
disable = @SaCheckDisable("submit-orders")
)
@RequestMapping("test")
public SaResult test() {
// ...
return SaResult.ok();
}
@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("/user/doLogin");
}
}
@Configuration public class SaTokenConfigure implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 注册 Sa-Token 拦截器,定义详细认证规则 registry.addInterceptor(new SaInterceptor(handler -> { // 指定一条 match 规则 SaRouter .match("/**") // 拦截的 path 列表,可以写多个 */ .notMatch("/user/doLogin") // 排除掉的 path 列表,可以写多个 .check(r -> StpUtil.checkLogin()); // 要执行的校验动作,可以写完整的 lambda 表达式 // 根据路由划分模块,不同模块不同鉴权 SaRouter.match("/user/**", r -> StpUtil.checkPermission("user")); //SaRouter.stop() 会停止匹配,进入Controller。 SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin")).stop(); //SaRouter.back() 会停止匹配,直接返回结果到前端。 SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods")).back("要返回给前端的内容"); SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders")); SaRouter.match("/notice/**", r -> StpUtil.checkPermission("notice")); // 进入 free 独立作用域 SaRouter.match("/**").free(r -> { SaRouter.match("/a/**").check(/* --- */); SaRouter.match("/b/**").check(/* --- */).stop(); SaRouter.match("/c/**").check(/* --- */); }); // 执行 stop() 函数跳出 free 后继续执行下面的 match 匹配 SaRouter.match("/**").check(/* --- */); SaRouter.match("/comment/**", r -> StpUtil.checkPermission("comment")); // 指定关闭掉注解鉴权能力,这样框架就只会做路由拦截校验了 })/*.isAnnotation(false)*/).addPathPatterns("/**"); } }
在 Sa-Token 中,Session 分为三种,分别是:
// 获取当前账号 id 的 Account-Session (必须是登录后才能调用)
StpUtil.getSession();
// 获取当前账号 id 的 Account-Session, 并决定在 Session 尚未创建时,是否新建并返回
StpUtil.getSession(true);
// 获取账号 id 为 10001 的 Account-Session
StpUtil.getSessionByLoginId(10001);
// 获取账号 id 为 10001 的 Account-Session, 并决定在 Session 尚未创建时,是否新建并返回
StpUtil.getSessionByLoginId(10001, true);
// 获取 SessionId 为 xxxx-xxxx 的 Account-Session, 在 Session 尚未创建时, 返回 null
StpUtil.getSessionBySessionId("xxxx-xxxx");
// 获取当前 Token 的 Token-Session 对象
StpUtil.getTokenSession();
// 获取指定 Token 的 Token-Session 对象
StpUtil.getTokenSessionByToken(token);
以一个特定的值作为 SessionId 来分配的Session
// 查询指定key的Session是否存在
SaSessionCustomUtil.isExists("goods-10001");
// 获取指定key的Session,如果没有,则新建并返回
SaSessionCustomUtil.getSessionById("goods-10001");
// 获取指定key的Session,如果没有,第二个参数决定是否新建并返回
SaSessionCustomUtil.getSessionById("goods-10001", false);
// 删除指定key的Session
SaSessionCustomUtil.deleteSessionById("goods-10001");
// 写值 session.set("name", "zhang"); // 写值 (只有在此key原本无值的时候才会写入) session.setDefaultValue("name", "zhang"); // 取值 session.get("name"); // 取值 (指定默认值) session.get("name", "<defaultValue>"); // 取值 (若无值则执行参数方法, 之后将结果保存到此键名下,并返回此结果 若有值则直接返回, 无需执行参数方法) session.get("name", () -> { return ...; }); // ---------- 数据类型转换: ---------- session.getInt("age"); // 取值 (转int类型) session.getLong("age"); // 取值 (转long类型) session.getString("name"); // 取值 (转String类型) session.getDouble("result"); // 取值 (转double类型) session.getFloat("result"); // 取值 (转float类型) session.getModel("key", Student.class); // 取值 (指定转换类型) session.getModel("key", Student.class, <defaultValue>); // 取值 (指定转换类型, 并指定值为Null时返回的默认值) // 是否含有某个key (返回 true 或 false) session.has("key"); // 删值 session.delete('name'); // 清空所有值 session.clear(); // 获取此 Session 的所有key (返回Set<String>) session.keys();
默认场景下,只有登录后才能通过 StpUtil.getTokenSession() 获取 Token-Session。如果想要在未登录场景下获取 Token-Session ,有两种方法:
// 获取当前 Token 的匿名 Token-Session (可在未登录情况下使用的 Token-Session)
StpUtil.getAnonTokenSession();
以上就是springboot整合Sa-token要讲的内容,本文仅仅简单介绍了最基本的使用,如果想继续深入了解Sa-Token,可以查看Sa-Token官方文档。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。