v1.27.1 新增jwt集成插件

This commit is contained in:
click33
2021-10-18 22:05:26 +08:00
parent 4a91553a77
commit 6d26761fd5
30 changed files with 679 additions and 389 deletions

View File

@@ -0,0 +1,235 @@
package cn.dev33.satoken.jwt;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTException;
/**
* jwt操作工具类封装
* @author kong
*
*/
public class SaJwtUtil {
/**
* key账号类型
*/
public static final String LOGIN_TYPE = "loginType";
/**
* key账号id
*/
public static final String LOGIN_ID = "loginId";
/**
* key登录设备
*/
public static final String DEVICE = "device";
/**
* key有效截止期 (时间戳)
*/
public static final String EFF = "eff";
/**
* 当有效期被设为此值时,代表永不过期
*/
public static final long NEVER_EXPIRE = SaTokenDao.NEVER_EXPIRE;
/**
* 创建 jwt (简单方式)
* @param loginId 账号id
* @param keyt 秘钥
* @return jwt-token
*/
public static String createToken(Object loginId, String keyt) {
// 秘钥不可以为空
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
// 构建
String token = JWT.create()
.setPayload(LOGIN_ID, loginId)
// 混入随机字符
.setPayload("rn", SaFoxUtil.getRandomString(32))
.setKey(keyt.getBytes())
.sign();
// 返回
return token;
}
/**
* 创建 jwt (全参数方式)
* @param loginType 账号类型
* @param loginId 账号id
* @param device 设备标识
* @param timeout token有效期 (单位 秒)
* @param keyt 秘钥
* @return jwt-token
*/
public static String createToken(String loginType, Object loginId, String device, long timeout, String keyt) {
// 秘钥不可以为空
SaTokenException.throwByNull(keyt, "请配置jwt秘钥");
// 计算有效期
long effTime = timeout;
if(timeout != NEVER_EXPIRE) {
effTime = timeout * 1000 + System.currentTimeMillis();
}
// 构建
String token = JWT.create()
.setPayload(LOGIN_TYPE, loginType)
.setPayload(LOGIN_ID, loginId)
.setPayload(DEVICE, device)
.setPayload(EFF, effTime)
.setKey(keyt.getBytes())
.sign();
// 返回
return token;
}
/**
* jwt 解析(校验签名和密码)
* @param token Jwt-Token值
* @param keyt 秘钥
* @return 解析后的jwt 对象
*/
public static JWT parseToken(String token, String keyt) {
// 如果token为null
if(token == null) {
throw NotLoginException.newInstance(null, NotLoginException.NOT_TOKEN);
}
// 解析
JWT jwt = null;
try {
jwt = JWT.of(token);
} catch (JWTException e) {
// 解析失败
throw NotLoginException.newInstance(null, NotLoginException.INVALID_TOKEN, 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);
};
// 校验 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);
}
}
// 返回
return jwt;
}
/**
* 获取 jwt 数据载荷 (校验签名和密码)
* @param token token值
* @param keyt 秘钥
* @return 载荷
*/
public static JSONObject getPayloads(String token, String keyt) {
return parseToken(token, keyt).getPayloads();
}
/**
* 获取 jwt 数据载荷 (不校验签名和密码)
* @param token token值
* @param keyt 秘钥
* @return 载荷
*/
public static JSONObject getPayloadsNotCheck(String token, String keyt) {
try {
JWT jwt = JWT.of(token);
JSONObject payloads = jwt.getPayloads();
return payloads;
} catch (JWTException e) {
return new JSONObject();
}
}
/**
* 获取 jwt 代表的账号id
* @param token Token值
* @param keyt 秘钥
* @return 值
*/
public static Object getLoginId(String token, String keyt) {
return getPayloads(token, keyt).get(LOGIN_ID);
}
/**
* 获取 jwt 代表的账号id (未登录时返回null)
* @param token Token值
* @param keyt 秘钥
* @return 值
*/
public static Object getLoginIdOrNull(String token, String keyt) {
try {
return getPayloads(token, keyt).get(LOGIN_ID);
} catch (NotLoginException e) {
return null;
}
}
/**
* 获取 jwt 剩余有效期
* @param token JwtToken值
* @param keyt 秘钥
* @return 值
*/
public static long getTimeout(String token, String keyt) {
// 如果token为null
if(token == null) {
return SaTokenDao.NOT_VALUE_EXPIRE;
}
// 取出数据
JWT jwt = null;
try {
jwt = JWT.of(token);
} catch (JWTException e) {
// 解析失败
return SaTokenDao.NOT_VALUE_EXPIRE;
}
JSONObject payloads = jwt.getPayloads();
// 如果签名无效
boolean verify = jwt.setKey(keyt.getBytes()).verify();
if(verify == false) {
return SaTokenDao.NOT_VALUE_EXPIRE;
};
// 如果被设置为:永不过期
Long effTime = payloads.get(EFF, Long.class);
if(effTime == NEVER_EXPIRE) {
return NEVER_EXPIRE;
}
// 如果已经超时
if(effTime == null || effTime < System.currentTimeMillis()) {
return SaTokenDao.NOT_VALUE_EXPIRE;
}
// 计算timeout (转化为以秒为单位的有效时间)
return (effTime - System.currentTimeMillis()) / 1000;
}
}

View File

@@ -0,0 +1,188 @@
package cn.dev33.satoken.jwt;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
/**
* Sa-Token 整合 jwt -- stateless 无状态
* @author kong
*
*/
public class StpLogicJwtForStateless extends StpLogic {
/**
* 异常描述
*/
public static final String ERROR_MESSAGE = "This API is disabled";
/**
* 初始化StpLogic, 并指定账号类型
* @param loginType 账号体系标识
*/
public StpLogicJwtForStateless() {
super(StpUtil.TYPE);
}
/**
* 初始化StpLogic, 并指定账号类型
* @param loginType 账号体系标识
*/
public StpLogicJwtForStateless(String loginType) {
super(loginType);
}
/**
* 获取jwt秘钥
* @return /
*/
public String jwtSecretKey() {
return getConfig().getJwtSecretKey();
}
//
// ------ 重写方法
//
// ------------------- 获取token 相关 -------------------
/**
* 创建一个TokenValue
*/
@Override
public String createTokenValue(Object loginId) {
return SaJwtUtil.createToken(loginId, 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;
}
// ------------------- 登录相关操作 -------------------
/**
* 会话登录并指定所有登录参数Model
*/
@Override
public void login(Object id, SaLoginModel loginModel) {
SaTokenException.throwByNull(id, "账号id不能为空");
// ------ 1、初始化 loginModel
loginModel.build(getConfig());
// ------ 2、生成一个token
String tokenValue = SaJwtUtil.createToken(
loginType,
id,
loginModel.getDeviceOrDefalut(),
loginModel.getTimeout(),
jwtSecretKey()
);
// 3、在当前会话写入tokenValue
setTokenValue(tokenValue, loginModel.getCookieTimeout());
// $$ 通知监听器账号xxx 登录成功
SaManager.getSaTokenListener().doLogin(loginType, id, loginModel);
}
/**
* 获取指定Token对应的账号id (不做任何特殊处理)
*/
@Override
public String getLoginIdNotHandle(String tokenValue) {
// 先验证 loginType如果不符相当于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() {
// stateless模式下清除Cookie即可
// 如果打开了cookie模式把cookie清除掉
if(getConfig().getIsReadCookie() == true){
SaManager.getSaTokenContext().getResponse().deleteCookie(getTokenName());
}
}
// ------------------- 过期时间相关 -------------------
/**
* 获取当前登录者的 token 剩余有效时间 (单位: 秒)
*/
@Override
public long getTokenTimeout() {
return SaJwtUtil.getTimeout(getTokenValue(), jwtSecretKey());
}
// ------------------- id 反查 token 相关操作 -------------------
/**
* 返回当前会话的登录设备
* @return 当前令牌的登录设备
*/
@Override
public String getLoginDevice() {
// 如果没有token直接返回 null
String tokenValue = getTokenValue();
if(tokenValue == null) {
return null;
}
// 如果还未登录,直接返回 null
if(!isLogin()) {
return null;
}
// 获取
return SaJwtUtil.getPayloadsNotCheck(tokenValue, jwtSecretKey()).getStr(SaJwtUtil.DEVICE);
}
// ------------------- Bean对象代理 -------------------
/**
* 返回持久化对象
*/
@Override
public SaTokenDao getSaTokenDao() {
throw new SaTokenException(ERROR_MESSAGE);
}
}

View File

@@ -0,0 +1,49 @@
package cn.dev33.satoken.jwt;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
/**
* Sa-Token 整合 jwt -- Token风格
* @author kong
*
*/
public class StpLogicJwtForTokenStyle extends StpLogic {
/**
* 初始化StpLogic, 并指定账号类型
* @param loginType 账号体系标识
*/
public StpLogicJwtForTokenStyle() {
super(StpUtil.TYPE);
}
/**
* 初始化StpLogic, 并指定账号类型
* @param loginType 账号体系标识
*/
public StpLogicJwtForTokenStyle(String loginType) {
super(loginType);
}
/**
* 获取jwt秘钥
* @return /
*/
public String jwtSecretKey() {
return getConfig().getJwtSecretKey();
}
// ------ 重写方法
/**
* 创建一个TokenValue
* @param loginId loginId
* @return 生成的tokenValue
*/
@Override
public String createTokenValue(Object loginId) {
return SaJwtUtil.createToken(loginId, jwtSecretKey());
}
}