1
0
mirror of https://gitee.com/dromara/sa-token.git synced 2025-04-05 17:37:53 +08:00

新增身份临时切换功能

This commit is contained in:
shengzhang 2021-01-11 23:10:11 +08:00
parent c7f3b6d493
commit 4bfad95bad
10 changed files with 219 additions and 53 deletions
sa-token-core/src/main/java/cn/dev33/satoken
sa-token-demo-springboot/src/main/java/com/pj
sa-token-doc/doc/use
sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/interceptor

View File

@ -0,0 +1,15 @@
package cn.dev33.satoken.stp;
/**
* 模拟身份方法的辅助类
* @author kong
*
*/
public interface SaFunction {
/**
* 执行的方法
*/
public void run();
}

View File

@ -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(getJustCreatedSaveKey()) != null) {
tokenValue = String.valueOf(request.getAttribute(getJustCreatedSaveKey()));
}
// 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信息
@ -191,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(getJustCreatedSaveKey(), tokenValue); // 将token保存到本次request里
setLastActivityToNow(tokenValue); // 写入 [最后操作时间]
if(config.getIsReadCookie() == true){ // cookie注入
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", (int)config.getTimeout());
@ -303,6 +313,10 @@ public class StpLogic {
* @return 账号id
*/
public Object getLoginId() {
// 如果正在[临时身份切换]
if(isSwitch()) {
return getSwitchLoginId();
}
// 如果获取不到token则抛出无token
String tokenValue = getTokenValue();
if(tokenValue == null) {
@ -363,6 +377,10 @@ public class StpLogic {
* @return 账号id
*/
public Object getLoginIdDefaultNull() {
// 如果正在[临时身份切换]
if(isSwitch()) {
return getSwitchLoginId();
}
// 如果连token都是空的则直接返回
String tokenValue = getTokenValue();
if(tokenValue == null) {
@ -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(getJustCreatedSaveKey(), tokenValue);
setLastActivityToNow(tokenValue); // 写入 [最后操作时间]
if(getConfig().getIsReadCookie() == true){ // cookie注入
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", (int)getConfig().getTimeout());
@ -1039,6 +1057,20 @@ public class StpLogic {
return getConfig().getTokenName() + ":" + loginKey + ":last-activity:" + tokenValue;
}
/**
* 在进行身份切换时使用的存储key
*/
public String getKeySwitch() {
return SaTokenConsts.SWITCH_TO_SAVE_KEY + getLoginKey();
}
/**
* 如果token为本次请求新创建的则以此字符串为key存储在当前request中
*/
public String getJustCreatedSaveKey() {
return SaTokenConsts.JUST_CREATED_SAVE_KEY + getLoginKey();
}
// =================== Bean对象代理 ===================
@ -1052,7 +1084,7 @@ public class StpLogic {
}
// =================== 注解鉴权 ===================
// =================== 其它方法 ===================
/**
* 对一个Method对象进行注解检查注解鉴权内部实现
@ -1111,6 +1143,55 @@ 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());
}
/**
* 当前是否正处于[身份临时切换]
*/
public boolean isSwitch() {
return SaTokenManager.getSaTokenServlet().getRequest().getAttribute(getKeySwitch()) != null;
}
/**
* 返回[身份临时切换]的loginId
*/
public Object getSwitchLoginId() {
return SaTokenManager.getSaTokenServlet().getRequest().getAttribute(getKeySwitch());
}
/**
* 在一个代码段里方法内临时切换身份为指定loginId
* @param loginId 指定loginId
*/
public void switchTo(Object loginId, SaFunction function) {
try {
switchTo(loginId);
function.run();
} catch (Exception e) {
throw e;
} finally {
endSwitch();
}
}

View File

@ -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,37 @@ public class StpUtil {
}
// =================== 身份切换 ===================
/**
* 临时切换身份为指定loginId
* @param loginId 指定loginId
*/
public static void switchTo(Object loginId) {
stpLogic.switchTo(loginId);
}
/**
* 结束临时切换身份
*/
public static void endSwitch() {
stpLogic.endSwitch();
}
/**
* 当前是否正处于[身份临时切换]
*/
public static boolean isSwitch() {
return stpLogic.isSwitch();
}
/**
* 在一个代码段里方法内临时切换身份为指定loginId
* @param loginId 指定loginId
*/
public static void switchTo(Object loginId, SaFunction function) {
stpLogic.switchTo(loginId, function);
}
}

View File

@ -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_";

View File

@ -11,6 +11,7 @@ public class SaTokenDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SaTokenDemoApplication.class, args);
System.out.println("\n启动成功sa-token配置如下" + SaTokenManager.getConfig());
}
}

View File

@ -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")

View File

@ -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("------- [身份临时切换]调用结束...");
```

View File

@ -44,7 +44,7 @@ public class MySaTokenConfig implements WebMvcConfigurer {
registry.addInterceptor(SaRouteInterceptor.createPermissionVal("user:add", "user:deelete")).addPathPatterns("/UserController/**");
// 注册一个自定义认证拦截器 (可以写任意认证代码)
registry.addInterceptor(new SaRouteInterceptor(new SaFunction() {
registry.addInterceptor(new SaRouteInterceptor(new SaRouteFunction() {
@Override
public void run(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 你可以在这里写任意认证代码, 例如: StpUtil.checkLogin();
@ -52,7 +52,7 @@ public class MySaTokenConfig implements WebMvcConfigurer {
}
})).addPathPatterns("/**");
/** ------ 如果你使用的JDK版本是1.8或以上,上面一坨可以简写为以下形式 ------ */
/** ------ 如果你使用的JDK版本是1.8或以上,上面一坨可以简写为以下形式 ------ */
// 注册一个自定义认证拦截器 (可以写任意认证代码)
registry.addInterceptor(new SaRouteInterceptor((request, response, handler)->{
@ -66,7 +66,7 @@ public class MySaTokenConfig implements WebMvcConfigurer {
(你不必像上面的示例一样注册所有拦截器,只要按需注册即可
## 3、所有拦截器示例

View File

@ -8,7 +8,7 @@ import javax.servlet.http.HttpServletResponse;
* @author kong
*
*/
public interface SaFunction {
public interface SaRouteFunction {
/**
* 执行验证的方法

View File

@ -41,7 +41,7 @@ public class SaRouteInterceptor implements HandlerInterceptor {
/**
* 自定义模式下的执行函数
*/
private SaFunction function;
private SaRouteFunction function;
/**
@ -131,14 +131,14 @@ public class SaRouteInterceptor implements HandlerInterceptor {
/**
* @return 自定义模式下的执行函数
*/
public SaFunction getFunction() {
public SaRouteFunction getFunction() {
return function;
}
/**
* @param function 设置自定义模式下的执行函数
*/
public SaRouteInterceptor setFunction(SaFunction function) {
public SaRouteInterceptor setFunction(SaRouteFunction function) {
this.type = SaRouteInterceptor.CUSTOM;
this.function = function;
return this;
@ -171,7 +171,7 @@ public class SaRouteInterceptor implements HandlerInterceptor {
* 创建 (默认为自定义认证)
* @param function 自定义模式下的执行函数
*/
public SaRouteInterceptor(SaFunction function) {
public SaRouteInterceptor(SaRouteFunction function) {
this(SaRouteInterceptor.CUSTOM, null, new String[0]);
setFunction(function);
}
@ -207,7 +207,7 @@ public class SaRouteInterceptor implements HandlerInterceptor {
* @param function 自定义模式下的执行函数
* @return sa拦截器
*/
public static SaRouteInterceptor createCustomVal(SaFunction function) {
public static SaRouteInterceptor createCustomVal(SaRouteFunction function) {
return new SaRouteInterceptor(function);
}