mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-04-05 17:37:53 +08:00
二级认证新增指定业务标识功能
This commit is contained in:
parent
05b227e332
commit
432bfe36bf
@ -5,6 +5,8 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
/**
|
||||
* 二级认证校验:必须二级认证之后才能进入该方法
|
||||
*
|
||||
@ -18,8 +20,14 @@ public @interface SaCheckSafe {
|
||||
|
||||
/**
|
||||
* 多账号体系下所属的账号体系标识
|
||||
* @return see note
|
||||
* @return /
|
||||
*/
|
||||
String type() default "";
|
||||
|
||||
/**
|
||||
* 要校验的服务
|
||||
* @return /
|
||||
*/
|
||||
String value() default SaTokenConsts.DEFAULT_SAFE_AUTH_SERVICE;
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
/**
|
||||
* 一个异常:代表会话未能通过二级认证
|
||||
* 一个异常:代表会话未能通过二级认证校验
|
||||
*
|
||||
* @author kong
|
||||
*/
|
||||
@ -13,13 +13,62 @@ public class NotSafeException extends SaTokenException {
|
||||
private static final long serialVersionUID = 6806129545290130144L;
|
||||
|
||||
/** 异常提示语 */
|
||||
public static final String BE_MESSAGE = "二级认证失败";
|
||||
public static final String BE_MESSAGE = "二级认证校验失败";
|
||||
|
||||
/**
|
||||
* 一个异常:代表会话未通过二级认证
|
||||
* 账号类型
|
||||
*/
|
||||
public NotSafeException() {
|
||||
super(BE_MESSAGE);
|
||||
private String loginType;
|
||||
|
||||
/**
|
||||
* 未通过校验的 Token 值
|
||||
*/
|
||||
private Object tokenValue;
|
||||
|
||||
/**
|
||||
* 未通过校验的服务
|
||||
*/
|
||||
private String service;
|
||||
|
||||
/**
|
||||
* 获取:账号类型
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public String getLoginType() {
|
||||
return loginType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取: 未通过校验的 Token 值
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public Object getTokenValue() {
|
||||
return tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取: 未通过校验的服务
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public Object getService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一个异常:代表会话未能通过二级认证校验
|
||||
*
|
||||
* @param loginType 账号类型
|
||||
* @param tokenValue 未通过校验的 Token 值
|
||||
* @param service 未通过校验的服务
|
||||
*/
|
||||
public NotSafeException(String loginType, String tokenValue, String service) {
|
||||
super(BE_MESSAGE + ":" + service);
|
||||
this.tokenValue = tokenValue;
|
||||
this.loginType = loginType;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -247,6 +247,18 @@ public class StpLogic {
|
||||
// 5. 返回
|
||||
return tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前上下文的 TokenValue(如果获取不到则抛出异常)
|
||||
* @return /
|
||||
*/
|
||||
public String getTokenValueNotNull(){
|
||||
String tokenValue = getTokenValue();
|
||||
if(SaFoxUtil.isEmpty(tokenValue)) {
|
||||
throw new SaTokenException("未能读取到有效Token");
|
||||
}
|
||||
return tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的Token信息
|
||||
@ -1676,7 +1688,7 @@ public class StpLogic {
|
||||
* @param at 注解对象
|
||||
*/
|
||||
public void checkByAnnotation(SaCheckSafe at) {
|
||||
this.checkSafe();
|
||||
this.checkSafe(at.value());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1993,8 +2005,19 @@ public class StpLogic {
|
||||
* @param safeTime 维持时间 (单位: 秒)
|
||||
*/
|
||||
public void openSafe(long safeTime) {
|
||||
long eff = System.currentTimeMillis() + safeTime * 1000;
|
||||
getTokenSession().set(SaTokenConsts.SAFE_AUTH_SAVE_KEY, eff);
|
||||
openSafe(SaTokenConsts.DEFAULT_SAFE_AUTH_SERVICE, safeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话 开启二级认证
|
||||
* @param service 业务标识
|
||||
* @param safeTime 维持时间 (单位: 秒)
|
||||
*/
|
||||
public void openSafe(String service, long safeTime) {
|
||||
// 开启二级认证前必须处于登录状态
|
||||
checkLogin();
|
||||
// 写入key
|
||||
getSaTokenDao().set(splicingKeySafe(getTokenValueNotNull(), service), SaTokenConsts.SAFE_AUTH_SAVE_VALUE, safeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2002,19 +2025,50 @@ public class StpLogic {
|
||||
* @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
|
||||
*/
|
||||
public boolean isSafe() {
|
||||
long eff = getTokenSession().get(SaTokenConsts.SAFE_AUTH_SAVE_KEY, 0L);
|
||||
if(eff == 0 || eff < System.currentTimeMillis()) {
|
||||
return isSafe(SaTokenConsts.DEFAULT_SAFE_AUTH_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话 是否处于二级认证时间内
|
||||
* @param service 业务标识
|
||||
* @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
|
||||
*/
|
||||
public boolean isSafe(String service) {
|
||||
return isSafe(getTokenValue(), service);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定 Token 是否处于二级认证时间内
|
||||
* @param tokenValue Token 值
|
||||
* @param service 业务标识
|
||||
* @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
|
||||
*/
|
||||
public boolean isSafe(String tokenValue, String service) {
|
||||
// 如果 Token 为空,则直接视为未认证
|
||||
if(SaFoxUtil.isEmpty(tokenValue)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
// 如果DB中可以查询出指定的键值,则代表已认证,否则视为未认证
|
||||
String value = getSaTokenDao().get(splicingKeySafe(tokenValue, service));
|
||||
return !(SaFoxUtil.isEmpty(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前会话是否已通过二级认证,如未通过则抛出异常
|
||||
*/
|
||||
public void checkSafe() {
|
||||
if (isSafe() == false) {
|
||||
throw new NotSafeException();
|
||||
checkSafe(SaTokenConsts.DEFAULT_SAFE_AUTH_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前会话是否已通过二级认证,如未通过则抛出异常
|
||||
* @param service 业务标识
|
||||
*/
|
||||
public void checkSafe(String service) {
|
||||
String tokenValue = getTokenValue();
|
||||
if (isSafe(tokenValue, service) == false) {
|
||||
throw new NotSafeException(loginType, tokenValue, service);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2023,18 +2077,45 @@ public class StpLogic {
|
||||
* @return 剩余有效时间
|
||||
*/
|
||||
public long getSafeTime() {
|
||||
long eff = getTokenSession().get(SaTokenConsts.SAFE_AUTH_SAVE_KEY, 0L);
|
||||
if(eff == 0 || eff < System.currentTimeMillis()) {
|
||||
return getSafeTime(SaTokenConsts.DEFAULT_SAFE_AUTH_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
|
||||
* @param service 业务标识
|
||||
* @return 剩余有效时间
|
||||
*/
|
||||
public long getSafeTime(String service) {
|
||||
// 如果上下文中没有 Token,则直接视为未认证
|
||||
String tokenValue = getTokenValue();
|
||||
if(SaFoxUtil.isEmpty(tokenValue)) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
return (eff - System.currentTimeMillis()) / 1000;
|
||||
|
||||
// 从DB中查询这个key的剩余有效期
|
||||
return getSaTokenDao().getTimeout(splicingKeySafe(tokenValue, service));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话 结束二级认证
|
||||
*/
|
||||
public void closeSafe() {
|
||||
getTokenSession().delete(SaTokenConsts.SAFE_AUTH_SAVE_KEY);
|
||||
closeSafe(SaTokenConsts.DEFAULT_SAFE_AUTH_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话 结束二级认证
|
||||
* @param service 业务标识
|
||||
*/
|
||||
public void closeSafe(String service) {
|
||||
// 如果上下文中没有 Token,则无需任何操作
|
||||
String tokenValue = getTokenValue();
|
||||
if(SaFoxUtil.isEmpty(tokenValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除 key
|
||||
getSaTokenDao().delete(splicingKeySafe(tokenValue, service));
|
||||
}
|
||||
|
||||
|
||||
@ -2111,6 +2192,18 @@ public class StpLogic {
|
||||
return getConfig().getTokenName() + ":" + loginType + ":disable:" + service + ":" + loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接key: 二级认证
|
||||
* @param tokenValue 要认证的 Token
|
||||
* @param service 要认证的业务标识
|
||||
* @return key
|
||||
*/
|
||||
public String splicingKeySafe(String tokenValue, String service) {
|
||||
// 格式:<Token名称>:<账号类型>:<safe>:<业务标识>:<Token值>
|
||||
// 形如:satoken:login:safe:important:gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__
|
||||
return getConfig().getTokenName() + ":" + loginType + ":safe:" + service + ":" + tokenValue;
|
||||
}
|
||||
|
||||
|
||||
// ------------------- Bean对象代理 -------------------
|
||||
|
||||
|
@ -1002,6 +1002,15 @@ public class StpUtil {
|
||||
stpLogic.openSafe(safeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话 开启二级认证
|
||||
* @param service 业务标识
|
||||
* @param safeTime 维持时间 (单位: 秒)
|
||||
*/
|
||||
public static void openSafe(String service, long safeTime) {
|
||||
stpLogic.openSafe(service, safeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话 是否处于二级认证时间内
|
||||
* @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
|
||||
@ -1010,12 +1019,39 @@ public class StpUtil {
|
||||
return stpLogic.isSafe();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话 是否处于二级认证时间内
|
||||
* @param service 业务标识
|
||||
* @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
|
||||
*/
|
||||
public static boolean isSafe(String service) {
|
||||
return stpLogic.isSafe(service);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定 Token 是否处于二级认证时间内
|
||||
* @param tokenValue Token 值
|
||||
* @param service 业务标识
|
||||
* @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
|
||||
*/
|
||||
public static boolean isSafe(String tokenValue, String service) {
|
||||
return stpLogic.isSafe(tokenValue, service);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前会话是否已通过二级认证,如未通过则抛出异常
|
||||
*/
|
||||
public static void checkSafe() {
|
||||
stpLogic.checkSafe();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前会话是否已通过二级认证,如未通过则抛出异常
|
||||
* @param service 业务标识
|
||||
*/
|
||||
public static void checkSafe(String service) {
|
||||
stpLogic.checkSafe(service);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
|
||||
@ -1025,6 +1061,15 @@ public class StpUtil {
|
||||
return stpLogic.getSafeTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
|
||||
* @param service 业务标识
|
||||
* @return 剩余有效时间
|
||||
*/
|
||||
public static long getSafeTime(String service) {
|
||||
return stpLogic.getSafeTime(service);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话 结束二级认证
|
||||
*/
|
||||
@ -1032,4 +1077,12 @@ public class StpUtil {
|
||||
stpLogic.closeSafe();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话 结束二级认证
|
||||
* @param service 业务标识
|
||||
*/
|
||||
public static void closeSafe(String service) {
|
||||
stpLogic.closeSafe(service);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -80,10 +80,21 @@ public class SaTokenConsts {
|
||||
public static final String SWITCH_TO_SAVE_KEY = "SWITCH_TO_SAVE_KEY_";
|
||||
|
||||
/**
|
||||
* 常量key标记: 在进行Token二级验证时使用的key
|
||||
* 常量key标记: 在进行Token二级验证时,使用的key
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String SAFE_AUTH_SAVE_KEY = "SAFE_AUTH_SAVE_KEY_";
|
||||
|
||||
/**
|
||||
* 常量key标记: 在进行 Token 二级验证时,写入的 value 值
|
||||
*/
|
||||
public static final String SAFE_AUTH_SAVE_VALUE = "SAFE_AUTH_SAVE_VALUE";
|
||||
|
||||
/**
|
||||
* 常量key标记: 在进行 Token 二级验证时,默认的业务类型
|
||||
*/
|
||||
public static final String DEFAULT_SAFE_AUTH_SERVICE = "important";
|
||||
|
||||
|
||||
// =================== token-style 相关 ===================
|
||||
|
||||
|
@ -23,7 +23,7 @@ public class SafeAuthController {
|
||||
* 测试步骤:
|
||||
1、前端调用 deleteProject 接口,尝试删除仓库。 ---- http://localhost:8081/safe/deleteProject
|
||||
2、后端校验会话尚未完成二级认证,返回: 仓库删除失败,请完成二级认证后再次访问接口。
|
||||
3、前端将信息提示给用户,用户输入密码,调用 openSafe 接口。 ---- http://localhost:8081/safe/openSafe
|
||||
3、前端将信息提示给用户,用户输入密码,调用 openSafe 接口。 ---- http://localhost:8081/safe/openSafe?password=123456
|
||||
4、后端比对用户输入的密码,完成二级认证,有效期为:120秒。
|
||||
5、前端在 120 秒内再次调用 deleteProject 接口,尝试删除仓库。 ---- http://localhost:8081/safe/deleteProject
|
||||
6、后端校验会话已完成二级认证,返回:仓库删除成功。
|
||||
@ -46,13 +46,13 @@ public class SafeAuthController {
|
||||
return SaResult.ok("仓库删除成功");
|
||||
}
|
||||
|
||||
// 提供密码进行二级认证 ---- http://localhost:8081/safe/openSafe
|
||||
// 提供密码进行二级认证 ---- http://localhost:8081/safe/openSafe?password=123456
|
||||
@RequestMapping("openSafe")
|
||||
public SaResult openSafe(String password) {
|
||||
// 比对密码(此处只是举例,真实项目时可拿其它参数进行校验)
|
||||
if("123456".equals(password)) {
|
||||
|
||||
// 比对成功,为当前会话打开二级认证,有效期为120秒
|
||||
// 比对成功,为当前会话打开二级认证,有效期为120秒,意为在120秒内再调用 deleteProject 接口都无需提供密码
|
||||
StpUtil.openSafe(120);
|
||||
return SaResult.ok("二级认证成功");
|
||||
}
|
||||
@ -61,4 +61,40 @@ public class SafeAuthController {
|
||||
return SaResult.error("二级认证失败");
|
||||
}
|
||||
|
||||
|
||||
// ------------------ 指定业务类型进行二级认证
|
||||
|
||||
// 获取应用秘钥 ---- http://localhost:8081/safe/getClientSecret
|
||||
@RequestMapping("getClientSecret")
|
||||
public SaResult getClientSecret() {
|
||||
// 第1步,先检查当前会话是否已完成 client业务 的二级认证
|
||||
StpUtil.checkSafe("client");
|
||||
|
||||
// 第2步,如果已完成二级认证,则返回数据
|
||||
return SaResult.data("aaaa-bbbb-cccc-dddd-eeee");
|
||||
}
|
||||
|
||||
// 提供手势密码进行二级认证 ---- http://localhost:8081/safe/openClientSafe?gesture=35789
|
||||
@RequestMapping("openClientSafe")
|
||||
public SaResult openClientSafe(String gesture) {
|
||||
// 比对手势密码(此处只是举例,真实项目时可拿其它参数进行校验)
|
||||
if("35789".equals(gesture)) {
|
||||
|
||||
// 比对成功,为当前会话打开二级认证:
|
||||
// 业务类型为:client
|
||||
// 有效期为600秒==10分钟,意为在10分钟内,调用 getClientSecret 时都无需再提供手势密码
|
||||
StpUtil.openSafe("client", 600);
|
||||
return SaResult.ok("二级认证成功");
|
||||
}
|
||||
|
||||
// 如果密码校验失败,则二级认证也会失败
|
||||
return SaResult.error("二级认证失败");
|
||||
}
|
||||
|
||||
// 查询当前会话是否已完成指定的二级认证 ---- http://localhost:8081/safe/isClientSafe
|
||||
@RequestMapping("isClientSafe")
|
||||
public SaResult isClientSafe() {
|
||||
return SaResult.ok("当前是否已完成 client 二级认证:" + StpUtil.isSafe("client"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ public class GlobalException {
|
||||
@ExceptionHandler(NotSafeException.class)
|
||||
public SaResult handlerException(NotSafeException e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error("二级认证校验失败");
|
||||
return SaResult.error("二级认证校验失败:" + e.getService());
|
||||
}
|
||||
|
||||
// 拦截:服务封禁异常
|
||||
|
Loading…
Reference in New Issue
Block a user