重构 sa-token-jwt 模块

This commit is contained in:
click33 2022-05-01 04:46:28 +08:00
parent d45d404fa0
commit a7ef71737e
16 changed files with 513 additions and 378 deletions

View File

@ -318,7 +318,10 @@ public class StpLogic {
if(config.getIsConcurrent()) {
// 如果配置为共享token, 则尝试从Session签名记录里取出token
if(getConfigOfIsShare()) {
tokenValue = getTokenValueByLoginId(id, loginModel.getDeviceOrDefault());
// 为确保 jwt-simple 模式的 token Extra 数据生成不受旧token影响这里必须确保 is-share 配置项在 ExtraData 为空时才可以生效
if(loginModel.getExtraData() == null || loginModel.getExtraData().size() == 0) {
tokenValue = getTokenValueByLoginId(id, loginModel.getDeviceOrDefault());
}
} else {
//
}

View File

@ -6,7 +6,7 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
import cn.dev33.satoken.jwt.StpLogicJwtForStyle;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.stp.StpLogic;
@ -32,7 +32,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
*/
@Bean
public StpLogic getStpLogicJwt() {
return new StpLogicJwtForStyle();
return new StpLogicJwtForSimple();
}
}

View File

@ -2,6 +2,10 @@
server:
port: 9001
forest:
# 打开/关闭Forest请求日志默认为 true
log-request: true
spring:
# Redis连接
redis:

View File

@ -32,38 +32,38 @@ sa-token:
<!------------------------------ tabs:start ------------------------------>
<!-- tab: Style 模式 -->
Style 模式Token 风格替换
<!-- tab: Simple 简单模式 -->
Simple 模式Token 风格替换
``` java
@Configuration
public class SaTokenConfigure {
// Sa-Token 整合 jwt (Style模式)
// Sa-Token 整合 jwt (Simple 简单模式)
@Bean
public StpLogic getStpLogicJwt() {
return new StpLogicJwtForStyle();
return new StpLogicJwtForSimple();
}
}
```
<!-- tab: Mix 模式 -->
Mix 模式:混入部分逻辑
<!-- tab: Mixin 混入模式 -->
Mixin 模式:混入部分逻辑
``` java
@Configuration
public class SaTokenConfigure {
// Sa-Token 整合 jwt (Mix模式)
// Sa-Token 整合 jwt (Mixin 混入模式)
@Bean
public StpLogic getStpLogicJwt() {
return new StpLogicJwtForMix();
return new StpLogicJwtForMixin();
}
}
```
<!-- tab: Stateless模式 -->
<!-- tab: Stateless 无状态模式 -->
Stateless 模式:服务器完全无状态
``` java
@Configuration
public class SaTokenConfigure {
// Sa-Token 整合 jwt (Stateless模式)
// Sa-Token 整合 jwt (Stateless 无状态模式)
@Bean
public StpLogic getStpLogicJwt() {
return new StpLogicJwtForStateless();
@ -134,7 +134,7 @@ String name = StpUtil.getExtra("name");
注入不同模式会让框架具有不同的行为策略,以下是三种模式的差异点(为方便叙述,以下比较以同时引入 jwt 与 Redis 作为前提):
| 功能点 | Style 模式 | Mix 模式 | Stateless 模式 |
| 功能点 | Simple 简单模式 | Mixin 混入模式 | Stateless 无状态模式 |
| :-------- | :-------- | :-------- | :-------- |
| Token风格 | jwt风格 | jwt风格 | jwt风格 |
| 登录数据存储 | Redis中 | Token中 | Token中 |

View File

@ -102,6 +102,7 @@ implementation 'cn.dev33:sa-token-core:${sa.top.version}'
├── sa-token-temp-jwt // [插件] Sa-Token 整合 jwt 临时令牌鉴权
├── sa-token-quick-login // [插件] Sa-Token 快速注入登录页插件
├── sa-token-alone-redis // [插件] Sa-Token 独立Redis插件实现[权限缓存与业务缓存分离]
├── sa-token-sso // [插件] Sa-Token 整合 SSO 单点登录
├── sa-token-oauth2 // [插件] Sa-Token 实现 OAuth2.0 模块
├── sa-token-dialect-thymeleaf // [插件] Sa-Token 标签方言Thymeleaf版
├── sa-token-jwt // [插件] Sa-Token 整合 jwt 登录认证

View File

@ -1,16 +1,18 @@
package cn.dev33.satoken.jwt;
import java.util.Map;
import java.util.Objects;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.jwt.exception.SaJwtException;
import cn.dev33.satoken.jwt.exception.SaJwtExceptionCode;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTException;
/**
* jwt操作工具类封装
* jwt 操作工具类封装
* @author kong
*
*/
@ -36,27 +38,38 @@ public class SaJwtUtil {
*/
public static final String EFF = "eff";
/**
* key乱数 混入随机字符串防止每次生成的 token 都是一样的
*/
public static final String RN_STR = "rnStr";
/**
* 当有效期被设为此值时代表永不过期
*/
public static final long NEVER_EXPIRE = SaTokenDao.NEVER_EXPIRE;
/**
* 表示一个值不存在
*/
public static final long NOT_VALUE_EXPIRE = SaTokenDao.NOT_VALUE_EXPIRE;
// ------ 创建
/**
* 创建 jwt 简单方式
* @param loginType 登录类型
* @param loginId 账号id
* @param extraData 扩展数据
* @param keyt 秘钥
* @return jwt-token
*/
public static String createToken(Object loginId, Map<String, Object> extraData, String keyt) {
public static String createToken(String loginType, Object loginId, Map<String, Object> extraData, String keyt) {
// 构建
String token = JWT.create()
.setPayload(LOGIN_TYPE, loginType)
.setPayload(LOGIN_ID, loginId)
// 混入随机字符串防止每次生成的 token 都是一样的
.setPayload("rn", SaFoxUtil.getRandomString(32))
.setPayload(RN_STR, SaFoxUtil.getRandomString(32))
.addPayloads(extraData)
.setKey(keyt.getBytes())
.sign();
@ -90,6 +103,7 @@ public class SaJwtUtil {
.setPayload(LOGIN_ID, loginId)
.setPayload(DEVICE, device)
.setPayload(EFF, effTime)
.setPayload(RN_STR, SaFoxUtil.getRandomString(32))
.addPayloads(extraData);
// 返回
@ -99,16 +113,18 @@ public class SaJwtUtil {
// ------ 解析
/**
* jwt 解析校验签名和有效期
* jwt 解析
* @param token Jwt-Token值
* @param loginType 登录类型
* @param keyt 秘钥
* @param isCheckTimeout 是否校验 timeout 字段
* @return 解析后的jwt 对象
*/
public static JWT parseToken(String token, String keyt) {
public static JWT parseToken(String token, String loginType, String keyt, boolean isCheckTimeout) {
// 如果token为null
if(token == null) {
throw NotLoginException.newInstance(null, NotLoginException.NOT_TOKEN);
throw new SaJwtException("jwt 字符串不可为空");
}
// 解析
@ -116,23 +132,29 @@ public class SaJwtUtil {
try {
jwt = JWT.of(token);
} catch (JWTException e) {
// 解析失败
throw NotLoginException.newInstance(null, NotLoginException.INVALID_TOKEN, token);
throw new SaJwtException("jwt 解析失败:" + token, e).setCode(SaJwtExceptionCode.CODE_40101);
}
JSONObject payloads = jwt.getPayloads();
// 校验 Token 签名
boolean verify = jwt.setKey(keyt.getBytes()).verify();
if(verify == false) {
throw NotLoginException.newInstance(payloads.getStr(LOGIN_TYPE), NotLoginException.INVALID_TOKEN, token);
throw new SaJwtException("jwt 签名无效:" + token).setCode(SaJwtExceptionCode.CODE_40102);
};
// 校验 loginType
if(Objects.equals(loginType, payloads.getStr(LOGIN_TYPE)) == false) {
throw new SaJwtException("jwt loginType 无效:" + token).setCode(SaJwtExceptionCode.CODE_40103);
}
// 校验 Token 有效期
Long effTime = payloads.getLong(EFF, 0L);
if(effTime != NEVER_EXPIRE) {
if(effTime == null || effTime < System.currentTimeMillis()) {
throw NotLoginException.newInstance(payloads.getStr(LOGIN_TYPE), NotLoginException.TOKEN_TIMEOUT, token);
}
if(isCheckTimeout) {
Long effTime = payloads.getLong(EFF, 0L);
if(effTime != NEVER_EXPIRE) {
if(effTime == null || effTime < System.currentTimeMillis()) {
throw new SaJwtException("jwt 已过期:" + token).setCode(SaJwtExceptionCode.CODE_40104);
}
}
}
// 返回
@ -140,58 +162,49 @@ public class SaJwtUtil {
}
/**
* 获取 jwt 数据载荷 校验签名和有效期
* 获取 jwt 数据载荷 校验 signloginTypetimeout
* @param token token值
* @param loginType 登录类型
* @param keyt 秘钥
* @return 载荷
*/
public static JSONObject getPayloads(String token, String keyt) {
return parseToken(token, keyt).getPayloads();
public static JSONObject getPayloads(String token, String loginType, String keyt) {
return parseToken(token, loginType, keyt, true).getPayloads();
}
/**
* 获取 jwt 数据载荷 只校验签名不校验有效期
* 获取 jwt 数据载荷 校验 signloginType不校验 timeout
* @param token token值
* @param loginType 登录类型
* @param keyt 秘钥
* @return 载荷
*/
public static JSONObject getPayloadsNotCheck(String token, String keyt) {
try {
JWT jwt = JWT.of(token);
JSONObject payloads = jwt.getPayloads();
// 校验 Token 签名
boolean verify = jwt.setKey(keyt.getBytes()).verify();
if(verify == false) {
throw NotLoginException.newInstance(payloads.getStr(LOGIN_TYPE), NotLoginException.INVALID_TOKEN, token);
};
return payloads;
} catch (JWTException e) {
return new JSONObject();
}
public static JSONObject getPayloadsNotCheck(String token, String loginType, String keyt) {
return parseToken(token, loginType, keyt, false).getPayloads();
}
/**
* 获取 jwt 代表的账号id
* @param token Token值
* @param loginType 登录类型
* @param keyt 秘钥
* @return
*/
public static Object getLoginId(String token, String keyt) {
return getPayloads(token, keyt).get(LOGIN_ID);
public static Object getLoginId(String token, String loginType, String keyt) {
return getPayloads(token, loginType, keyt).get(LOGIN_ID);
}
/**
* 获取 jwt 代表的账号id (未登录时返回null)
* @param token Token值
* @param loginType 登录类型
* @param keyt 秘钥
* @return
*/
public static Object getLoginIdOrNull(String token, String keyt) {
public static Object getLoginIdOrNull(String token, String loginType, String keyt) {
try {
return getPayloads(token, keyt).get(LOGIN_ID);
} catch (NotLoginException e) {
return getPayloads(token, loginType, keyt).get(LOGIN_ID);
} catch (SaJwtException e) {
return null;
}
}
@ -199,14 +212,15 @@ public class SaJwtUtil {
/**
* 获取 jwt 剩余有效期
* @param token JwtToken值
* @param loginType 登录类型
* @param keyt 秘钥
* @return
*/
public static long getTimeout(String token, String keyt) {
public static long getTimeout(String token, String loginType, String keyt) {
// 如果token为null
if(token == null) {
return SaTokenDao.NOT_VALUE_EXPIRE;
return NOT_VALUE_EXPIRE;
}
// 取出数据
@ -215,15 +229,20 @@ public class SaJwtUtil {
jwt = JWT.of(token);
} catch (JWTException e) {
// 解析失败
return SaTokenDao.NOT_VALUE_EXPIRE;
return NOT_VALUE_EXPIRE;
}
JSONObject payloads = jwt.getPayloads();
// 如果签名无效
boolean verify = jwt.setKey(keyt.getBytes()).verify();
if(verify == false) {
return SaTokenDao.NOT_VALUE_EXPIRE;
return NOT_VALUE_EXPIRE;
};
// 如果 loginType 无效
if(Objects.equals(loginType, payloads.getStr(LOGIN_TYPE)) == false) {
return NOT_VALUE_EXPIRE;
}
// 如果被设置为永不过期
Long effTime = payloads.get(EFF, Long.class);
@ -232,38 +251,11 @@ public class SaJwtUtil {
}
// 如果已经超时
if(effTime == null || effTime < System.currentTimeMillis()) {
return SaTokenDao.NOT_VALUE_EXPIRE;
return NOT_VALUE_EXPIRE;
}
// 计算timeout (转化为以秒为单位的有效时间)
return (effTime - System.currentTimeMillis()) / 1000;
}
// ------ 废弃API
/**
* 创建 jwt 简单方式
* @param loginId 账号id
* @param keyt 秘钥
* @return jwt-token
*/
@Deprecated
public static String createToken(Object loginId, String keyt) {
return createToken(loginId, null, keyt);
}
/**
* 创建 jwt 全参数方式
* @param loginType 账号类型
* @param loginId 账号id
* @param device 设备类型
* @param timeout token有效期 (单位 )
* @param keyt 秘钥
* @return jwt-token
*/
@Deprecated
public static String createToken(String loginType, Object loginId, String device, long timeout, String keyt) {
return createToken(loginType, loginId, device, timeout, null, keyt);
}
}

View File

@ -1,221 +1,12 @@
package cn.dev33.satoken.jwt;
import java.util.List;
import java.util.Map;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
/**
* Sa-Token 整合 jwt -- Mix 混入
* 已更名为 StpLogicJwtForMixin请更换
*
* @author kong
*
* @date: 2022-5-1
*/
public class StpLogicJwtForMix extends StpLogic {
@Deprecated
public class StpLogicJwtForMix extends StpLogicJwtForMixin {
/**
* Sa-Token 整合 jwt -- Mix 混入
*/
public StpLogicJwtForMix() {
super(StpUtil.TYPE);
}
/**
* Sa-Token 整合 jwt -- Mix 混入
* @param loginType 账号体系标识
*/
public StpLogicJwtForMix(String loginType) {
super(loginType);
}
/**
* 获取jwt秘钥
* @return /
*/
public String jwtSecretKey() {
String keyt = getConfig().getJwtSecretKey();
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
return keyt;
}
//
// ------ 重写方法
//
// ------------------- 获取token 相关 -------------------
/**
* 创建一个TokenValue
*/
@Override
public String createTokenValue(Object loginId, String device, long timeout, Map<String, Object> extraData) {
return SaJwtUtil.createToken(loginType, loginId, device, timeout, extraData, jwtSecretKey());
}
/**
* 获取当前会话的Token信息
* @return token信息
*/
@Override
public SaTokenInfo getTokenInfo() {
SaTokenInfo info = new SaTokenInfo();
info.tokenName = getTokenName();
info.tokenValue = getTokenValue();
info.isLogin = isLogin();
info.loginId = getLoginIdDefaultNull();
info.loginType = getLoginType();
info.tokenTimeout = getTokenTimeout();
info.sessionTimeout = SaTokenDao.NOT_VALUE_EXPIRE;
info.tokenSessionTimeout = SaTokenDao.NOT_VALUE_EXPIRE;
info.tokenActivityTimeout = SaTokenDao.NOT_VALUE_EXPIRE;
info.loginDevice = getLoginDevice();
return info;
}
// ------------------- 登录相关操作 -------------------
/**
* 获取指定Token对应的账号id (不做任何特殊处理)
*/
@Override
public String getLoginIdNotHandle(String tokenValue) {
// 先验证 loginType如果不符则视为无效token返回null
String loginType = SaJwtUtil.getPayloadsNotCheck(tokenValue, jwtSecretKey()).getStr(SaJwtUtil.LOGIN_TYPE);
if(getLoginType().equals(loginType) == false) {
return null;
}
// 获取 loginId
try {
Object loginId = SaJwtUtil.getLoginId(tokenValue, jwtSecretKey());
return String.valueOf(loginId);
} catch (NotLoginException e) {
return null;
}
}
/**
* 会话注销
*/
@Override
public void logout() {
// ...
// 从当前 [storage存储器] 里删除
SaHolder.getStorage().delete(splicingKeyJustCreatedSave());
// 如果打开了Cookie模式则把cookie清除掉
if(getConfig().getIsReadCookie()){
SaHolder.getResponse().deleteCookie(getTokenName());
}
}
/**
* [禁用] 会话注销根据账号id 设备类型
*/
@Override
public void logout(Object loginId, String device) {
throw new ApiDisabledException();
}
/**
* [禁用] 会话注销根据指定 Token
*/
@Override
public void logoutByTokenValue(String tokenValue) {
throw new ApiDisabledException();
}
/**
* [禁用] 踢人下线根据账号id 设备类型
*/
@Override
public void kickout(Object loginId, String device) {
throw new ApiDisabledException();
}
/**
* [禁用] 踢人下线根据指定 Token
*/
@Override
public void kickoutByTokenValue(String tokenValue) {
throw new ApiDisabledException();
}
/**
* [禁用] 顶人下线根据账号id 设备类型
*/
@Override
public void replaced(Object loginId, String device) {
throw new ApiDisabledException();
}
/**
* 获取Token携带的扩展信息
*/
@Override
public Object getExtra(String key) {
return SaJwtUtil.getPayloads(getTokenValue(), jwtSecretKey()).get(key);
}
/**
* 删除 Token-Id 映射
*/
@Override
public void deleteTokenToIdMapping(String tokenValue) {
// not action
}
/**
* 更改 Token 指向的 账号Id
*/
@Override
public void updateTokenToIdMapping(String tokenValue, Object loginId) {
// not action
}
/**
* 存储 Token-Id 映射
*/
@Override
public void saveTokenToIdMapping(String tokenValue, Object loginId, long timeout) {
// not action
}
// ------------------- 过期时间相关 -------------------
/**
* 获取当前登录者的 token 剩余有效时间 (单位: )
*/
@Override
public long getTokenTimeout() {
return SaJwtUtil.getTimeout(getTokenValue(), jwtSecretKey());
}
// ------------------- 会话管理 -------------------
/**
* [禁用] 根据条件查询Token
*/
@Override
public List<String> searchTokenValue(String keyword, int start, int size) {
throw new ApiDisabledException();
}
// ------------------- Bean对象代理 -------------------
/**
* 返回全局配置对象的isShare属性
* @return /
*/
@Override
public boolean getConfigOfIsShare() {
return false;
}
}

View File

@ -0,0 +1,215 @@
package cn.dev33.satoken.jwt;
import java.util.List;
import java.util.Map;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.jwt.exception.SaJwtException;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
/**
* Sa-Token 整合 jwt -- Mixin 混入模式
* @author kong
*
*/
public class StpLogicJwtForMixin extends StpLogic {
/**
* Sa-Token 整合 jwt -- Mixin 混入
*/
public StpLogicJwtForMixin() {
super(StpUtil.TYPE);
}
/**
* Sa-Token 整合 jwt -- Mixin 混入
* @param loginType 账号体系标识
*/
public StpLogicJwtForMixin(String loginType) {
super(loginType);
}
/**
* 获取jwt秘钥
* @return /
*/
public String jwtSecretKey() {
String keyt = getConfig().getJwtSecretKey();
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
return keyt;
}
//
// ------ 重写方法
//
// ------------------- 获取token 相关 -------------------
/**
* 创建一个TokenValue
*/
@Override
public String createTokenValue(Object loginId, String device, long timeout, Map<String, Object> extraData) {
return SaJwtUtil.createToken(loginType, loginId, device, timeout, extraData, jwtSecretKey());
}
/**
* 获取当前会话的Token信息
* @return token信息
*/
@Override
public SaTokenInfo getTokenInfo() {
SaTokenInfo info = new SaTokenInfo();
info.tokenName = getTokenName();
info.tokenValue = getTokenValue();
info.isLogin = isLogin();
info.loginId = getLoginIdDefaultNull();
info.loginType = getLoginType();
info.tokenTimeout = getTokenTimeout();
info.sessionTimeout = SaTokenDao.NOT_VALUE_EXPIRE;
info.tokenSessionTimeout = SaTokenDao.NOT_VALUE_EXPIRE;
info.tokenActivityTimeout = SaTokenDao.NOT_VALUE_EXPIRE;
info.loginDevice = getLoginDevice();
return info;
}
// ------------------- 登录相关操作 -------------------
/**
* 获取指定Token对应的账号id (不做任何特殊处理)
*/
@Override
public String getLoginIdNotHandle(String tokenValue) {
try {
Object loginId = SaJwtUtil.getLoginId(tokenValue, loginType, jwtSecretKey());
return String.valueOf(loginId);
} catch (SaJwtException e) {
return null;
}
}
/**
* 会话注销
*/
@Override
public void logout() {
// ...
// 从当前 [storage存储器] 里删除
SaHolder.getStorage().delete(splicingKeyJustCreatedSave());
// 如果打开了Cookie模式则把cookie清除掉
if(getConfig().getIsReadCookie()){
SaHolder.getResponse().deleteCookie(getTokenName());
}
}
/**
* [禁用] 会话注销根据账号id 设备类型
*/
@Override
public void logout(Object loginId, String device) {
throw new ApiDisabledException();
}
/**
* [禁用] 会话注销根据指定 Token
*/
@Override
public void logoutByTokenValue(String tokenValue) {
throw new ApiDisabledException();
}
/**
* [禁用] 踢人下线根据账号id 设备类型
*/
@Override
public void kickout(Object loginId, String device) {
throw new ApiDisabledException();
}
/**
* [禁用] 踢人下线根据指定 Token
*/
@Override
public void kickoutByTokenValue(String tokenValue) {
throw new ApiDisabledException();
}
/**
* [禁用] 顶人下线根据账号id 设备类型
*/
@Override
public void replaced(Object loginId, String device) {
throw new ApiDisabledException();
}
/**
* 获取Token携带的扩展信息
*/
@Override
public Object getExtra(String key) {
return SaJwtUtil.getPayloads(getTokenValue(), loginType, jwtSecretKey()).get(key);
}
/**
* 删除 Token-Id 映射
*/
@Override
public void deleteTokenToIdMapping(String tokenValue) {
// not action
}
/**
* 更改 Token 指向的 账号Id
*/
@Override
public void updateTokenToIdMapping(String tokenValue, Object loginId) {
// not action
}
/**
* 存储 Token-Id 映射
*/
@Override
public void saveTokenToIdMapping(String tokenValue, Object loginId, long timeout) {
// not action
}
// ------------------- 过期时间相关 -------------------
/**
* 获取当前登录者的 token 剩余有效时间 (单位: )
*/
@Override
public long getTokenTimeout() {
return SaJwtUtil.getTimeout(getTokenValue(), loginType, jwtSecretKey());
}
// ------------------- 会话管理 -------------------
/**
* [禁用] 根据条件查询Token
*/
@Override
public List<String> searchTokenValue(String keyword, int start, int size) {
throw new ApiDisabledException();
}
// ------------------- Bean对象代理 -------------------
/**
* 返回全局配置对象的isShare属性
* @return /
*/
@Override
public boolean getConfigOfIsShare() {
return false;
}
}

View File

@ -0,0 +1,59 @@
package cn.dev33.satoken.jwt;
import java.util.Map;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
/**
* Sa-Token 整合 jwt -- Simple 简单模式
* @author kong
*
*/
public class StpLogicJwtForSimple extends StpLogic {
/**
* Sa-Token 整合 jwt -- Simple模式
*/
public StpLogicJwtForSimple() {
super(StpUtil.TYPE);
}
/**
* Sa-Token 整合 jwt -- Simple模式
* @param loginType 账号体系标识
*/
public StpLogicJwtForSimple(String loginType) {
super(loginType);
}
/**
* 获取jwt秘钥
* @return /
*/
public String jwtSecretKey() {
String keyt = getConfig().getJwtSecretKey();
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
return keyt;
}
// ------ 重写方法
/**
* 创建一个TokenValue
*/
@Override
public String createTokenValue(Object loginId, String device, long timeout, Map<String, Object> extraData) {
return SaJwtUtil.createToken(loginType, loginId, extraData, jwtSecretKey());
}
/**
* 获取Token携带的扩展信息
*/
@Override
public Object getExtra(String key) {
return SaJwtUtil.getPayloadsNotCheck(getTokenValue(), loginType, jwtSecretKey()).get(key);
}
}

View File

@ -1,34 +1,35 @@
package cn.dev33.satoken.jwt;
import java.util.Map;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.jwt.exception.SaJwtException;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import java.util.Map;
import cn.dev33.satoken.util.SaFoxUtil;
/**
* Sa-Token 整合 jwt -- stateless 无状态
* Sa-Token 整合 jwt -- Stateless 无状态模式
* @author kong
*
*/
public class StpLogicJwtForStateless extends StpLogic {
/**
* Sa-Token 整合 jwt -- stateless 无状态
* Sa-Token 整合 jwt -- Stateless 无状态
*/
public StpLogicJwtForStateless() {
super(StpUtil.TYPE);
}
/**
* Sa-Token 整合 jwt -- stateless 无状态
* Sa-Token 整合 jwt -- Stateless 无状态
* @param loginType 账号体系标识
*/
public StpLogicJwtForStateless(String loginType) {
@ -108,16 +109,10 @@ public class StpLogicJwtForStateless extends StpLogic {
*/
@Override
public String getLoginIdNotHandle(String tokenValue) {
// 先验证 loginType如果不符则视为无效token返回null
String loginType = SaJwtUtil.getPayloadsNotCheck(tokenValue, jwtSecretKey()).getStr(SaJwtUtil.LOGIN_TYPE);
if(getLoginType().equals(loginType) == false) {
return null;
}
// 获取 loginId
try {
Object loginId = SaJwtUtil.getLoginId(tokenValue, jwtSecretKey());
Object loginId = SaJwtUtil.getLoginId(tokenValue, loginType, jwtSecretKey());
return String.valueOf(loginId);
} catch (NotLoginException e) {
} catch (SaJwtException e) {
return null;
}
}
@ -127,7 +122,11 @@ public class StpLogicJwtForStateless extends StpLogic {
*/
@Override
public void logout() {
// ...
// 如果连token都没有那么无需执行任何操作
String tokenValue = getTokenValue();
if(SaFoxUtil.isEmpty(tokenValue)) {
return;
}
// 从当前 [storage存储器] 里删除
SaHolder.getStorage().delete(splicingKeyJustCreatedSave());
@ -143,7 +142,7 @@ public class StpLogicJwtForStateless extends StpLogic {
*/
@Override
public Object getExtra(String key) {
return SaJwtUtil.getPayloads(getTokenValue(), jwtSecretKey()).get(key);
return SaJwtUtil.getPayloads(getTokenValue(), loginType, jwtSecretKey()).get(key);
}
@ -154,7 +153,7 @@ public class StpLogicJwtForStateless extends StpLogic {
*/
@Override
public long getTokenTimeout() {
return SaJwtUtil.getTimeout(getTokenValue(), jwtSecretKey());
return SaJwtUtil.getTimeout(getTokenValue(), loginType, jwtSecretKey());
}
@ -176,7 +175,7 @@ public class StpLogicJwtForStateless extends StpLogic {
return null;
}
// 获取
return SaJwtUtil.getPayloadsNotCheck(tokenValue, jwtSecretKey()).getStr(SaJwtUtil.DEVICE);
return SaJwtUtil.getPayloadsNotCheck(tokenValue, loginType, jwtSecretKey()).getStr(SaJwtUtil.DEVICE);
}

View File

@ -1,59 +1,12 @@
package cn.dev33.satoken.jwt;
import java.util.Map;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
/**
* Sa-Token 整合 jwt -- Style模式
* 已更名为 StpLogicJwtForSimple请更换
*
* @author kong
*
* @date: 2022-5-1
*/
public class StpLogicJwtForStyle extends StpLogic {
/**
* Sa-Token 整合 jwt -- Style模式
*/
public StpLogicJwtForStyle() {
super(StpUtil.TYPE);
}
/**
* Sa-Token 整合 jwt -- Style模式
* @param loginType 账号体系标识
*/
public StpLogicJwtForStyle(String loginType) {
super(loginType);
}
/**
* 获取jwt秘钥
* @return /
*/
public String jwtSecretKey() {
String keyt = getConfig().getJwtSecretKey();
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
return keyt;
}
// ------ 重写方法
/**
* 创建一个TokenValue
*/
@Override
public String createTokenValue(Object loginId, String device, long timeout, Map<String, Object> extraData) {
return SaJwtUtil.createToken(loginId, extraData, jwtSecretKey());
}
/**
* 获取Token携带的扩展信息
*/
@Override
public Object getExtra(String key) {
return SaJwtUtil.getPayloadsNotCheck(getTokenValue(), jwtSecretKey()).get(key);
}
@Deprecated
public class StpLogicJwtForStyle extends StpLogicJwtForSimple {
}

View File

@ -0,0 +1,56 @@
package cn.dev33.satoken.jwt.exception;
import cn.dev33.satoken.exception.SaTokenException;
/**
* 一个异常代表 jwt 解析错误
*
* @author kong
*/
public class SaJwtException extends SaTokenException {
/**
* 序列化版本号
*/
private static final long serialVersionUID = 6806129555290130114L;
/**
* jwt 解析错误
* @param message 异常描述
*/
public SaJwtException(String message) {
super(message);
}
/**
* jwt 解析错误
* @param message 异常描述
* @param cause 异常对象
*/
public SaJwtException(String message, Throwable cause) {
super(message, cause);
}
/**
* 写入异常细分状态码
* @param code 异常细分状态码
* @return 对象自身
*/
public SaJwtException setCode(int code) {
super.setCode(code);
return this;
}
/**
* 如果flag==true则抛出message异常
* @param flag 标记
* @param message 异常信息
*/
public static void throwBy(boolean flag, String message) {
if(flag) {
throw new SaJwtException(message);
}
}
}

View File

@ -0,0 +1,23 @@
package cn.dev33.satoken.jwt.exception;
/**
* 定义所有 JWT 异常细分状态码
*
* @author kong
* @date: 2022-5-1
*/
public class SaJwtExceptionCode {
/** 对 jwt 字符串解析失败 */
public static final int CODE_40101 = 40101;
/** 此 jwt 的签名无效 */
public static final int CODE_40102 = 40102;
/** 此 jwt 的 loginType 字段不符合预期 */
public static final int CODE_40103 = 40103;
/** 此 jwt 已超时 */
public static final int CODE_40104 = 40104;
}

View File

@ -14,21 +14,22 @@ import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.exception.DisableLoginException;
import cn.dev33.satoken.jwt.SaJwtUtil;
import cn.dev33.satoken.jwt.StpLogicJwtForMix;
import cn.dev33.satoken.jwt.StpLogicJwtForMixin;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginConfig;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaTokenConsts;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
/**
* Sa-Token 整合 jwtmix 模式 测试
* Sa-Token 整合 jwtmixin 模式 测试
*
* @author kong
*
*/
@SpringBootTest(classes = StartUpApplication.class)
public class JwtForMixTest {
public class JwtForMixinTest {
// 持久化Bean
@Autowired(required = false)
@ -38,7 +39,7 @@ public class JwtForMixTest {
@BeforeAll
public static void beforeClass() {
System.out.println("\n\n------------------------ JwtForMixTest star ...");
StpUtil.setStpLogic(new StpLogicJwtForMix());
StpUtil.setStpLogic(new StpLogicJwtForMixin());
}
// 结束
@ -254,5 +255,17 @@ public class JwtForMixTest {
Assertions.assertTrue(list.size() >= 5);
});
}
// 测试getExtra
@Test
public void getExtra() {
// 登录
StpUtil.login(10001, SaLoginConfig.setExtra("name", "zhangsan"));
// 可以取到
Assertions.assertEquals(StpUtil.getExtra("name"), "zhangsan");
// 取不到
Assertions.assertEquals(StpUtil.getExtra("name2"), null);
}
}

View File

@ -8,22 +8,23 @@ import org.springframework.boot.test.context.SpringBootTest;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.jwt.StpLogicJwtForStyle;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginConfig;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaTokenConsts;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
/**
* Sa-Token 整合 jwtStyle 模式 测试
* Sa-Token 整合 jwtSimple 模式 测试
*
* @author kong
*
*/
//@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartUpApplication.class)
public class JwtForStyleTest {
public class JwtForSimpleTest {
// 持久化Bean
static SaTokenDao dao;
@ -33,7 +34,7 @@ public class JwtForStyleTest {
public static void beforeClass() {
System.out.println("\n\n------------------------ JwtForStyleTest star ...");
dao = SaManager.getSaTokenDao();
StpUtil.setStpLogic(new StpLogicJwtForStyle());
StpUtil.setStpLogic(new StpLogicJwtForSimple());
}
// 结束
@ -69,5 +70,17 @@ public class JwtForStyleTest {
Assertions.assertEquals(session.getId(), "satoken:login:session:" + 10001);
Assertions.assertTrue(session.getTokenSignList().size() >= 1);
}
// 测试getExtra
@Test
public void getExtra() {
// 登录
StpUtil.login(10001, SaLoginConfig.setExtra("name", "zhangsan"));
// 可以取到
Assertions.assertEquals(StpUtil.getExtra("name"), "zhangsan");
// 取不到
Assertions.assertEquals(StpUtil.getExtra("name2"), null);
}
}

View File

@ -12,6 +12,7 @@ import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.jwt.SaJwtUtil;
import cn.dev33.satoken.jwt.StpLogicJwtForStateless;
import cn.dev33.satoken.stp.SaLoginConfig;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaTokenConsts;
import cn.hutool.json.JSONObject;
@ -164,4 +165,16 @@ public class JwtForStatelessTest {
});
}
// 测试getExtra
@Test
public void getExtra() {
// 登录
StpUtil.login(10001, SaLoginConfig.setExtra("name", "zhangsan"));
// 可以取到
Assertions.assertEquals(StpUtil.getExtra("name"), "zhangsan");
// 取不到
Assertions.assertEquals(StpUtil.getExtra("name2"), null);
}
}