mirror of
https://gitee.com/dromara/sa-token.git
synced 2026-02-27 16:50:24 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba640295c5 | ||
|
|
1ce7b945d8 | ||
|
|
4bfad95bad | ||
|
|
c7f3b6d493 | ||
|
|
c3bedaef99 | ||
|
|
ac4ac37175 | ||
|
|
713758c304 | ||
|
|
857e260a0a | ||
|
|
f1503d93a0 | ||
|
|
1648422617 |
@@ -1,11 +1,11 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150" style="margin-bottom: 10px;">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.11.0</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.12.0</h1>
|
||||
<h4 align="center">这可能是史上功能最全的Java权限认证框架!</h4>
|
||||
<h4 align="center">
|
||||
<a href="https://gitee.com/sz6/sa-token/stargazers"><img src="https://gitee.com/sz6/sa-token/badge/star.svg"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.11.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.12.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token/stargazers"><img src="https://img.shields.io/github/stars/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/watchers"><img src="https://img.shields.io/github/watchers/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/network/members"><img src="https://img.shields.io/github/forks/click33/sa-token"></a>
|
||||
@@ -71,6 +71,7 @@ StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session
|
||||
StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌值
|
||||
StpUtil.setLoginId(10001, "PC"); // 指定设备标识登录
|
||||
StpUtil.logoutByLoginId(10001, "PC"); // 指定设备标识进行强制注销 (不同端不受影响)
|
||||
StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
```
|
||||
sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档
|
||||
|
||||
@@ -80,11 +81,13 @@ sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳
|
||||
- **权限验证** —— 适配RBAC模型,不同角色不同授权
|
||||
- **Session会话** —— 专业的数据缓存中心
|
||||
- **踢人下线** —— 将违规用户立刻清退下线
|
||||
- **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- **持久层扩展** —— 可集成redis、MongoDB等专业缓存中间件
|
||||
- **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- **无Cookie模式** —— APP、小程序等前后台分离场景
|
||||
- **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- **路由拦截式鉴权** —— 设定全局路由拦截,并排除指定路由
|
||||
- **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- **临时身份切换** —— 将会话身份临时切换为其它账号
|
||||
- **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- **自动续签** —— 提供两种token过期策略,灵活搭配使用,还可自动续签
|
||||
- **同端互斥登录** —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -8,7 +8,7 @@
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
|
||||
<!-- 项目介绍 -->
|
||||
<name>sa-token</name>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
@@ -21,5 +21,13 @@ public class SaTokenException extends RuntimeException {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个异常
|
||||
* @param cause 异常对象
|
||||
*/
|
||||
public SaTokenException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
/**
|
||||
* 模拟身份方法的辅助类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaFunction {
|
||||
|
||||
/**
|
||||
* 执行的方法
|
||||
*/
|
||||
public void run();
|
||||
|
||||
}
|
||||
@@ -44,6 +44,24 @@ public class StpLogic {
|
||||
this.loginKey = loginKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前StpLogin的loginKey
|
||||
* @return 当前StpLogin的loginKey
|
||||
*/
|
||||
public String getLoginKey(){
|
||||
return loginKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入当前StpLogin的loginKey
|
||||
* @param loginKey loginKey
|
||||
* @return 对象自身
|
||||
*/
|
||||
public StpLogic setLoginKey(String loginKey){
|
||||
this.loginKey = loginKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
@@ -77,8 +95,8 @@ public class StpLogic {
|
||||
String tokenValue = null;
|
||||
|
||||
// 1. 尝试从request里读取
|
||||
if(request.getAttribute(SaTokenConsts.JUST_CREATED_SAVE_KEY) != null) {
|
||||
tokenValue = String.valueOf(request.getAttribute(SaTokenConsts.JUST_CREATED_SAVE_KEY));
|
||||
if(request.getAttribute(getKeyJustCreatedSave()) != null) {
|
||||
tokenValue = String.valueOf(request.getAttribute(getKeyJustCreatedSave()));
|
||||
}
|
||||
// 2. 尝试从请求体里面读取
|
||||
if(tokenValue == null && config.getIsReadBody() == true){
|
||||
@@ -100,14 +118,6 @@ public class StpLogic {
|
||||
return tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前StpLogin的loginKey
|
||||
* @return 当前StpLogin的loginKey
|
||||
*/
|
||||
public String getLoginKey(){
|
||||
return loginKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的token信息
|
||||
* @return token信息
|
||||
@@ -145,14 +155,6 @@ public class StpLogic {
|
||||
*/
|
||||
public void setLoginId(Object loginId, String device) {
|
||||
|
||||
// ------ 0、如果当前会话已经登录上了此LoginId,且登录设备相同,则立即返回
|
||||
Object loggedId = getLoginIdDefaultNull();
|
||||
if(loggedId != null && loggedId.toString().equals(loginId.toString())) {
|
||||
String loggedDevice = getLoginDevice();
|
||||
if(loggedDevice != null && loggedDevice.equals(device)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ------ 1、获取相应对象
|
||||
HttpServletRequest request = SaTokenManager.getSaTokenServlet().getRequest();
|
||||
@@ -199,7 +201,7 @@ public class StpLogic {
|
||||
|
||||
// ------ 4. 持久化其它数据
|
||||
dao.setValue(getKeyTokenValue(tokenValue), String.valueOf(loginId), config.getTimeout()); // token -> uid
|
||||
request.setAttribute(SaTokenConsts.JUST_CREATED_SAVE_KEY, tokenValue); // 将token保存到本次request里
|
||||
request.setAttribute(getKeyJustCreatedSave(), tokenValue); // 将token保存到本次request里
|
||||
setLastActivityToNow(tokenValue); // 写入 [最后操作时间]
|
||||
if(config.getIsReadCookie() == true){ // cookie注入
|
||||
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", (int)config.getTimeout());
|
||||
@@ -231,7 +233,7 @@ public class StpLogic {
|
||||
clearLastActivity(tokenValue);
|
||||
|
||||
// 2. 尝试清除token-id键值对 (先从db中获取loginId值,如果根本查不到loginId,那么无需继续操作 )
|
||||
String loginId = SaTokenManager.getSaTokenDao().getValue(getKeyTokenValue(tokenValue));
|
||||
String loginId = getLoginIdNotHandle(tokenValue);
|
||||
if(loginId == null || NotLoginException.ABNORMAL_LIST.contains(loginId)) {
|
||||
return;
|
||||
}
|
||||
@@ -311,13 +313,17 @@ public class StpLogic {
|
||||
* @return 账号id
|
||||
*/
|
||||
public Object getLoginId() {
|
||||
// 如果正在[临时身份切换]
|
||||
if(isSwitch()) {
|
||||
return getSwitchLoginId();
|
||||
}
|
||||
// 如果获取不到token,则抛出:无token
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
throw NotLoginException.newInstance(loginKey, NotLoginException.NOT_TOKEN);
|
||||
}
|
||||
// 查找此token对应loginId, 则抛出:无效token
|
||||
String loginId = SaTokenManager.getSaTokenDao().getValue(getKeyTokenValue(tokenValue));
|
||||
String loginId = getLoginIdNotHandle(tokenValue);
|
||||
if(loginId == null) {
|
||||
throw NotLoginException.newInstance(loginKey, NotLoginException.INVALID_TOKEN);
|
||||
}
|
||||
@@ -371,13 +377,17 @@ public class StpLogic {
|
||||
* @return 账号id
|
||||
*/
|
||||
public Object getLoginIdDefaultNull() {
|
||||
// 如果正在[临时身份切换]
|
||||
if(isSwitch()) {
|
||||
return getSwitchLoginId();
|
||||
}
|
||||
// 如果连token都是空的,则直接返回
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return null;
|
||||
}
|
||||
// loginId为null或者在异常项里面,均视为未登录
|
||||
Object loginId = SaTokenManager.getSaTokenDao().getValue(getKeyTokenValue(tokenValue));
|
||||
Object loginId = getLoginIdNotHandle(tokenValue);
|
||||
if(loginId == null || NotLoginException.ABNORMAL_LIST.contains(loginId)) {
|
||||
return null;
|
||||
}
|
||||
@@ -427,16 +437,24 @@ public class StpLogic {
|
||||
* @return 登录id
|
||||
*/
|
||||
public Object getLoginIdByToken(String tokenValue) {
|
||||
if(tokenValue != null) {
|
||||
Object loginId = SaTokenManager.getSaTokenDao().getValue(getKeyTokenValue(tokenValue));
|
||||
if(loginId != null) {
|
||||
return loginId;
|
||||
}
|
||||
if(tokenValue == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return getLoginIdNotHandle(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定token对应的登录id (不做任何特殊处理)
|
||||
* @param tokenValue token值
|
||||
* @return loginId
|
||||
*/
|
||||
public String getLoginIdNotHandle(String tokenValue) {
|
||||
return SaTokenManager.getSaTokenDao().getValue(getKeyTokenValue(tokenValue));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// =================== session相关 ===================
|
||||
|
||||
/**
|
||||
@@ -536,7 +554,7 @@ public class StpLogic {
|
||||
if(tokenValue == null || Objects.equals(tokenValue, "")) {
|
||||
// 随机一个token送给ta
|
||||
tokenValue = createTokenValue(null);
|
||||
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(SaTokenConsts.JUST_CREATED_SAVE_KEY, tokenValue);
|
||||
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(getKeyJustCreatedSave(), tokenValue);
|
||||
setLastActivityToNow(tokenValue); // 写入 [最后操作时间]
|
||||
if(getConfig().getIsReadCookie() == true){ // cookie注入
|
||||
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", (int)getConfig().getTimeout());
|
||||
@@ -1039,6 +1057,22 @@ public class StpLogic {
|
||||
return getConfig().getTokenName() + ":" + loginKey + ":last-activity:" + tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在进行身份切换时,使用的存储key
|
||||
* @return key
|
||||
*/
|
||||
public String getKeySwitch() {
|
||||
return SaTokenConsts.SWITCH_TO_SAVE_KEY + getLoginKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果token为本次请求新创建的,则以此字符串为key存储在当前request中
|
||||
* @return key
|
||||
*/
|
||||
public String getKeyJustCreatedSave() {
|
||||
return SaTokenConsts.JUST_CREATED_SAVE_KEY + getLoginKey();
|
||||
}
|
||||
|
||||
|
||||
// =================== Bean对象代理 ===================
|
||||
|
||||
@@ -1052,7 +1086,7 @@ public class StpLogic {
|
||||
}
|
||||
|
||||
|
||||
// =================== 注解鉴权 ===================
|
||||
// =================== 其它方法 ===================
|
||||
|
||||
/**
|
||||
* 对一个Method对象进行注解检查(注解鉴权内部实现)
|
||||
@@ -1111,6 +1145,58 @@ public class StpLogic {
|
||||
|
||||
// 验证通过
|
||||
}
|
||||
|
||||
|
||||
// =================== 身份切换 ===================
|
||||
|
||||
/**
|
||||
* 临时切换身份为指定loginId
|
||||
* @param loginId 指定loginId
|
||||
*/
|
||||
public void switchTo(Object loginId) {
|
||||
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(getKeySwitch(), loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束临时切换身份
|
||||
*/
|
||||
public void endSwitch() {
|
||||
SaTokenManager.getSaTokenServlet().getRequest().removeAttribute(getKeySwitch());
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前是否正处于[身份临时切换]中
|
||||
* @return 是否正处于[身份临时切换]中
|
||||
*/
|
||||
public boolean isSwitch() {
|
||||
return SaTokenManager.getSaTokenServlet().getRequest().getAttribute(getKeySwitch()) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回[身份临时切换]的loginId
|
||||
* @return 返回[身份临时切换]的loginId
|
||||
*/
|
||||
public Object getSwitchLoginId() {
|
||||
return SaTokenManager.getSaTokenServlet().getRequest().getAttribute(getKeySwitch());
|
||||
}
|
||||
|
||||
/**
|
||||
* 在一个代码段里方法内,临时切换身份为指定loginId
|
||||
* @param loginId 指定loginId
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
public void switchTo(Object loginId, SaFunction function) {
|
||||
try {
|
||||
switchTo(loginId);
|
||||
function.run();
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
} finally {
|
||||
endSwitch();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,16 @@ public class StpUtil {
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
public static StpLogic stpLogic = new StpLogic("login");
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前StpLogin的loginKey
|
||||
* @return 当前StpLogin的loginKey
|
||||
*/
|
||||
public static String getLoginKey(){
|
||||
return stpLogic.getLoginKey();
|
||||
}
|
||||
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
@@ -34,14 +43,6 @@ public class StpUtil {
|
||||
return stpLogic.getTokenValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前StpLogin的loginKey
|
||||
* @return 当前StpLogin的loginKey
|
||||
*/
|
||||
public static String getLoginKey(){
|
||||
return stpLogic.getLoginKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的token信息
|
||||
* @return token信息
|
||||
@@ -486,4 +487,39 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
|
||||
// =================== 身份切换 ===================
|
||||
|
||||
/**
|
||||
* 临时切换身份为指定loginId
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在一个代码段里方法内,临时切换身份为指定loginId
|
||||
* @param loginId 指定loginId
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
public static void switchTo(Object loginId, SaFunction function) {
|
||||
stpLogic.switchTo(loginId, function);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public class SaTokenConsts {
|
||||
/**
|
||||
* sa-token 版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.11.0";
|
||||
public static final String VERSION_NO = "v1.12.0";
|
||||
|
||||
/**
|
||||
* sa-token 开源地址
|
||||
@@ -18,7 +18,7 @@ public class SaTokenConsts {
|
||||
public static final String GITHUB_URL = "https://github.com/click33/sa-token";
|
||||
|
||||
/**
|
||||
* 如果token为本次请求新创建的,则以此字符串为key存储在当前request中 JUST_CREATED_SAVE_KEY
|
||||
* 如果token为本次请求新创建的,则以此字符串为key存储在当前request中
|
||||
*/
|
||||
public static final String JUST_CREATED_SAVE_KEY = "JUST_CREATED_SAVE_KEY_";
|
||||
|
||||
@@ -32,11 +32,10 @@ public class SaTokenConsts {
|
||||
*/
|
||||
public static final String DEFAULT_LOGIN_DEVICE = "default-device";
|
||||
|
||||
// /**
|
||||
// * 在用一个字符串存储多个token时,所使用的分隔符
|
||||
// */
|
||||
// public static final String MULTIPLE_TOKEN_SEPARATOR = ",";
|
||||
|
||||
/**
|
||||
* 在进行临时身份切换时使用的key
|
||||
*/
|
||||
public static final String SWITCH_TO_SAVE_KEY = "SWITCH_TO_SAVE_KEY_";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
<!-- RedisTemplate 相关操作API -->
|
||||
<dependency>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
<!-- RedisTemplate 相关操作API -->
|
||||
<dependency>
|
||||
|
||||
12
sa-token-demo-jwt/.gitignore
vendored
Normal file
12
sa-token-demo-jwt/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
||||
80
sa-token-demo-jwt/pom.xml
Normal file
80
sa-token-demo-jwt/pom.xml
Normal file
@@ -0,0 +1,80 @@
|
||||
<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-jwt</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<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>1.12.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- jwt -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token整合redis (使用jdk默认序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<version>1.12.0</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>1.12.0</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- 提供redis连接池 -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合SpringAOP实现注解鉴权 -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-aop</artifactId>
|
||||
<version>1.12.0</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SaTokenJwtDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenJwtDemoApplication.class, args);
|
||||
System.out.println("\n启动成功:sa-token配置如下:" + SaTokenManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.pj.satoken.jwt;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
|
||||
@Component
|
||||
public class SaTokenJwtUtil {
|
||||
|
||||
/**
|
||||
* 秘钥 (随便手打几个字母就好了)
|
||||
*/
|
||||
public static final String BASE64_SECURITY = "79e7c69681b8270162386e6daa53d1dd";
|
||||
|
||||
/**
|
||||
* token有效期 (单位: 秒)
|
||||
*/
|
||||
public static final long TIMEOUT = 60 * 60 * 2;
|
||||
|
||||
|
||||
public static final String LOGIN_ID_KEY = "loginId";
|
||||
|
||||
|
||||
/**
|
||||
* 根据userId生成token
|
||||
* @param loginId 账号id
|
||||
* @param base64Security 秘钥
|
||||
* @return jwt-token
|
||||
*/
|
||||
public static String createToken(Object loginId) {
|
||||
// 判断,不可使用默认秘钥
|
||||
if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1d3")) {
|
||||
throw new SaTokenException("请更换秘钥");
|
||||
}
|
||||
// 在这里你可以使用官方提供的claim方法构建载荷,也可以使用setPayload自定义载荷,但是两者不可一起使用
|
||||
JwtBuilder builder = Jwts.builder()
|
||||
.setHeaderParam("type", "JWT")
|
||||
.claim(LOGIN_ID_KEY, loginId)
|
||||
.setIssuedAt(new Date()) // 签发日期
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 1000 * TIMEOUT)) // 有效截止日期
|
||||
.signWith(SignatureAlgorithm.HS256, BASE64_SECURITY.getBytes()); // 加密算法
|
||||
//生成JWT
|
||||
return builder.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个jwt里面解析出Claims
|
||||
* @param tokenValue token值
|
||||
* @param base64Security 秘钥
|
||||
* @return Claims对象
|
||||
*/
|
||||
public static Claims getClaims(String tokenValue) {
|
||||
// System.out.println(tokenValue);
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(BASE64_SECURITY.getBytes())
|
||||
.parseClaimsJws(tokenValue).getBody();
|
||||
return claims;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个jwt里面解析loginId
|
||||
* @param tokenValue token值
|
||||
* @param base64Security 秘钥
|
||||
* @return loginId
|
||||
*/
|
||||
public static String getLoginId(String tokenValue) {
|
||||
try {
|
||||
Object loginId = getClaims(tokenValue).get(LOGIN_ID_KEY);
|
||||
if(loginId == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(loginId);
|
||||
} catch (ExpiredJwtException e) {
|
||||
// throw NotLoginException.newInstance(StpUtil.stpLogic.loginKey, NotLoginException.TOKEN_TIMEOUT);
|
||||
return NotLoginException.TOKEN_TIMEOUT;
|
||||
} catch (MalformedJwtException e) {
|
||||
throw NotLoginException.newInstance(StpUtil.stpLogic.loginKey, NotLoginException.INVALID_TOKEN);
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static {
|
||||
|
||||
// 判断秘钥
|
||||
if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1dd")) {
|
||||
String warn = "-------------------------------------\n";
|
||||
warn += "请更换JWT秘钥,不要使用示例默认秘钥\n";
|
||||
warn += "-------------------------------------";
|
||||
System.err.println(warn);
|
||||
}
|
||||
|
||||
// 修改默认实现
|
||||
StpUtil.stpLogic = new StpLogic("login") {
|
||||
|
||||
// 重写 (随机生成一个tokenValue)
|
||||
@Override
|
||||
public String createTokenValue(Object loginId) {
|
||||
return SaTokenJwtUtil.createToken(loginId);
|
||||
}
|
||||
|
||||
// 重写 (在当前会话上登录id )
|
||||
@Override
|
||||
public void setLoginId(Object loginId, String device) {
|
||||
// ------ 1、获取相应对象
|
||||
HttpServletRequest request = SaTokenManager.getSaTokenServlet().getRequest();
|
||||
SaTokenConfig config = getConfig();
|
||||
// ------ 2、生成一个token
|
||||
String tokenValue = createTokenValue(loginId);
|
||||
request.setAttribute(getKeyJustCreatedSave(), tokenValue); // 将token保存到本次request里
|
||||
if(config.getIsReadCookie() == true){ // cookie注入
|
||||
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", (int)config.getTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
// 重写 (获取指定token对应的登录id)
|
||||
@Override
|
||||
public String getLoginIdNotHandle(String tokenValue) {
|
||||
try {
|
||||
return SaTokenJwtUtil.getLoginId(tokenValue);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 重写 (当前会话注销登录)
|
||||
@Override
|
||||
public void logout() {
|
||||
// 如果连token都没有,那么无需执行任何操作
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return;
|
||||
}
|
||||
// 如果打开了cookie模式,把cookie清除掉
|
||||
if(getConfig().getIsReadCookie() == true){
|
||||
SaTokenManager.getSaTokenCookie().delCookie(SaTokenManager.getSaTokenServlet().getRequest(), SaTokenManager.getSaTokenServlet().getResponse(), getTokenName());
|
||||
}
|
||||
}
|
||||
|
||||
// 重写 (获取指定key的session)
|
||||
@Override
|
||||
public SaSession getSessionBySessionId(String sessionId, boolean isCreate) {
|
||||
throw new SaTokenException("jwt has not session");
|
||||
}
|
||||
|
||||
// 重写 (获取当前登录者的token剩余有效时间 (单位: 秒))
|
||||
@Override
|
||||
public long getTokenTimeout() {
|
||||
// 如果没有token
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
// 开始取值
|
||||
Claims claims = null;
|
||||
try {
|
||||
claims = SaTokenJwtUtil.getClaims(tokenValue);
|
||||
} catch (Exception e) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
if(claims == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
Date expiration = claims.getExpiration();
|
||||
if(expiration == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
return (expiration.getTime() - System.currentTimeMillis()) / 1000;
|
||||
}
|
||||
|
||||
// 重写 (返回当前token的登录设备)
|
||||
@Override
|
||||
public String getLoginDevice() {
|
||||
return SaTokenConsts.DEFAULT_LOGIN_DEVICE;
|
||||
}
|
||||
|
||||
// 重写 (获取当前会话的token信息)
|
||||
@Override
|
||||
public SaTokenInfo getTokenInfo() {
|
||||
SaTokenInfo info = new SaTokenInfo();
|
||||
info.tokenName = getTokenName();
|
||||
info.tokenValue = getTokenValue();
|
||||
info.isLogin = isLogin();
|
||||
info.loginId = getLoginIdDefaultNull();
|
||||
info.loginKey = getLoginKey();
|
||||
info.tokenTimeout = getTokenTimeout();
|
||||
// info.sessionTimeout = getSessionTimeout();
|
||||
// info.tokenSessionTimeout = getTokenSessionTimeout();
|
||||
// info.tokenActivityTimeout = getTokenActivityTimeout();
|
||||
info.loginDevice = getLoginDevice();
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
100
sa-token-demo-jwt/src/main/java/com/pj/test/GlobalException.java
Normal file
100
sa-token-demo-jwt/src/main/java/com/pj/test/GlobalException.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@RestControllerAdvice // 可指定包前缀,比如:(basePackages = "com.pj.admin")
|
||||
public class GlobalException {
|
||||
|
||||
// 在每个控制器之前触发的操作
|
||||
@ModelAttribute
|
||||
public void get(HttpServletRequest request) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
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.getCode());
|
||||
} else { // 普通异常, 输出:500 + 异常信息
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回给前端
|
||||
return aj;
|
||||
|
||||
// 输出到客户端
|
||||
// response.setContentType("application/json; charset=utf-8"); // http说明,我要返回JSON对象
|
||||
// response.getWriter().print(new ObjectMapper().writeValueAsString(aj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 全局异常拦截(拦截项目中的NotLoginException异常)
|
||||
// @ExceptionHandler(NotLoginException.class)
|
||||
// public AjaxJson handlerNotLoginException(NotLoginException nle, HttpServletRequest request, HttpServletResponse response)
|
||||
// throws Exception {
|
||||
//
|
||||
// // 打印堆栈,以供调试
|
||||
// nle.printStackTrace();
|
||||
//
|
||||
// // 判断场景值,定制化异常信息
|
||||
// String message = "";
|
||||
// if(nle.getType().equals(NotLoginException.NOT_TOKEN)) {
|
||||
// message = "未提供token";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
|
||||
// message = "token无效";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
|
||||
// message = "token已过期";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.BE_REPLACED)) {
|
||||
// message = "token已被顶下线";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.KICK_OUT)) {
|
||||
// message = "token已被踢下线";
|
||||
// }
|
||||
// else {
|
||||
// message = "当前会话未登录";
|
||||
// }
|
||||
//
|
||||
// // 返回给前端
|
||||
// return AjaxJson.getError(message);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
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 cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 测试专用Controller
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/test/")
|
||||
public class TestJwtController {
|
||||
|
||||
|
||||
|
||||
// 测试登录接口, 浏览器访问: 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.setLoginId(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();
|
||||
}
|
||||
|
||||
// 打印当前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);
|
||||
}
|
||||
|
||||
|
||||
// 测试会话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().getAttribute("name"));
|
||||
StpUtil.getSession().setAttribute("name", new Date()); // 写入一个值
|
||||
System.out.println("测试取值name:" + StpUtil.getSession().getAttribute("name"));
|
||||
System.out.println( new ObjectMapper().writeValueAsString(StpUtil.getSession()));
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
StpUtil.getTokenSession().logout();
|
||||
StpUtil.logoutByLoginId(10001);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
162
sa-token-demo-jwt/src/main/java/com/pj/util/AjaxJson.java
Normal file
162
sa-token-demo-jwt/src/main/java/com/pj/util/AjaxJson.java
Normal file
@@ -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
|
||||
+ "}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
45
sa-token-demo-jwt/src/main/resources/application.yml
Normal file
45
sa-token-demo-jwt/src/main/resources/application.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
spring:
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken
|
||||
# token有效期,单位s 默认30天, -1代表永不过期
|
||||
timeout: 2592000
|
||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
activity-timeout: -1
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
allow-concurrent-login: true
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: true
|
||||
# token风格
|
||||
token-style: uuid
|
||||
|
||||
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 10000ms
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
@@ -29,21 +29,21 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token整合redis (使用jdk默认序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- 提供redis连接池 -->
|
||||
@@ -56,7 +56,7 @@
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-aop</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.interceptor.SaCheckInterceptor;
|
||||
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
|
||||
|
||||
/**
|
||||
* sa-token代码方式进行配置
|
||||
@@ -30,7 +30,8 @@ public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
// 注册sa-token的拦截器,打开注解式鉴权功能
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new SaCheckInterceptor()).addPathPatterns("/**"); // 全局拦截器
|
||||
// 注册注解拦截器
|
||||
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**"); // 全局拦截器
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -145,7 +145,6 @@ public class TestController {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
// 打印当前token信息, 浏览器访问: http://localhost:8081/test/tokenInfo
|
||||
@RequestMapping("tokenInfo")
|
||||
public AjaxJson tokenInfo() {
|
||||
@@ -203,6 +202,18 @@ public class TestController {
|
||||
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")
|
||||
@@ -234,8 +245,14 @@ public class TestController {
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
StpUtil.getTokenSession().logout();
|
||||
StpUtil.logoutByLoginId(10001);
|
||||
// StpUtil.getTokenSession().logout();
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test2
|
||||
@RequestMapping("test2")
|
||||
public AjaxJson test2() {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150" style="margin-bottom: 10px;">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.11.0</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.12.0</h1>
|
||||
<h4 align="center">这可能是史上功能最全的Java权限认证框架!</h4>
|
||||
<h4 align="center">
|
||||
<a href="https://gitee.com/sz6/sa-token/stargazers"><img src="https://gitee.com/sz6/sa-token/badge/star.svg"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.11.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.12.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token/stargazers"><img src="https://img.shields.io/github/stars/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/watchers"><img src="https://img.shields.io/github/watchers/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/network/members"><img src="https://img.shields.io/github/forks/click33/sa-token"></a>
|
||||
@@ -71,6 +71,7 @@ StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session
|
||||
StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌值
|
||||
StpUtil.setLoginId(10001, "PC"); // 指定设备标识登录
|
||||
StpUtil.logoutByLoginId(10001, "PC"); // 指定设备标识进行强制注销 (不同端不受影响)
|
||||
StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
```
|
||||
sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档
|
||||
|
||||
@@ -80,11 +81,13 @@ sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳
|
||||
- **权限验证** —— 适配RBAC模型,不同角色不同授权
|
||||
- **Session会话** —— 专业的数据缓存中心
|
||||
- **踢人下线** —— 将违规用户立刻清退下线
|
||||
- **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- **持久层扩展** —— 可集成redis、MongoDB等专业缓存中间件
|
||||
- **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- **无Cookie模式** —— APP、小程序等前后台分离场景
|
||||
- **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- **路由拦截式鉴权** —— 设定全局路由拦截,并排除指定路由
|
||||
- **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- **临时身份切换** —— 将会话身份临时切换为其它账号
|
||||
- **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- **自动续签** —— 提供两种token过期策略,灵活搭配使用,还可自动续签
|
||||
- **同端互斥登录** —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
- [多账号验证](/use/many-account)
|
||||
- [同端互斥登录](/use/mutex-login)
|
||||
- [注解式鉴权](/use/at-check)
|
||||
- [路由拦截式鉴权](/use/route-check)
|
||||
- [花式token](/use/token-style)
|
||||
- [框架配置](/use/config)
|
||||
- [会话治理](/use/search-session)
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>sa-token</title>
|
||||
<title>sa-token 官方文档</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Description">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="keywords" content="sa-token|sa-token框架|sa-token文档|sa-token在线文档|sa-token官方文档|权限认证框架">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,功能全面,上手简单,登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、多账号体系、注解式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,功能全面,上手简单,登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<link rel="stylesheet" href="https://unpkg.zhimg.com/docsify@4.11.3/lib/themes/vue.css">
|
||||
<link rel="stylesheet" href="./lib/index.css">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="logo.png">
|
||||
@@ -22,6 +22,7 @@
|
||||
<nav>
|
||||
<select onchange="location.href=this.value">
|
||||
<option value="http://sa-token.dev33.cn/doc/index.html">最新版</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.11.0/doc/index.html">v1.11.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.10.0/doc/index.html">v1.10.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.9.0/doc/index.html">v1.9.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.8.0/doc/index.html">v1.8.0</option>
|
||||
@@ -40,7 +41,7 @@
|
||||
</div>
|
||||
<script>
|
||||
var name = '<img style="width: 50px; height: 50px; vertical-align: middle;" src="logo.png" alt="logo" /> ';
|
||||
name += '<b style="font-size: 24px; vertical-align: middle;">sa-token</b> <sub>v1.11.0</sub>'
|
||||
name += '<b style="font-size: 24px; vertical-align: middle;">sa-token</b> <sub>v1.12.0</sub>'
|
||||
window.$docsify = {
|
||||
name: name, // 名字
|
||||
repo: 'https://github.com/click33/sa-token', // github地址
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
# 更新日志
|
||||
|
||||
|
||||
### 2021-1-9 @v1.11.0
|
||||
### 2021-1-12 @v1.12.0
|
||||
- 新增:提供JWT集成示例 **[重要]**
|
||||
- 新增:新增路由式鉴权,可方便的根据路由匹配鉴权 **[重要]**
|
||||
- 新增:新增身份临时切换功能,可在一个代码段内将会话临时切换为其它账号 **[重要]**
|
||||
- 优化:将`SaCheckInterceptor.java`更名为`SaAnnotationInterceptor.java`,更语义化的名称
|
||||
- 优化:优化文档
|
||||
|
||||
|
||||
### 2021-1-10 @v1.11.0
|
||||
- 新增:提供AOP注解鉴权方案 **[重要]**
|
||||
- 优化自动生成token的算法
|
||||
|
||||
|
||||
@@ -9,10 +9,15 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Gradle依赖
|
||||
Gradle用户引入依赖:
|
||||
```
|
||||
implementation 'cn.dev33:sa-token-spring-boot-starter:1.12.0'
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -27,9 +32,11 @@
|
||||
├── sa-token-core // sa-token核心模块
|
||||
├── sa-token-spring-boot-starter // sa-token整合springboot快速集成
|
||||
├── sa-token-dao-redis // sa-token整合redis (使用jdk默认序列化方式)
|
||||
├── sa-token-dao-redis-jackson // sa-token整合redis (使用jackson序列化方式)
|
||||
├── sa-token-demo-springboot // sa-token示例
|
||||
├── sa-token-doc // sa-token开发文档
|
||||
├── sa-token-dao-redis-jackson // sa-token整合redis (使用jackson序列化方式)
|
||||
├── sa-token-spring-aop // sa-token整合SpringAOP 注解鉴权
|
||||
├── sa-token-demo-springboot // sa-token示例
|
||||
├── sa-token-demo-jwt // sa-token集成jwt示例
|
||||
├── sa-token-doc // sa-token开发文档
|
||||
├──pom.xml
|
||||
```
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
``` java
|
||||
@Configuration
|
||||
public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
// 注册sa-token的拦截器,打开注解式鉴权功能
|
||||
// 注册sa-token的注解拦截器,打开注解式鉴权功能
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new SaCheckInterceptor()).addPathPatterns("/**"); // 全局拦截器
|
||||
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -90,7 +90,7 @@ mode有两种取值:
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-aop</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -119,22 +119,3 @@ public class UserService {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- ## 3、扩展
|
||||
- 其实在注册拦截器时,我们也可以根据路由前缀设置不同 `StpLogic`, 从而达到不同模块不同鉴权方式的目的
|
||||
- 以下为参考示例:
|
||||
``` java
|
||||
@Configuration
|
||||
public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
// 注册sa-token的拦截器,打开注解式鉴权功能
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new SaCheckInterceptor(StpUtil.stpLogic)).addPathPatterns("/admin/**");
|
||||
registry.addInterceptor(new SaCheckInterceptor(StpUserUtil.stpLogic)).addPathPatterns("/user/**");
|
||||
}
|
||||
}
|
||||
``` -->
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
```
|
||||
优点:兼容性好,缺点:session序列化后基本不可读,对开发者来讲等同于乱码
|
||||
@@ -23,7 +23,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
```
|
||||
优点:session序列化后可读性强,可灵活手动修改,缺点:兼容性稍差
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# 模拟他人
|
||||
---
|
||||
|
||||
|
||||
- 以上介绍的api都是操作当前账号,对当前账号进行各种鉴权操作,你可能会问,我能不能对别的账号进行一些操作?
|
||||
- 比如:查看账号`10001`有无某个权限码、获取id账号为`10002`的用户`session`,等等...
|
||||
- `sa-token`在api设计时充分考虑了这一点,暴露出多个api进行此类操作
|
||||
@@ -8,23 +9,47 @@
|
||||
|
||||
## 有关操作其它账号的api
|
||||
|
||||
#### StpUtil.getTokenValueByLoginId(Object loginId)
|
||||
- 获取指定`loginId`的`tokenValue`值
|
||||
|
||||
#### StpUtil.logoutByLoginId(Object loginId)
|
||||
- 指定`loginId`的会话注销登录(踢人下线)
|
||||
|
||||
#### StpUtil.getSessionByLoginId(Object loginId)
|
||||
- 获取指定`loginId`的`session`(如果此id尚未创建`session`, 则返回`null`)
|
||||
- 类似API还有:
|
||||
- `StpUtil.getSessionByLoginId(Object loginId, boolean isCreate)` 获取当前会话登录id, `isCreate`代表指定是否在无`session`的情况下新建并返回
|
||||
|
||||
#### StpUtil.hasRole(Object loginId, String role)
|
||||
- 指定`loginId`是否含有指定角色
|
||||
|
||||
#### StpUtil.hasPermission(Object loginId, String permission)
|
||||
- 指定`loginId`是否含有指定权限
|
||||
``` java
|
||||
StpUtil.getTokenValueByLoginId(10001); // 获取指定账号10001的`tokenValue`值
|
||||
StpUtil.logoutByLoginId(10001); // 将账号10001的会话注销登录(踢人下线)
|
||||
StpUtil.getSessionByLoginId(10001); // 获取账号10001的Session对象, 如果session尚未创建, 则新建并返回
|
||||
StpUtil.getSessionByLoginId(10001, false); // 获取账号10001的Session对象, 如果session尚未创建, 则返回null
|
||||
StpUtil.hasRole(10001, false); // 获取账号10001是否含有指定角色标识
|
||||
StpUtil.hasPermission(10001, false); // 获取账号10001是否含有指定权限码
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 临时身份切换
|
||||
|
||||
有时候,我们需要直接将当前会话的身份切换为其它账号,比如:
|
||||
``` java
|
||||
StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
StpUtil.getLoginId(); // 此时再调用此方法会返回 10044
|
||||
StpUtil.endSwitch(); // 结束 [身份临时切换]
|
||||
```
|
||||
|
||||
你还可以: 直接在一个代码段里方法内,临时切换身份为指定loginId (此方式无需手动调用`StpUtil.endSwitch()`关闭身份切换)
|
||||
``` java
|
||||
System.out.println("------- [身份临时切换]调用开始...");
|
||||
StpUtil.switchTo(10044, new SaFunction() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
System.out.println("获取当前登录账号id: " + StpUtil.getLoginId());
|
||||
}
|
||||
});
|
||||
System.out.println("------- [身份临时切换]调用结束...");
|
||||
```
|
||||
|
||||
如果你使用的JDK版本是1.8或以上,上面这一坨可以简写为以下形式:
|
||||
``` java
|
||||
System.out.println("------- [身份临时切换]调用开始...");
|
||||
StpUtil.switchTo(10044, () -> {
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
System.out.println("获取当前登录账号id: " + StpUtil.getLoginId());
|
||||
});
|
||||
System.out.println("------- [身份临时切换]调用结束...");
|
||||
```
|
||||
|
||||
|
||||
|
||||
80
sa-token-doc/doc/use/route-check.md
Normal file
80
sa-token-doc/doc/use/route-check.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# 路由拦截式鉴权
|
||||
---
|
||||
|
||||
假设我们有如下需求:
|
||||
> 项目中所有接口均需要登录验证,只有'登录接口'本身对外开放
|
||||
|
||||
我们怎么实现呢?给每个接口加上鉴权注解?手写全局拦截器?似乎都不是非常方便。<br/>
|
||||
在这个需求中我们真正需要的是一种基于路由拦截的鉴权模式, 那么在sa-token怎么实现路由拦截鉴权呢?
|
||||
|
||||
|
||||
|
||||
## 1、注册路由拦截器
|
||||
以`springboot2.0`为例, 新建配置类`MySaTokenConfig.java`
|
||||
``` java
|
||||
@Configuration
|
||||
public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
// 注册sa-token的登录拦截器
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册登录拦截器,并排除登录接口地址
|
||||
registry.addInterceptor(new SaRouteInterceptor()).addPathPatterns("/**").excludePathPatterns("/user/doLogin");
|
||||
}
|
||||
}
|
||||
```
|
||||
以上代码,我们注册了一个登录验证拦截器,并且排除了`/user/doLogin`接口用来开放登录 <br>
|
||||
那么我们如何进行权限认证拦截呢,且往下看
|
||||
|
||||
|
||||
## 2、所有拦截器示例
|
||||
``` java
|
||||
@Configuration
|
||||
public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
// 注册sa-token的所有拦截器
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
|
||||
// 注册一个登录验证拦截器
|
||||
registry.addInterceptor(SaRouteInterceptor.createLoginVal()).addPathPatterns("/**").excludePathPatterns("/user/doLogin");
|
||||
|
||||
// 注册一个角色认证拦截器
|
||||
registry.addInterceptor(SaRouteInterceptor.createRoleVal("super-admin")).addPathPatterns("/**");
|
||||
|
||||
// 注册一个权限认证拦截器
|
||||
registry.addInterceptor(SaRouteInterceptor.createPermissionVal("user:add", "user:deelete")).addPathPatterns("/UserController/**");
|
||||
|
||||
// 注册一个自定义认证拦截器 (可以写任意认证代码)
|
||||
registry.addInterceptor(new SaRouteInterceptor(new SaRouteFunction() {
|
||||
@Override
|
||||
public void run(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 你可以在这里写任意认证代码, 例如: StpUtil.checkLogin();
|
||||
System.out.println("---------- 进入自定义认证 --------------- ");
|
||||
}
|
||||
})).addPathPatterns("/**");
|
||||
|
||||
/** ------ 如果你使用的JDK版本是1.8或以上,上面这一坨可以简写为以下形式 ------ */
|
||||
|
||||
// 注册一个自定义认证拦截器 (可以写任意认证代码)
|
||||
registry.addInterceptor(new SaRouteInterceptor((request, response, handler)->{
|
||||
// 你可以在这里写任意认证代码, 例如: StpUtil.checkLogin();
|
||||
System.out.println("---------- 进入自定义认证2 --------------- ");
|
||||
})).addPathPatterns("/**");
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
(你不必像上面的示例一样注册所有拦截器,只要按需注册即可 )
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>sa-token 官方文档</title>
|
||||
<title>sa-token</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Description">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="keywords" content="sa-token|sa-token框架|sa-token文档|sa-token在线文档|权限认证框架">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,功能全面,上手简单,登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、多账号体系、注解式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,功能全面,上手简单,登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<link rel="stylesheet" href="https://unpkg.zhimg.com/docsify@4.11.3/lib/themes/vue.css">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="doc/logo.png">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
@@ -43,10 +43,10 @@
|
||||
<!-- 内容部分 -->
|
||||
<div class="main-box">
|
||||
<div class="content-box">
|
||||
<h1>sa-token<small>v1.11.0</small></h1>
|
||||
<div class="sub-title">一个JavaWeb轻量级权限认证框架,功能全面,上手简单</div>
|
||||
<h1>sa-token<small>v1.12.0</small></h1>
|
||||
<div class="sub-title">这可能是史上功能最全的Java权限认证框架!</div>
|
||||
<!-- <p>0配置开箱即用,低学习成本</p> -->
|
||||
<p>登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、多账号体系、注解式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...</p>
|
||||
<p>登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...</p>
|
||||
<p>零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有</p>
|
||||
<div class="btn-box">
|
||||
<a href="https://github.com/click33/sa-token" target="_blank">GitHub</a>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
<!-- spring-boot-starter-aop -->
|
||||
<dependency>
|
||||
|
||||
@@ -19,16 +19,18 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
public class SaCheckAspect {
|
||||
|
||||
/**
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
public StpLogic stpLogic = null;
|
||||
|
||||
/**
|
||||
* 创建,并指定一个默认的 StpLogic
|
||||
* 创建
|
||||
*/
|
||||
public SaCheckAspect() {
|
||||
this.stpLogic = StpUtil.stpLogic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本切面使用的StpLogic
|
||||
*/
|
||||
public StpLogic getStpLogic() {
|
||||
return StpUtil.stpLogic;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 定义AOP签名 (切入所有使用sa-token鉴权注解的方法)
|
||||
@@ -54,7 +56,7 @@ public class SaCheckAspect {
|
||||
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
// 注解鉴权
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
stpLogic.checkMethodAnnotation(signature.getMethod());
|
||||
getStpLogic().checkMethodAnnotation(signature.getMethod());
|
||||
try {
|
||||
// 执行原有逻辑
|
||||
Object obj = joinPoint.proceed();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -15,7 +15,7 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
* 注解式鉴权 - 拦截器
|
||||
* @author kong
|
||||
*/
|
||||
public class SaCheckInterceptor implements HandlerInterceptor {
|
||||
public class SaAnnotationInterceptor implements HandlerInterceptor {
|
||||
|
||||
|
||||
/**
|
||||
@@ -24,23 +24,32 @@ public class SaCheckInterceptor implements HandlerInterceptor {
|
||||
public StpLogic stpLogic = null;
|
||||
|
||||
/**
|
||||
* 创建,并指定一个默认的 StpLogic
|
||||
* @return 底层的 StpLogic 对象
|
||||
*/
|
||||
public SaCheckInterceptor() {
|
||||
this(StpUtil.stpLogic);
|
||||
public StpLogic getStpLogic() {
|
||||
if(stpLogic == null) {
|
||||
stpLogic = StpUtil.stpLogic;
|
||||
}
|
||||
return stpLogic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建,并指定一个的 StpLogic
|
||||
* @param stpLogic 指定的StpLogic
|
||||
*/
|
||||
public SaCheckInterceptor(StpLogic stpLogic) {
|
||||
this.stpLogic = stpLogic;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 每次请求之前触发
|
||||
* @param stpLogic 底层的 StpLogic 对象
|
||||
*/
|
||||
public SaAnnotationInterceptor setStpLogic(StpLogic stpLogic) {
|
||||
this.stpLogic = stpLogic;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建,并指定一个默认的 StpLogic
|
||||
*/
|
||||
public SaAnnotationInterceptor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次请求之前触发的方法
|
||||
*/
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
@@ -53,7 +62,7 @@ public class SaCheckInterceptor implements HandlerInterceptor {
|
||||
Method method = ((HandlerMethod) handler).getMethod();
|
||||
|
||||
// 进行验证
|
||||
stpLogic.checkMethodAnnotation(method);
|
||||
getStpLogic().checkMethodAnnotation(method);
|
||||
|
||||
// 通过验证
|
||||
return true;
|
||||
@@ -61,5 +70,4 @@ public class SaCheckInterceptor implements HandlerInterceptor {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.dev33.satoken.interceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 执行验证方法的辅助类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaRouteFunction {
|
||||
|
||||
/**
|
||||
* 执行验证的方法
|
||||
*/
|
||||
public void run(HttpServletRequest request, HttpServletResponse response, Object handler);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package cn.dev33.satoken.interceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* sa-token基于路由的拦截式鉴权
|
||||
* @author kong
|
||||
*/
|
||||
public class SaRouteInterceptor implements HandlerInterceptor {
|
||||
|
||||
|
||||
// ----------------- 属性 -----------------
|
||||
|
||||
/**
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
private StpLogic stpLogic;
|
||||
|
||||
/**
|
||||
* 验证类型 (1=登录验证, 2=角色验证, 3=权限验证, 4=自定义验证)
|
||||
*/
|
||||
private int type;
|
||||
|
||||
/**
|
||||
* 验证模式 AND | OR
|
||||
*/
|
||||
private SaMode mode;
|
||||
|
||||
/**
|
||||
* 标识码数组
|
||||
*/
|
||||
private String[] codes;
|
||||
|
||||
/**
|
||||
* 自定义模式下的执行函数
|
||||
*/
|
||||
private SaRouteFunction function;
|
||||
|
||||
|
||||
/**
|
||||
* 表示登录验证
|
||||
*/
|
||||
public static final int LOGIN = 1;
|
||||
|
||||
/**
|
||||
* 表示角色验证
|
||||
*/
|
||||
public static final int ROLE = 2;
|
||||
|
||||
/**
|
||||
* 表示权限验证
|
||||
*/
|
||||
public static final int PERMISSION = 3;
|
||||
|
||||
/**
|
||||
* 表示自定义验证
|
||||
*/
|
||||
public static final int CUSTOM = 4;
|
||||
|
||||
|
||||
/**
|
||||
* @return 底层的 StpLogic 对象
|
||||
*/
|
||||
public StpLogic getStpLogic() {
|
||||
if(stpLogic == null) {
|
||||
stpLogic = StpUtil.stpLogic;
|
||||
}
|
||||
return stpLogic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stpLogic 底层的 StpLogic 对象
|
||||
*/
|
||||
public SaRouteInterceptor setStpLogic(StpLogic stpLogic) {
|
||||
this.stpLogic = stpLogic;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 验证类型 (1=登录验证, 2=角色验证, 3=权限验证, 4=自定义验证)
|
||||
*/
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type 验证类型 (1=登录验证, 2=角色验证, 3=权限验证, 4=自定义验证)
|
||||
*/
|
||||
public SaRouteInterceptor setType(int type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 验证模式 AND | OR
|
||||
*/
|
||||
public SaMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mode 验证模式 AND | OR
|
||||
*/
|
||||
public SaRouteInterceptor setMode(SaMode mode) {
|
||||
this.mode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 标识码数组
|
||||
*/
|
||||
public String[] getCodes() {
|
||||
return codes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param codes 标识码数组
|
||||
*/
|
||||
public SaRouteInterceptor setCodes(String... codes) {
|
||||
this.codes = codes;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 自定义模式下的执行函数
|
||||
*/
|
||||
public SaRouteFunction getFunction() {
|
||||
return function;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param function 设置自定义模式下的执行函数
|
||||
*/
|
||||
public SaRouteInterceptor setFunction(SaRouteFunction function) {
|
||||
this.type = SaRouteInterceptor.CUSTOM;
|
||||
this.function = function;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ----------------- 构建相关 -----------------
|
||||
|
||||
/**
|
||||
* 创建 (全参数)
|
||||
* @param type 验证类型 (1=登录验证, 2=角色验证, 3=权限验证, 4=自定义验证)
|
||||
* @param mode 验证模式 AND | OR
|
||||
* @param codes 标识码数组
|
||||
*/
|
||||
public SaRouteInterceptor(int type, SaMode mode, String... codes) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.mode = mode;
|
||||
this.codes = codes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 (默认为登录验证)
|
||||
*/
|
||||
public SaRouteInterceptor() {
|
||||
this(SaRouteInterceptor.LOGIN, null, new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 (默认为自定义认证)
|
||||
* @param function 自定义模式下的执行函数
|
||||
*/
|
||||
public SaRouteInterceptor(SaRouteFunction function) {
|
||||
this(SaRouteInterceptor.CUSTOM, null, new String[0]);
|
||||
setFunction(function);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个模式为登录认证的sa路由拦截器
|
||||
* @return sa拦截器
|
||||
*/
|
||||
public static SaRouteInterceptor createLoginVal() {
|
||||
return new SaRouteInterceptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个模式为角色认证的sa路由拦截器
|
||||
* @param roles 需要验证的角色标识列表
|
||||
* @return sa拦截器
|
||||
*/
|
||||
public static SaRouteInterceptor createRoleVal(String... roles) {
|
||||
return new SaRouteInterceptor(SaRouteInterceptor.ROLE, SaMode.AND, roles);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个模式为权限认证的sa路由拦截器
|
||||
* @param permissions 需要验证的权限列表
|
||||
* @return sa拦截器
|
||||
*/
|
||||
public static SaRouteInterceptor createPermissionVal(String... permissions) {
|
||||
return new SaRouteInterceptor(SaRouteInterceptor.PERMISSION, SaMode.AND, permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个模式为自定义认证的sa路由拦截器
|
||||
* @param function 自定义模式下的执行函数
|
||||
* @return sa拦截器
|
||||
*/
|
||||
public static SaRouteInterceptor createCustomVal(SaRouteFunction function) {
|
||||
return new SaRouteInterceptor(function);
|
||||
}
|
||||
|
||||
|
||||
// ----------------- 验证方法 -----------------
|
||||
|
||||
/**
|
||||
* 每次请求之前触发的方法
|
||||
*/
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
|
||||
// 根据模式进行验证
|
||||
if(this.type == SaRouteInterceptor.LOGIN) {
|
||||
getStpLogic().checkLogin();
|
||||
} else if(this.type == SaRouteInterceptor.ROLE) {
|
||||
if(mode == SaMode.AND) {
|
||||
getStpLogic().checkRoleAnd(codes);
|
||||
} else {
|
||||
getStpLogic().checkRoleOr(codes);
|
||||
}
|
||||
} else if(this.type == SaRouteInterceptor.PERMISSION) {
|
||||
if(mode == SaMode.AND) {
|
||||
getStpLogic().checkPermissionAnd(codes);
|
||||
} else {
|
||||
getStpLogic().checkPermissionOr(codes);
|
||||
}
|
||||
} else if(this.type == SaRouteInterceptor.CUSTOM) {
|
||||
function.run(request, response, handler);
|
||||
}
|
||||
|
||||
// 通过验证
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user