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

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

@ -63,7 +63,7 @@ public class SaTokenConfig implements Serializable {
private Boolean isLog = false;
/**
* jwt秘钥 (只有集成 sa-token-temp-jwt 模块时此参数才会生效)
* jwt秘钥 (只有集成 jwt 模块时此参数才会生效)
*/
private String jwtSecretKey;
@ -337,14 +337,14 @@ public class SaTokenConfig implements Serializable {
}
/**
* @return jwt秘钥 (只有集成 sa-token-temp-jwt 模块时此参数才会生效)
* @return jwt秘钥 (只有集成 jwt 模块时此参数才会生效)
*/
public String getJwtSecretKey() {
return jwtSecretKey;
}
/**
* @param jwtSecretKey jwt秘钥 (只有集成 sa-token-temp-jwt 模块时此参数才会生效)
* @param jwtSecretKey jwt秘钥 (只有集成 jwt 模块时此参数才会生效)
* @return 对象自身
*/
public SaTokenConfig setJwtSecretKey(String jwtSecretKey) {

View File

@ -3,6 +3,8 @@ package cn.dev33.satoken.exception;
import java.util.Arrays;
import java.util.List;
import cn.dev33.satoken.util.SaFoxUtil;
/**
* 一个异常代表会话未能通过登录认证
* @author kong
@ -24,23 +26,23 @@ public class NotLoginException extends SaTokenException {
/** 表示未提供token */
public static final String NOT_TOKEN = "-1";
public static final String NOT_TOKEN_MESSAGE = "未提供token";
public static final String NOT_TOKEN_MESSAGE = "未提供Token";
/** 表示token无效 */
public static final String INVALID_TOKEN = "-2";
public static final String INVALID_TOKEN_MESSAGE = "token无效";
public static final String INVALID_TOKEN_MESSAGE = "Token无效";
/** 表示token已过期 */
public static final String TOKEN_TIMEOUT = "-3";
public static final String TOKEN_TIMEOUT_MESSAGE = "token已过期";
public static final String TOKEN_TIMEOUT_MESSAGE = "Token已过期";
/** 表示token已被顶下线 */
public static final String BE_REPLACED = "-4";
public static final String BE_REPLACED_MESSAGE = "token已被顶下线";
public static final String BE_REPLACED_MESSAGE = "Token已被顶下线";
/** 表示token已被踢下线 */
public static final String KICK_OUT = "-5";
public static final String KICK_OUT_MESSAGE = "token已被踢下线";
public static final String KICK_OUT_MESSAGE = "Token已被踢下线";
/** 默认的提示语 */
public static final String DEFAULT_MESSAGE = "当前会话未登录";
@ -99,6 +101,17 @@ public class NotLoginException extends SaTokenException {
* @return 构建完毕的异常对象
*/
public static NotLoginException newInstance(String loginType, String type) {
return newInstance(loginType, type, null);
}
/**
* 静态方法构建一个NotLoginException
* @param loginType 账号类型
* @param type 账号类型
* @param token 引起异常的Token值
* @return 构建完毕的异常对象
*/
public static NotLoginException newInstance(String loginType, String type, String token) {
String message = null;
if(NOT_TOKEN.equals(type)) {
message = NOT_TOKEN_MESSAGE;
@ -118,6 +131,9 @@ public class NotLoginException extends SaTokenException {
else {
message = DEFAULT_MESSAGE;
}
if(SaFoxUtil.isEmpty(token) == false) {
message = message + "" + token;
}
return new NotLoginException(message, loginType, type);
}

View File

@ -504,19 +504,19 @@ public class StpLogic {
// 查找此token对应loginId, 如果找不到则抛出无效token
String loginId = getLoginIdNotHandle(tokenValue);
if(loginId == null) {
throw NotLoginException.newInstance(loginType, NotLoginException.INVALID_TOKEN);
throw NotLoginException.newInstance(loginType, NotLoginException.INVALID_TOKEN, tokenValue);
}
// 如果是已经过期则抛出已经过期
if(loginId.equals(NotLoginException.TOKEN_TIMEOUT)) {
throw NotLoginException.newInstance(loginType, NotLoginException.TOKEN_TIMEOUT);
throw NotLoginException.newInstance(loginType, NotLoginException.TOKEN_TIMEOUT, tokenValue);
}
// 如果是已经被顶替下去了, 则抛出已被顶下线
if(loginId.equals(NotLoginException.BE_REPLACED)) {
throw NotLoginException.newInstance(loginType, NotLoginException.BE_REPLACED);
throw NotLoginException.newInstance(loginType, NotLoginException.BE_REPLACED, tokenValue);
}
// 如果是已经被踢下线了, 则抛出已被踢下线
if(loginId.equals(NotLoginException.KICK_OUT)) {
throw NotLoginException.newInstance(loginType, NotLoginException.KICK_OUT);
throw NotLoginException.newInstance(loginType, NotLoginException.KICK_OUT, tokenValue);
}
// 检查是否已经 [临时过期]
checkActivityTimeout(tokenValue);
@ -623,7 +623,7 @@ public class StpLogic {
* @return 账号id
*/
public String getLoginIdNotHandle(String tokenValue) {
return SaManager.getSaTokenDao().get(splicingKeyTokenValue(tokenValue));
return getSaTokenDao().get(splicingKeyTokenValue(tokenValue));
}
// ---- 其它操作
@ -640,7 +640,7 @@ public class StpLogic {
* @param tokenValue token值
*/
public void deleteTokenToIdMapping(String tokenValue) {
SaManager.getSaTokenDao().delete(splicingKeyTokenValue(tokenValue));
getSaTokenDao().delete(splicingKeyTokenValue(tokenValue));
}
/**
* 更改 Token 指向的 账号Id
@ -649,7 +649,7 @@ public class StpLogic {
*/
public void updateTokenToIdMapping(String tokenValue, Object loginId) {
SaTokenException.throwBy(SaFoxUtil.isEmpty(loginId), "LoginId 不能为空");
SaManager.getSaTokenDao().update(splicingKeyTokenValue(tokenValue), loginId.toString());
getSaTokenDao().update(splicingKeyTokenValue(tokenValue), loginId.toString());
}
/**
* 存储 Token-Id 映射
@ -658,7 +658,7 @@ public class StpLogic {
* @param timeout 会话有效期 (单位: )
*/
public void saveTokenToIdMapping(String tokenValue, Object loginId, long timeout) {
SaManager.getSaTokenDao().set(splicingKeyTokenValue(tokenValue), String.valueOf(loginId), timeout);
getSaTokenDao().set(splicingKeyTokenValue(tokenValue), String.valueOf(loginId), timeout);
}
@ -672,10 +672,10 @@ public class StpLogic {
* @return Session对象
*/
public SaSession getSessionBySessionId(String sessionId, boolean isCreate) {
SaSession session = SaManager.getSaTokenDao().getSession(sessionId);
SaSession session = getSaTokenDao().getSession(sessionId);
if(session == null && isCreate) {
session = SaStrategy.me.createSession.apply(sessionId);
SaManager.getSaTokenDao().setSession(session, getConfig().getTimeout());
getSaTokenDao().setSession(session, getConfig().getTimeout());
}
return session;
}
@ -786,10 +786,10 @@ public class StpLogic {
* @param tokenValue token值
*/
public void deleteTokenSession(String tokenValue) {
SaManager.getSaTokenDao().delete(splicingKeyTokenSession(tokenValue));
getSaTokenDao().delete(splicingKeyTokenSession(tokenValue));
}
// ------------------- [临时] 验证相关 -------------------
// ------------------- [临时有效] 验证相关 -------------------
/**
* 写入指定token的 [最后操作时间] 为当前时间戳
@ -801,7 +801,7 @@ public class StpLogic {
return;
}
// [最后操作时间]标记为当前时间戳
SaManager.getSaTokenDao().set(splicingKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()), getConfig().getTimeout());
getSaTokenDao().set(splicingKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()), getConfig().getTimeout());
}
/**
@ -814,7 +814,7 @@ public class StpLogic {
return;
}
// 删除[最后操作时间]
SaManager.getSaTokenDao().delete(splicingKeyLastActivityTime(tokenValue));
getSaTokenDao().delete(splicingKeyLastActivityTime(tokenValue));
// 清除标记
SaHolder.getStorage().delete(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY);
}
@ -842,7 +842,7 @@ public class StpLogic {
}
// -2 代表已过期抛出异常
if(timeout == SaTokenDao.NOT_VALUE_EXPIRE) {
throw NotLoginException.newInstance(loginType, NotLoginException.TOKEN_TIMEOUT);
throw NotLoginException.newInstance(loginType, NotLoginException.TOKEN_TIMEOUT, tokenValue);
}
// --- 至此验证已通过
@ -866,7 +866,7 @@ public class StpLogic {
if(tokenValue == null || getConfig().getActivityTimeout() == SaTokenDao.NEVER_EXPIRE) {
return;
}
SaManager.getSaTokenDao().update(splicingKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()));
getSaTokenDao().update(splicingKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()));
}
/**
@ -886,7 +886,7 @@ public class StpLogic {
* @return token剩余有效时间
*/
public long getTokenTimeout() {
return SaManager.getSaTokenDao().getTimeout(splicingKeyTokenValue(getTokenValue()));
return getSaTokenDao().getTimeout(splicingKeyTokenValue(getTokenValue()));
}
/**
@ -895,7 +895,7 @@ public class StpLogic {
* @return token剩余有效时间
*/
public long getTokenTimeoutByLoginId(Object loginId) {
return SaManager.getSaTokenDao().getTimeout(splicingKeyTokenValue(getTokenValueByLoginId(loginId)));
return getSaTokenDao().getTimeout(splicingKeyTokenValue(getTokenValueByLoginId(loginId)));
}
/**
@ -912,7 +912,7 @@ public class StpLogic {
* @return token剩余有效时间
*/
public long getSessionTimeoutByLoginId(Object loginId) {
return SaManager.getSaTokenDao().getSessionTimeout(splicingKeySession(loginId));
return getSaTokenDao().getSessionTimeout(splicingKeySession(loginId));
}
/**
@ -929,7 +929,7 @@ public class StpLogic {
* @return token剩余有效时间
*/
public long getTokenSessionTimeoutByTokenValue(String tokenValue) {
return SaManager.getSaTokenDao().getSessionTimeout(splicingKeyTokenSession(tokenValue));
return getSaTokenDao().getSessionTimeout(splicingKeyTokenSession(tokenValue));
}
/**
@ -957,7 +957,7 @@ public class StpLogic {
// ------ 开始查询
// 获取相关数据
String keyLastActivityTime = splicingKeyLastActivityTime(tokenValue);
String lastActivityTimeString = SaManager.getSaTokenDao().get(keyLastActivityTime);
String lastActivityTimeString = getSaTokenDao().get(keyLastActivityTime);
// 查不到返回-2
if(lastActivityTimeString == null) {
return SaTokenDao.NOT_VALUE_EXPIRE;
@ -1299,7 +1299,7 @@ public class StpLogic {
* @return token集合
*/
public List<String> searchTokenValue(String keyword, int start, int size) {
return SaManager.getSaTokenDao().searchData(splicingKeyTokenValue(""), keyword, start, size);
return getSaTokenDao().searchData(splicingKeyTokenValue(""), keyword, start, size);
}
/**
@ -1310,7 +1310,7 @@ public class StpLogic {
* @return sessionId集合
*/
public List<String> searchSessionId(String keyword, int start, int size) {
return SaManager.getSaTokenDao().searchData(splicingKeySession(""), keyword, start, size);
return getSaTokenDao().searchData(splicingKeySession(""), keyword, start, size);
}
/**
@ -1321,7 +1321,7 @@ public class StpLogic {
* @return sessionId集合
*/
public List<String> searchTokenSessionId(String keyword, int start, int size) {
return SaManager.getSaTokenDao().searchData(splicingKeyTokenSession(""), keyword, start, size);
return getSaTokenDao().searchData(splicingKeyTokenSession(""), keyword, start, size);
}
@ -1394,7 +1394,7 @@ public class StpLogic {
*/
public void disable(Object loginId, long disableTime) {
// 标注为已被封禁
SaManager.getSaTokenDao().set(splicingKeyDisable(loginId), DisableLoginException.BE_VALUE, disableTime);
getSaTokenDao().set(splicingKeyDisable(loginId), DisableLoginException.BE_VALUE, disableTime);
// $$ 通知监听器
SaManager.getSaTokenListener().doDisable(loginType, loginId, disableTime);
@ -1406,7 +1406,7 @@ public class StpLogic {
* @return see note
*/
public boolean isDisable(Object loginId) {
return SaManager.getSaTokenDao().get(splicingKeyDisable(loginId)) != null;
return getSaTokenDao().get(splicingKeyDisable(loginId)) != null;
}
/**
@ -1415,7 +1415,7 @@ public class StpLogic {
* @return see note
*/
public long getDisableTime(Object loginId) {
return SaManager.getSaTokenDao().getTimeout(splicingKeyDisable(loginId));
return getSaTokenDao().getTimeout(splicingKeyDisable(loginId));
}
/**
@ -1423,7 +1423,7 @@ public class StpLogic {
* @param loginId 账号id
*/
public void untieDisable(Object loginId) {
SaManager.getSaTokenDao().delete(splicingKeyDisable(loginId));
getSaTokenDao().delete(splicingKeyDisable(loginId));
// $$ 通知监听器
SaManager.getSaTokenListener().doUntieDisable(loginType, loginId);
@ -1607,13 +1607,21 @@ public class StpLogic {
// ------------------- Bean对象代理 -------------------
/**
* 返回配置对象
* @return 配置对象
* 返回全局配置对象
* @return /
*/
public SaTokenConfig getConfig() {
// 为什么再次代理一层? 为某些极端业务场景下[需要不同StpLogic不同配置]提供便利
return SaManager.getConfig();
}
/**
* 返回持久化对象
* @return /
*/
public SaTokenDao getSaTokenDao() {
return SaManager.getSaTokenDao();
}
/**
* 判断集合中是否包含指定元素模糊匹配
@ -1628,7 +1636,7 @@ public class StpLogic {
// ------------------- 历史API兼容旧版本 -------------------
/**
* <h1> 本函数设计已过时未来版本可能移除此函数请及时更换为 StpUtil.kickout() 使用方式保持不变 </h1>
* <h1> 本函数设计已过时未来版本可能移除此函数请及时更换为 StpUtil.kickout(id) 使用方式保持不变 </h1>
*
* 会话注销根据账号id 踢人下线
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2
@ -1639,7 +1647,7 @@ public class StpLogic {
}
/**
* <h1> 本函数设计已过时未来版本可能移除此函数请及时更换为 StpUtil.kickout() 使用方式保持不变 </h1>
* <h1> 本函数设计已过时未来版本可能移除此函数请及时更换为 StpUtil.kickout(id) 使用方式保持不变 </h1>
*
* 会话注销根据账号id and 设备标识 踢人下线
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2 </p>

View File

@ -340,7 +340,7 @@ public class StpUtil {
}
// =================== [临时] 验证相关 ===================
// =================== [临时有效] 验证相关 ===================
/**
* 检查当前token 是否已经[临时过期]如果已经过期则抛出异常

View File

@ -26,52 +26,32 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- sa-token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa-token-version}</version>
</dependency>
<!-- jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- sa-token整合redis (使用jdk默认序列化方式) -->
<!-- <dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis</artifactId>
<artifactId>sa-token-jwt</artifactId>
<version>${sa-token-version}</version>
</dependency> -->
<!-- sa-token整合redis (使用jackson序列化方式) -->
<!-- <dependency>
</dependency>
<!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>${sa-token-version}</version>
</dependency> -->
<!-- 提供redis连接池 -->
<!-- <dependency>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency> -->
</dependency>
<!-- sa-token整合SpringAOP实现注解鉴权 -->
<!-- <dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-aop</artifactId>
<version>${sa-token-version}</version>
</dependency> -->
<!-- @ConfigurationProperties -->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -79,12 +59,6 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<!-- <version>2.3.1</version> -->
</dependency>
</dependencies>

View File

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

View File

@ -0,0 +1,38 @@
package com.pj.satoken;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.StpLogicJwtForStateless;
import cn.dev33.satoken.stp.StpLogic;
/**
* [Sa-Token 权限认证] 配置类
* @author kong
*
*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
/**
* 注册Sa-Token 的拦截器打开注解式鉴权功能
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册注解拦截器
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
}
/**
* Sa-Token 整合 jwt
*/
@Bean
public StpLogic getStpLogicJwt() {
return new StpLogicJwtForStateless();
}
}

View File

@ -1,235 +0,0 @@
package com.pj.satoken.jwt;
import java.util.Date;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.session.SaSession;
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 cn.dev33.satoken.util.SaTokenConsts;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
@Component
public class SaTokenJwtUtil {
/**
* 秘钥 (随便手打几个字母就好了)
*/
public static final String BASE64_SECURITY = "79e7c69681b8270162386e6daa53d1dd";
/**
* token有效期 (单位: )
*/
public static final long TIMEOUT = 60 * 60 * 2;
public static final String LOGIN_ID_KEY = "loginId";
/**
* 根据userId生成token
* @param loginId 账号id
* @param base64Security 秘钥
* @return jwt-token
*/
public static String createToken(Object loginId) {
// 判断不可使用默认秘钥
// if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1dd")) {
// throw new SaTokenException("请更换秘钥");
// }
// 在这里你可以使用官方提供的claim方法构建载荷也可以使用setPayload自定义载荷但是两者不可一起使用
JwtBuilder builder = Jwts.builder()
.setHeaderParam("type", "JWT")
.claim(LOGIN_ID_KEY, loginId)
.setIssuedAt(new Date()) // 签发日期
.setExpiration(new Date(System.currentTimeMillis() + 1000 * TIMEOUT)) // 有效截止日期
.signWith(SignatureAlgorithm.HS256, BASE64_SECURITY.getBytes()); // 加密算法
//生成JWT
return builder.compact();
}
/**
* 从一个jwt里面解析出Claims
* @param tokenValue token值
* @param base64Security 秘钥
* @return Claims对象
*/
public static Claims getClaims(String tokenValue) {
// System.out.println(tokenValue);
Claims claims = Jwts.parser()
.setSigningKey(BASE64_SECURITY.getBytes())
.parseClaimsJws(tokenValue).getBody();
return claims;
}
/**
* 从一个jwt里面解析loginId
* @param tokenValue token值
* @param base64Security 秘钥
* @return loginId
*/
public static String getLoginId(String tokenValue) {
try {
Object loginId = getClaims(tokenValue).get(LOGIN_ID_KEY);
if(loginId == null) {
return null;
}
return String.valueOf(loginId);
} catch (ExpiredJwtException e) {
// throw NotLoginException.newInstance(StpUtil.TYPE, NotLoginException.TOKEN_TIMEOUT);
return NotLoginException.TOKEN_TIMEOUT;
} catch (MalformedJwtException e) {
throw NotLoginException.newInstance(StpUtil.stpLogic.loginType, NotLoginException.INVALID_TOKEN);
} catch (Exception e) {
throw new SaTokenException(e);
}
}
static {
// 判断秘钥
if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1dd")) {
String warn = "-------------------------------------\n";
warn += "请更换JWT秘钥不要使用示例默认秘钥\n";
warn += "-------------------------------------";
System.err.println(warn);
}
// 提前调用一下方法促使其属性初始化
StpUtil.getLoginType();
// 修改默认实现
StpUtil.stpLogic = new StpLogic("login") {
// 重写 (随机生成一个tokenValue)
@Override
public String createTokenValue(Object loginId) {
return SaTokenJwtUtil.createToken(loginId);
}
// 重写 (在当前会话上登录id )
@Override
public void login(Object loginId, SaLoginModel loginModel) {
// ------ 1获取相应对象
SaStorage storage = SaManager.getSaTokenContext().getStorage();
SaTokenConfig config = getConfig();
// ------ 2生成一个token
String tokenValue = createTokenValue(loginId);
storage.set(splicingKeyJustCreatedSave(), tokenValue); // 将token保存到本次request里
if(config.getIsReadCookie() == true){ // cookie注入
SaManager.getSaTokenContext().getResponse().addCookie(getTokenName(), tokenValue, "/", config.getCookie().getDomain(), (int)config.getTimeout());
}
}
// 重写 (获取指定token对应的登录id)
@Override
public String getLoginIdNotHandle(String tokenValue) {
try {
return SaTokenJwtUtil.getLoginId(tokenValue);
} catch (Exception e) {
return null;
}
}
// 重写 (当前会话注销登录)
@Override
public void logout() {
// 如果连token都没有那么无需执行任何操作
String tokenValue = getTokenValue();
if(tokenValue == null) {
return;
}
// 如果打开了cookie模式把cookie清除掉
if(getConfig().getIsReadCookie() == true){
SaManager.getSaTokenContext().getResponse().deleteCookie(getTokenName());
}
}
// 重写 (获取指定key的session)
@Override
public SaSession getSessionBySessionId(String sessionId, boolean isCreate) {
throw new SaTokenException("jwt has not session");
}
// 重写 (获取当前登录者的token剩余有效时间 (单位: ))
@Override
public long getTokenTimeout() {
// 如果没有token
String tokenValue = getTokenValue();
if(tokenValue == null) {
return SaTokenDao.NOT_VALUE_EXPIRE;
}
// 开始取值
Claims claims = null;
try {
claims = SaTokenJwtUtil.getClaims(tokenValue);
} catch (Exception e) {
return SaTokenDao.NOT_VALUE_EXPIRE;
}
if(claims == null) {
return SaTokenDao.NOT_VALUE_EXPIRE;
}
Date expiration = claims.getExpiration();
if(expiration == null) {
return SaTokenDao.NOT_VALUE_EXPIRE;
}
return (expiration.getTime() - System.currentTimeMillis()) / 1000;
}
// 重写 (返回当前token的登录设备)
@Override
public String getLoginDevice() {
return SaTokenConsts.DEFAULT_LOGIN_DEVICE;
}
// 重写 (获取当前会话的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 = getSessionTimeout();
// info.tokenSessionTimeout = getTokenSessionTimeout();
// info.tokenActivityTimeout = getTokenActivityTimeout();
info.loginDevice = getLoginDevice();
return info;
}
};
}
}

View File

@ -1,12 +1,9 @@
package com.pj.test;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.pj.util.AjaxJson;
@ -21,15 +18,6 @@ import cn.dev33.satoken.exception.NotRoleException;
@RestControllerAdvice // 可指定包前缀比如(basePackages = "com.pj.admin")
public class GlobalException {
// 在每个控制器之前触发的操作
@ModelAttribute
public void get(HttpServletRequest request) throws IOException {
}
// 全局异常拦截拦截项目中的所有异常
@ExceptionHandler
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
@ -55,46 +43,5 @@ public class GlobalException {
// 返回给前端
return aj;
// 输出到客户端
// response.setContentType("application/json; charset=utf-8"); // http说明我要返回JSON对象
// response.getWriter().print(new ObjectMapper().writeValueAsString(aj));
}
// 全局异常拦截拦截项目中的NotLoginException异常
// @ExceptionHandler(NotLoginException.class)
// public AjaxJson handlerNotLoginException(NotLoginException nle, HttpServletRequest request, HttpServletResponse response)
// throws Exception {
//
// // 打印堆栈以供调试
// nle.printStackTrace();
//
// // 判断场景值定制化异常信息
// String message = "";
// if(nle.getType().equals(NotLoginException.NOT_TOKEN)) {
// message = "未提供token";
// }
// else if(nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
// message = "token无效";
// }
// else if(nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
// message = "token已过期";
// }
// else if(nle.getType().equals(NotLoginException.BE_REPLACED)) {
// message = "token已被顶下线";
// }
// else if(nle.getType().equals(NotLoginException.KICK_OUT)) {
// message = "token已被踢下线";
// }
// else {
// message = "当前会话未登录";
// }
//
// // 返回给前端
// return AjaxJson.getError(message);
// }
}

View File

@ -10,6 +10,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pj.util.AjaxJson;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
@ -22,8 +23,6 @@ import cn.dev33.satoken.stp.StpUtil;
@RequestMapping("/test/")
public class TestJwtController {
// 测试登录接口 浏览器访问 http://localhost:8081/test/login
@RequestMapping("login")
public AjaxJson login(@RequestParam(defaultValue="10001") String id) {
@ -51,7 +50,7 @@ public class TestJwtController {
System.out.println(tokenInfo);
return AjaxJson.getSuccessData(tokenInfo);
}
// 测试会话session接口 浏览器访问 http://localhost:8081/test/session
@RequestMapping("session")
@ -70,11 +69,10 @@ public class TestJwtController {
// 测试 浏览器访问 http://localhost:8081/test/test
@RequestMapping("test")
@SaCheckLogin
public AjaxJson test() {
System.out.println();
System.out.println("--------------进入请求--------------");
StpUtil.login(10001);
System.out.println(StpUtil.getTokenInfo().getTokenValue());
return AjaxJson.getSuccess();
}

View File

@ -16,6 +16,9 @@ sa-token:
is-share: true
# token风格
token-style: uuid
# jwt秘钥
jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk
spring:
# redis配置
redis:

View File

@ -59,7 +59,7 @@
- **插件**
- [AOP注解鉴权](/plugin/aop-at)
- [临时Token证](/plugin/temp-token)
- [临时Token证](/plugin/temp-token)
- [Quick-Login快速登录插件](/plugin/quick-login)
- [Alone独立Redis插件](/plugin/alone-redis)
- [持久层扩展](/plugin/dao-extend)

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8">
<title>Sa-Token</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="sa-token是一个java权限认证框架功能全面上手简单登录验证、权限验证、Session会话、踢人下线、账号封禁、集成Redis、前后台分离、分布式会话、微服务网关鉴权、单点登录、OAuth2.0、临时Token验证、记住我模式、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、密码加密、jwt集成、Spring集成、WebFlux集成...有了sa-token你所有的权限认证问题都不再是问题">
<meta name="description" content="sa-token是一个java权限认证框架功能全面上手简单登录认证、权限认证、Session会话、踢人下线、账号封禁、集成Redis、前后台分离、分布式会话、微服务网关鉴权、单点登录、OAuth2.0、临时Token验证、记住我模式、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、密码加密、jwt集成、Spring集成、WebFlux集成...有了sa-token你所有的权限认证问题都不再是问题">
<meta name="keywords" content="sa-token,sa-token框架,sa-token文档,java权限认证">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="logo.png">

View File

@ -83,7 +83,7 @@ public class SaTokenConfigure {
.addExclude("/favicon.ico")
// 鉴权方法:每次访问进入
.setAuth(obj -> {
// 登录验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
// 登录验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
SaRouter.match("/**", "/user/doLogin", r -> StpUtil.checkLogin());
// 权限认证 -- 不同模块, 校验不同权限

View File

@ -12,7 +12,7 @@ SaManager.getStpInterface(); // 获取权限认证对象
SaManager.getSaTokenAction(); // 获取框架行为对象
SaManager.getSaTokenContext(); // 获取上下文处理对象
SaManager.getSaTokenListener(); // 获取侦听器对象
SaManager.getSaTemp(); // 获取临时令牌证模块对象
SaManager.getSaTemp(); // 获取临时令牌证模块对象
SaManager.getStpLogic("type"); // 获取指定账号类型的StpLogic对象
```

View File

@ -1,4 +1,4 @@
# 临时Token令牌
# 临时Token令牌
---
@ -37,7 +37,7 @@ http://xxx.com/apply?token=oEwQBnglXDoGraSJdGaLooPZnGrk
### 相关API
**[sa-token-temp临时证模块]** 已内嵌到核心包,无需引入其它依赖即可使用
**[sa-token-temp临时证模块]** 已内嵌到核心包,无需引入其它依赖即可使用
``` java
// 根据 value 创建一个 token
@ -55,7 +55,7 @@ SaTempUtil.deleteToken(token);
### 集成jwt
提到 [临时Token证]你是不是想到一个专门干这件事的框架就是JWT
提到 [临时Token证]你是不是想到一个专门干这件事的框架就是JWT
**[sa-token-temp]** 模块允许以JWT作为逻辑内核完成工作你只需要引入以下依赖所有上层API保持不变

View File

@ -198,7 +198,7 @@ public class SaSsoClientApplication {
至此,测试完毕!
可以看出,除了在`Client1`端我们需要手动登录一次之外,在`Client2端`和`Client3端`都是可以无需证,直接登录成功的。
可以看出,除了在`Client1`端我们需要手动登录一次之外,在`Client2端`和`Client3端`都是可以无需再次认证,直接登录成功的。
我们可以通过 F12控制台 Netword跟踪整个过程

View File

@ -107,6 +107,14 @@ public Object myinfo() {
访问测试:[http://sa-sso-client1.com:9001/sso/myinfo](http://sa-sso-client1.com:9001/sso/myinfo)
#### 3.3、疑问
群里有小伙伴提问:`SaSsoUtil.getUserinfo` 提供的参数太少,只有一个 loginId无法满足业务需求怎么办
SaSsoUtil.getUserinfo只是为了避免你在项目中硬编码认证中心 url 而提供的简易封装如果这个API无法满足你的业务需求
你完全可以在 Server 端自定义一些接口然后从 Client 端使用 http 工具调用即可。
### 4、无刷单点注销

View File

@ -42,7 +42,7 @@ public class SaTokenConfigure {
.setAuth(obj -> {
System.out.println("---------- 进入Sa-Token全局认证 -----------");
// 登录证 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
// 登录证 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
SaRouter.match("/**", "/user/doLogin", () -> StpUtil.checkLogin());
// 更多拦截处理方式,请参考“路由拦截式鉴权”章节

View File

@ -5,7 +5,7 @@
有的时候,我们会在一个项目中设计两套账号体系,比如一个电商系统的 `user表``admin表`
在这种场景下,如果两套账号我们都使用 `StpUtil` 类的API进行登录鉴权那么势必会发生逻辑冲突
在Sa-Token中这个问题的模型叫做多账号体系
在Sa-Token中这个问题的模型叫做多账号体系
要解决这个问题,我们必须有一个合理的机制将这两套账号的授权给区分开,让它们互不干扰才行
@ -22,7 +22,7 @@
### 2、解决方案
前面几篇介绍的api调用都是经过 StpUtil 类的各种静态方法进行授权证,
前面几篇介绍的api调用都是经过 StpUtil 类的各种静态方法进行授权证,
而如果我们深入它的源码,[点此阅览](https://gitee.com/dromara/sa-token/blob/master/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java) <br/>
就会发现,此类并没有任何代码逻辑,唯一做的事就是对成员变量`stpLogic`的各个API包装一下进行转发
@ -33,8 +33,8 @@
### 3、操作示例
比如说,对于原生`StpUtil`类,我们只做`admin账号`权限证,而对于`user账号`,我们则:
1. 新建一个新的权限证类,比如: `StpUserUtil.java`
比如说,对于原生`StpUtil`类,我们只做`admin账号`权限证,而对于`user账号`,我们则:
1. 新建一个新的权限证类,比如: `StpUserUtil.java`
2. 将`StpUtil.java`类的全部代码复制粘贴到 `StpUserUtil.java`
3. 更改一下其 `LoginType` 比如:

View File

@ -112,7 +112,8 @@ StpUtil.checkRoleOr("super-admin", "shop-admin");
### 拦截全局异常
有同学要问,鉴权失败,抛出异常,然后呢?要把异常显示给用户看吗?**当然不可以!** <br>
你可以创建一个全局异常拦截器,统一返回给前端的格式,参考:[码云GlobalException.java](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-springboot/src/main/java/com/pj/test/GlobalException.java)
你可以创建一个全局异常拦截器,统一返回给前端的格式,参考:
[码云GlobalException.java](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-springboot/src/main/java/com/pj/current/GlobalException.java)
### 权限通配符

View File

@ -2,7 +2,7 @@
---
假设我们有如下需求:
> 项目中所有接口均需要登录证,只有'登录接口'本身对外开放
> 项目中所有接口均需要登录证,只有'登录接口'本身对外开放
我们怎么实现呢?给每个接口加上鉴权注解?手写全局拦截器?似乎都不是非常方便。<br/>
在这个需求中我们真正需要的是一种基于路由拦截的鉴权模式, 那么在Sa-Token怎么实现路由拦截鉴权呢

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8">
<title>Sa-Token</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="sa-token是一个java权限认证框架功能全面上手简单登录验证、权限验证、Session会话、踢人下线、账号封禁、集成Redis、前后台分离、分布式会话、微服务网关鉴权、单点登录、OAuth2.0、临时Token验证、记住我模式、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、密码加密、jwt集成、Spring集成、WebFlux集成...有了sa-token你所有的权限认证问题都不再是问题">
<meta name="description" content="sa-token是一个java权限认证框架功能全面上手简单登录认证、权限认证、Session会话、踢人下线、账号封禁、集成Redis、前后台分离、分布式会话、微服务网关鉴权、单点登录、OAuth2.0、临时Token验证、记住我模式、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、密码加密、jwt集成、Spring集成、WebFlux集成...有了sa-token你所有的权限认证问题都不再是问题">
<meta name="keywords" content="sa-token,sa-token框架,sa-token文档,java权限认证">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="doc/logo.png">

View File

@ -25,6 +25,7 @@
<module>sa-token-quick-login</module>
<module>sa-token-spring-aop</module>
<module>sa-token-temp-jwt</module>
<module>sa-token-jwt</module>
</modules>
<dependencies>

12
sa-token-plugin/sa-token-jwt/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
target/
node_modules/
bin/
.settings/
unpackage/
.classpath
.project
.factorypath
.idea/

View File

@ -0,0 +1,33 @@
<?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-plugin</artifactId>
<version>1.27.0</version>
</parent>
<packaging>jar</packaging>
<name>sa-token-jwt</name>
<artifactId>sa-token-jwt</artifactId>
<description>sa-token-jwt</description>
<dependencies>
<!-- sa-token-core -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<version>${sa-token-version}</version>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.14</version>
</dependency>
</dependencies>
</project>

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());
}
}

View File

@ -14,6 +14,7 @@ import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.session.TokenSign;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
/**
@ -160,5 +161,18 @@ public class ManyLoginTest {
Assert.assertNull(StpUtil.getSessionByLoginId(10001, false));
Assert.assertNull(dao.getSession("satoken:login:session:" + 10001));
}
// 测试多账号模式在一个账号体系里登录成功在另一个账号体系不会校验通过
@Test
public void login7() {
SaManager.setConfig(new SaTokenConfig());
StpUtil.login(10001);
String token1 = StpUtil.getTokenValue();
StpLogic stp = new StpLogic("user");
Assert.assertNotNull(StpUtil.getLoginIdByToken(token1));
Assert.assertNull(stp.getLoginIdByToken(token1));
}
}