mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-05-05 05:07:54 +08:00
Merge branch 'dev' of https://gitee.com/noear/sa-token into dev
This commit is contained in:
commit
c84c600fd4
75
README.md
75
README.md
@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://sa-token.dev33.cn/doc/logo.png" width="150" height="150">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.30.0.RC</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.30.0</h1>
|
||||
<h4 align="center">一个轻量级 Java 权限认证框架,让鉴权变得简单、优雅!</h4>
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg?theme=gvp"></a>
|
||||
@ -28,64 +28,6 @@
|
||||
**Sa-Token** 是一个轻量级 Java 权限认证框架,主要解决:**`登录认证`**、**`权限认证`**、**`Session会话`**、**`单点登录`**、**`OAuth2.0`**、**`微服务网关鉴权`**
|
||||
等一系列权限相关问题。
|
||||
|
||||
Sa-Token 的 API 设计非常简单,有多简单呢?以登录认证为例,你只需要:
|
||||
|
||||
``` java
|
||||
// 在登录时写入当前会话的账号id
|
||||
StpUtil.login(10001);
|
||||
|
||||
// 然后在需要校验登录处调用以下方法:
|
||||
// 如果当前会话未登录,这句代码会抛出 `NotLoginException` 异常
|
||||
StpUtil.checkLogin();
|
||||
```
|
||||
|
||||
至此,我们已经借助 Sa-Token 完成登录认证!
|
||||
|
||||
此时的你小脑袋可能飘满了问号,就这么简单?自定义 Realm 呢?全局过滤器呢?我不用写各种配置文件吗?
|
||||
|
||||
没错,在 Sa-Token 中,登录认证就是如此简单,不需要任何的复杂前置工作,只需这一行简单的API调用,就可以完成会话登录认证!
|
||||
|
||||
当你受够 Shiro、SpringSecurity 等框架的三拜九叩之后,你就会明白,相对于这些传统老牌框架,Sa-Token 的 API 设计是多么的简单、优雅!
|
||||
|
||||
权限认证示例(只有具备 `user:add` 权限的会话才可以进入请求)
|
||||
``` java
|
||||
@SaCheckPermission("user:add")
|
||||
@RequestMapping("/user/insert")
|
||||
public String insert(SysUser user) {
|
||||
// ...
|
||||
return "用户增加";
|
||||
}
|
||||
```
|
||||
|
||||
将某个账号踢下线(待到对方再次访问系统时会抛出`NotLoginException`异常)
|
||||
``` java
|
||||
// 将账号id为 10001 的会话踢下线
|
||||
StpUtil.kickout(10001);
|
||||
```
|
||||
|
||||
在 Sa-Token 中,绝大多数功能都可以 **一行代码** 完成:
|
||||
``` java
|
||||
StpUtil.login(10001); // 标记当前会话登录的账号id
|
||||
StpUtil.getLoginId(); // 获取当前会话登录的账号id
|
||||
StpUtil.isLogin(); // 获取当前会话是否已经登录, 返回true或false
|
||||
StpUtil.logout(); // 当前会话注销登录
|
||||
StpUtil.kickout(10001); // 将账号为10001的会话踢下线
|
||||
StpUtil.hasRole("super-admin"); // 查询当前账号是否含有指定角色标识, 返回true或false
|
||||
StpUtil.hasPermission("user:add"); // 查询当前账号是否含有指定权限, 返回true或false
|
||||
StpUtil.getSession(); // 获取当前账号id的Session
|
||||
StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session
|
||||
StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌值
|
||||
StpUtil.login(10001, "PC"); // 指定设备类型登录,常用于“同端互斥登录”
|
||||
StpUtil.kickout(10001, "PC"); // 指定账号指定设备类型踢下线 (不同端不受影响)
|
||||
StpUtil.openSafe(120); // 在当前会话开启二级认证,有效期为120秒
|
||||
StpUtil.checkSafe(); // 校验当前会话是否处于二级认证有效期内,校验失败会抛出异常
|
||||
StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
```
|
||||
|
||||
即使不运行测试,相信您也能意会到绝大多数 API 的用法。
|
||||
|
||||
|
||||
## Sa-Token 功能一览
|
||||
|
||||
- **登录认证** —— 单端登录、多端登录、同端互斥登录、七天内免登录
|
||||
- **权限认证** —— 权限认证、角色认证、会话二级认证
|
||||
@ -119,7 +61,7 @@ StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
|
||||
|
||||
## Sa-Token-SSO 单点登录
|
||||
网上的单点登录教程大多以CAS流程为主,其实对于不同的系统架构,实现单点登录的步骤也大为不同,Sa-Token由简入难将其划分为三种模式:
|
||||
Sa-Token-SSO 由简入难划分为三种模式,解决不同架构下的 SSO 接入问题:
|
||||
|
||||
| 系统架构 | 采用模式 | 简介 | 文档链接 |
|
||||
| :-------- | :-------- | :-------- | :-------- |
|
||||
@ -131,8 +73,6 @@ StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
1. 前端同域:就是指多个系统可以部署在同一个主域名之下,比如:`c1.domain.com`、`c2.domain.com`、`c3.domain.com`
|
||||
2. 后端同Redis:就是指多个系统可以连接同一个Redis。PS:这里并不需要把所有项目的数据都放在同一个Redis中,Sa-Token提供了 **`[权限缓存与业务缓存分离]`** 的解决方案,详情戳:[Alone独立Redis插件](http://sa-token.dev33.cn/doc/index.html#/plugin/alone-redis)
|
||||
3. 如果既无法做到前端同域,也无法做到后端同Redis,那么只能走模式三,Http请求获取会话(Sa-Token对SSO提供了完整的封装,你只需要按照示例从文档上复制几段代码便可以轻松集成)
|
||||
4. 技术选型一定要根据系统架构对症下药,切不可胡乱选择
|
||||
|
||||
|
||||
## Sa-Token-OAuth2.0 授权登录
|
||||
Sa-OAuth2 模块基于 [RFC-6749 标准](https://tools.ietf.org/html/rfc6749) 编写,通过Sa-OAuth2你可以非常轻松的实现系统的OAuth2.0授权认证
|
||||
@ -154,12 +94,6 @@ Sa-OAuth2 模块基于 [RFC-6749 标准](https://tools.ietf.org/html/rfc6749)
|
||||

|
||||
|
||||
|
||||
## Star 趋势
|
||||
[](https://giteye.net/chart/77YQZ6UK)
|
||||
|
||||
[](https://starchart.cc/dromara/sa-token)
|
||||
|
||||
|
||||
## 使用Sa-Token的开源项目
|
||||
- [[ sa-plus ]](https://gitee.com/click33/sa-plus):一个基于 SpringBoot 架构的快速开发框架,内置代码生成器
|
||||
|
||||
@ -201,11 +135,6 @@ Sa-OAuth2 模块基于 [RFC-6749 标准](https://tools.ietf.org/html/rfc6749)
|
||||
- [[ TLog ]](https://gitee.com/dromara/TLog):一个轻量级的分布式日志标记追踪神器
|
||||
|
||||
|
||||
## 贡献者名单
|
||||
感谢每一个为 Sa-Token 贡献代码的小伙伴
|
||||
|
||||
[](https://giteye.net/chart/CGZ7GT8E)
|
||||
|
||||
|
||||
## 交流群
|
||||
QQ交流群:1群:1002350610 (已满) 、
|
||||
|
2
pom.xml
2
pom.xml
@ -37,7 +37,7 @@
|
||||
|
||||
<!-- 一些属性 -->
|
||||
<properties>
|
||||
<revision>1.30.0.RC</revision>
|
||||
<revision>1.30.0</revision>
|
||||
<jdk.version>1.8</jdk.version>
|
||||
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
|
||||
|
@ -1,54 +0,0 @@
|
||||
//package cn.dev33.satoken.action;
|
||||
//
|
||||
//import java.lang.reflect.AnnotatedElement;
|
||||
//import java.lang.reflect.Method;
|
||||
//import java.util.List;
|
||||
//
|
||||
//import cn.dev33.satoken.session.SaSession;
|
||||
//
|
||||
///**
|
||||
// * <h1> v1.27+ 此接口已废弃,目前版本暂时向下兼容,请及时更换为 SaStrategy </h1>
|
||||
// * <p>Sa-Token 逻辑代理接口 </p>
|
||||
// * <p>此接口将会代理框架内部的一些关键性逻辑,方便开发者进行按需重写</p>
|
||||
// * @author kong
|
||||
// *
|
||||
// */
|
||||
//@Deprecated
|
||||
//public interface SaTokenAction {
|
||||
//
|
||||
// /**
|
||||
// * 创建一个Token
|
||||
// * @param loginId 账号id
|
||||
// * @param loginType 账号类型
|
||||
// * @return token
|
||||
// */
|
||||
// public String createToken(Object loginId, String loginType);
|
||||
//
|
||||
// /**
|
||||
// * 创建一个Session
|
||||
// * @param sessionId Session的Id
|
||||
// * @return 创建后的Session
|
||||
// */
|
||||
// public SaSession createSession(String sessionId);
|
||||
//
|
||||
// /**
|
||||
// * 判断:集合中是否包含指定元素(模糊匹配)
|
||||
// * @param list 集合
|
||||
// * @param element 元素
|
||||
// * @return 是否包含
|
||||
// */
|
||||
// public boolean hasElement(List<String> list, String element);
|
||||
//
|
||||
// /**
|
||||
// * 对一个Method对象进行注解检查(注解鉴权内部实现)
|
||||
// * @param method Method对象
|
||||
// */
|
||||
// public void checkMethodAnnotation(Method method);
|
||||
//
|
||||
// /**
|
||||
// * 从指定元素校验注解
|
||||
// * @param target /
|
||||
// */
|
||||
// public void validateAnnotation(AnnotatedElement target);
|
||||
//
|
||||
//}
|
@ -1,150 +0,0 @@
|
||||
//package cn.dev33.satoken.action;
|
||||
//
|
||||
//import java.lang.reflect.AnnotatedElement;
|
||||
//import java.lang.reflect.Method;
|
||||
//import java.util.List;
|
||||
//import java.util.UUID;
|
||||
//
|
||||
//import cn.dev33.satoken.SaManager;
|
||||
//import cn.dev33.satoken.annotation.SaCheckBasic;
|
||||
//import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
//import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
//import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
//import cn.dev33.satoken.annotation.SaCheckSafe;
|
||||
//import cn.dev33.satoken.basic.SaBasicUtil;
|
||||
//import cn.dev33.satoken.session.SaSession;
|
||||
//import cn.dev33.satoken.strategy.SaStrategy;
|
||||
//import cn.dev33.satoken.util.SaFoxUtil;
|
||||
//import cn.dev33.satoken.util.SaTokenConsts;
|
||||
//
|
||||
///**
|
||||
// * <h1> v1.27+ 此接口已废弃,目前版本暂时向下兼容,请及时更换为 SaStrategy </h1>
|
||||
// * <p> Sa-Token 逻辑代理接口 [默认实现类] </p>
|
||||
// * @author kong
|
||||
// *
|
||||
// */
|
||||
//@Deprecated
|
||||
//public class SaTokenActionDefaultImpl implements SaTokenAction {
|
||||
//
|
||||
// /**
|
||||
// * 创建一个Token
|
||||
// */
|
||||
// @Override
|
||||
// public String createToken(Object loginId, String loginType) {
|
||||
// // 根据配置的tokenStyle生成不同风格的token
|
||||
// String tokenStyle = SaManager.getConfig().getTokenStyle();
|
||||
// // uuid
|
||||
// if(SaTokenConsts.TOKEN_STYLE_UUID.equals(tokenStyle)) {
|
||||
// return UUID.randomUUID().toString();
|
||||
// }
|
||||
// // 简单uuid (不带下划线)
|
||||
// if(SaTokenConsts.TOKEN_STYLE_SIMPLE_UUID.equals(tokenStyle)) {
|
||||
// return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
// }
|
||||
// // 32位随机字符串
|
||||
// if(SaTokenConsts.TOKEN_STYLE_RANDOM_32.equals(tokenStyle)) {
|
||||
// return SaFoxUtil.getRandomString(32);
|
||||
// }
|
||||
// // 64位随机字符串
|
||||
// if(SaTokenConsts.TOKEN_STYLE_RANDOM_64.equals(tokenStyle)) {
|
||||
// return SaFoxUtil.getRandomString(64);
|
||||
// }
|
||||
// // 128位随机字符串
|
||||
// if(SaTokenConsts.TOKEN_STYLE_RANDOM_128.equals(tokenStyle)) {
|
||||
// return SaFoxUtil.getRandomString(128);
|
||||
// }
|
||||
// // tik风格 (2_14_16)
|
||||
// if(SaTokenConsts.TOKEN_STYLE_TIK.equals(tokenStyle)) {
|
||||
// return SaFoxUtil.getRandomString(2) + "_" + SaFoxUtil.getRandomString(14) + "_" + SaFoxUtil.getRandomString(16) + "__";
|
||||
// }
|
||||
// // 默认,还是uuid
|
||||
// return UUID.randomUUID().toString();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 创建一个Session
|
||||
// */
|
||||
// @Override
|
||||
// public SaSession createSession(String sessionId) {
|
||||
// return new SaSession(sessionId);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 判断:集合中是否包含指定元素(模糊匹配)
|
||||
// */
|
||||
// @Override
|
||||
// public boolean hasElement(List<String> list, String element) {
|
||||
//
|
||||
// // 空集合直接返回false
|
||||
// if(list == null || list.size() == 0) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// // 先尝试一下简单匹配,如果可以匹配成功则无需继续模糊匹配
|
||||
// if (list.contains(element)) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // 开始模糊匹配
|
||||
// for (String patt : list) {
|
||||
// if(SaFoxUtil.vagueMatch(patt, element)) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 走出for循环说明没有一个元素可以匹配成功
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 对一个Method对象进行注解检查(注解鉴权内部实现)
|
||||
// */
|
||||
// @Override
|
||||
// public void checkMethodAnnotation(Method method) {
|
||||
//
|
||||
// // 先校验 Method 所属 Class 上的注解
|
||||
// validateAnnotation(method.getDeclaringClass());
|
||||
//
|
||||
// // 再校验 Method 上的注解
|
||||
// validateAnnotation(method);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 从指定元素校验注解
|
||||
// * @param target see note
|
||||
// */
|
||||
// public void validateAnnotation(AnnotatedElement target) {
|
||||
//
|
||||
// // 校验 @SaCheckLogin 注解
|
||||
// SaCheckLogin checkLogin = (SaCheckLogin) SaStrategy.me.getAnnotation.apply(target, SaCheckLogin.class);
|
||||
// if(checkLogin != null) {
|
||||
// SaManager.getStpLogic(checkLogin.type()).checkByAnnotation(checkLogin);
|
||||
// }
|
||||
//
|
||||
// // 校验 @SaCheckRole 注解
|
||||
// SaCheckRole checkRole = (SaCheckRole) SaStrategy.me.getAnnotation.apply(target, SaCheckRole.class);
|
||||
// if(checkRole != null) {
|
||||
// SaManager.getStpLogic(checkRole.type()).checkByAnnotation(checkRole);
|
||||
// }
|
||||
//
|
||||
// // 校验 @SaCheckPermission 注解
|
||||
// SaCheckPermission checkPermission = (SaCheckPermission) SaStrategy.me.getAnnotation.apply(target, SaCheckPermission.class);
|
||||
// if(checkPermission != null) {
|
||||
// SaManager.getStpLogic(checkPermission.type()).checkByAnnotation(checkPermission);
|
||||
// }
|
||||
//
|
||||
// // 校验 @SaCheckSafe 注解
|
||||
// SaCheckSafe checkSafe = (SaCheckSafe) SaStrategy.me.getAnnotation.apply(target, SaCheckSafe.class);
|
||||
// if(checkSafe != null) {
|
||||
// SaManager.getStpLogic(checkSafe.type()).checkByAnnotation(checkSafe);
|
||||
// }
|
||||
//
|
||||
// // 校验 @SaCheckBasic 注解
|
||||
// SaCheckBasic checkBasic = (SaCheckBasic) SaStrategy.me.getAnnotation.apply(target, SaCheckBasic.class);
|
||||
// if(checkBasic != null) {
|
||||
// SaBasicUtil.check(checkBasic.realm(), checkBasic.account());
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
//}
|
@ -8,7 +8,7 @@ package cn.dev33.satoken.exception;
|
||||
*/
|
||||
public class SaExceptionCode {
|
||||
|
||||
/** 代表未指定异常细分状态码 */
|
||||
/** 代表这个异常在抛出时未指定异常细分状态码 */
|
||||
public static final int CODE_UNDEFINED = -1;
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ public class SaTokenConsts {
|
||||
/**
|
||||
* Sa-Token 当前版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.30.0.RC";
|
||||
public static final String VERSION_NO = "v1.30.0";
|
||||
|
||||
/**
|
||||
* Sa-Token 开源地址 Gitee
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
12
sa-token-demo/sa-token-demo-springboot-redis/.gitignore
vendored
Normal file
12
sa-token-demo/sa-token-demo-springboot-redis/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
72
sa-token-demo/sa-token-demo-springboot-redis/pom.xml
Normal file
72
sa-token-demo/sa-token-demo-springboot-redis/pom.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<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>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-springboot-redis</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.12</version>
|
||||
<!-- <version>1.5.9.RELEASE</version> -->
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用jdk默认序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- Sa-Token整合 Redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 提供Redis连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,21 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
/**
|
||||
* Sa-Token整合SpringBoot 示例,整合redis
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaTokenDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenDemoApplication.class, args);
|
||||
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package com.pj.current;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.DisableLoginException;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalException {
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
System.out.println("全局异常---------------");
|
||||
e.printStackTrace();
|
||||
|
||||
// 不同异常返回不同状态码
|
||||
AjaxJson aj = null;
|
||||
if (e instanceof NotLoginException) { // 如果是未登录异常
|
||||
NotLoginException ee = (NotLoginException) e;
|
||||
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
|
||||
}
|
||||
else if(e instanceof NotRoleException) { // 如果是角色异常
|
||||
NotRoleException ee = (NotRoleException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||
}
|
||||
else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getPermission());
|
||||
}
|
||||
else if(e instanceof DisableLoginException) { // 如果是被封禁异常
|
||||
DisableLoginException ee = (DisableLoginException) e;
|
||||
aj = AjaxJson.getNotJur("账号被封禁:" + ee.getDisableTime() + "秒后解封");
|
||||
}
|
||||
else { // 普通异常, 输出:500 + 异常信息
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回给前端
|
||||
return aj;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.pj.current;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 处理 404
|
||||
* @author kong
|
||||
*/
|
||||
@RestController
|
||||
public class NotFoundHandle implements ErrorController {
|
||||
|
||||
@RequestMapping("/error")
|
||||
public Object error(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
response.setStatus(200);
|
||||
return SaResult.get(404, "not found", null);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.filter.SaServletFilter;
|
||||
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
|
||||
|
||||
/**
|
||||
* [Sa-Token 权限认证] 配置类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 注册Sa-Token 的拦截器,打开注解式鉴权功能
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册注解拦截器
|
||||
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 [Sa-Token 全局过滤器]
|
||||
*/
|
||||
@Bean
|
||||
public SaServletFilter getSaServletFilter() {
|
||||
return new SaServletFilter()
|
||||
|
||||
// 指定 [拦截路由] 与 [放行路由]
|
||||
.addInclude("/**")// .addExclude("/favicon.ico")
|
||||
|
||||
// 认证函数: 每次请求执行
|
||||
.setAuth(obj -> {
|
||||
// System.out.println("---------- sa全局认证 " + SaHolder.getRequest().getRequestPath());
|
||||
|
||||
})
|
||||
|
||||
// 异常处理函数:每次认证函数发生异常时执行此函数
|
||||
.setError(e -> {
|
||||
System.out.println("---------- sa全局异常 ");
|
||||
return AjaxJson.getError(e.getMessage());
|
||||
})
|
||||
|
||||
// 前置函数:在每次认证函数之前执行
|
||||
.setBeforeAuth(r -> {
|
||||
// ---------- 设置一些安全响应头 ----------
|
||||
SaHolder.getResponse()
|
||||
// 服务器名称
|
||||
.setServer("sa-server")
|
||||
// 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
|
||||
.setHeader("X-Frame-Options", "SAMEORIGIN")
|
||||
// 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面
|
||||
.setHeader("X-XSS-Protection", "1; mode=block")
|
||||
// 禁用浏览器内容嗅探
|
||||
.setHeader("X-Content-Type-Options", "nosniff")
|
||||
;
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写 Sa-Token 框架内部算法策略
|
||||
*/
|
||||
@Autowired
|
||||
public void rewriteSaStrategy() {
|
||||
// 重写Sa-Token的注解处理器,增加注解合并功能
|
||||
SaStrategy.me.getAnnotation = (element, annotationClass) -> {
|
||||
return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
/**
|
||||
* 自定义权限验证接口扩展
|
||||
*/
|
||||
@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的权限码集合
|
||||
*/
|
||||
@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-delete");
|
||||
list.add("user-update");
|
||||
list.add("user-get");
|
||||
list.add("article-get");
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.pj.satoken.at;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
|
||||
/**
|
||||
* 登录认证(User版):只有登录之后才能进入该方法
|
||||
* <p> 可标注在函数、类上(效果等同于标注在此类的所有方法上)
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SaCheckLogin(type = StpUserUtil.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface SaUserCheckLogin {
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.pj.satoken.at;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
|
||||
/**
|
||||
* 权限认证(User版):必须具有指定权限才能进入该方法
|
||||
* <p> 可标注在函数、类上(效果等同于标注在此类的所有方法上)
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SaCheckPermission(type = StpUserUtil.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface SaUserCheckPermission {
|
||||
|
||||
/**
|
||||
* 需要校验的权限码
|
||||
* @return 需要校验的权限码
|
||||
*/
|
||||
@AliasFor(annotation = SaCheckPermission.class)
|
||||
String [] value() default {};
|
||||
|
||||
/**
|
||||
* 验证模式:AND | OR,默认AND
|
||||
* @return 验证模式
|
||||
*/
|
||||
@AliasFor(annotation = SaCheckPermission.class)
|
||||
SaMode mode() default SaMode.AND;
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.pj.satoken.at;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
|
||||
/**
|
||||
* 角色认证(User版):必须具有指定角色标识才能进入该方法
|
||||
* <p> 可标注在函数、类上(效果等同于标注在此类的所有方法上)
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SaCheckRole(type = StpUserUtil.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface SaUserCheckRole {
|
||||
|
||||
/**
|
||||
* 需要校验的角色标识
|
||||
* @return 需要校验的角色标识
|
||||
*/
|
||||
@AliasFor(annotation = SaCheckRole.class)
|
||||
String [] value() default {};
|
||||
|
||||
/**
|
||||
* 验证模式:AND | OR,默认AND
|
||||
* @return 验证模式
|
||||
*/
|
||||
@AliasFor(annotation = SaCheckRole.class)
|
||||
SaMode mode() default SaMode.AND;
|
||||
|
||||
}
|
@ -0,0 +1,912 @@
|
||||
package com.pj.satoken.at;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.fun.SaFunction;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* Sa-Token 权限认证工具类 (user版)
|
||||
* @author kong
|
||||
*/
|
||||
public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* 账号类型标识
|
||||
*/
|
||||
public static final String TYPE = "user";
|
||||
|
||||
/**
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
public static StpLogic stpLogic = new StpLogic(TYPE);
|
||||
|
||||
/**
|
||||
* 获取当前 StpLogic 的账号类型
|
||||
* @return See Note
|
||||
*/
|
||||
public static String getLoginType(){
|
||||
return stpLogic.getLoginType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置 StpLogic 对象
|
||||
* @param stpLogic /
|
||||
*/
|
||||
public static void setStpLogic(StpLogic stpLogic) {
|
||||
StpUtil.stpLogic = stpLogic;
|
||||
// 防止自定义 stpLogic 被覆盖
|
||||
SaManager.putStpLogic(stpLogic);
|
||||
}
|
||||
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
/**
|
||||
* 返回token名称
|
||||
* @return 此StpLogic的token名称
|
||||
*/
|
||||
public static String getTokenName() {
|
||||
return stpLogic.getTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话写入当前TokenValue
|
||||
* @param tokenValue token值
|
||||
*/
|
||||
public static void setTokenValue(String tokenValue){
|
||||
stpLogic.setTokenValue(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话写入当前TokenValue
|
||||
* @param tokenValue token值
|
||||
* @param cookieTimeout Cookie存活时间(秒)
|
||||
*/
|
||||
public static void setTokenValue(String tokenValue, int cookieTimeout){
|
||||
stpLogic.setTokenValue(tokenValue, cookieTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前TokenValue
|
||||
* @return 当前tokenValue
|
||||
*/
|
||||
public static String getTokenValue() {
|
||||
return stpLogic.getTokenValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前TokenValue (不裁剪前缀)
|
||||
* @return /
|
||||
*/
|
||||
public static String getTokenValueNotCut(){
|
||||
return stpLogic.getTokenValueNotCut();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的Token信息
|
||||
* @return token信息
|
||||
*/
|
||||
public static SaTokenInfo getTokenInfo() {
|
||||
return stpLogic.getTokenInfo();
|
||||
}
|
||||
|
||||
|
||||
// =================== 登录相关操作 ===================
|
||||
|
||||
// --- 登录
|
||||
|
||||
/**
|
||||
* 会话登录
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
*/
|
||||
public static void login(Object id) {
|
||||
stpLogic.login(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话登录,并指定登录设备类型
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
* @param device 设备类型
|
||||
*/
|
||||
public static void login(Object id, String device) {
|
||||
stpLogic.login(id, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话登录,并指定是否 [记住我]
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
*/
|
||||
public static void login(Object id, boolean isLastingCookie) {
|
||||
stpLogic.login(id, isLastingCookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话登录,并指定所有登录参数Model
|
||||
* @param id 登录id,建议的类型:(long | int | String)
|
||||
* @param loginModel 此次登录的参数Model
|
||||
*/
|
||||
public static void login(Object id, SaLoginModel loginModel) {
|
||||
stpLogic.login(id, loginModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定账号id的登录会话
|
||||
* @param id 登录id,建议的类型:(long | int | String)
|
||||
* @return 返回会话令牌
|
||||
*/
|
||||
public static String createLoginSession(Object id) {
|
||||
return stpLogic.createLoginSession(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定账号id的登录会话
|
||||
* @param id 登录id,建议的类型:(long | int | String)
|
||||
* @param loginModel 此次登录的参数Model
|
||||
* @return 返回会话令牌
|
||||
*/
|
||||
public static String createLoginSession(Object id, SaLoginModel loginModel) {
|
||||
return stpLogic.createLoginSession(id, loginModel);
|
||||
}
|
||||
|
||||
// --- 注销
|
||||
|
||||
/**
|
||||
* 会话注销
|
||||
*/
|
||||
public static void logout() {
|
||||
stpLogic.logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void logout(Object loginId) {
|
||||
stpLogic.logout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id 和 设备类型
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备类型 (填null代表注销所有设备类型)
|
||||
*/
|
||||
public static void logout(Object loginId, String device) {
|
||||
stpLogic.logout(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据指定 Token
|
||||
*
|
||||
* @param tokenValue 指定token
|
||||
*/
|
||||
public static void logoutByTokenValue(String tokenValue) {
|
||||
stpLogic.logoutByTokenValue(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人下线,根据账号id
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void kickout(Object loginId) {
|
||||
stpLogic.kickout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人下线,根据账号id 和 设备类型
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备类型 (填null代表踢出所有设备类型)
|
||||
*/
|
||||
public static void kickout(Object loginId, String device) {
|
||||
stpLogic.kickout(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人下线,根据指定 Token
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param tokenValue 指定token
|
||||
*/
|
||||
public static void kickoutByTokenValue(String tokenValue) {
|
||||
stpLogic.kickoutByTokenValue(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶人下线,根据账号id 和 设备类型
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备类型 (填null代表顶替所有设备类型)
|
||||
*/
|
||||
public static void replaced(Object loginId, String device) {
|
||||
stpLogic.replaced(loginId, device);
|
||||
}
|
||||
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
* 当前会话是否已经登录
|
||||
* @return 是否已登录
|
||||
*/
|
||||
public static boolean isLogin() {
|
||||
return stpLogic.isLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验当前会话是否已经登录,如未登录,则抛出异常
|
||||
*/
|
||||
public static void checkLogin() {
|
||||
stpLogic.checkLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 如果未登录,则抛出异常
|
||||
* @return 账号id
|
||||
*/
|
||||
public static Object getLoginId() {
|
||||
return stpLogic.getLoginId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 如果未登录,则返回默认值
|
||||
* @param <T> 返回类型
|
||||
* @param defaultValue 默认值
|
||||
* @return 登录id
|
||||
*/
|
||||
public static <T> T getLoginId(T defaultValue) {
|
||||
return stpLogic.getLoginId(defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 如果未登录,则返回null
|
||||
* @return 账号id
|
||||
*/
|
||||
public static Object getLoginIdDefaultNull() {
|
||||
return stpLogic.getLoginIdDefaultNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 并转换为String类型
|
||||
* @return 账号id
|
||||
*/
|
||||
public static String getLoginIdAsString() {
|
||||
return stpLogic.getLoginIdAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 并转换为int类型
|
||||
* @return 账号id
|
||||
*/
|
||||
public static int getLoginIdAsInt() {
|
||||
return stpLogic.getLoginIdAsInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 并转换为long类型
|
||||
* @return 账号id
|
||||
*/
|
||||
public static long getLoginIdAsLong() {
|
||||
return stpLogic.getLoginIdAsLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定Token对应的账号id,如果未登录,则返回 null
|
||||
* @param tokenValue token
|
||||
* @return 账号id
|
||||
*/
|
||||
public static Object getLoginIdByToken(String tokenValue) {
|
||||
return stpLogic.getLoginIdByToken(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Token扩展信息(只在jwt模式下有效)
|
||||
* @param key 键值
|
||||
* @return 对应的扩展数据
|
||||
*/
|
||||
public static Object getExtra(String key) {
|
||||
return stpLogic.getExtra(key);
|
||||
}
|
||||
|
||||
|
||||
// =================== User-Session 相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回
|
||||
* @param loginId 账号id
|
||||
* @param isCreate 是否新建
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getSessionByLoginId(Object loginId, boolean isCreate) {
|
||||
return stpLogic.getSessionByLoginId(loginId, isCreate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的Session, 如果Session尚未创建,则返回null
|
||||
* @param sessionId SessionId
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getSessionBySessionId(String sessionId) {
|
||||
return stpLogic.getSessionBySessionId(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id的Session,如果Session尚未创建,则新建并返回
|
||||
* @param loginId 账号id
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getSessionByLoginId(Object loginId) {
|
||||
return stpLogic.getSessionByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的Session, 如果Session尚未创建,isCreate=是否新建并返回
|
||||
* @param isCreate 是否新建
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getSession(boolean isCreate) {
|
||||
return stpLogic.getSession(isCreate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的Session,如果Session尚未创建,则新建并返回
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getSession() {
|
||||
return stpLogic.getSession();
|
||||
}
|
||||
|
||||
|
||||
// =================== Token-Session 相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定Token-Session,如果Session尚未创建,则新建并返回
|
||||
* @param tokenValue Token值
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getTokenSessionByToken(String tokenValue) {
|
||||
return stpLogic.getTokenSessionByToken(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前Token-Session,如果Session尚未创建,则新建并返回
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getTokenSession() {
|
||||
return stpLogic.getTokenSession();
|
||||
}
|
||||
|
||||
|
||||
// =================== [临时有效期] 验证相关 ===================
|
||||
|
||||
/**
|
||||
* 检查当前token 是否已经[临时过期],如果已经过期则抛出异常
|
||||
*/
|
||||
public static void checkActivityTimeout() {
|
||||
stpLogic.checkActivityTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 续签当前token:(将 [最后操作时间] 更新为当前时间戳)
|
||||
* <h1>请注意: 即使token已经 [临时过期] 也可续签成功,
|
||||
* 如果此场景下需要提示续签失败,可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
|
||||
*/
|
||||
public static void updateLastActivityToNow() {
|
||||
stpLogic.updateLastActivityToNow();
|
||||
}
|
||||
|
||||
|
||||
// =================== 过期时间相关 ===================
|
||||
|
||||
/**
|
||||
* 获取当前登录者的 token 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getTokenTimeout() {
|
||||
return stpLogic.getTokenTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getSessionTimeout() {
|
||||
return stpLogic.getSessionTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 Token-Session 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getTokenSessionTimeout() {
|
||||
return stpLogic.getTokenSessionTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
|
||||
* @return token [临时过期] 剩余有效时间
|
||||
*/
|
||||
public static long getTokenActivityTimeout() {
|
||||
return stpLogic.getTokenActivityTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对当前 Token 的 timeout 值进行续期
|
||||
* @param timeout 要修改成为的有效时间 (单位: 秒)
|
||||
*/
|
||||
public static void renewTimeout(long timeout) {
|
||||
stpLogic.renewTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对指定 Token 的 timeout 值进行续期
|
||||
* @param tokenValue 指定token
|
||||
* @param timeout 要修改成为的有效时间 (单位: 秒)
|
||||
*/
|
||||
public static void renewTimeout(String tokenValue, long timeout) {
|
||||
stpLogic.renewTimeout(tokenValue, timeout);
|
||||
}
|
||||
|
||||
// =================== 角色验证操作 ===================
|
||||
|
||||
/**
|
||||
* 获取:当前账号的角色集合
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getRoleList() {
|
||||
return stpLogic.getRoleList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:指定账号的角色集合
|
||||
* @param loginId 指定账号id
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getRoleList(Object loginId) {
|
||||
return stpLogic.getRoleList(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否拥有指定角色, 返回true或false
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
*/
|
||||
public static boolean hasRole(String role) {
|
||||
return stpLogic.hasRole(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:指定账号是否含有指定角色标识, 返回true或false
|
||||
* @param loginId 账号id
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
*/
|
||||
public static boolean hasRole(Object loginId, String role) {
|
||||
return stpLogic.hasRole(loginId, role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
|
||||
* @param roleArray 角色标识数组
|
||||
* @return true或false
|
||||
*/
|
||||
public static boolean hasRoleAnd(String... roleArray){
|
||||
return stpLogic.hasRoleAnd(roleArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
|
||||
* @param roleArray 角色标识数组
|
||||
* @return true或false
|
||||
*/
|
||||
public static boolean hasRoleOr(String... roleArray){
|
||||
return stpLogic.hasRoleOr(roleArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
|
||||
* @param role 角色标识
|
||||
*/
|
||||
public static void checkRole(String role) {
|
||||
stpLogic.checkRole(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
|
||||
* @param roleArray 角色标识数组
|
||||
*/
|
||||
public static void checkRoleAnd(String... roleArray){
|
||||
stpLogic.checkRoleAnd(roleArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
|
||||
* @param roleArray 角色标识数组
|
||||
*/
|
||||
public static void checkRoleOr(String... roleArray){
|
||||
stpLogic.checkRoleOr(roleArray);
|
||||
}
|
||||
|
||||
|
||||
// =================== 权限验证操作 ===================
|
||||
|
||||
/**
|
||||
* 获取:当前账号的权限码集合
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getPermissionList() {
|
||||
return stpLogic.getPermissionList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:指定账号的权限码集合
|
||||
* @param loginId 指定账号id
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getPermissionList(Object loginId) {
|
||||
return stpLogic.getPermissionList(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否含有指定权限, 返回true或false
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(String permission) {
|
||||
return stpLogic.hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:指定账号id是否含有指定权限, 返回true或false
|
||||
* @param loginId 账号id
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(Object loginId, String permission) {
|
||||
return stpLogic.hasPermission(loginId, permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否含有指定权限, [指定多个,必须全部具有]
|
||||
* @param permissionArray 权限码数组
|
||||
* @return true 或 false
|
||||
*/
|
||||
public static boolean hasPermissionAnd(String... permissionArray){
|
||||
return stpLogic.hasPermissionAnd(permissionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
* @param permissionArray 权限码数组
|
||||
* @return true 或 false
|
||||
*/
|
||||
public static boolean hasPermissionOr(String... permissionArray){
|
||||
return stpLogic.hasPermissionOr(permissionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
* @param permission 权限码
|
||||
*/
|
||||
public static void checkPermission(String permission) {
|
||||
stpLogic.checkPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
|
||||
* @param permissionArray 权限码数组
|
||||
*/
|
||||
public static void checkPermissionAnd(String... permissionArray) {
|
||||
stpLogic.checkPermissionAnd(permissionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
* @param permissionArray 权限码数组
|
||||
*/
|
||||
public static void checkPermissionOr(String... permissionArray) {
|
||||
stpLogic.checkPermissionOr(permissionArray);
|
||||
}
|
||||
|
||||
|
||||
// =================== id 反查token 相关操作 ===================
|
||||
|
||||
/**
|
||||
* 获取指定账号id的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @return token值
|
||||
*/
|
||||
public static String getTokenValueByLoginId(Object loginId) {
|
||||
return stpLogic.getTokenValueByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id指定设备类型端的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @param device 设备类型
|
||||
* @return token值
|
||||
*/
|
||||
public static String getTokenValueByLoginId(Object loginId, String device) {
|
||||
return stpLogic.getTokenValueByLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id的tokenValue集合
|
||||
* @param loginId 账号id
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public static List<String> getTokenValueListByLoginId(Object loginId) {
|
||||
return stpLogic.getTokenValueListByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id指定设备类型端的tokenValue 集合
|
||||
* @param loginId 账号id
|
||||
* @param device 设备类型
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
|
||||
return stpLogic.getTokenValueListByLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前会话的登录设备类型
|
||||
* @return 当前令牌的登录设备类型
|
||||
*/
|
||||
public static String getLoginDevice() {
|
||||
return stpLogic.getLoginDevice();
|
||||
}
|
||||
|
||||
|
||||
// =================== 会话管理 ===================
|
||||
|
||||
/**
|
||||
* 根据条件查询Token
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return token集合
|
||||
*/
|
||||
public static List<String> searchTokenValue(String keyword, int start, int size) {
|
||||
return stpLogic.searchTokenValue(keyword, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询SessionId
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public static List<String> searchSessionId(String keyword, int start, int size) {
|
||||
return stpLogic.searchSessionId(keyword, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询Token专属Session的Id
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public static List<String> searchTokenSessionId(String keyword, int start, int size) {
|
||||
return stpLogic.searchTokenSessionId(keyword, start, size);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 账号封禁 -------------------
|
||||
|
||||
/**
|
||||
* 封禁指定账号
|
||||
* <p> 此方法不会直接将此账号id踢下线,而是在对方再次登录时抛出`DisableLoginException`异常
|
||||
* @param loginId 指定账号id
|
||||
* @param disableTime 封禁时间, 单位: 秒 (-1=永久封禁)
|
||||
*/
|
||||
public static void disable(Object loginId, long disableTime) {
|
||||
stpLogic.disable(loginId, disableTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定账号是否已被封禁 (true=已被封禁, false=未被封禁)
|
||||
* @param loginId 账号id
|
||||
* @return see note
|
||||
*/
|
||||
public static boolean isDisable(Object loginId) {
|
||||
return stpLogic.isDisable(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号剩余封禁时间,单位:秒(-1=永久封禁,-2=未被封禁)
|
||||
* @param loginId 账号id
|
||||
* @return see note
|
||||
*/
|
||||
public static long getDisableTime(Object loginId) {
|
||||
return stpLogic.getDisableTime(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解封指定账号
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void untieDisable(Object loginId) {
|
||||
stpLogic.untieDisable(loginId);
|
||||
}
|
||||
|
||||
|
||||
// =================== 身份切换 ===================
|
||||
|
||||
/**
|
||||
* 临时切换身份为指定账号id
|
||||
* @param loginId 指定loginId
|
||||
*/
|
||||
public static void switchTo(Object loginId) {
|
||||
stpLogic.switchTo(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束临时切换身份
|
||||
*/
|
||||
public static void endSwitch() {
|
||||
stpLogic.endSwitch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前是否正处于[身份临时切换]中
|
||||
* @return 是否正处于[身份临时切换]中
|
||||
*/
|
||||
public static boolean isSwitch() {
|
||||
return stpLogic.isSwitch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在一个代码段里方法内,临时切换身份为指定账号id
|
||||
* @param loginId 指定账号id
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
public static void switchTo(Object loginId, SaFunction function) {
|
||||
stpLogic.switchTo(loginId, function);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 二级认证 -------------------
|
||||
|
||||
/**
|
||||
* 在当前会话 开启二级认证
|
||||
* @param safeTime 维持时间 (单位: 秒)
|
||||
*/
|
||||
public static void openSafe(long safeTime) {
|
||||
stpLogic.openSafe(safeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话 是否处于二级认证时间内
|
||||
* @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
|
||||
*/
|
||||
public static boolean isSafe() {
|
||||
return stpLogic.isSafe();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前会话是否已通过二级认证,如未通过则抛出异常
|
||||
*/
|
||||
public static void checkSafe() {
|
||||
stpLogic.checkSafe();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
|
||||
* @return 剩余有效时间
|
||||
*/
|
||||
public static long getSafeTime() {
|
||||
return stpLogic.getSafeTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话 结束二级认证
|
||||
*/
|
||||
public static void closeSafe() {
|
||||
stpLogic.closeSafe();
|
||||
}
|
||||
|
||||
|
||||
// =================== 历史API,兼容旧版本 ===================
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 获取当前StpLogin的loginKey
|
||||
* @return 当前StpLogin的loginKey
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getLoginKey(){
|
||||
return stpLogic.getLoginType();
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setLoginId(Object loginId) {
|
||||
stpLogic.login(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备类型
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param device 设备类型
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setLoginId(Object loginId, String device) {
|
||||
stpLogic.login(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备类型
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setLoginId(Object loginId, boolean isLastingCookie) {
|
||||
stpLogic.login(loginId, isLastingCookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定所有登录参数Model
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param loginModel 此次登录的参数Model
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setLoginId(Object loginId, SaLoginModel loginModel) {
|
||||
stpLogic.login(loginId, loginModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 会话注销,根据账号id (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
@Deprecated
|
||||
public static void logoutByLoginId(Object loginId) {
|
||||
stpLogic.kickout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 会话注销,根据账号id and 设备类型 (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
|
||||
* @param loginId 账号id
|
||||
* @param device 设备类型 (填null代表注销所有设备类型)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void logoutByLoginId(Object loginId, String device) {
|
||||
stpLogic.kickout(loginId, device);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckBasic;
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaCheckSafe;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 注解鉴权测试
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/at/")
|
||||
public class AtController {
|
||||
|
||||
// 登录认证,登录之后才可以进入方法 ---- http://localhost:8081/at/checkLogin
|
||||
@SaCheckLogin
|
||||
@RequestMapping("checkLogin")
|
||||
public SaResult checkLogin() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 权限认证,具备user-add权限才可以进入方法 ---- http://localhost:8081/at/checkPermission
|
||||
@SaCheckPermission("user-add")
|
||||
@RequestMapping("checkPermission")
|
||||
public SaResult checkPermission() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 权限认证,同时具备所有权限才可以进入 ---- http://localhost:8081/at/checkPermissionAnd
|
||||
@SaCheckPermission({"user-add", "user-delete", "user-update"})
|
||||
@RequestMapping("checkPermissionAnd")
|
||||
public SaResult checkPermissionAnd() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 权限认证,只要具备其中一个就可以进入 ---- http://localhost:8081/at/checkPermissionOr
|
||||
@SaCheckPermission(value = {"user-add", "user-delete", "user-update"}, mode = SaMode.OR)
|
||||
@RequestMapping("checkPermissionOr")
|
||||
public SaResult checkPermissionOr() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 角色认证,只有具备admin角色才可以进入 ---- http://localhost:8081/at/checkRole
|
||||
@SaCheckRole("admin")
|
||||
@RequestMapping("checkRole")
|
||||
public SaResult checkRole() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 完成二级认证 ---- http://localhost:8081/at/openSafe
|
||||
@RequestMapping("openSafe")
|
||||
public SaResult openSafe() {
|
||||
StpUtil.openSafe(200); // 打开二级认证,有效期为200秒
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 通过二级认证后才可以进入 ---- http://localhost:8081/at/checkSafe
|
||||
@SaCheckSafe
|
||||
@RequestMapping("checkSafe")
|
||||
public SaResult checkSafe() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 通过Basic认证后才可以进入 ---- http://localhost:8081/at/checkBasic
|
||||
@SaCheckBasic(account = "sa:123456")
|
||||
@RequestMapping("checkBasic")
|
||||
public SaResult checkBasic() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 登录测试
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/acc/")
|
||||
public class LoginController {
|
||||
|
||||
// 测试登录 ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456
|
||||
@RequestMapping("doLogin")
|
||||
public SaResult doLogin(String name, String pwd) {
|
||||
// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
|
||||
if("zhang".equals(name) && "123456".equals(pwd)) {
|
||||
StpUtil.login(10001);
|
||||
return SaResult.ok("登录成功");
|
||||
}
|
||||
return SaResult.error("登录失败");
|
||||
}
|
||||
|
||||
// 查询登录状态 ---- http://localhost:8081/acc/isLogin
|
||||
@RequestMapping("isLogin")
|
||||
public SaResult isLogin() {
|
||||
return SaResult.ok("是否登录:" + StpUtil.isLogin());
|
||||
}
|
||||
|
||||
// 查询 Token 信息 ---- http://localhost:8081/acc/tokenInfo
|
||||
@RequestMapping("tokenInfo")
|
||||
public SaResult tokenInfo() {
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
}
|
||||
|
||||
// 测试注销 ---- http://localhost:8081/acc/logout
|
||||
@RequestMapping("logout")
|
||||
public SaResult logout() {
|
||||
StpUtil.logout();
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
import com.pj.util.Ttime;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 压力测试
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/s-test/")
|
||||
public class StressTestController {
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/s-test/login
|
||||
// 测试前,请先将 is-read-cookie 配置为 false
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login() {
|
||||
// StpUtil.getTokenSession().logout();
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
|
||||
int count = 10; // 循环多少轮
|
||||
int loginCount = 10000; // 每轮循环多少次
|
||||
|
||||
// 循环10次 取平均时间
|
||||
List<Double> list = new ArrayList<>();
|
||||
for (int i = 1; i <= count; i++) {
|
||||
System.out.println("\n---------------------第" + i + "轮---------------------");
|
||||
Ttime t = new Ttime().start();
|
||||
// 每次登录的次数
|
||||
for (int j = 1; j <= loginCount; j++) {
|
||||
StpUtil.login("1000" + j, "PC-" + j);
|
||||
if(j % 1000 == 0) {
|
||||
System.out.println("已登录:" + j);
|
||||
}
|
||||
}
|
||||
t.end();
|
||||
list.add((t.returnMs() + 0.0) / 1000);
|
||||
System.out.println("第" + i + "轮" + "用时:" + t.toString());
|
||||
}
|
||||
// System.out.println(((SaTokenDaoDefaultImpl)SaTokenManager.getSaTokenDao()).dataMap.size());
|
||||
|
||||
System.out.println("\n---------------------测试结果---------------------");
|
||||
System.out.println(list.size() + "次测试: " + list);
|
||||
double ss = 0;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
ss += list.get(i);
|
||||
}
|
||||
System.out.println("平均用时: " + ss / list.size());
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,251 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pj.util.AjaxJson;
|
||||
import com.pj.util.Ttime;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.session.SaSessionCustomUtil;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 测试专用Controller
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/test/")
|
||||
public class TestController {
|
||||
|
||||
// 测试登录接口, 浏览器访问: http://localhost:8081/test/login
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login(@RequestParam(defaultValue="10001") String id) {
|
||||
System.out.println("======================= 进入方法,测试登录接口 ========================= ");
|
||||
System.out.println("当前会话的token:" + StpUtil.getTokenValue());
|
||||
System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("当前登录账号:" + StpUtil.getLoginIdDefaultNull());
|
||||
|
||||
StpUtil.login(id); // 在当前会话登录此账号
|
||||
System.out.println("登录成功");
|
||||
System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("当前登录账号:" + StpUtil.getLoginId());
|
||||
// System.out.println("当前登录账号并转为int:" + StpUtil.getLoginIdAsInt());
|
||||
System.out.println("当前登录设备:" + StpUtil.getLoginDevice());
|
||||
// System.out.println("当前token信息:" + StpUtil.getTokenInfo());
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试退出登录 , 浏览器访问: http://localhost:8081/test/logout
|
||||
@RequestMapping("logout")
|
||||
public AjaxJson logout() {
|
||||
StpUtil.logout();
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试角色接口, 浏览器访问: http://localhost:8081/test/testRole
|
||||
@RequestMapping("testRole")
|
||||
public AjaxJson testRole() {
|
||||
System.out.println("======================= 进入方法,测试角色接口 ========================= ");
|
||||
|
||||
System.out.println("是否具有角色标识 user " + StpUtil.hasRole("user"));
|
||||
System.out.println("是否具有角色标识 admin " + StpUtil.hasRole("admin"));
|
||||
|
||||
System.out.println("没有admin权限就抛出异常");
|
||||
StpUtil.checkRole("admin");
|
||||
|
||||
System.out.println("在【admin、user】中只要拥有一个就不会抛出异常");
|
||||
StpUtil.checkRoleOr("admin", "user");
|
||||
|
||||
System.out.println("在【admin、user】中必须全部拥有才不会抛出异常");
|
||||
StpUtil.checkRoleAnd("admin", "user");
|
||||
|
||||
System.out.println("角色测试通过");
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试权限接口, 浏览器访问: http://localhost:8081/test/testJur
|
||||
@RequestMapping("testJur")
|
||||
public AjaxJson testJur() {
|
||||
System.out.println("======================= 进入方法,测试权限接口 ========================= ");
|
||||
|
||||
System.out.println("是否具有权限101" + StpUtil.hasPermission("101"));
|
||||
System.out.println("是否具有权限user-add" + StpUtil.hasPermission("user-add"));
|
||||
System.out.println("是否具有权限article-get" + StpUtil.hasPermission("article-get"));
|
||||
|
||||
System.out.println("没有user-add权限就抛出异常");
|
||||
StpUtil.checkPermission("user-add");
|
||||
|
||||
System.out.println("在【101、102】中只要拥有一个就不会抛出异常");
|
||||
StpUtil.checkPermissionOr("101", "102");
|
||||
|
||||
System.out.println("在【101、102】中必须全部拥有才不会抛出异常");
|
||||
StpUtil.checkPermissionAnd("101", "102");
|
||||
|
||||
System.out.println("权限测试通过");
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试会话session接口, 浏览器访问: http://localhost:8081/test/session
|
||||
@RequestMapping("session")
|
||||
public AjaxJson session() throws JsonProcessingException {
|
||||
System.out.println("======================= 进入方法,测试会话session接口 ========================= ");
|
||||
System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("当前登录账号session的id" + StpUtil.getSession().getId());
|
||||
System.out.println("当前登录账号session的id" + StpUtil.getSession().getId());
|
||||
System.out.println("测试取值name:" + StpUtil.getSession().get("name"));
|
||||
StpUtil.getSession().set("name", new Date()); // 写入一个值
|
||||
System.out.println("测试取值name:" + StpUtil.getSession().get("name"));
|
||||
System.out.println( new ObjectMapper().writeValueAsString(StpUtil.getSession()));
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试自定义session接口, 浏览器访问: http://localhost:8081/test/session2
|
||||
@RequestMapping("session2")
|
||||
public AjaxJson session2() {
|
||||
System.out.println("======================= 进入方法,测试自定义session接口 ========================= ");
|
||||
// 自定义session就是无需登录也可以使用 的session :比如拿用户的手机号当做 key, 来获取 session
|
||||
System.out.println("自定义 session的id为:" + SaSessionCustomUtil.getSessionById("1895544896").getId());
|
||||
System.out.println("测试取值name:" + SaSessionCustomUtil.getSessionById("1895544896").get("name"));
|
||||
SaSessionCustomUtil.getSessionById("1895544896").set("name", "张三"); // 写入值
|
||||
System.out.println("测试取值name:" + SaSessionCustomUtil.getSessionById("1895544896").get("name"));
|
||||
System.out.println("测试取值name:" + SaSessionCustomUtil.getSessionById("1895544896").get("name"));
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// ----------
|
||||
// 测试token专属session, 浏览器访问: http://localhost:8081/test/getTokenSession
|
||||
@RequestMapping("getTokenSession")
|
||||
public AjaxJson getTokenSession() {
|
||||
System.out.println("======================= 进入方法,测试会话session接口 ========================= ");
|
||||
System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("当前token专属session: " + StpUtil.getTokenSession().getId());
|
||||
|
||||
System.out.println("测试取值name:" + StpUtil.getTokenSession().get("name"));
|
||||
StpUtil.getTokenSession().set("name", "张三"); // 写入一个值
|
||||
System.out.println("测试取值name:" + StpUtil.getTokenSession().get("name"));
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 打印当前token信息, 浏览器访问: http://localhost:8081/test/tokenInfo
|
||||
@RequestMapping("tokenInfo")
|
||||
public AjaxJson tokenInfo() {
|
||||
System.out.println("======================= 进入方法,打印当前token信息 ========================= ");
|
||||
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
|
||||
System.out.println(tokenInfo);
|
||||
return AjaxJson.getSuccessData(tokenInfo);
|
||||
}
|
||||
|
||||
// 测试注解式鉴权, 浏览器访问: http://localhost:8081/test/atCheck
|
||||
@SaCheckLogin // 注解式鉴权:当前会话必须登录才能通过
|
||||
@SaCheckRole("super-admin") // 注解式鉴权:当前会话必须具有指定角色标识才能通过
|
||||
@SaCheckPermission("user-add") // 注解式鉴权:当前会话必须具有指定权限才能通过
|
||||
@RequestMapping("atCheck")
|
||||
public AjaxJson atCheck() {
|
||||
System.out.println("======================= 进入方法,测试注解鉴权接口 ========================= ");
|
||||
System.out.println("只有通过注解鉴权,才能进入此方法");
|
||||
// StpUtil.checkActivityTimeout();
|
||||
// StpUtil.updateLastActivityToNow();
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试注解式鉴权, 浏览器访问: http://localhost:8081/test/atJurOr
|
||||
@RequestMapping("atJurOr")
|
||||
@SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR) // 注解式鉴权:只要具有其中一个权限即可通过校验
|
||||
public AjaxJson atJurOr() {
|
||||
return AjaxJson.getSuccessData("用户信息");
|
||||
}
|
||||
|
||||
// [活动时间] 续签: http://localhost:8081/test/rene
|
||||
@RequestMapping("rene")
|
||||
public AjaxJson rene() {
|
||||
StpUtil.checkActivityTimeout();
|
||||
StpUtil.updateLastActivityToNow();
|
||||
return AjaxJson.getSuccess("续签成功");
|
||||
}
|
||||
|
||||
// 测试踢人下线 浏览器访问: http://localhost:8081/test/kickOut
|
||||
@RequestMapping("kickOut")
|
||||
public AjaxJson kickOut() {
|
||||
// 先登录上
|
||||
StpUtil.login(10001);
|
||||
// 踢下线
|
||||
StpUtil.kickout(10001);
|
||||
// 再尝试获取
|
||||
StpUtil.getLoginId();
|
||||
// 返回
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试登录接口, 按照设备登录, 浏览器访问: http://localhost:8081/test/login2
|
||||
@RequestMapping("login2")
|
||||
public AjaxJson login2(@RequestParam(defaultValue="10001") String id, @RequestParam(defaultValue="PC") String device) {
|
||||
StpUtil.login(id, device);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试身份临时切换: http://localhost:8081/test/switchTo
|
||||
@RequestMapping("switchTo")
|
||||
public AjaxJson switchTo() {
|
||||
System.out.println("当前会话身份:" + StpUtil.getLoginIdDefaultNull());
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
StpUtil.switchTo(10044, () -> {
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
System.out.println("当前会话身份已被切换为:" + StpUtil.getLoginId());
|
||||
});
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试会话治理 浏览器访问: http://localhost:8081/test/search
|
||||
@RequestMapping("search")
|
||||
public AjaxJson search() {
|
||||
System.out.println("--------------");
|
||||
Ttime t = new Ttime().start();
|
||||
List<String> tokenValue = StpUtil.searchTokenValue("8feb8265f773", 0, 10);
|
||||
for (String v : tokenValue) {
|
||||
// SaSession session = StpUtil.getSessionBySessionId(sid);
|
||||
System.out.println(v);
|
||||
}
|
||||
System.out.println("用时:" + t.end().toString());
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试指定设备登录 浏览器访问: http://localhost:8081/test/loginByDevice
|
||||
@RequestMapping("loginByDevice")
|
||||
public AjaxJson loginByDevice() {
|
||||
System.out.println("--------------");
|
||||
StpUtil.login(10001, "PC");
|
||||
return AjaxJson.getSuccessData("登录成功");
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
System.out.println("------------进来了");
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test2
|
||||
@RequestMapping("test2")
|
||||
public AjaxJson test2() {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
package com.pj.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* ajax请求返回Json格式数据的封装
|
||||
*/
|
||||
public class AjaxJson implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L; // 序列化版本号
|
||||
|
||||
public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||
public static final int CODE_ERROR = 500; // 错误状态码
|
||||
public static final int CODE_WARNING = 501; // 警告状态码
|
||||
public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||
|
||||
public int code; // 状态码
|
||||
public String msg; // 描述信息
|
||||
public Object data; // 携带对象
|
||||
public Long dataCount; // 数据总数,用于分页
|
||||
|
||||
/**
|
||||
* 返回code
|
||||
* @return
|
||||
*/
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给msg赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
public String getMsg() {
|
||||
return this.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给data赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setData(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将data还原为指定类型并返回
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(Class<T> cs) {
|
||||
return (T) data;
|
||||
}
|
||||
|
||||
// ============================ 构建 ==================================
|
||||
|
||||
public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.dataCount = dataCount;
|
||||
}
|
||||
|
||||
// 返回成功
|
||||
public static AjaxJson getSuccess() {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg, Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessData(Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessArray(Object... data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
|
||||
// 返回失败
|
||||
public static AjaxJson getError() {
|
||||
return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||
}
|
||||
public static AjaxJson getError(String msg) {
|
||||
return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回警告
|
||||
public static AjaxJson getWarning() {
|
||||
return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||
}
|
||||
public static AjaxJson getWarning(String msg) {
|
||||
return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回未登录
|
||||
public static AjaxJson getNotLogin() {
|
||||
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||
}
|
||||
|
||||
// 返回没有权限的
|
||||
public static AjaxJson getNotJur(String msg) {
|
||||
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回一个自定义状态码的
|
||||
public static AjaxJson get(int code, String msg){
|
||||
return new AjaxJson(code, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回分页和数据的
|
||||
public static AjaxJson getPageData(Long dataCount, Object data){
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||
}
|
||||
|
||||
// 返回,根据受影响行数的(大于0=ok,小于0=error)
|
||||
public static AjaxJson getByLine(int line){
|
||||
if(line > 0){
|
||||
return getSuccess("ok", line);
|
||||
}
|
||||
return getError("error").setData(line);
|
||||
}
|
||||
|
||||
// 返回,根据布尔值来确定最终结果的 (true=ok,false=error)
|
||||
public static AjaxJson getByBoolean(boolean b){
|
||||
return b ? getSuccess("ok") : getError("error");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public String toString() {
|
||||
String data_string = null;
|
||||
if(data == null){
|
||||
|
||||
} else if(data instanceof List){
|
||||
data_string = "List(length=" + ((List)data).size() + ")";
|
||||
} else {
|
||||
data_string = data.toString();
|
||||
}
|
||||
return "{"
|
||||
+ "\"code\": " + this.getCode()
|
||||
+ ", \"msg\": \"" + this.getMsg() + "\""
|
||||
+ ", \"data\": " + data_string
|
||||
+ ", \"dataCount\": " + dataCount
|
||||
+ "}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.pj.util;
|
||||
|
||||
|
||||
/**
|
||||
* 用于测试用时
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class Ttime {
|
||||
|
||||
private long start=0; //开始时间
|
||||
private long end=0; //结束时间
|
||||
|
||||
public static Ttime t = new Ttime(); //static快捷使用
|
||||
|
||||
/**
|
||||
* 开始计时
|
||||
* @return
|
||||
*/
|
||||
public Ttime start() {
|
||||
start=System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 结束计时
|
||||
*/
|
||||
public Ttime end() {
|
||||
end=System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回所用毫秒数
|
||||
*/
|
||||
public long returnMs() {
|
||||
return end-start;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出结果
|
||||
*/
|
||||
public void outTime() {
|
||||
System.out.println(this.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束并格式化输出结果
|
||||
*/
|
||||
public void endOutTime() {
|
||||
this.end().outTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (returnMs() + 0.0) / 1000 + "s"; // 格式化为:0.01s
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken
|
||||
# token有效期,单位s 默认30天, -1代表永不过期
|
||||
timeout: 2592000
|
||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
activity-timeout: -1
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: true
|
||||
# token风格
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: false
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@ -39,26 +39,6 @@
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用jdk默认序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- Sa-Token整合 Redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- 提供Redis连接池 -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency> -->
|
||||
|
||||
<!-- Sa-Token整合SpringAOP实现注解鉴权 -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
@ -73,13 +53,6 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool工具类,用来生成雪花算法唯一id -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.5.4</version>
|
||||
</dependency> -->
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0.RC</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://sa-token.dev33.cn/doc/logo.png" width="150" height="150">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.30.0.RC</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.30.0</h1>
|
||||
<h5 align="center">一个轻量级 Java 权限认证框架,让鉴权变得简单、优雅!</h5>
|
||||
<p align="center" class="badge-box">
|
||||
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg?theme=gvp"></a>
|
||||
@ -122,10 +122,6 @@ StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
|
||||
<p class="un-dec-a-pre"></p>
|
||||
|
||||
[](https://giteye.net/chart/77YQZ6UK)
|
||||
|
||||
<p class="un-dec-a-pre"></p>
|
||||
|
||||
[](https://starchart.cc/dromara/sa-token)
|
||||
|
||||
如果 Sa-Token 帮助到了您,希望您可以为其点上一个 `star`:
|
||||
@ -133,19 +129,10 @@ StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
[GitHub](https://github.com/dromara/sa-token)
|
||||
|
||||
|
||||
|
||||
|
||||
## 使用Sa-Token的开源项目
|
||||
参考:[Sa-Token 生态](/more/link)
|
||||
|
||||
|
||||
## 贡献者名单
|
||||
感谢每一个为 Sa-Token 贡献代码的小伙伴
|
||||
|
||||
<p class="un-dec-a-pre"></p>
|
||||
|
||||
[](https://giteye.net/chart/CGZ7GT8E)
|
||||
|
||||
## 交流群
|
||||
QQ交流群:1群:1002350610 (已满) 、
|
||||
2群:614714762 [点击加入](https://jq.qq.com/?_wv=1027&k=b759RZrL)
|
||||
|
@ -85,6 +85,7 @@
|
||||
- [Token有效期详解](/fun/token-timeout)
|
||||
- [Session模型详解](/fun/session-model)
|
||||
- [TokenInfo参数详解](/fun/token-info)
|
||||
- [异常细分状态码](/fun/exception-code)
|
||||
- [解决反向代理 uri 丢失的问题](/fun/curr-domain)
|
||||
- [参考:把权限放在缓存里](/fun/jur-cache)
|
||||
- [解决跨域问题](/fun/cors-filter)
|
||||
|
107
sa-token-doc/doc/fun/exception-code.md
Normal file
107
sa-token-doc/doc/fun/exception-code.md
Normal file
@ -0,0 +1,107 @@
|
||||
# 异常细分状态码
|
||||
|
||||
---
|
||||
|
||||
### 获取异常细分状态码
|
||||
|
||||
Sa-Token 中的基础异常类是 `SaTokenException`,在此基础上,又针对一些特定场景划分出诸如 `NotLoginException`、`NotPermissionException` 等。
|
||||
|
||||
但是框架中异常抛出点远远多于异常种类的划分,比如在 SSO 插件中,[ redirect 重定向地址无效 ] 和 [ ticket 参数值无效 ] 都会导致 SSO 授权的失败,
|
||||
但是它们抛出的异常都是 `SaSsoException`,如果你需要对这两种异常情形做出不同的处理,仅仅判断异常的 ClassType 显然不够。
|
||||
|
||||
为了解决上述需求,Sa-Token 对每个异常抛出点都会指定一个特定的 code 值,就像这样:
|
||||
|
||||
``` java
|
||||
if(SaFoxUtil.isUrl(url) == false) {
|
||||
throw new SaSsoException("无效redirect:" + url).setCode(SaSsoExceptionCode.CODE_20001);
|
||||
}
|
||||
```
|
||||
|
||||
就像是打上一个特定的标记,不同异常情形标记的 code 码值也会不同,这就为你精细化异常处理提供了前提。
|
||||
|
||||
要在捕获异常时获取这个 code 码也非常简单:
|
||||
|
||||
``` java
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(SaTokenException.class)
|
||||
public SaResult handlerSaTokenException(SaTokenException e) {
|
||||
|
||||
// 根据不同异常细分状态码返回不同的提示
|
||||
if(e.getCode() == 20001) {
|
||||
return SaResult.error("redirect 重定向 url 是一个无效地址");
|
||||
}
|
||||
if(e.getCode() == 20002) {
|
||||
return SaResult.error("redirect 重定向 url 不在 allowUrl 允许的范围内");
|
||||
}
|
||||
if(e.getCode() == 20004) {
|
||||
return SaResult.error("提供的 ticket 是无效的");
|
||||
}
|
||||
// 更多 code 码判断 ...
|
||||
|
||||
// 默认的提示
|
||||
return SaResult.error("服务器繁忙,请稍后重试...");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
SaToken 中的所有异常都是继承于 `SaTokenException` 的,也就是说,所有异常你都可以通过 `e.getCode()` 的方式获取对应的异常细分状态码。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 异常细分状态码-参照表
|
||||
|
||||
!> 目前仅对 sso 插件和 jwt 插件做了异常状态码细分,后续版本升级会支持更多模块
|
||||
|
||||
**sa-token-code 核心包:**
|
||||
|
||||
| code码值 | 含义 |
|
||||
| :-------- | :-------- |
|
||||
| -1 | 代表这个异常在抛出时未指定异常细分状态码 |
|
||||
|
||||
|
||||
**sa-token-sso 单点登录相关:**
|
||||
|
||||
| code码值 | 含义 |
|
||||
| :-------- | :-------- |
|
||||
| 20001 | `redirect` 重定向 url 是一个无效地址 |
|
||||
| 20002 | `redirect` 重定向 url 不在 allowUrl 允许的范围内 |
|
||||
| 20003 | 接口调用方提供的 `secretkey` 秘钥无效 |
|
||||
| 20004 | 提供的 `ticket` 是无效的 |
|
||||
| 20005 | 在模式三下,sso-client 调用 sso-server 端 校验ticket接口 时,得到的响应是校验失败 |
|
||||
| 20006 | 在模式三下,sso-client 调用 sso-server 端 单点注销接口 时,得到的响应是注销失败 |
|
||||
| 20007 | http 请求调用 提供的 `timestamp` 与当前时间的差距超出允许的范围 |
|
||||
| 20008 | http 请求调用 提供的 `sign` 无效 |
|
||||
| 20009 | 本地系统没有配置 `secretkey` 字段 |
|
||||
|
||||
|
||||
|
||||
**sa-token-jwt 插件相关:**
|
||||
|
||||
| code码值 | 含义 |
|
||||
| :-------- | :-------- |
|
||||
| 40101 | 对 jwt 字符串解析失败 |
|
||||
| 40102 | 此 jwt 的签名无效 |
|
||||
| 40103 | 此 jwt 的 `loginType` 字段不符合预期 |
|
||||
| 40104 | 此 jwt 已超时 |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
<div class="logo-box">
|
||||
<img src="logo.png" title="logo" />
|
||||
<h1 class="logo-text">Sa-Token</h1>
|
||||
<sub>v1.30.0.RC</sub>
|
||||
<sub>v1.30.0</sub>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
@ -114,7 +114,7 @@
|
||||
<!-- -->
|
||||
<script src="./lib/docsify-plugin.js"></script>
|
||||
<script>
|
||||
var saTokenTopVersion = '1.30.0.RC'; // Sa-Token最新版本
|
||||
var saTokenTopVersion = '1.30.0'; // Sa-Token最新版本
|
||||
var name = '<img style="width: 60px; height: 60px; vertical-align: middle;" src="logo.png" alt="logo" /> ';
|
||||
name += '<b style="font-size: 28px; vertical-align: middle;">Sa-Token</b> <sub>v' + saTokenTopVersion + '</sub>';
|
||||
window.$docsify = {
|
||||
|
@ -81,6 +81,20 @@ var myDocsifyPlugin = function(hook, vm) {
|
||||
// $('.search input').val('');
|
||||
$('.results-panel').removeClass('show');
|
||||
});
|
||||
|
||||
// 点击按钮,加载图片
|
||||
$(document).on('click', '.show-img', function(){
|
||||
var src = $(this).attr('img-src');
|
||||
var img = '<img class="show-to-img" src="' + src + '" />';
|
||||
$(this).after(img);
|
||||
$(this).remove();
|
||||
})
|
||||
|
||||
// 点击按钮,加载图片
|
||||
$(document).on('click', '.show-to-img', function(){
|
||||
open(this.src);
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
}
|
@ -204,7 +204,20 @@ body {
|
||||
#main .toc-box a{border-color: rgba(0,0,0,0); transition: 0s;}
|
||||
#main .toc-box a span{color: inherit;}
|
||||
|
||||
|
||||
/* 加载图片的按钮 */
|
||||
.show-img{
|
||||
background-color: #FFF;
|
||||
padding: 8px 15px;
|
||||
border: 1px #42b983 solid;
|
||||
color: #42b983;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.show-img:hover{
|
||||
background-color: #eaf6eb;
|
||||
}
|
||||
.show-to-img{cursor: pointer;}
|
||||
|
||||
/* 导航栏悬浮时出现下滑条条 */
|
||||
/* .doc-header .nav-right .wzi::after {
|
||||
|
@ -28,6 +28,12 @@
|
||||
|
||||
由于`jwt`模式不在服务端存储数据,对于比较复杂的业务可能会功能受限,因此更加推荐使用方案三
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--dcs-session.gif">加载动态演示图</button>
|
||||
|
||||
|
||||
集成依赖示例:
|
||||
|
||||
``` xml
|
||||
<!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
@ -40,6 +46,7 @@
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
详细参考:[集成 Redis](/up/integ-redis)
|
||||
|
||||
|
||||
|
@ -102,7 +102,7 @@ jwt 的招牌便是无须借助服务端完成会话管理,如果集成`jwt`
|
||||
|
||||
|
||||
### 框架抛出的权限不足异常,我想根据自定义提示信息,可以吗?
|
||||
可以,在全局异常拦截器里捕获`NotPermissionException`,可以通过`getCode()`获取没有通过认证的权限码,可以据此自定义返回信息
|
||||
可以,在全局异常拦截器里捕获`NotPermissionException`,可以通过`getPermission()`获取没有通过认证的权限码,可以据此自定义返回信息
|
||||
|
||||
|
||||
### 我的项目权限模型不是RBAC模型,很复杂,可以集成吗?
|
||||
|
@ -19,6 +19,7 @@ Sa-Token 采用 Apache-2.0 开源协议,**承诺框架本身与官网文档永
|
||||
|
||||
| 赞助人 | 赞助金额 | 留言 | 时间 |
|
||||
| :-------- | :-------- | :-------- | :-------- |
|
||||
| [别处理](https://gitee.com/zshnb) | ¥ 10.0 | 非常好的项目,希望能一直做下去 | 2022-05-01 |
|
||||
| [李洪星](https://gitee.com/li_hong_xing) | ¥ 10.0 | 解决了很多之前项目中遇到的问题。感谢您的开源项目! | 2022-04-29 |
|
||||
| [乡村阿土哥](https://gitee.com/895995040) | ¥ 10.0 | 感谢您的开源项目! | 2022-04-29 |
|
||||
| [Horatio201](https://gitee.com/horatio201) | ¥ 20.0 | 太牛了! | 2022-04-25 |
|
||||
|
@ -1,7 +1,7 @@
|
||||
# 更新日志
|
||||
|
||||
|
||||
### 2022-05-01 @v1.30.0
|
||||
### 2022-05-9 @v1.30.0
|
||||
- 新增:新增集成 Web-Socket 鉴权示例。 **[重要]**
|
||||
- 新增:新增集成 Web-Socket(Spring封装版) 鉴权示例。
|
||||
- 新增:新增 jfinal 集成包 `sa-token-jfinal-plugin` **[重要]**
|
||||
@ -24,7 +24,8 @@
|
||||
- 升级:SpringBoot 相关组件依赖版本升级至 `2.5.12`
|
||||
- 文档:在线文档所有 `AjaxJson` 改为 `SaResult`
|
||||
- 文档:“多账号认证” -> 改为 “多账户认证”
|
||||
- 升级:顶级异常类 `SaTokenException` 增加 code 异常细分状态码。 **[重要]**
|
||||
- 文档:部分章节新增动态演示图 **[重要]**
|
||||
- 升级:顶级异常类 `SaTokenException` 增加 code 异常细分状态码。[详见](/fun/exception-code) **[重要]**
|
||||
- **注意升级:受异常细分状态码影响,`NotPermissionException` 类中 `getCode()` 方法改为 `getPermission()`。** **[不向下兼容]**
|
||||
- SSO 模块升级:
|
||||
- 重构:SSO 模块从核心包拆分为独立插件 `sa-token-sso` **[重要]**
|
||||
|
@ -5,6 +5,10 @@ Sa-Token默认的Redis集成方式会把权限数据和业务缓存放在一起
|
||||
|
||||
> 搭建两个Redis服务器,一个专门用来做业务缓存,另一台专门存放Sa-Token权限数据
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--alone-redis.gif">加载动态演示图</button>
|
||||
|
||||
|
||||
要将Sa-Token的数据单独抽离出来很简单,你只需要为Sa-Token单独配置一个Redis连接信息即可
|
||||
|
||||
---
|
||||
|
@ -7,8 +7,9 @@
|
||||
框架已提供的集成包包括:
|
||||
|
||||
- 默认方式:储存在内存中,位于core核心包
|
||||
- sa-token-dao-redis:Redis集成包,使用jdk默认序列化方式
|
||||
- sa-token-dao-redis-jackson:Redis集成包,使用jackson序列化方式
|
||||
- sa-token-dao-redis:Redis集成包,使用 jdk 默认序列化方式
|
||||
- sa-token-dao-redis-jackson:Redis集成包,使用 jackson 序列化方式
|
||||
- sa-token-dao-redisx:Redisx 集成包
|
||||
|
||||
有关Redis集成,详细参考:[集成Redis](/up/integ-redis),更多存储方式欢迎提交PR
|
||||
|
||||
|
@ -34,6 +34,16 @@
|
||||
解决方案:在 sso-client 也新建上这个类,而且包名需要与 sso-server 端的一致(直接从 sso-server 把实体类复制过来就好了)
|
||||
|
||||
|
||||
### 问:如果 sso-client 端我没有集成 sa-token-sso,如何对接?
|
||||
需要手动调用 http 请求来对接 sso-server 开放的接口,参考示例:[sa-token-demo-sso3-client-nosdk](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-sso3-client-nosdk)
|
||||
|
||||
|
||||
### 问:如果 sso-client 端不是 java语言,可以对接吗?
|
||||
可以,只不过有点麻烦,基本思路和上个问题一致,需要手动调用 http 请求来对接 sso-server 开放的接口,参考:
|
||||
[SSO-Server 认证中心开放接口](/sso/sso-apidoc)
|
||||
|
||||
|
||||
|
||||
### 还有其它问题?
|
||||
可以加群反馈一下,比较典型的问题我们解决之后都会提交到此页面方便大家快速排查
|
||||
|
||||
|
@ -17,6 +17,13 @@
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 插件:整合SSO -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-sso</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
@ -42,10 +49,10 @@
|
||||
</dependency>
|
||||
```
|
||||
|
||||
除了 **sa-token-spring-boot-starter** 以外,其它包都是可选的:
|
||||
- 在SSO模式三时 Redis 相关包是可选的
|
||||
除了 `sa-token-spring-boot-starter` 和 `sa-token-sso` 以外,其它包都是可选的:
|
||||
- 在 SSO 模式三时 Redis 相关包是可选的
|
||||
- 在前后端分离模式下可以删除 thymeleaf 相关包
|
||||
- 在不需要SSO模式三单点注销的情况下可以删除 http 工具包
|
||||
- 在不需要 SSO 模式三单点注销的情况下可以删除 http 工具包
|
||||
|
||||
建议先完整测试三种模式之后再对pom依赖进行酌情删减。
|
||||
|
||||
|
@ -18,6 +18,10 @@
|
||||
|
||||
而共享Redis,并不需要我们把所有项目的数据都放在同一个Redis中,Sa-Token提供了 **[权限缓存与业务缓存分离]** 的解决方案,详情戳:[Alone独立Redis插件](/plugin/alone-redis)。
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--sso1.gif">加载动态演示图</button>
|
||||
|
||||
|
||||
OK,所有理论就绪,下面开始实战:
|
||||
|
||||
|
||||
@ -65,6 +69,12 @@ sa-token:
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
<!-- Sa-Token 插件:整合SSO -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-sso</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
|
@ -28,6 +28,10 @@
|
||||
整个过程,除了第四步用户在SSO认证中心登录时会被打断,其余过程均是自动化的,当用户在另一个子系统再次点击`[登录]`按钮,由于此用户在SSO认证中心已有会话存在,
|
||||
所以第四步也将自动化,也就是单点登录的最终目的 —— 一次登录,处处通行。
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--sso2.gif">加载动态演示图</button>
|
||||
|
||||
|
||||
下面我们按照步骤依次完成上述过程:
|
||||
|
||||
### 2、准备工作
|
||||
@ -65,6 +69,12 @@ sa-token:
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
<!-- Sa-Token 插件:整合SSO -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-sso</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
|
@ -131,6 +131,10 @@ public Object myinfo() {
|
||||
5. Server 端注销下线。
|
||||
6. 单点注销完成。
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--sso3-logout.gif">加载动态演示图</button>
|
||||
|
||||
|
||||
这些逻辑 Sa-Token 内部已经封装完毕,你只需按照文档增加以下配置即可:
|
||||
|
||||
#### 4.1、SSO-Client 端新增配置
|
||||
|
@ -7,8 +7,8 @@
|
||||
|
||||
<!------------------------------ tabs:start ------------------------------>
|
||||
|
||||
<!-- tab:SpringMVC环境 (ServletAPI) -->
|
||||
如果你使用的框架基于 ServletAPI 构建( SpringMVC、SpringBoot、Zuul等 ),请引入此包
|
||||
<!------------- tab:SpringMVC环境 (ServletAPI) ------------->
|
||||
如果你使用的框架基于 ServletAPI 构建( SpringMVC、SpringBoot等 ),请引入此包
|
||||
``` xml
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
@ -18,7 +18,7 @@
|
||||
</dependency>
|
||||
```
|
||||
|
||||
<!-- tab:WebFlux环境 (Reactor) -->
|
||||
<!------------- tab:WebFlux环境 (Reactor) ------------->
|
||||
注:如果你使用的框架基于 Reactor 模型构建(Netty、WebFlux、ShenYu、SC Gateway等),请引入此包
|
||||
``` xml
|
||||
<!-- Sa-Token 权限认证(Reactor响应式集成), 在线文档:http://sa-token.dev33.cn/ -->
|
||||
@ -29,7 +29,40 @@
|
||||
</dependency>
|
||||
```
|
||||
|
||||
<!-- tab:Servlet容器环境 -->
|
||||
<!------------- tab:Solon 集成 ------------->
|
||||
参考:[Solon官网](https://solon.noear.org/)
|
||||
``` xml
|
||||
<!-- Sa-Token 整合 Solon, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-solon-plugin</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
<!------------- tab:JFinal 集成 ------------->
|
||||
参考:[JFinal官网](https://jfinal.com/)
|
||||
``` xml
|
||||
<!-- Sa-Token 整合 JFinal, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-jfinal-plugin</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
<!------------- tab:Jboot 集成 ------------->
|
||||
参考:[Jboot官网](http://www.jboot.com.cn/)
|
||||
``` xml
|
||||
<!-- Sa-Token 整合 Jboot, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-jboot-plugin</artifactId>
|
||||
<version>${sa.top.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
<!------------- tab:裸Servlet容器环境 ------------->
|
||||
注:如果你的项目没有使用Spring,但是Web框架是基于 ServletAPI 规范的,可以引入此包
|
||||
``` xml
|
||||
<!-- Sa-Token 权限认证(ServletAPI规范), 在线文档:http://sa-token.dev33.cn/ -->
|
||||
@ -41,7 +74,7 @@
|
||||
```
|
||||
引入此依赖需要自定义 SaTokenContext 实现,参考:[自定义 SaTokenContext 指南](/fun/sa-token-context)
|
||||
|
||||
<!-- tab:其它 -->
|
||||
<!------------- tab:其它 ------------->
|
||||
注:如果你的项目既没有使用 SpringMVC、WebFlux,也不是基于 ServletAPI 规范,那么可以引入core核心包
|
||||
``` xml
|
||||
<!-- Sa-Token 权限认证(core核心包), 在线文档:http://sa-token.dev33.cn/ -->
|
||||
@ -52,6 +85,7 @@
|
||||
</dependency>
|
||||
```
|
||||
引入此依赖需要自定义 SaTokenContext 实现,参考:[自定义 SaTokenContext 指南](/fun/sa-token-context)
|
||||
|
||||
<!---------------------------- tabs:end ------------------------------>
|
||||
|
||||
|
||||
@ -61,18 +95,37 @@
|
||||
``` xml
|
||||
implementation 'cn.dev33:sa-token-spring-boot-starter:${sa.top.version}'
|
||||
```
|
||||
|
||||
<!-- tab:WebFlux环境 (Reactor) -->
|
||||
``` xml
|
||||
implementation 'cn.dev33:sa-token-reactor-spring-boot-starter:${sa.top.version}'
|
||||
```
|
||||
<!-- tab:Servlet容器环境 -->
|
||||
|
||||
<!-- tab:Solon 集成 -->
|
||||
``` xml
|
||||
implementation 'cn.dev33:sa-token-solon-plugin:${sa.top.version}'
|
||||
```
|
||||
|
||||
<!-- tab:JFinal 集成 -->
|
||||
``` xml
|
||||
implementation 'cn.dev33:sa-token-jfinal-plugin:${sa.top.version}'
|
||||
```
|
||||
|
||||
<!-- tab:Jboot 集成 -->
|
||||
``` xml
|
||||
implementation 'cn.dev33:sa-token-jboot-plugin:${sa.top.version}'
|
||||
```
|
||||
|
||||
<!-- tab:裸Servlet容器环境 -->
|
||||
``` xml
|
||||
implementation 'cn.dev33:sa-token-servlet:${sa.top.version}'
|
||||
```
|
||||
|
||||
<!-- tab:其它 -->
|
||||
``` xml
|
||||
implementation 'cn.dev33:sa-token-core:${sa.top.version}'
|
||||
```
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
注:JDK版本:`v1.8+`,SpringBoot:`建议2.0以上`
|
||||
@ -108,6 +161,7 @@ implementation 'cn.dev33:sa-token-core:${sa.top.version}'
|
||||
├── sa-token-jwt // [插件] Sa-Token 整合 jwt 登录认证
|
||||
├── sa-token-demo // [示例] Sa-Token 示例合集
|
||||
├── sa-token-demo-springboot // [示例] Sa-Token 整合 SpringBoot
|
||||
├── sa-token-demo-springboot-redis // [示例] Sa-Token 整合 SpringBoot
|
||||
├── sa-token-demo-webflux // [示例] Sa-Token 整合 WebFlux
|
||||
├── sa-token-demo-jwt // [示例] Sa-Token 集成 jwt
|
||||
├── sa-token-demo-solon // [示例] Sa-Token 集成 Solon
|
||||
|
@ -22,7 +22,7 @@ public class MySaTokenListener implements SaTokenListener {
|
||||
|
||||
/** 每次登录时触发 */
|
||||
@Override
|
||||
public void doLogin(String loginType, Object loginId, SaLoginModel loginModel) {
|
||||
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
|
||||
// ...
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,9 @@
|
||||
如果你经常使用腾讯QQ,就会发现它的登录有如下特点:它可以手机电脑同时在线,但是不能在两个手机上同时登录一个账号 <br/>
|
||||
同端互斥登录,指的就是像腾讯QQ一样,在同一类型设备上只允许单地点登录,在不同类型设备上允许同时在线
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--mutex-login.gif">加载动态演示图</button>
|
||||
|
||||
---
|
||||
|
||||
## 具体API
|
||||
|
@ -23,13 +23,16 @@ StpUtil.login(10001, false);
|
||||
### 实现原理
|
||||
Cookie作为浏览器提供的默认会话跟踪机制,其生命周期有两种形式,分别是:
|
||||
- 临时Cookie:有效期为本次会话,只要关闭浏览器窗口,Cookie就会消失
|
||||
- 永久Cookie:有效期为一个具体的时间,在时间未到期之前,即使用户关闭了浏览器Cookie也不会消失
|
||||
- 持久Cookie:有效期为一个具体的时间,在时间未到期之前,即使用户关闭了浏览器Cookie也不会消失
|
||||
|
||||
利用Cookie的此特性,我们便可以轻松实现 [记住我] 模式:
|
||||
- 勾选 [记住我] 按钮时:调用`StpUtil.login(10001, true)`,在浏览器写入一个`永久Cookie`储存 Token,此时用户即使重启浏览器 Token 依然有效
|
||||
- 勾选 [记住我] 按钮时:调用`StpUtil.login(10001, true)`,在浏览器写入一个`持久Cookie`储存 Token,此时用户即使重启浏览器 Token 依然有效
|
||||
- 不勾选 [记住我] 按钮时:调用`StpUtil.login(10001, false)`,在浏览器写入一个`临时Cookie`储存 Token,此时用户在重启浏览器后 Token 便会消失,导致会话失效
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--remember-me.gif">加载动态演示图</button>
|
||||
|
||||
|
||||
### 前后台分离模式下如何实现[记住我]?
|
||||
|
||||
此时机智的你😏很快发现一个问题,Cookie虽好,却无法在前后端分离环境下使用,那是不是代表上述方案在APP、小程序等环境中无效?
|
||||
@ -39,7 +42,7 @@ Cookie作为浏览器提供的默认会话跟踪机制,其生命周期有两
|
||||
|
||||
以经典跨端框架 [uni-app](https://uniapp.dcloud.io/) 为例,我们可以使用如下方式达到同样的效果:
|
||||
``` js
|
||||
// 使用本地存储保存token,达到 [永久Cookie] 的效果
|
||||
// 使用本地存储保存token,达到 [持久Cookie] 的效果
|
||||
uni.setStorageSync("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");
|
||||
|
||||
// 使用globalData保存token,达到 [临时Cookie] 的效果
|
||||
@ -48,7 +51,7 @@ getApp().globalData.satoken = "xxxx-xxxx-xxxx-xxxx-xxx";
|
||||
|
||||
如果你决定在PC浏览器环境下进行前后台分离模式开发,那么更加简单:
|
||||
``` js
|
||||
// 使用 localStorage 保存token,达到 [永久Cookie] 的效果
|
||||
// 使用 localStorage 保存token,达到 [持久Cookie] 的效果
|
||||
localStorage.setItem("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");
|
||||
|
||||
// 使用 sessionStorage 保存token,达到 [临时Cookie] 的效果
|
||||
|
@ -10,6 +10,10 @@
|
||||
再往底了说,就是每个账号都会拥有一个权限码集合,我来校验这个集合中是否包含指定的权限码 <br/>
|
||||
例如:当前账号拥有权限码集合:`["user-add", "user-delete", "user-get"]`,这时候我来校验权限 `"user-update"`,则其结果就是:**验证失败,禁止访问** <br/>
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--jur-auth.gif">加载动态演示图</button>
|
||||
|
||||
|
||||
所以现在问题的核心就是:
|
||||
1. 如何获取一个账号所拥有的的权限码集合
|
||||
2. 本次操作需要验证的权限码是哪个
|
||||
|
@ -26,6 +26,8 @@ StpUtil.kickoutByTokenValue("token"); // 将指定 Token 踢下线
|
||||
- 踢人下线不会清除Token信息,而是将其打上特定标记,再次访问会提示:Token已被踢下线。
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--kickout.gif">加载动态演示图</button>
|
||||
|
||||
|
||||
### 3、账号封禁
|
||||
对于违规账号,有时候我们仅仅将其踢下线还是远远不够的,我们还需要对其进行**账号封禁**防止其再次登录
|
||||
|
@ -8,6 +8,9 @@
|
||||
那么如何判断一个会话是否登录?框架会在登录成功后给你做个标记,每次登录认证时校验这个标记,有标记者视为已登录,无标记者视为未登录!
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--login-auth.gif">加载动态演示图</button>
|
||||
|
||||
|
||||
### 登录与注销
|
||||
根据以上思路,我们很容易想到以下api:
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
<img src="./doc/logo.png" title="logo" />
|
||||
<span class="logo-text">Sa-Token</span>
|
||||
<!-- <h1 class="logo-text">Sa-Token</h1> -->
|
||||
<!-- <sub>v1.30.0.RC</sub> -->
|
||||
<!-- <sub>v1.30.0</sub> -->
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
@ -69,7 +69,7 @@
|
||||
<div class="main-box">
|
||||
<div class="content-box">
|
||||
<div class="fenge"></div>
|
||||
<h1>Sa-Token<small>v1.30.0.RC</small></h1>
|
||||
<h1>Sa-Token<small>v1.30.0</small></h1>
|
||||
<div class="sub-title">一个轻量级 java 权限认证框架,让鉴权变得简单、优雅!</div>
|
||||
<div class="btn-box">
|
||||
<a href="https://github.com/dromara/sa-token" target="_blank">GitHub</a>
|
||||
|
Loading…
Reference in New Issue
Block a user