mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-06-28 13:34:18 +08:00
feat: 重构 temp token 模块,新增 value 反查 token 机制
This commit is contained in:
parent
14e645a8ac
commit
9ecaf72e9f
@ -41,8 +41,7 @@ import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.temp.SaTempDefaultImpl;
|
||||
import cn.dev33.satoken.temp.SaTempInterface;
|
||||
import cn.dev33.satoken.temp.SaTempTemplate;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
@ -167,20 +166,20 @@ public class SaManager {
|
||||
/**
|
||||
* 临时 token 认证模块
|
||||
*/
|
||||
private volatile static SaTempInterface saTemp;
|
||||
public static void setSaTemp(SaTempInterface saTemp) {
|
||||
SaManager.saTemp = saTemp;
|
||||
SaTokenEventCenter.doRegisterComponent("SaTempInterface", saTemp);
|
||||
private volatile static SaTempTemplate saTempTemplate;
|
||||
public static void setSaTempTemplate(SaTempTemplate saTempTemplate) {
|
||||
SaManager.saTempTemplate = saTempTemplate;
|
||||
SaTokenEventCenter.doRegisterComponent("SaTempInterface", saTempTemplate);
|
||||
}
|
||||
public static SaTempInterface getSaTemp() {
|
||||
if (saTemp == null) {
|
||||
public static SaTempTemplate getSaTempTemplate() {
|
||||
if (saTempTemplate == null) {
|
||||
synchronized (SaManager.class) {
|
||||
if (saTemp == null) {
|
||||
SaManager.saTemp = new SaTempDefaultImpl();
|
||||
if (saTempTemplate == null) {
|
||||
SaManager.saTempTemplate = new SaTempTemplate();
|
||||
}
|
||||
}
|
||||
}
|
||||
return saTemp;
|
||||
return saTempTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,7 @@ import cn.dev33.satoken.exception.ApiKeyException;
|
||||
import cn.dev33.satoken.exception.ApiKeyScopeException;
|
||||
import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.session.SaSessionRawUtil;
|
||||
import cn.dev33.satoken.session.raw.SaRawSessionDelegator;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
@ -42,9 +42,9 @@ import java.util.List;
|
||||
public class SaApiKeyTemplate {
|
||||
|
||||
/**
|
||||
* ApiKey 的 raw-session 类型
|
||||
* Raw Session 读写委托
|
||||
*/
|
||||
public static final String SESSION_TYPE = "apikey";
|
||||
public SaRawSessionDelegator rawSessionDelegator = new SaRawSessionDelegator("apikey");
|
||||
|
||||
/**
|
||||
* 在 raw-session 中的保存索引列表使用的 key
|
||||
@ -132,10 +132,10 @@ public class SaApiKeyTemplate {
|
||||
getSaTokenDao().setObject(saveKey, ak, ak.expiresIn());
|
||||
}
|
||||
|
||||
// 调整索引
|
||||
if (SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) {
|
||||
// 记录索引
|
||||
SaSession session = SaSessionRawUtil.getSessionById(SESSION_TYPE, ak.getLoginId());
|
||||
if (SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) {
|
||||
// 添加索引
|
||||
SaSession session = rawSessionDelegator.getSessionById(ak.getLoginId());
|
||||
ArrayList<String> apiKeyList = session.get(API_KEY_LIST, ArrayList::new);
|
||||
if(! apiKeyList.contains(ak.getApiKey())) {
|
||||
apiKeyList.add(ak.getApiKey());
|
||||
@ -172,7 +172,7 @@ public class SaApiKeyTemplate {
|
||||
// 删索引
|
||||
if(SaManager.getSaApiKeyDataLoader().getIsRecordIndex()) {
|
||||
// RawSession 中不存在,提前退出
|
||||
SaSession session = SaSessionRawUtil.getSessionById(SESSION_TYPE, ak.getLoginId(), false);
|
||||
SaSession session = rawSessionDelegator.getSessionById(ak.getLoginId(), false);
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
@ -184,7 +184,7 @@ public class SaApiKeyTemplate {
|
||||
|
||||
// 如果只有一个 ApiKey,则整个 RawSession 删掉
|
||||
if (apiKeyList.size() == 1) {
|
||||
SaSessionRawUtil.deleteSessionById(SESSION_TYPE, ak.getLoginId());
|
||||
rawSessionDelegator.deleteSessionById(ak.getLoginId());
|
||||
} else {
|
||||
// 否则移除此 ApiKey 并保存
|
||||
apiKeyList.remove(apiKey);
|
||||
@ -205,7 +205,7 @@ public class SaApiKeyTemplate {
|
||||
}
|
||||
|
||||
// RawSession 中不存在,提前退出
|
||||
SaSession session = SaSessionRawUtil.getSessionById(SESSION_TYPE, loginId, false);
|
||||
SaSession session = rawSessionDelegator.getSessionById(loginId, false);
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
@ -217,7 +217,7 @@ public class SaApiKeyTemplate {
|
||||
}
|
||||
|
||||
// 再删索引
|
||||
SaSessionRawUtil.deleteSessionById(SESSION_TYPE, loginId);
|
||||
rawSessionDelegator.deleteSessionById(loginId);
|
||||
}
|
||||
|
||||
// ------- 创建
|
||||
@ -382,7 +382,7 @@ public class SaApiKeyTemplate {
|
||||
|
||||
// 未提供则现场查询
|
||||
if(session == null) {
|
||||
session = SaSessionRawUtil.getSessionById(SESSION_TYPE, loginId, false);
|
||||
session = rawSessionDelegator.getSessionById(loginId, false);
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
@ -400,6 +400,11 @@ public class SaApiKeyTemplate {
|
||||
apiKeyNewList.add(apikey);
|
||||
apiKeyModelList.add(ak);
|
||||
}
|
||||
// 如果队列里已无有效值,则删除该 session
|
||||
if(apiKeyNewList.isEmpty()) {
|
||||
rawSessionDelegator.deleteSessionById(loginId);
|
||||
return;
|
||||
}
|
||||
session.set(API_KEY_LIST, apiKeyNewList);
|
||||
|
||||
// 调整 SaSession TTL
|
||||
@ -433,7 +438,7 @@ public class SaApiKeyTemplate {
|
||||
|
||||
// 先查 RawSession
|
||||
List<ApiKeyModel> apiKeyModelList = new ArrayList<>();
|
||||
SaSession session = SaSessionRawUtil.getSessionById(SESSION_TYPE, loginId, false);
|
||||
SaSession session = rawSessionDelegator.getSessionById(loginId, false);
|
||||
if(session == null) {
|
||||
return apiKeyModelList;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
package cn.dev33.satoken.application;
|
||||
|
||||
import cn.dev33.satoken.fun.SaRetFunction;
|
||||
import cn.dev33.satoken.fun.SaRetGenericFunction;
|
||||
|
||||
/**
|
||||
* 对写值的一组方法封装
|
||||
@ -55,7 +56,7 @@ public interface SaSetValueInterface extends SaGetValueInterface {
|
||||
* @return 值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T> T get(String key, SaRetFunction fun) {
|
||||
default <T> T get(String key, SaRetGenericFunction<T> fun) {
|
||||
Object value = get(key);
|
||||
if(value == null) {
|
||||
value = fun.run();
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.session.raw;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* SaSession 读写工具类 委托
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.42.0
|
||||
*/
|
||||
public class SaRawSessionDelegator {
|
||||
|
||||
/**
|
||||
* raw session 类型
|
||||
*/
|
||||
public String type;
|
||||
|
||||
public SaRawSessionDelegator(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:指定 SaSession 是否存在
|
||||
*
|
||||
* @param valueId /
|
||||
* @return 是否存在
|
||||
*/
|
||||
public boolean isExists(Object valueId) {
|
||||
return SaRawSessionUtil.isExists(type, valueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 SaSession 对象, 如果此 SaSession 尚未在 Cache 创建,isCreate 参数代表是否则新建并返回
|
||||
*
|
||||
* @param valueId /
|
||||
* @param isCreate 如果此 SaSession 尚未在 DB 创建,是否新建并返回
|
||||
* @return SaSession 对象
|
||||
*/
|
||||
public SaSession getSessionById(Object valueId, boolean isCreate) {
|
||||
return SaRawSessionUtil.getSessionById(type, valueId, isCreate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 SaSession, 如果此 SaSession 尚未在 DB 创建,则新建并返回
|
||||
*
|
||||
* @param valueId /
|
||||
* @return SaSession 对象
|
||||
*/
|
||||
public SaSession getSessionById(Object valueId) {
|
||||
return SaRawSessionUtil.getSessionById(type, valueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定 SaSession
|
||||
*
|
||||
* @param valueId /
|
||||
*/
|
||||
public void deleteSessionById(Object valueId) {
|
||||
SaRawSessionUtil.deleteSessionById(type, valueId);
|
||||
}
|
||||
|
||||
}
|
@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.session;
|
||||
package cn.dev33.satoken.session.raw;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
|
||||
/**
|
||||
@ -24,9 +25,9 @@ import cn.dev33.satoken.strategy.SaStrategy;
|
||||
* @author click33
|
||||
* @since 1.42.0
|
||||
*/
|
||||
public class SaSessionRawUtil {
|
||||
public class SaRawSessionUtil {
|
||||
|
||||
private SaSessionRawUtil() {
|
||||
private SaRawSessionUtil() {
|
||||
}
|
||||
|
||||
/**
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.temp;
|
||||
|
||||
/**
|
||||
* Sa-Token 临时令牌验证模块 默认实现类
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.20.0
|
||||
*/
|
||||
public class SaTempDefaultImpl implements SaTempInterface {
|
||||
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.temp;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
/**
|
||||
* Sa-Token 临时 token 验证模块 - 接口
|
||||
*
|
||||
* <p>
|
||||
* 有效期很短的一种token,一般用于一次性接口防盗用、短时间资源访问等业务场景
|
||||
* </p>
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.20.0
|
||||
*/
|
||||
public interface SaTempInterface {
|
||||
|
||||
// -------- 创建
|
||||
|
||||
/**
|
||||
* 为指定 value 创建一个临时 Token
|
||||
* @param value 指定值
|
||||
* @param timeout 有效时间,单位:秒,-1 代表永久有效
|
||||
* @return 生成的 token
|
||||
*/
|
||||
default String createToken(Object value, long timeout) {
|
||||
return createToken(SaTokenConsts.DEFAULT_TEMP_TOKEN_SERVICE, value, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指定 业务标识、指定 value 创建一个 Token
|
||||
* @param service 业务标识
|
||||
* @param value 指定值
|
||||
* @param timeout 有效期,单位:秒,-1 代表永久有效
|
||||
* @return 生成的token
|
||||
*/
|
||||
default String createToken(String service, Object value, long timeout) {
|
||||
|
||||
// 生成 token
|
||||
String token = SaStrategy.instance.createToken.apply(null, null);
|
||||
|
||||
// 持久化映射关系
|
||||
String key = splicingKeyTempToken(service, token);
|
||||
SaManager.getSaTokenDao().setObject(key, value, timeout);
|
||||
|
||||
// 返回
|
||||
return token;
|
||||
}
|
||||
|
||||
// -------- 解析
|
||||
|
||||
/**
|
||||
* 解析 Token 获取 value
|
||||
* @param token 指定 Token
|
||||
* @return /
|
||||
*/
|
||||
default Object parseToken(String token) {
|
||||
return parseToken(SaTokenConsts.DEFAULT_TEMP_TOKEN_SERVICE, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Token 获取 value
|
||||
* @param service 业务标识
|
||||
* @param token 指定 Token
|
||||
* @return /
|
||||
*/
|
||||
default Object parseToken(String service, String token) {
|
||||
String key = splicingKeyTempToken(service, token);
|
||||
return SaManager.getSaTokenDao().getObject(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Token 获取 value,并转换为指定类型
|
||||
* @param token 指定 Token
|
||||
* @param cs 指定类型
|
||||
* @param <T> 默认值的类型
|
||||
* @return /
|
||||
*/
|
||||
default<T> T parseToken(String token, Class<T> cs) {
|
||||
return parseToken(SaTokenConsts.DEFAULT_TEMP_TOKEN_SERVICE, token, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Token 获取 value,并转换为指定类型
|
||||
* @param service 业务标识
|
||||
* @param token 指定 Token
|
||||
* @param cs 指定类型
|
||||
* @param <T> 默认值的类型
|
||||
* @return /
|
||||
*/
|
||||
default<T> T parseToken(String service, String token, Class<T> cs) {
|
||||
return SaFoxUtil.getValueByType(parseToken(service, token), cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 Token 的剩余有效期,单位:秒
|
||||
* <p> 返回值 -1 代表永久,-2 代表token无效
|
||||
* @param token 指定 Token
|
||||
* @return /
|
||||
*/
|
||||
default long getTimeout(String token) {
|
||||
return getTimeout(SaTokenConsts.DEFAULT_TEMP_TOKEN_SERVICE, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 业务标识、指定 Token 的剩余有效期,单位:秒
|
||||
* <p> 返回值 -1 代表永久,-2 代表token无效
|
||||
* @param service 业务标识
|
||||
* @param token 指定 Token
|
||||
* @return /
|
||||
*/
|
||||
default long getTimeout(String service, String token) {
|
||||
String key = splicingKeyTempToken(service, token);
|
||||
return SaManager.getSaTokenDao().getObjectTimeout(key);
|
||||
}
|
||||
|
||||
// -------- 删除
|
||||
|
||||
/**
|
||||
* 删除一个 Token
|
||||
* @param token 指定 Token
|
||||
*/
|
||||
default void deleteToken(String token) {
|
||||
deleteToken(SaTokenConsts.DEFAULT_TEMP_TOKEN_SERVICE, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个 Token
|
||||
* @param service 业务标识
|
||||
* @param token 指定 Token
|
||||
*/
|
||||
default void deleteToken(String service, String token) {
|
||||
String key = splicingKeyTempToken(service, token);
|
||||
SaManager.getSaTokenDao().deleteObject(key);
|
||||
}
|
||||
|
||||
// -------- 其它
|
||||
|
||||
/**
|
||||
* 获取:在存储临时 token 数据时,应该使用的 key
|
||||
* @param service 业务标识
|
||||
* @param token token值
|
||||
* @return key
|
||||
*/
|
||||
default String splicingKeyTempToken(String service, String token) {
|
||||
return SaManager.getConfig().getTokenName() + ":temp-token:" + service + ":" + token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return jwt秘钥 (只有集成 sa-token-temp-jwt 模块时此参数才会生效)
|
||||
*/
|
||||
default String getJwtSecretKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.temp;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.session.raw.SaRawSessionDelegator;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Sa-Token 临时 token 验证模块
|
||||
*
|
||||
* <p>
|
||||
* 有效期很短的一种token,一般用于一次性接口防盗用、短时间资源访问等业务场景
|
||||
* </p>
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.42.0
|
||||
*/
|
||||
public class SaTempTemplate {
|
||||
|
||||
/**
|
||||
* Raw Session 读写委托
|
||||
*/
|
||||
public SaRawSessionDelegator rawSessionDelegator = new SaRawSessionDelegator("temp-token");
|
||||
|
||||
/**
|
||||
* 在 raw-session 中的保存索引列表使用的 key
|
||||
*/
|
||||
public static final String TEMP_TOKEN_MAP = "__HD_TEMP_TOKEN_MAP";
|
||||
|
||||
|
||||
// -------- 创建
|
||||
|
||||
/**
|
||||
* 为指定 value 创建一个临时 token (如果多条业务线均需要创建临时 token,请自行在 value 拼接不同前缀)
|
||||
*
|
||||
* @param value 指定值
|
||||
* @param timeout 有效时间,单位:秒,-1 代表永久有效
|
||||
* @return 生成的 token
|
||||
*/
|
||||
public String createToken(Object value, long timeout) {
|
||||
return createToken(value, timeout, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指定 业务标识、指定 value 创建一个 Token
|
||||
* @param value 指定值
|
||||
* @param timeout 有效期,单位:秒,-1 代表永久有效
|
||||
* @param isRecordIndex 是否记录索引,以便后续使用 value 反查 token
|
||||
* @return 生成的token
|
||||
*/
|
||||
public String createToken(Object value, long timeout, boolean isRecordIndex) {
|
||||
|
||||
// 生成 temp-token
|
||||
String tempToken = createTempTokenValue(value);
|
||||
|
||||
// 持久化映射关系
|
||||
_saveToken(tempToken, value, timeout);
|
||||
|
||||
// 记录索引
|
||||
if(isRecordIndex) {
|
||||
SaSession session = rawSessionDelegator.getSessionById(value);
|
||||
addTempTokenIndex(session, tempToken, timeout);
|
||||
adjustIndex(value, session);
|
||||
}
|
||||
|
||||
// 返回
|
||||
return tempToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个 temp-token 值
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public String createTempTokenValue(Object value) {
|
||||
return SaStrategy.instance.generateUniqueToken.execute(
|
||||
"Temp Token",
|
||||
SaManager.getConfig().getMaxTryTimes(),
|
||||
() -> randomTempToken(value),
|
||||
_apiKey -> parseToken(_apiKey) == null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机一个 temp-token
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public String randomTempToken(Object value) {
|
||||
return UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
|
||||
// -------- 解析
|
||||
|
||||
/**
|
||||
* 解析 Token 获取 value
|
||||
* @param token 指定 Token
|
||||
* @return /
|
||||
*/
|
||||
public Object parseToken(String token) {
|
||||
return _getValue(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Token 获取 value,并转换为指定类型
|
||||
*
|
||||
* @param token 指定 Token
|
||||
* @param cs 指定类型
|
||||
* @param <T> 默认值的类型
|
||||
* @return /
|
||||
*/
|
||||
public<T> T parseToken(String token, Class<T> cs) {
|
||||
return parseToken(token, null, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 token 获取 value,并裁剪指定前缀,然后转换为指定类型
|
||||
*
|
||||
* @param token 指定 Token
|
||||
* @param cs 指定类型
|
||||
* @param <T> 默认值的类型
|
||||
* @return /
|
||||
*/
|
||||
public<T> T parseToken(String token, String cutPrefix, Class<T> cs) {
|
||||
// 解析值
|
||||
Object value = parseToken(token);
|
||||
|
||||
// 如果未指定裁剪前缀,则直接返回
|
||||
if(SaFoxUtil.isEmpty(cutPrefix)) {
|
||||
return SaFoxUtil.getValueByType(value, cs);
|
||||
}
|
||||
|
||||
// 如果符合前缀则裁剪并返回,如果不符合前缀则返回 null
|
||||
String str = SaFoxUtil.valueToString(value);
|
||||
if(str.startsWith(cutPrefix)) {
|
||||
return SaFoxUtil.getValueByType(str.substring(cutPrefix.length()), cs);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定指定 Token 的剩余有效期,单位:秒
|
||||
* <p> 返回值 -1 代表永久,-2 代表 token 无效
|
||||
*
|
||||
* @param token /
|
||||
* @return /
|
||||
*/
|
||||
public long getTimeout(String token) {
|
||||
return _getTimeout(token);
|
||||
}
|
||||
|
||||
|
||||
// -------- 删除
|
||||
|
||||
/**
|
||||
* 删除一个 token
|
||||
* @param token 指定 Token
|
||||
*/
|
||||
public void deleteToken(String token) {
|
||||
// 如果无此数据,则直接返回
|
||||
Object value = parseToken(token);
|
||||
if(SaFoxUtil.isEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除 token 本身
|
||||
_deleteToken(token);
|
||||
|
||||
// 调整索引
|
||||
SaSession session = rawSessionDelegator.getSessionById(value, false);
|
||||
if(session != null) {
|
||||
deleteTempTokenIndex(session, token);
|
||||
adjustIndex(value, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------------------- 索引操作
|
||||
|
||||
/**
|
||||
* 调整索引
|
||||
*
|
||||
* @param value 值
|
||||
* @param session 可填写 null,代表使用 value 现场查询
|
||||
* @return 调整后的索引列表
|
||||
*/
|
||||
public Map<String, Long> adjustIndex(Object value, SaSession session) {
|
||||
|
||||
// 未提供则现场查询
|
||||
if(session == null) {
|
||||
session = rawSessionDelegator.getSessionById(value, false);
|
||||
if(session == null) {
|
||||
return newTempTokenMap();
|
||||
}
|
||||
}
|
||||
|
||||
// 重新整理索引列表
|
||||
Map<String, Long> tempTokenNewList = newTempTokenMap();
|
||||
ArrayList<Long> tempTokenTtlList = new ArrayList<>();
|
||||
Map<String, Long> tempTokenMap = session.get(TEMP_TOKEN_MAP, this::newTempTokenMap);
|
||||
for (Map.Entry<String, Long> entry : tempTokenMap.entrySet()) {
|
||||
long ttl = expireTimeToTtl(entry.getValue());
|
||||
if(ttl != SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
tempTokenNewList.put(entry.getKey(), entry.getValue());
|
||||
tempTokenTtlList.add(ttl);
|
||||
}
|
||||
}
|
||||
|
||||
// 有则保存,无则删除
|
||||
if( ! tempTokenNewList.isEmpty()) {
|
||||
session.set(TEMP_TOKEN_MAP, tempTokenNewList);
|
||||
} else {
|
||||
rawSessionDelegator.deleteSessionById(value);
|
||||
return tempTokenNewList;
|
||||
}
|
||||
|
||||
// 调整 SaSession TTL
|
||||
long maxTtl = getMaxTtl(tempTokenTtlList);
|
||||
if(maxTtl != 0) {
|
||||
session.updateTimeout(maxTtl);
|
||||
}
|
||||
return tempTokenNewList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 value 的 temp-token 列表记录
|
||||
* @param value /
|
||||
* @return /
|
||||
*/
|
||||
public List<String> getTempTokenList(Object value) {
|
||||
// 先调增索引再获取,否则有可能获取到的不是最新有效数据
|
||||
Map<String, Long> tempTokenMap = adjustIndex(value, null);
|
||||
return new ArrayList<>(tempTokenMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 SaSession 上添加临时 temp-token 索引
|
||||
* @param session /
|
||||
* @param token /
|
||||
* @param timeout /
|
||||
*/
|
||||
protected void addTempTokenIndex(SaSession session, String token, long timeout) {
|
||||
Map<String, Long> tempTokenMap = session.get(TEMP_TOKEN_MAP, this::newTempTokenMap);
|
||||
if(! tempTokenMap.containsKey(token)) {
|
||||
tempTokenMap.put(token, ttlToExpireTime(timeout));
|
||||
session.set(TEMP_TOKEN_MAP, tempTokenMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 SaSession 上删除临时 temp-token 索引
|
||||
* @param session /
|
||||
* @param token /
|
||||
*/
|
||||
protected void deleteTempTokenIndex(SaSession session, String token) {
|
||||
Map<String, Long> tempTokenMap = session.get(TEMP_TOKEN_MAP, this::newTempTokenMap);
|
||||
if(tempTokenMap.containsKey(token)) {
|
||||
tempTokenMap.remove(token);
|
||||
session.set(TEMP_TOKEN_MAP, tempTokenMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个新的 TempTokenMap 集合
|
||||
* @return /
|
||||
*/
|
||||
protected Map<String, Long> newTempTokenMap() {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
|
||||
// -------- 元操作
|
||||
|
||||
protected Object _getValue(String token) {
|
||||
String key = splicingTempTokenSaveKey(token);
|
||||
return SaManager.getSaTokenDao().getObject(key);
|
||||
}
|
||||
protected void _saveToken(String token, Object value, long timeout) {
|
||||
String key = splicingTempTokenSaveKey(token);
|
||||
SaManager.getSaTokenDao().setObject(key, value, timeout);
|
||||
}
|
||||
protected void _deleteToken(String token) {
|
||||
String key = splicingTempTokenSaveKey(token);
|
||||
SaManager.getSaTokenDao().deleteObject(key);
|
||||
}
|
||||
protected long _getTimeout(String token) {
|
||||
String key = splicingTempTokenSaveKey(token);
|
||||
return SaManager.getSaTokenDao().getObjectTimeout(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -------- 其它
|
||||
|
||||
/**
|
||||
* 过期时间转 ttl (秒) 获取最大 ttl 值
|
||||
* @param tempTokenTtlList /
|
||||
* @return /
|
||||
*/
|
||||
protected long getMaxTtl(ArrayList<Long> tempTokenTtlList) {
|
||||
long maxTtl = 0;
|
||||
for (long ttl : tempTokenTtlList) {
|
||||
if(ttl == SaTokenDao.NEVER_EXPIRE) {
|
||||
maxTtl = SaTokenDao.NEVER_EXPIRE;
|
||||
break;
|
||||
}
|
||||
if(ttl > maxTtl) {
|
||||
maxTtl = ttl;
|
||||
}
|
||||
}
|
||||
return maxTtl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过期时间转 ttl (秒)
|
||||
* @param expireTime /
|
||||
* @return /
|
||||
*/
|
||||
protected long expireTimeToTtl(long expireTime) {
|
||||
if(expireTime == SaTokenDao.NEVER_EXPIRE) {
|
||||
return SaTokenDao.NEVER_EXPIRE;
|
||||
}
|
||||
if(expireTime == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
return (expireTime - System.currentTimeMillis()) / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttl (秒) 转 过期时间
|
||||
* @param ttl /
|
||||
* @return /
|
||||
*/
|
||||
protected long ttlToExpireTime(long ttl) {
|
||||
if(ttl == SaTokenDao.NEVER_EXPIRE) {
|
||||
return SaTokenDao.NEVER_EXPIRE;
|
||||
}
|
||||
if(ttl == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
return ttl * 1000 + System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:在存储临时 token 数据时,应该使用的 key
|
||||
* @param token token值
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTempTokenSaveKey(String token) {
|
||||
return SaManager.getConfig().getTokenName() + ":temp-token:" + token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return jwt秘钥 (只有集成 sa-token-temp-jwt 模块时此参数才会生效)
|
||||
*/
|
||||
public String getJwtSecretKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -17,6 +17,8 @@ package cn.dev33.satoken.temp;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sa-Token 临时 token 验证模块 - 工具类
|
||||
*
|
||||
@ -35,24 +37,25 @@ public class SaTempUtil {
|
||||
// -------- 创建
|
||||
|
||||
/**
|
||||
* 为指定 value 创建一个临时 Token
|
||||
* 为指定 value 创建一个临时 token (如果多条业务线均需要创建临时 token,请自行在 value 拼接不同前缀)
|
||||
*
|
||||
* @param value 指定值
|
||||
* @param timeout 有效期,单位:秒,-1 代表永久有效
|
||||
* @param timeout 有效时间,单位:秒,-1 代表永久有效
|
||||
* @return 生成的 token
|
||||
*/
|
||||
public static String createToken(Object value, long timeout) {
|
||||
return SaManager.getSaTemp().createToken(value, timeout);
|
||||
return SaManager.getSaTempTemplate().createToken(value, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指定 业务标识、指定 value 创建一个 Token
|
||||
* @param service 业务标识
|
||||
* @param value 指定值
|
||||
* @param timeout 有效期,单位:秒,-1 代表永久有效
|
||||
* @param isRecordIndex 是否记录索引,以便后续使用 value 反查 token
|
||||
* @return 生成的token
|
||||
*/
|
||||
public static String createToken(String service, Object value, long timeout) {
|
||||
return SaManager.getSaTemp().createToken(service, value, timeout);
|
||||
public static String createToken(Object value, long timeout, boolean isRecordIndex) {
|
||||
return SaManager.getSaTempTemplate().createToken(value, timeout, isRecordIndex);
|
||||
}
|
||||
|
||||
// -------- 解析
|
||||
@ -63,80 +66,65 @@ public class SaTempUtil {
|
||||
* @return /
|
||||
*/
|
||||
public static Object parseToken(String token) {
|
||||
return SaManager.getSaTemp().parseToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Token 获取 value
|
||||
* @param service 业务标识
|
||||
* @param token 指定 Token
|
||||
* @return /
|
||||
*/
|
||||
public static Object parseToken(String service, String token) {
|
||||
return SaManager.getSaTemp().parseToken(service, token);
|
||||
return SaManager.getSaTempTemplate().parseToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Token 获取 value,并转换为指定类型
|
||||
*
|
||||
* @param token 指定 Token
|
||||
* @param cs 指定类型
|
||||
* @param <T> 默认值的类型
|
||||
* @return /
|
||||
*/
|
||||
public static<T> T parseToken(String token, Class<T> cs) {
|
||||
return SaManager.getSaTemp().parseToken(token, cs);
|
||||
return SaManager.getSaTempTemplate().parseToken(token, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Token 获取 value,并转换为指定类型
|
||||
* @param service 业务标识
|
||||
* 解析 token 获取 value,并裁剪指定前缀,然后转换为指定类型
|
||||
*
|
||||
* @param token 指定 Token
|
||||
* @param cs 指定类型
|
||||
* @param <T> 默认值的类型
|
||||
* @return /
|
||||
*/
|
||||
public static<T> T parseToken(String service, String token, Class<T> cs) {
|
||||
return SaManager.getSaTemp().parseToken(service, token, cs);
|
||||
public static<T> T parseToken(String token, String cutPrefix, Class<T> cs) {
|
||||
return SaManager.getSaTempTemplate().parseToken(token, cutPrefix, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 Token 的剩余有效期,单位:秒
|
||||
* 获取指定指定 Token 的剩余有效期,单位:秒
|
||||
* <p> 返回值 -1 代表永久,-2 代表 token 无效
|
||||
* @param token 指定 Token
|
||||
*
|
||||
* @param token /
|
||||
* @return /
|
||||
*/
|
||||
public static long getTimeout(String token) {
|
||||
return SaManager.getSaTemp().getTimeout(token);
|
||||
return SaManager.getSaTempTemplate().getTimeout(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 业务标识、指定 Token 的剩余有效期,单位:秒
|
||||
* <p> 返回值 -1 代表永久,-2 代表token无效
|
||||
* @param service 业务标识
|
||||
* @param token 指定 Token
|
||||
* @return /
|
||||
*/
|
||||
public static long getTimeout(String service, String token) {
|
||||
return SaManager.getSaTemp().getTimeout(service, token);
|
||||
}
|
||||
|
||||
// -------- 删除
|
||||
|
||||
/**
|
||||
* 删除一个 Token
|
||||
* 删除一个 token
|
||||
* @param token 指定 Token
|
||||
*/
|
||||
public static void deleteToken(String token) {
|
||||
SaManager.getSaTemp().deleteToken(token);
|
||||
SaManager.getSaTempTemplate().deleteToken(token);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 索引操作
|
||||
|
||||
/**
|
||||
* 删除一个 Token
|
||||
* @param service 业务标识
|
||||
* @param token 指定 Token
|
||||
* 获取指定 value 的 temp-token 列表记录
|
||||
* @param value /
|
||||
* @return /
|
||||
*/
|
||||
public static void deleteToken(String service, String token) {
|
||||
SaManager.getSaTemp().deleteToken(service, token);
|
||||
public static List<String> getTempTokenList(Object value) {
|
||||
return SaManager.getSaTempTemplate().getTempTokenList(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -828,5 +828,17 @@ public class SaFoxUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 value 转化为 String,如果 value 为 null,则返回空字符串
|
||||
* @param value /
|
||||
* @return /
|
||||
*/
|
||||
public static String valueToString(Object value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ public class SaTokenConsts {
|
||||
/**
|
||||
* 常量 key 标记: 临时 Token 认证模块,默认的业务类型
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String DEFAULT_TEMP_TOKEN_SERVICE = "record";
|
||||
|
||||
|
||||
|
@ -59,6 +59,13 @@
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 临时 token 模块整合 jwt -->
|
||||
<!--<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-temp-jwt</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>-->
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -0,0 +1,69 @@
|
||||
package com.pj.cases.plugin;
|
||||
|
||||
import cn.dev33.satoken.temp.SaTempUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 测试专用 Controller
|
||||
* @author click33
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/temp-token/")
|
||||
public class TempTokenController {
|
||||
|
||||
// 创建 浏览器访问: http://localhost:8081/temp-token/create
|
||||
@RequestMapping("create")
|
||||
public SaResult create() {
|
||||
String token = SaTempUtil.createToken(10001, 1200);
|
||||
System.out.println("创建成功:" + token);
|
||||
return SaResult.data(token);
|
||||
}
|
||||
|
||||
// 创建,获取时裁剪前缀 浏览器访问: http://localhost:8081/temp-token/create2
|
||||
@RequestMapping("create2")
|
||||
public SaResult create2() {
|
||||
String token = SaTempUtil.createToken("shop_" + 1001, 1200);
|
||||
System.out.println("创建成功:" + token);
|
||||
System.out.println("获取对应值:" + SaTempUtil.parseToken(token));
|
||||
System.out.println("获取对应值,并裁剪前缀:" + SaTempUtil.parseToken(token, "shop_", Long.class));
|
||||
System.out.println("指定错误前缀来获取:" + SaTempUtil.parseToken(token, "art_", Long.class));
|
||||
return SaResult.data(token);
|
||||
}
|
||||
|
||||
// 创建,回收 浏览器访问: http://localhost:8081/temp-token/create3
|
||||
@RequestMapping("create3")
|
||||
public SaResult create3() {
|
||||
String token = SaTempUtil.createToken(10003, 1200);
|
||||
System.out.println("创建成功:" + token);
|
||||
System.out.println("获取对应值:" + SaTempUtil.parseToken(token));
|
||||
SaTempUtil.deleteToken(token);
|
||||
System.out.println("回收后再获取:" + SaTempUtil.parseToken(token));
|
||||
return SaResult.data(token);
|
||||
}
|
||||
|
||||
// 创建,记录索引 浏览器访问: http://localhost:8081/temp-token/create4
|
||||
@RequestMapping("create4")
|
||||
public SaResult create4() {
|
||||
String token1 = SaTempUtil.createToken(10004, 1200, true);
|
||||
String token2 = SaTempUtil.createToken(10004, 1300, true);
|
||||
String token3 = SaTempUtil.createToken(10004, -1, true);
|
||||
|
||||
System.out.println("token1 剩余有效期:" + SaTempUtil.getTimeout(token1));
|
||||
System.out.println("token2 剩余有效期:" + SaTempUtil.getTimeout(token2));
|
||||
System.out.println("token3 剩余有效期:" + SaTempUtil.getTimeout(token3));
|
||||
|
||||
SaTempUtil.deleteToken(token3);
|
||||
|
||||
// 获取已创建的 token 列表
|
||||
System.out.println("获取已创建的 token 列表 ");
|
||||
List<String> tempTokenList = SaTempUtil.getTempTokenList(10004);
|
||||
System.out.println(tempTokenList);
|
||||
return SaResult.data(token1);
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
package com.pj.cases.up;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Sa-Token 同端互斥登录示例
|
||||
@ -62,7 +61,7 @@ public class MutexLoginController {
|
||||
public SaResult isLogin() {
|
||||
// StpUtil.isLogin() 查询当前客户端是否登录,返回 true 或 false
|
||||
boolean isLogin = StpUtil.isLogin();
|
||||
return SaResult.ok("当前客户端是否登录:" + isLogin + ",登录的设备是:" + StpUtil.getLoginDevice());
|
||||
return SaResult.ok("当前客户端是否登录:" + isLogin + ",登录的设备是:" + StpUtil.getLoginDeviceType());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ sa-token:
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
# jwt 秘钥
|
||||
jwt-secret-key: JfdDSgfCmPsDfmsAaQwnXk
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
|
@ -34,7 +34,7 @@
|
||||
<dubbo.version>2.7.21</dubbo.version>
|
||||
<grpc-spring-boot-starter.version>2.10.1.RELEASE</grpc-spring-boot-starter.version>
|
||||
<hutool-jwt.version>5.8.36</hutool-jwt.version>
|
||||
<jjwt.version>0.9.1</jjwt.version>
|
||||
<jjwt.version>0.12.6</jjwt.version>
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
<fastjson2.version>2.0.15</fastjson2.version>
|
||||
<redisson.version>3.45.0</redisson.version>
|
||||
|
@ -16,7 +16,7 @@
|
||||
package cn.dev33.satoken.plugin;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.temp.jwt.SaTempForJwt;
|
||||
import cn.dev33.satoken.temp.jwt.SaTempTemplateForJwt;
|
||||
|
||||
/**
|
||||
* SaToken 插件安装:临时 token 生成器 - Jwt 版
|
||||
@ -28,7 +28,7 @@ public class SaTokenPluginForTempForJwt implements SaTokenPlugin {
|
||||
|
||||
@Override
|
||||
public void install() {
|
||||
SaManager.setSaTemp(new SaTempForJwt());
|
||||
SaManager.setSaTempTemplate(new SaTempTemplateForJwt());
|
||||
}
|
||||
|
||||
}
|
@ -17,11 +17,14 @@ package cn.dev33.satoken.temp.jwt;
|
||||
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.secure.SaSecureUtil;
|
||||
import cn.dev33.satoken.temp.jwt.error.SaTempJwtErrorCode;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
/**
|
||||
* jwt 相关操作工具类,封装一下
|
||||
@ -47,13 +50,12 @@ public class SaJwtUtil {
|
||||
/**
|
||||
* 根据指定值创建 jwt-token
|
||||
*
|
||||
* @param key 存储value使用的key
|
||||
* @param value 要保存的值
|
||||
* @param timeout token有效期 (单位 秒)
|
||||
* @param keyt 秘钥
|
||||
* @return jwt-token
|
||||
*/
|
||||
public static String createToken(String key, Object value, long timeout, String keyt) {
|
||||
public static String createToken(Object value, long timeout, String keyt) {
|
||||
// 计算eff有效期:
|
||||
// 如果 timeout 指定为 -1,那么 eff 也为 -1,代表永不过期
|
||||
// 如果 timeout 指定为一个具体的值,那么 eff 为 13 位时间戳,代表此数据到期的时间
|
||||
@ -63,11 +65,12 @@ public class SaJwtUtil {
|
||||
}
|
||||
|
||||
// 在这里你可以使用官方提供的claim方法构建载荷,也可以使用setPayload自定义载荷,但是两者不可一起使用
|
||||
SecretKey key = Keys.hmacShaKeyFor(SaSecureUtil.md5(keyt).getBytes());
|
||||
JwtBuilder builder = Jwts.builder()
|
||||
// .setHeaderParam("typ", "JWT")
|
||||
.claim(KEY_VALUE + key, value)
|
||||
.header().add("typ", "JWT").and()
|
||||
.claim(KEY_VALUE, value)
|
||||
.claim(KEY_EFF, eff)
|
||||
.signWith(SignatureAlgorithm.HS256, keyt.getBytes());
|
||||
.signWith(key);
|
||||
|
||||
// 生成jwt-token
|
||||
return builder.compact();
|
||||
@ -81,19 +84,20 @@ public class SaJwtUtil {
|
||||
*/
|
||||
public static Claims parseToken(String jwtToken, String keyt) {
|
||||
// 解析出载荷
|
||||
SecretKey key = Keys.hmacShaKeyFor(SaSecureUtil.md5(keyt).getBytes());
|
||||
return Jwts.parser()
|
||||
.setSigningKey(keyt.getBytes())
|
||||
.parseClaimsJws(jwtToken).getBody();
|
||||
.verifyWith(key)
|
||||
.build()
|
||||
.parseSignedClaims(jwtToken).getPayload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个 jwt-token 解析出载荷, 并取出数据
|
||||
* @param key 存储value使用的key
|
||||
* @param jwtToken JwtToken值
|
||||
* @param keyt 秘钥
|
||||
* @return 值
|
||||
*/
|
||||
public static Object getValue(String key, String jwtToken, String keyt) {
|
||||
public static Object getValue(String jwtToken, String keyt) {
|
||||
// 取出数据
|
||||
Claims claims = parseToken(jwtToken, keyt);
|
||||
|
||||
@ -104,25 +108,19 @@ public class SaJwtUtil {
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
return claims.get(KEY_VALUE + key);
|
||||
return claims.get(KEY_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个 jwt-token 解析出载荷, 并取出其剩余有效期
|
||||
* @param service 指定的服务类型
|
||||
* @param jwtToken JwtToken值
|
||||
* @param keyt 秘钥
|
||||
* @return 值
|
||||
*/
|
||||
public static long getTimeout(String service, String jwtToken, String keyt) {
|
||||
public static long getTimeout(String jwtToken, String keyt) {
|
||||
// 取出数据
|
||||
Claims claims = parseToken(jwtToken, keyt);
|
||||
|
||||
// 如果给定的 service 不对
|
||||
if(claims.get(KEY_VALUE + service) == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
|
||||
// 验证是否超时
|
||||
Long eff = claims.get(KEY_EFF, Long.class);
|
||||
|
||||
@ -131,7 +129,7 @@ public class SaJwtUtil {
|
||||
return NEVER_EXPIRE;
|
||||
}
|
||||
// 已经超时
|
||||
if(eff == null || eff < System.currentTimeMillis()) {
|
||||
if(eff < System.currentTimeMillis()) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
|
||||
|
@ -18,50 +18,61 @@ package cn.dev33.satoken.temp.jwt;
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.exception.ApiDisabledException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.temp.SaTempInterface;
|
||||
import cn.dev33.satoken.temp.SaTempTemplate;
|
||||
import cn.dev33.satoken.temp.jwt.error.SaTempJwtErrorCode;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sa-Token 临时令牌验证模块接口 JWT实现类,提供以 JWT 为逻辑内核的临时 token 验证功能
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.20.0
|
||||
*/
|
||||
public class SaTempForJwt implements SaTempInterface {
|
||||
public class SaTempTemplateForJwt extends SaTempTemplate {
|
||||
|
||||
/**
|
||||
* 根据value创建一个token
|
||||
*/
|
||||
@Override
|
||||
public String createToken(String service, Object value, long timeout) {
|
||||
return SaJwtUtil.createToken(service, value, timeout, getJwtSecretKey());
|
||||
public String createToken(Object value, long timeout, boolean isRecordIndex) {
|
||||
return SaJwtUtil.createToken(value, timeout, getJwtSecretKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析token获取value
|
||||
*/
|
||||
@Override
|
||||
public Object parseToken(String service, String token) {
|
||||
return SaJwtUtil.getValue(service, token, getJwtSecretKey());
|
||||
public Object parseToken(String token) {
|
||||
return SaJwtUtil.getValue(token, getJwtSecretKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回指定token的剩余有效期,单位:秒
|
||||
*/
|
||||
@Override
|
||||
public long getTimeout(String service, String token) {
|
||||
return SaJwtUtil.getTimeout(service, token, getJwtSecretKey());
|
||||
public long getTimeout(String token) {
|
||||
return SaJwtUtil.getTimeout(token, getJwtSecretKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个token
|
||||
*/
|
||||
@Override
|
||||
public void deleteToken(String service, String token) {
|
||||
public void deleteToken(String token) {
|
||||
throw new ApiDisabledException("jwt cannot delete token").setCode(SaTempJwtErrorCode.CODE_30302);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 value 的 temp-token 列表记录
|
||||
* @param value /
|
||||
* @return /
|
||||
*/
|
||||
public List<String> getTempTokenList(Object value) {
|
||||
throw new ApiDisabledException("jwt cannot get token list").setCode(SaTempJwtErrorCode.CODE_30304);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取jwt秘钥
|
||||
* @return jwt秘钥
|
@ -32,4 +32,7 @@ public interface SaTempJwtErrorCode {
|
||||
/** Token已超时 */
|
||||
int CODE_30303 = 30303;
|
||||
|
||||
/** jwt 模式不可以查询旧 Token 列表 */
|
||||
int CODE_30304 = 30304;
|
||||
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.strategy.SaAnnotationStrategy;
|
||||
import cn.dev33.satoken.strategy.SaFirewallStrategy;
|
||||
import cn.dev33.satoken.strategy.hooks.SaFirewallCheckHook;
|
||||
import cn.dev33.satoken.temp.SaTempInterface;
|
||||
import cn.dev33.satoken.temp.SaTempTemplate;
|
||||
import org.noear.solon.annotation.Bean;
|
||||
import org.noear.solon.annotation.Condition;
|
||||
import org.noear.solon.annotation.Configuration;
|
||||
@ -145,12 +145,12 @@ public class SaBeanInject {
|
||||
/**
|
||||
* 注入临时令牌验证模块 Bean
|
||||
*
|
||||
* @param saTemp saTemp对象
|
||||
* @param saTempTemplate /
|
||||
*/
|
||||
@Condition(onBean = SaTempInterface.class)
|
||||
@Condition(onBean = SaTempTemplate.class)
|
||||
@Bean
|
||||
public void setSaTemp(SaTempInterface saTemp) {
|
||||
SaManager.setSaTemp(saTemp);
|
||||
public void setSaTempTemplate(SaTempTemplate saTempTemplate) {
|
||||
SaManager.setSaTempTemplate(saTempTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,7 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.strategy.SaAnnotationStrategy;
|
||||
import cn.dev33.satoken.strategy.SaFirewallStrategy;
|
||||
import cn.dev33.satoken.strategy.hooks.SaFirewallCheckHook;
|
||||
import cn.dev33.satoken.temp.SaTempInterface;
|
||||
import cn.dev33.satoken.temp.SaTempTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.util.PathMatcher;
|
||||
@ -139,11 +139,11 @@ public class SaBeanInject {
|
||||
/**
|
||||
* 注入临时令牌验证模块 Bean
|
||||
*
|
||||
* @param saTemp saTemp对象
|
||||
* @param saTempTemplate /
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaTemp(SaTempInterface saTemp) {
|
||||
SaManager.setSaTemp(saTemp);
|
||||
public void setSaTempTemplate(SaTempTemplate saTempTemplate) {
|
||||
SaManager.setSaTempTemplate(saTempTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,6 +24,7 @@
|
||||
<module>sa-token-springboot-test</module>
|
||||
<!-- <module>sa-token-springboot-integrate-test</module> -->
|
||||
<module>sa-token-jwt-test</module>
|
||||
<module>sa-token-temp-jwt-test</module>
|
||||
<module>sa-token-json-test</module>
|
||||
<module>sa-token-serializer-test</module>
|
||||
</modules>
|
||||
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.core.temp;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.temp.SaTempUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
/**
|
||||
* 临时Token模块测试
|
||||
*
|
||||
* @author click33
|
||||
* @since 2022-9-1
|
||||
*/
|
||||
public class SaTempTest {
|
||||
|
||||
// 测试:临时Token认证模块
|
||||
@Test
|
||||
public void testSaTemp() {
|
||||
SaTokenDao dao = SaManager.getSaTokenDao();
|
||||
|
||||
// 生成token
|
||||
String token = SaTempUtil.createToken("group-1014", 200);
|
||||
Assertions.assertNotNull(token);
|
||||
// System.out.println(((SaTokenDaoDefaultImpl)SaManager.getSaTokenDao()).dataMap);
|
||||
|
||||
// 解析token
|
||||
String value = SaTempUtil.parseToken(token, String.class);
|
||||
Assertions.assertEquals(value, "group-1014");
|
||||
Assertions.assertEquals(dao.getObject("satoken:temp-token:" + SaTokenConsts.DEFAULT_TEMP_TOKEN_SERVICE + ":" + token), "group-1014");
|
||||
|
||||
// 默认类型
|
||||
Object value3 = SaTempUtil.parseToken(token);
|
||||
Assertions.assertEquals(value3, "group-1014");
|
||||
|
||||
// 转换类型
|
||||
String value4 = SaTempUtil.parseToken(token, String.class);
|
||||
Assertions.assertEquals(value4, "group-1014");
|
||||
|
||||
// 过期时间
|
||||
long timeout = SaTempUtil.getTimeout(token);
|
||||
Assertions.assertTrue(timeout > 195);
|
||||
|
||||
// 回收token
|
||||
SaTempUtil.deleteToken(token);
|
||||
String value2 = SaTempUtil.parseToken(token, String.class);
|
||||
Assertions.assertEquals(value2, null);
|
||||
Assertions.assertEquals(dao.getObject("satoken:temp-token:" + SaTokenConsts.DEFAULT_TEMP_TOKEN_SERVICE + ":" + token), null);
|
||||
}
|
||||
|
||||
// 测试:临时Token认证模块,带 Service 参数
|
||||
@Test
|
||||
public void testSaTempService() {
|
||||
SaTokenDao dao = SaManager.getSaTokenDao();
|
||||
|
||||
// 生成token
|
||||
String token = SaTempUtil.createToken("shop", "1001", 200);
|
||||
Assertions.assertNotNull(token);
|
||||
// System.out.println(((SaTokenDaoDefaultImpl)SaManager.getSaTokenDao()).dataMap);
|
||||
|
||||
// 解析token
|
||||
String value = SaTempUtil.parseToken("shop", token, String.class);
|
||||
Assertions.assertEquals(value, "1001");
|
||||
Assertions.assertEquals(dao.getObject("satoken:temp-token:" + "shop" + ":" + token), "1001");
|
||||
|
||||
// 默认类型
|
||||
Object value3 = SaTempUtil.parseToken("shop", token);
|
||||
Assertions.assertEquals(value3, "1001");
|
||||
|
||||
// 转换类型
|
||||
String value4 = SaTempUtil.parseToken("shop", token, String.class);
|
||||
Assertions.assertEquals(value4, "1001");
|
||||
|
||||
// service 参数不对的情况下,无法取出
|
||||
String value5 = SaTempUtil.parseToken("goods", token, String.class);
|
||||
Assertions.assertNull(value5);
|
||||
|
||||
// 过期时间
|
||||
long timeout = SaTempUtil.getTimeout("shop", token);
|
||||
Assertions.assertTrue(timeout > 195);
|
||||
|
||||
// 回收token
|
||||
SaTempUtil.deleteToken("shop", token);
|
||||
String value2 = SaTempUtil.parseToken("shop", token, String.class);
|
||||
Assertions.assertEquals(value2, null);
|
||||
Assertions.assertEquals(dao.getObject("satoken:temp-token:" + "shop" + ":" + token), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaTemp2() {
|
||||
// 秘钥默认为null
|
||||
String jwtSecretKey = SaManager.getSaTemp().getJwtSecretKey();
|
||||
Assertions.assertEquals(jwtSecretKey, null);
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.core.temp;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.temp.SaTempUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 临时Token模块测试
|
||||
*
|
||||
* @author click33
|
||||
* @since 2022-9-1
|
||||
*/
|
||||
public class SaTempTokenTest {
|
||||
|
||||
// 测试:临时Token认证模块
|
||||
@Test
|
||||
public void testSaTemp() {
|
||||
SaTokenDao dao = SaManager.getSaTokenDao();
|
||||
|
||||
// 生成token
|
||||
String token = SaTempUtil.createToken("group-1014", 200);
|
||||
// System.out.println(((SaTokenDaoDefaultImpl)SaManager.getSaTokenDao()).timedCache.dataMap.keySet());
|
||||
// System.out.println("satoken:temp-token:" + ":" + token);
|
||||
Assertions.assertNotNull(token);
|
||||
Assertions.assertEquals(dao.getObject("satoken:temp-token:" + token), "group-1014");
|
||||
|
||||
// 解析token
|
||||
String value = SaTempUtil.parseToken(token, String.class);
|
||||
Assertions.assertEquals(value, "group-1014");
|
||||
|
||||
// 解析 token 并裁剪前缀
|
||||
long value2 = SaTempUtil.parseToken(token, "group-", Long.class);
|
||||
Assertions.assertEquals(value2, 1014);
|
||||
|
||||
// 默认类型
|
||||
Object value3 = SaTempUtil.parseToken(token);
|
||||
Assertions.assertEquals(value3, "group-1014");
|
||||
|
||||
// 转换类型
|
||||
String value4 = SaTempUtil.parseToken(token, String.class);
|
||||
Assertions.assertEquals(value4, "group-1014");
|
||||
|
||||
// 过期时间
|
||||
long timeout = SaTempUtil.getTimeout(token);
|
||||
Assertions.assertTrue(timeout > 195);
|
||||
Assertions.assertTrue(timeout < 201);
|
||||
|
||||
// 回收token
|
||||
SaTempUtil.deleteToken(token);
|
||||
String value5 = SaTempUtil.parseToken(token, String.class);
|
||||
Assertions.assertNull(value5);
|
||||
Assertions.assertNull(dao.getObject("satoken:temp-token:" + ":" + token));
|
||||
}
|
||||
|
||||
// 测试:临时Token认证模块索引
|
||||
@Test
|
||||
public void testSaTempIndex() {
|
||||
SaTokenDao dao = SaManager.getSaTokenDao();
|
||||
|
||||
// 生成token
|
||||
String token1 = SaTempUtil.createToken("1001", 200, true);
|
||||
String token2 = SaTempUtil.createToken("1001", 300, true);
|
||||
String token3 = SaTempUtil.createToken("1001", 400, true);
|
||||
|
||||
Assertions.assertNotNull(token1);
|
||||
Assertions.assertNotNull(token2);
|
||||
Assertions.assertNotNull(token3);
|
||||
// System.out.println(((SaTokenDaoDefaultImpl)SaManager.getSaTokenDao()).dataMap);
|
||||
|
||||
// 解析token
|
||||
Assertions.assertEquals(SaTempUtil.parseToken(token1, String.class), "1001");
|
||||
Assertions.assertEquals(SaTempUtil.parseToken(token2, String.class), "1001");
|
||||
Assertions.assertEquals(SaTempUtil.parseToken(token3, String.class), "1001");
|
||||
|
||||
// 缓存数据比对
|
||||
Assertions.assertEquals(dao.getObject("satoken:temp-token:" + token1), "1001");
|
||||
Assertions.assertEquals(dao.getObject("satoken:temp-token:" + token2), "1001");
|
||||
Assertions.assertEquals(dao.getObject("satoken:temp-token:" + token3), "1001");
|
||||
|
||||
// 索引
|
||||
List<String> tempTokenList = SaTempUtil.getTempTokenList("1001");
|
||||
Assertions.assertEquals(tempTokenList.size(), 3);
|
||||
Assertions.assertTrue(tempTokenList.contains(token1));
|
||||
Assertions.assertTrue(tempTokenList.contains(token2));
|
||||
Assertions.assertTrue(tempTokenList.contains(token3));
|
||||
|
||||
long sessionTimeout = dao.getSessionTimeout("satoken:raw-session:temp-token:" + "1001");
|
||||
Assertions.assertTrue(sessionTimeout > 395);
|
||||
Assertions.assertTrue(sessionTimeout < 401);
|
||||
|
||||
// 移除一个 token
|
||||
SaTempUtil.deleteToken(token3);
|
||||
Assertions.assertNull(SaTempUtil.parseToken(token3, String.class));
|
||||
Assertions.assertNull(dao.getObject("satoken:temp-token:" + token3));
|
||||
|
||||
List<String> tempTokenList2 = SaTempUtil.getTempTokenList("1001");
|
||||
Assertions.assertEquals(tempTokenList2.size(), 2);
|
||||
Assertions.assertFalse(tempTokenList2.contains(token3));
|
||||
|
||||
long sessionTimeout2 = dao.getSessionTimeout("satoken:raw-session:temp-token:" + "1001");
|
||||
Assertions.assertTrue(sessionTimeout2 > 295);
|
||||
Assertions.assertTrue(sessionTimeout2 < 301);
|
||||
|
||||
// 新增一个 token
|
||||
String token4 = SaTempUtil.createToken("1001", -1, true);
|
||||
Assertions.assertEquals(SaTempUtil.parseToken(token4, String.class), "1001");
|
||||
|
||||
List<String> tempTokenList3 = SaTempUtil.getTempTokenList("1001");
|
||||
Assertions.assertEquals(tempTokenList3.size(), 3);
|
||||
Assertions.assertTrue(tempTokenList3.contains(token4));
|
||||
|
||||
long sessionTimeout4 = dao.getSessionTimeout("satoken:raw-session:temp-token:" + "1001");
|
||||
Assertions.assertEquals(-1, sessionTimeout4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetJwtSecretKey() {
|
||||
// 秘钥默认为null
|
||||
String jwtSecretKey = SaManager.getSaTempTemplate().getJwtSecretKey();
|
||||
Assertions.assertNull(jwtSecretKey);
|
||||
}
|
||||
|
||||
}
|
@ -15,11 +15,10 @@
|
||||
*/
|
||||
package cn.dev33.satoken.integrate.configure.inject;
|
||||
|
||||
import cn.dev33.satoken.temp.SaTempTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.temp.SaTempDefaultImpl;
|
||||
|
||||
@Component
|
||||
public class MySaTemp extends SaTempDefaultImpl {
|
||||
public class MySaTempTemplate extends SaTempTemplate {
|
||||
|
||||
}
|
30
sa-token-test/sa-token-temp-jwt-test/pom.xml
Normal file
30
sa-token-test/sa-token-temp-jwt-test/pom.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-test</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sa-token-temp-jwt-test</name>
|
||||
<artifactId>sa-token-temp-jwt-test</artifactId>
|
||||
<description>sa-token-temp-jwt-test</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-temp-jwt</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,105 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.ApiDisabledException;
|
||||
import cn.dev33.satoken.temp.SaTempUtil;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* Sa-Token 整合 temp jwt
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.42.0
|
||||
*/
|
||||
@SpringBootTest(classes = StartUpApplication.class)
|
||||
public class SaTempTemplateForJwtTest {
|
||||
|
||||
// 开始
|
||||
@BeforeAll
|
||||
public static void beforeClass() {
|
||||
System.out.println("\n\n------------------------ SaTempTemplateForJwtTest star ...");
|
||||
}
|
||||
|
||||
// 结束
|
||||
@AfterAll
|
||||
public static void afterClass() {
|
||||
System.out.println("\n\n------------------------ SaTempTemplateForJwtTest end ... \n");
|
||||
}
|
||||
|
||||
// 测试:临时Token认证模块
|
||||
@Test
|
||||
public void testSaTemp() {
|
||||
|
||||
// 生成token
|
||||
String token = SaTempUtil.createToken("group-1014", 200);
|
||||
// System.out.println(((SaTokenDaoDefaultImpl)SaManager.getSaTokenDao()).timedCache.dataMap.keySet());
|
||||
// System.out.println("satoken:temp-token:" + ":" + token);
|
||||
Assertions.assertNotNull(token);
|
||||
|
||||
// 解析token
|
||||
String value = SaTempUtil.parseToken(token, String.class);
|
||||
Assertions.assertEquals(value, "group-1014");
|
||||
|
||||
// 解析 token 并裁剪前缀
|
||||
long value2 = SaTempUtil.parseToken(token, "group-", Long.class);
|
||||
Assertions.assertEquals(value2, 1014);
|
||||
|
||||
// 默认类型
|
||||
Object value3 = SaTempUtil.parseToken(token);
|
||||
Assertions.assertEquals(value3, "group-1014");
|
||||
|
||||
// 转换类型
|
||||
String value4 = SaTempUtil.parseToken(token, String.class);
|
||||
Assertions.assertEquals(value4, "group-1014");
|
||||
|
||||
// 过期时间
|
||||
long timeout = SaTempUtil.getTimeout(token);
|
||||
Assertions.assertTrue(timeout > 195);
|
||||
Assertions.assertTrue(timeout < 201);
|
||||
|
||||
// 回收token
|
||||
Assertions.assertThrows(ApiDisabledException.class, () -> SaTempUtil.deleteToken(token) );
|
||||
}
|
||||
|
||||
// 测试:临时Token认证模块索引
|
||||
@Test
|
||||
public void testSaTempIndex() {
|
||||
SaTokenDao dao = SaManager.getSaTokenDao();
|
||||
|
||||
// 生成token
|
||||
String token1 = SaTempUtil.createToken("1001", 200, true);
|
||||
String token2 = SaTempUtil.createToken("1001", 300, true);
|
||||
String token3 = SaTempUtil.createToken("1001", 400, true);
|
||||
|
||||
Assertions.assertNotNull(token1);
|
||||
Assertions.assertNotNull(token2);
|
||||
Assertions.assertNotNull(token3);
|
||||
// System.out.println(((SaTokenDaoDefaultImpl)SaManager.getSaTokenDao()).dataMap);
|
||||
|
||||
// 解析token
|
||||
Assertions.assertEquals(SaTempUtil.parseToken(token1, String.class), "1001");
|
||||
Assertions.assertEquals(SaTempUtil.parseToken(token2, String.class), "1001");
|
||||
Assertions.assertEquals(SaTempUtil.parseToken(token3, String.class), "1001");
|
||||
|
||||
// 缓存数据比对
|
||||
Assertions.assertNull(dao.getObject("satoken:temp-token:" + token1));
|
||||
Assertions.assertNull(dao.getObject("satoken:temp-token:" + token2));
|
||||
Assertions.assertNull(dao.getObject("satoken:temp-token:" + token3));
|
||||
|
||||
// 索引
|
||||
Assertions.assertThrows(ApiDisabledException.class, () -> SaTempUtil.getTempTokenList("1001") );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetJwtSecretKey() {
|
||||
// 秘钥默认为null
|
||||
String jwtSecretKey = SaManager.getSaTempTemplate().getJwtSecretKey();
|
||||
Assertions.assertNotNull(jwtSecretKey);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 启动类
|
||||
* @author Auster
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class StartUpApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(StartUpApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
# sa-token 配置
|
||||
sa-token:
|
||||
# token 名称 (同时也是 cookie 名称)
|
||||
token-name: satoken
|
||||
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||
timeout: 2592000
|
||||
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||
active-timeout: -1
|
||||
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||
is-share: false
|
||||
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||
token-style: uuid
|
||||
# jwt秘钥
|
||||
jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk
|
||||
|
||||
spring:
|
||||
# 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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user