Compare commits

...

26 Commits

Author SHA1 Message Date
shengzhang
f9ad71db66 v1.14.0 更新 2021-03-12 01:45:13 +08:00
shengzhang
9fe0e1f97b 优化文档 2021-03-11 01:06:27 +08:00
shengzhang
9f6717f8c0 更换二维码地址 2021-03-10 14:17:43 +08:00
shengzhang
af2d04ca1f 更换二维码 2021-03-10 14:04:58 +08:00
省长
64e6b58a05 update README.md. 2021-03-10 14:01:56 +08:00
shengzhang
275b3cec6f 完成 [记住我]模式 开发文档 2021-03-10 02:37:45 +08:00
省长
e92ff9fb7d !18 update README.md.
Merge pull request !18 from ZhuBJ0510/N/A
2021-03-09 15:02:10 +08:00
ZhuBJ0510
cffd462cd8 update README.md. 2021-03-09 15:01:57 +08:00
省长
9ca2cad5e6 !17 update README.md.
Merge pull request !17 from ZhuBJ0510/N/A
2021-03-09 14:57:28 +08:00
ZhuBJ0510
a076c8f8fc update README.md. 2021-03-09 14:33:17 +08:00
省长
dbddd05a6e !16 update sa-token-doc/doc/use/route-check.md.
Merge pull request !16 from AppleOfGray/N/A
2021-03-09 10:00:17 +08:00
AppleOfGray
18760010fc update sa-token-doc/doc/use/route-check.md. 2021-03-09 09:59:39 +08:00
shengzhang
9151f9a5c8 优化文档 2021-03-09 00:29:57 +08:00
shengzhang
cdaf9fa65a Merge branch 'dev' of https://gitee.com/sz6/sa-token into dev 2021-03-08 23:27:38 +08:00
shengzhang
c316746f0d [密码加密]模块的文档 2021-03-08 23:27:08 +08:00
省长
0695ee6cdf update sa-token-doc/doc/use/many-account.md. 2021-03-08 09:01:42 +08:00
shengzhang
3716b874e3 新增密码加密算法工具类 2021-03-08 02:13:18 +08:00
shengzhang
255a15033e 集成Redis章节提供常见问题说明 2021-03-07 00:15:53 +08:00
省长
ad70bb0300 update sa-token-doc/doc/more/link.md.
增加 vue-next-admin 友链
2021-03-06 14:26:41 +08:00
shengzhang
8c35d838ff SaSession新增refreshDataMap方法,用于一次性重置整个数据集合 2021-03-05 13:57:24 +08:00
shengzhang
1eae6861d1 更新微信群聊二维码 2021-03-04 10:13:00 +08:00
shengzhang
bbcc016fc7 SaOAuth2Config sa-token oauth2 配置类所有set方法新增支持链式调用 2021-03-03 10:47:32 +08:00
省长
c62f9482df update sa-token-doc/doc/README.md. 2021-03-02 11:24:28 +08:00
省长
d2c0ac3f52 update README.md. 2021-03-02 11:23:24 +08:00
shengzhang
51b76f07f4 起草[记住我]模式开发文档 2021-03-02 11:01:59 +08:00
shengzhang
0b32a1f552 优化源码注释 2021-03-02 09:22:55 +08:00
59 changed files with 1000 additions and 175 deletions

View File

@@ -1,11 +1,11 @@
<p align="center"> <p align="center">
<img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150"> <img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150">
</p> </p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.13.0</h1> <h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.14.0</h1>
<h4 align="center">这可能是史上功能最全的Java权限认证框架</h4> <h4 align="center">这可能是史上功能最全的Java权限认证框架</h4>
<h4 align="center"> <h4 align="center">
<a href="https://gitee.com/sz6/sa-token/stargazers"><img src="https://gitee.com/sz6/sa-token/badge/star.svg"></a> <a href="https://gitee.com/sz6/sa-token/stargazers"><img src="https://gitee.com/sz6/sa-token/badge/star.svg"></a>
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.13.0-2B9939"></a> <a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.14.0-2B9939"></a>
<a href="https://github.com/click33/sa-token/stargazers"><img src="https://img.shields.io/github/stars/click33/sa-token"></a> <a href="https://github.com/click33/sa-token/stargazers"><img src="https://img.shields.io/github/stars/click33/sa-token"></a>
<a href="https://github.com/click33/sa-token/watchers"><img src="https://img.shields.io/github/watchers/click33/sa-token"></a> <a href="https://github.com/click33/sa-token/watchers"><img src="https://img.shields.io/github/watchers/click33/sa-token"></a>
<a href="https://github.com/click33/sa-token/network/members"><img src="https://img.shields.io/github/forks/click33/sa-token"></a> <a href="https://github.com/click33/sa-token/network/members"><img src="https://img.shields.io/github/forks/click33/sa-token"></a>
@@ -87,7 +87,8 @@ StpUtil.checkLogin();
@SaCheckPermission("user:add") @SaCheckPermission("user:add")
@RequestMapping("/user/insert") @RequestMapping("/user/insert")
public String insert(SysUser user) { public String insert(SysUser user) {
return "用户增加"; // ...
return "用户增加";
} }
``` ```
@@ -185,10 +186,12 @@ sa-token秉承着开放的思想欢迎大家贡献代码为框架添砖加
## 交流群 ## 交流群
QQ交流群[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) QQ交流群[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM)
![扫码加群](https://color-test.oss-cn-qingdao.aliyuncs.com/sa-token/qq-group.png ':size=150') ![扫码加群](https://color-test.oss-cn-qingdao.aliyuncs.com/sa-token/qq-group.png ':size=230')
**微信群** **微信群**
![微信群](https://images.gitee.com/uploads/images/2021/0222/175350_55b70afe_8073399.jpeg "sa-token-wx.jpg") ![微信群](https://dev33-test.oss-cn-beijing.aliyuncs.com/sa-token/i-wx-qr.png ':size=230')
(扫码添加微信备注sa-token邀您加入群聊)
<br> <br>

View File

@@ -8,7 +8,7 @@
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-parent</artifactId> <artifactId>sa-token-parent</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>1.13.0</version> <version>1.14.0</version>
<!-- 项目介绍 --> <!-- 项目介绍 -->
<name>sa-token</name> <name>sa-token</name>
@@ -38,7 +38,7 @@
<!-- 一些属性 --> <!-- 一些属性 -->
<properties> <properties>
<sa-token-version>1.13.0</sa-token-version> <sa-token-version>1.14.0</sa-token-version>
<jdk.version>1.8</jdk.version> <jdk.version>1.8</jdk.version>
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding> <project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-parent</artifactId> <artifactId>sa-token-parent</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</parent> </parent>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@@ -16,7 +16,7 @@ public class SaTokenActionDefaultImpl implements SaTokenAction {
/** /**
* 生成一个token * 根据一定的算法生成一个token
*/ */
@Override @Override
public String createToken(Object loginId, String loginKey) { public String createToken(Object loginId, String loginKey) {

View File

@@ -23,14 +23,27 @@ public class IsRunFunction {
} }
/** /**
* 根据变量决定是否执行此函数 * 当 isRun == true 时执行此函数
*
* @param function 函数 * @param function 函数
* @return 对象自身
*/ */
public void exe(SaFunction function) { public IsRunFunction exe(SaFunction function) {
if (isRun) { if (isRun) {
function.run(); function.run();
} }
return this;
}
/**
* 当 isRun == false 时执行此函数
* @param function 函数
* @return 对象自身
*/
public IsRunFunction noExe(SaFunction function) {
if (!isRun) {
function.run();
}
return this;
} }
} }

View File

@@ -0,0 +1,61 @@
package cn.dev33.satoken.secure;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
/**
* Base64工具类
* @author kong
*
*/
public class SaBase64Util {
private static Base64.Encoder encoder = Base64.getEncoder();
private static Base64.Decoder decoder = Base64.getDecoder();
/**
* Base64编码byte[] 转 String
* @param bytes byte[]
* @return 字符串
*/
public static String encodeBytesToString(byte[] bytes){
return encoder.encodeToString(bytes);
}
/**
* Base64解码String 转 byte[]
* @param text 字符串
* @return byte[]
*/
public static byte[] decodeStringToBytes(String text){
return decoder.decode(text);
}
/**
* Base64编码String 转 String
* @param text 字符串
* @return Base64格式字符串
*/
public static String encode(String text){
try {
return encoder.encodeToString(text.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* Base64解码String 转 String
* @param base64Text Base64格式字符串
* @return 字符串
*/
public static String decode(String base64Text){
try {
return new String(decoder.decode(base64Text), "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,443 @@
package cn.dev33.satoken.secure;
import java.security.InvalidParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.UUID;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import cn.dev33.satoken.exception.SaTokenException;
/**
* sa-token 常见加密算法工具类
*
* @author kong
*
*/
public class SaSecureUtil {
/**
* Base64编码
*/
private static Base64.Encoder encoder = Base64.getEncoder();
/**
* Base64解码
*/
private static Base64.Decoder decoder = Base64.getDecoder();
// ----------------------- 摘要加密 -----------------------
/**
* md5加密
* @param str 指定字符串
* @return 加密后的字符串
*/
public static String md5(String str) {
str = (str == null ? "" : str);
char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
try {
byte[] btInput = str.getBytes();
MessageDigest mdInst = MessageDigest.getInstance("MD5");
mdInst.update(btInput);
byte[] md = mdInst.digest();
int j = md.length;
char[] strA = new char[j * 2];
int k = 0;
for (byte byte0 : md) {
strA[k++] = hexDigits[byte0 >>> 4 & 0xf];
strA[k++] = hexDigits[byte0 & 0xf];
}
return new String(strA);
} catch (Exception e) {
throw new SaTokenException(e);
}
}
/**
* sha1加密
*
* @param str 指定字符串
* @return 加密后的字符串
*/
public static String sha1(String str) {
try {
str = (str == null ? "" : str);
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] b = str.getBytes();
md.update(b);
byte[] b2 = md.digest();
int len = b2.length;
String strA = "0123456789abcdef";
char[] ch = strA.toCharArray();
char[] chs = new char[len * 2];
for (int i = 0, k = 0; i < len; i++) {
byte b3 = b2[i];
chs[k++] = ch[b3 >>> 4 & 0xf];
chs[k++] = ch[b3 & 0xf];
}
return new String(chs);
} catch (Exception e) {
throw new SaTokenException(e);
}
}
/**
* sha256加密
*
* @param str 指定字符串
* @return 加密后的字符串
*/
public static String sha256(String str) {
try {
str = (str == null ? "" : str);
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(str.getBytes("UTF-8"));
byte[] bytes = messageDigest.digest();
StringBuilder builder = new StringBuilder();
String temp;
for (int i = 0; i < bytes.length; i++) {
temp = Integer.toHexString(bytes[i] & 0xFF);
if (temp.length() == 1) {
builder.append("0");
}
builder.append(temp);
}
return builder.toString();
} catch (Exception e) {
throw new SaTokenException(e);
}
}
/**
* md5加盐加密: md5(md5(str) + md5(salt))
* @param str 字符串
* @param salt 盐
* @return 加密后的字符串
*/
public static String md5BySalt(String str, String salt) {
return md5(md5(str) + md5(salt));
}
// ----------------------- 对称加密 AES -----------------------
/**
* 默认密码算法
*/
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
/**
* AES加密
*
* @param key 加密的密钥
* @param text 需要加密的字符串
* @return 返回Base64转码后的加密数据
*/
public static String aesEncrypt(String key, String text) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
byte[] byteContent = text.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));
byte[] result = cipher.doFinal(byteContent);
return encoder.encodeToString(result);
} catch (Exception e) {
throw new SaTokenException(e);
}
}
/**
* AES解密
* @param key 加密的密钥
* @param text 已加密的密文
* @return 返回解密后的数据
*/
public static String aesDecrypt(String key, String text) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));
byte[] result = cipher.doFinal(decoder.decode(text));
return new String(result, "utf-8");
} catch (Exception e) {
throw new SaTokenException(e);
}
}
/**
* 生成加密秘钥
* @param password 秘钥
* @return SecretKeySpec
* @throws NoSuchAlgorithmException
*/
private static SecretKeySpec getSecretKey(final String password) throws NoSuchAlgorithmException {
KeyGenerator kg = KeyGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(password.getBytes());
kg.init(128, random);
SecretKey secretKey = kg.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), "AES");
}
// ----------------------- 非对称加密 RSA -----------------------
private static final String ALGORITHM = "RSA";
private static final int KEY_SIZE = 1024;
// ---------- 5个常用方法
/**
* 生成密钥对
* @return Map对象 (private=私钥, public=公钥)
* @throws Exception 异常
*/
public static HashMap<String, String> rsaGenerateKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
KeyPair keyPair;
try {
keyPairGenerator.initialize(KEY_SIZE,
new SecureRandom(UUID.randomUUID().toString().replaceAll("-", "").getBytes()));
keyPair = keyPairGenerator.generateKeyPair();
} catch (InvalidParameterException e) {
throw e;
} catch (NullPointerException e) {
throw e;
}
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
HashMap<String, String> map = new HashMap<String, String>(16);
map.put("private", encoder.encodeToString(rsaPrivateKey.getEncoded()));
map.put("public", encoder.encodeToString(rsaPublicKey.getEncoded()));
return map;
}
/**
* RSA公钥加密
* @param publicKeyString 公钥
* @param content 内容
* @return 加密后内容
*/
public static String rsaEncryptByPublic(String publicKeyString, String content) {
try {
// 获得公钥对象
PublicKey publicKey = getPublicKeyFromString(publicKeyString);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 该密钥能够加密的最大字节长度
int splitLength = ((RSAPublicKey) publicKey).getModulus().bitLength() / 8 - 11;
byte[][] arrays = splitBytes(content.getBytes(), splitLength);
StringBuffer stringBuffer = new StringBuffer();
for (byte[] array : arrays) {
stringBuffer.append(bytesToHexString(cipher.doFinal(array)));
}
return stringBuffer.toString();
} catch (Exception e) {
throw new SaTokenException(e);
}
}
/**
* RSA私钥加密
* @param privateKeyString 私钥
* @param content 内容
* @return 加密后内容
*/
public static String rsaEncryptByPrivate(String privateKeyString, String content) {
try {
PrivateKey privateKey = getPrivateKeyFromString(privateKeyString);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// 该密钥能够加密的最大字节长度
int splitLength = ((RSAPrivateKey) privateKey).getModulus().bitLength() / 8 - 11;
byte[][] arrays = splitBytes(content.getBytes(), splitLength);
StringBuffer stringBuffer = new StringBuffer();
for (byte[] array : arrays) {
stringBuffer.append(bytesToHexString(cipher.doFinal(array)));
}
return stringBuffer.toString();
} catch (Exception e) {
throw new SaTokenException(e);
}
}
/**
* RSA公钥解密
* @param publicKeyString 公钥
* @param content 已加密内容
* @return 解密后内容
*/
public static String rsaDecryptByPublic(String publicKeyString, String content) {
try {
PublicKey publicKey = getPublicKeyFromString(publicKeyString);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
// 该密钥能够加密的最大字节长度
int splitLength = ((RSAPublicKey) publicKey).getModulus().bitLength() / 8;
byte[] contentBytes = hexStringToBytes(content);
byte[][] arrays = splitBytes(contentBytes, splitLength);
StringBuffer stringBuffer = new StringBuffer();
for (byte[] array : arrays) {
stringBuffer.append(new String(cipher.doFinal(array)));
}
return stringBuffer.toString();
} catch (Exception e) {
throw new SaTokenException(e);
}
}
/**
* RSA私钥解密
* @param privateKeyString 公钥
* @param content 已加密内容
* @return 解密后内容
*/
public static String rsaDecryptByPrivate(String privateKeyString, String content) {
try {
PrivateKey privateKey = getPrivateKeyFromString(privateKeyString);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 该密钥能够加密的最大字节长度
int splitLength = ((RSAPrivateKey) privateKey).getModulus().bitLength() / 8;
byte[] contentBytes = hexStringToBytes(content);
byte[][] arrays = splitBytes(contentBytes, splitLength);
StringBuffer stringBuffer = new StringBuffer();
for (byte[] array : arrays) {
stringBuffer.append(new String(cipher.doFinal(array)));
}
return stringBuffer.toString();
} catch (Exception e) {
throw new SaTokenException(e);
}
}
// ---------- 获取*钥
/** 根据公钥字符串获取 公钥对象 */
private static PublicKey getPublicKeyFromString(String key)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// 过滤掉\r\n
key = key.replace("\r\n", "");
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(decoder.decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
return publicKey;
}
/** 根据私钥字符串获取 私钥对象 */
private static PrivateKey getPrivateKeyFromString(String key)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// 过滤掉\r\n
key = key.replace("\r\n", "");
// 取得私钥
PKCS8EncodedKeySpec x509KeySpec = new PKCS8EncodedKeySpec(decoder.decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(x509KeySpec);
return privateKey;
}
// ---------- 一些辅助方法
/** 根据限定的每组字节长度,将字节数组分组 */
private static byte[][] splitBytes(byte[] bytes, int splitLength) {
// bytes与splitLength的余数
int remainder = bytes.length % splitLength;
// 数据拆分后的组数余数不为0时加1
int quotient = remainder != 0 ? bytes.length / splitLength + 1 : bytes.length / splitLength;
byte[][] arrays = new byte[quotient][];
byte[] array = null;
for (int i = 0; i < quotient; i++) {
// 如果是最后一组quotient-1,同时余数不等于0就将最后一组设置为remainder的长度
if (i == quotient - 1 && remainder != 0) {
array = new byte[remainder];
System.arraycopy(bytes, i * splitLength, array, 0, remainder);
} else {
array = new byte[splitLength];
System.arraycopy(bytes, i * splitLength, array, 0, splitLength);
}
arrays[i] = array;
}
return arrays;
}
/** 将字节数组转换成16进制字符串 */
private static String bytesToHexString(byte[] bytes) {
StringBuffer sb = new StringBuffer(bytes.length);
String temp = null;
for (int i = 0; i < bytes.length; i++) {
temp = Integer.toHexString(0xFF & bytes[i]);
if (temp.length() < 2) {
sb.append(0);
}
sb.append(temp);
}
return sb.toString();
}
/** 将16进制字符串转换成字节数组 */
private static byte[] hexStringToBytes(String hex) {
int len = (hex.length() / 2);
hex = hex.toUpperCase();
byte[] result = new byte[len];
char[] chars = hex.toCharArray();
for (int i = 0; i < len; i++) {
int pos = i * 2;
result[i] = (byte) (toByte(chars[pos]) << 4 | toByte(chars[pos + 1]));
}
return result;
}
/** 将char转换为byte */
private static byte toByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
}

View File

@@ -226,6 +226,38 @@ public class SaSession implements Serializable {
return dataMap; return dataMap;
} }
/**
* 写入数据集合 (不改变底层对象只将此dataMap所有数据进行替换)
* @param dataMap 数据集合
*/
public void refreshDataMap(Map<String, Object> dataMap) {
this.dataMap.clear();
this.dataMap.putAll(dataMap);
this.update();
}
// ----------------------- 存取值 (类型转换)
/**
* 从Session中取值转化为Object类型
* @param key key
* @return 值
*/
public Object getObject(String key) {
return getAttribute(key);
}
/**
* 判断一个值是否为null
* @param value 指定值
* @return 此value是否为null
*/
public boolean valueIsNull(Object value) {
return value == null || value.equals("");
}
// ----------------------- 一些操作 // ----------------------- 一些操作
/** /**

View File

@@ -21,7 +21,7 @@ public class SaSessionCustomUtil {
* @param sessionId 会话id * @param sessionId 会话id
* @return sessionId * @return sessionId
*/ */
public static String getSessionKey(String sessionId) { public static String splicingSessionKey(String sessionId) {
return SaTokenManager.getConfig().getTokenName() + ":" + sessionKey + ":session:" + sessionId; return SaTokenManager.getConfig().getTokenName() + ":" + sessionKey + ":session:" + sessionId;
} }
@@ -32,7 +32,7 @@ public class SaSessionCustomUtil {
* @return 是否存在 * @return 是否存在
*/ */
public boolean isExists(String sessionId) { public boolean isExists(String sessionId) {
return SaTokenManager.getSaTokenDao().getSession(getSessionKey(sessionId)) != null; return SaTokenManager.getSaTokenDao().getSession(splicingSessionKey(sessionId)) != null;
} }
/** /**
@@ -43,7 +43,7 @@ public class SaSessionCustomUtil {
* @return SaSession * @return SaSession
*/ */
public static SaSession getSessionById(String sessionId, boolean isCreate) { public static SaSession getSessionById(String sessionId, boolean isCreate) {
SaSession session = SaTokenManager.getSaTokenDao().getSession(getSessionKey(sessionId)); SaSession session = SaTokenManager.getSaTokenDao().getSession(splicingSessionKey(sessionId));
if (session == null && isCreate) { if (session == null && isCreate) {
session = SaTokenManager.getSaTokenAction().createSession(sessionId); session = SaTokenManager.getSaTokenAction().createSession(sessionId);
SaTokenManager.getSaTokenDao().setSession(session, SaTokenManager.getConfig().getTimeout()); SaTokenManager.getSaTokenDao().setSession(session, SaTokenManager.getConfig().getTimeout());
@@ -67,7 +67,7 @@ public class SaSessionCustomUtil {
* @param sessionId 指定key * @param sessionId 指定key
*/ */
public static void deleteSessionById(String sessionId) { public static void deleteSessionById(String sessionId) {
SaTokenManager.getSaTokenDao().deleteSession(getSessionKey(sessionId)); SaTokenManager.getSaTokenDao().deleteSession(splicingSessionKey(sessionId));
} }
} }

View File

@@ -2,10 +2,11 @@ package cn.dev33.satoken.stp;
import cn.dev33.satoken.SaTokenManager; import cn.dev33.satoken.SaTokenManager;
import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.util.SaTokenConsts; import cn.dev33.satoken.util.SaTokenConsts;
/** /**
* 调用StpUtil.setLogin()时的配置 Model * 调用 `StpUtil.setLogin()` 时的 [配置参数 Model ]
* @author kong * @author kong
* *
*/ */
@@ -17,16 +18,16 @@ public class SaLoginModel {
*/ */
public String device; public String device;
/**
* 是否为持久Cookie临时Cookie在浏览器关闭时会自动删除持久Cookie在重新打开后依然存在
*/
public Boolean isLastingCookie;
/** /**
* 指定此次登录token的有效期, 单位:秒 如未指定自动取全局配置的timeout值 * 指定此次登录token的有效期, 单位:秒 如未指定自动取全局配置的timeout值
*/ */
public Long timeout; public Long timeout;
/**
* 是否为临时Cookie临时Cookie会在浏览器关闭时自动删除
*/
public Boolean isTempCookie;
/** /**
* @return device * @return device
@@ -44,6 +45,22 @@ public class SaLoginModel {
return this; return this;
} }
/**
* @return isLastingCookie
*/
public Boolean getIsLastingCookie() {
return isLastingCookie;
}
/**
* @param isLastingCookie 要设置的 isLastingCookie
* @return 对象自身
*/
public SaLoginModel setIsLastingCookie(Boolean isLastingCookie) {
this.isLastingCookie = isLastingCookie;
return this;
}
/** /**
* @return timeout * @return timeout
*/ */
@@ -60,22 +77,21 @@ public class SaLoginModel {
return this; return this;
} }
/**
* @return isTempCookie
*/
public Boolean getIsTempCookie() {
return isTempCookie;
}
/** /**
* @param isTempCookie 要设置的 isTempCookie * @return cookie时长
* @return 对象自身
*/ */
public SaLoginModel setIsTempCookie(Boolean isTempCookie) { public int getCookieTimeout() {
this.isTempCookie = isTempCookie; if(isLastingCookie == false) {
return this; return -1;
}
if(timeout == SaTokenDao.NEVER_EXPIRE) {
return Integer.MAX_VALUE;
}
return (int)(long)timeout;
} }
/** /**
* 构建对象,初始化默认值 * 构建对象,初始化默认值
* @return 对象自身 * @return 对象自身
@@ -93,12 +109,12 @@ public class SaLoginModel {
if(device == null) { if(device == null) {
device = SaTokenConsts.DEFAULT_LOGIN_DEVICE; device = SaTokenConsts.DEFAULT_LOGIN_DEVICE;
} }
if(isLastingCookie == null) {
isLastingCookie = true;
}
if(timeout == null) { if(timeout == null) {
timeout = config.getTimeout(); timeout = config.getTimeout();
} }
if(isTempCookie == null) {
isTempCookie = false;
}
return this; return this;
} }
@@ -112,4 +128,15 @@ public class SaLoginModel {
} }
/**
* toString
*/
@Override
public String toString() {
return "SaLoginModel [device=" + device + ", isLastingCookie=" + isLastingCookie + ", timeout=" + timeout + "]";
}
} }

View File

@@ -72,7 +72,7 @@ public class StpLogic {
* @return 此StpLogic的token名称 * @return 此StpLogic的token名称
*/ */
public String getTokenName() { public String getTokenName() {
return getKeyTokenName(); return splicingKeyTokenName();
} }
/** /**
@@ -96,8 +96,8 @@ public class StpLogic {
String tokenValue = null; String tokenValue = null;
// 1. 尝试从request里读取 // 1. 尝试从request里读取
if(request.getAttribute(getKeyJustCreatedSave()) != null) { if(request.getAttribute(splicingKeyJustCreatedSave()) != null) {
tokenValue = String.valueOf(request.getAttribute(getKeyJustCreatedSave())); tokenValue = String.valueOf(request.getAttribute(splicingKeyJustCreatedSave()));
} }
// 2. 尝试从请求体里面读取 // 2. 尝试从请求体里面读取
if(tokenValue == null && config.getIsReadBody()){ if(tokenValue == null && config.getIsReadBody()){
@@ -161,10 +161,10 @@ public class StpLogic {
/** /**
* 在当前会话上登录id, 并指定登录设备 * 在当前会话上登录id, 并指定登录设备
* @param loginId 登录id建议的类型long | int | String * @param loginId 登录id建议的类型long | int | String
* @param isTempCookie 是否为临时Cookie * @param isLastingCookie 是否为持久Cookie
*/ */
public void setLoginId(Object loginId, boolean isTempCookie) { public void setLoginId(Object loginId, boolean isLastingCookie) {
setLoginId(loginId, new SaLoginModel().setIsTempCookie(isTempCookie)); setLoginId(loginId, new SaLoginModel().setIsLastingCookie(isLastingCookie));
} }
/** /**
@@ -197,7 +197,7 @@ public class StpLogic {
for (TokenSign tokenSign : tokenSignList) { for (TokenSign tokenSign : tokenSignList) {
if(tokenSign.getDevice().equals(loginModel.getDevice())) { if(tokenSign.getDevice().equals(loginModel.getDevice())) {
// 1. 将此token 标记为已顶替 // 1. 将此token 标记为已顶替
dao.update(getKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED); dao.update(splicingKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED);
// 2. 清理掉[token-最后操作时间] // 2. 清理掉[token-最后操作时间]
clearLastActivity(tokenSign.getValue()); clearLastActivity(tokenSign.getValue());
// 3. 清理账号session上的token签名记录 // 3. 清理账号session上的token签名记录
@@ -226,17 +226,16 @@ public class StpLogic {
// ------ 4. 持久化其它数据 // ------ 4. 持久化其它数据
// token -> uid // token -> uid
dao.set(getKeyTokenValue(tokenValue), String.valueOf(loginId), loginModel.getTimeout()); dao.set(splicingKeyTokenValue(tokenValue), String.valueOf(loginId), loginModel.getTimeout());
// 将token保存到本次request里 // 将token保存到本次request里
request.setAttribute(getKeyJustCreatedSave(), tokenValue); request.setAttribute(splicingKeyJustCreatedSave(), tokenValue);
// 写入 [最后操作时间] // 写入 [最后操作时间]
setLastActivityToNow(tokenValue); setLastActivityToNow(tokenValue);
// 注入Cookie // 注入Cookie
if(config.getIsReadCookie() == true){ if(config.getIsReadCookie() == true){
HttpServletResponse response = SaTokenManager.getSaTokenServlet().getResponse(); HttpServletResponse response = SaTokenManager.getSaTokenServlet().getResponse();
int cookieTimeout = loginModel.getIsTempCookie() ? -1 : (int)(long)loginModel.getTimeout();
SaTokenManager.getSaTokenCookie().addCookie(response, getTokenName(), tokenValue, SaTokenManager.getSaTokenCookie().addCookie(response, getTokenName(), tokenValue,
"/", config.getCookieDomain(), cookieTimeout); "/", config.getCookieDomain(), loginModel.getCookieTimeout());
} }
} }
@@ -269,7 +268,7 @@ public class StpLogic {
if(loginId == null || NotLoginException.ABNORMAL_LIST.contains(loginId)) { if(loginId == null || NotLoginException.ABNORMAL_LIST.contains(loginId)) {
return; return;
} }
SaTokenManager.getSaTokenDao().delete(getKeyTokenValue(tokenValue)); SaTokenManager.getSaTokenDao().delete(splicingKeyTokenValue(tokenValue));
// 3. 尝试清理账号session上的token签名 (如果为null或已被标记为异常, 那么无需继续执行 ) // 3. 尝试清理账号session上的token签名 (如果为null或已被标记为异常, 那么无需继续执行 )
SaSession session = getSessionByLoginId(loginId, false); SaSession session = getSessionByLoginId(loginId, false);
@@ -313,7 +312,7 @@ public class StpLogic {
// 2. 清理掉[token-最后操作时间] // 2. 清理掉[token-最后操作时间]
clearLastActivity(tokenValue); clearLastActivity(tokenValue);
// 3. 标记:已被踢下线 // 3. 标记:已被踢下线
SaTokenManager.getSaTokenDao().update(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT); SaTokenManager.getSaTokenDao().update(splicingKeyTokenValue(tokenValue), NotLoginException.KICK_OUT);
// 4. 清理账号session上的token签名 // 4. 清理账号session上的token签名
session.removeTokenSign(tokenValue); session.removeTokenSign(tokenValue);
} }
@@ -475,7 +474,7 @@ public class StpLogic {
* @return loginId * @return loginId
*/ */
public String getLoginIdNotHandle(String tokenValue) { public String getLoginIdNotHandle(String tokenValue) {
return SaTokenManager.getSaTokenDao().get(getKeyTokenValue(tokenValue)); return SaTokenManager.getSaTokenDao().get(splicingKeyTokenValue(tokenValue));
} }
@@ -512,7 +511,7 @@ public class StpLogic {
* @return SaSession * @return SaSession
*/ */
public SaSession getSessionByLoginId(Object loginId, boolean isCreate) { public SaSession getSessionByLoginId(Object loginId, boolean isCreate) {
return getSessionBySessionId(getKeySession(loginId), isCreate); return getSessionBySessionId(splicingKeySession(loginId), isCreate);
} }
/** /**
@@ -551,7 +550,7 @@ public class StpLogic {
* @return session会话 * @return session会话
*/ */
public SaSession getTokenSessionByToken(String tokenValue, boolean isCreate) { public SaSession getTokenSessionByToken(String tokenValue, boolean isCreate) {
return getSessionBySessionId(getKeyTokenSession(tokenValue), isCreate); return getSessionBySessionId(splicingKeyTokenSession(tokenValue), isCreate);
} }
/** /**
@@ -560,7 +559,7 @@ public class StpLogic {
* @return session会话 * @return session会话
*/ */
public SaSession getTokenSessionByToken(String tokenValue) { public SaSession getTokenSessionByToken(String tokenValue) {
return getSessionBySessionId(getKeyTokenSession(tokenValue), true); return getSessionBySessionId(splicingKeyTokenSession(tokenValue), true);
} }
/** /**
@@ -579,18 +578,19 @@ public class StpLogic {
// 随机一个token送给Ta // 随机一个token送给Ta
tokenValue = createTokenValue(null); tokenValue = createTokenValue(null);
// Request做上标记 // Request做上标记
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(getKeyJustCreatedSave(), tokenValue); SaTokenManager.getSaTokenServlet().getRequest().setAttribute(splicingKeyJustCreatedSave(), tokenValue);
// 写入 [最后操作时间] // 写入 [最后操作时间]
setLastActivityToNow(tokenValue); setLastActivityToNow(tokenValue);
// cookie注入 // cookie注入
if(getConfig().getIsReadCookie() == true){ if(getConfig().getIsReadCookie() == true){
int cookieTimeout = (int)(getConfig().getTimeout() == SaTokenDao.NEVER_EXPIRE ? Integer.MAX_VALUE : getConfig().getTimeout());
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue,
"/", getConfig().getCookieDomain(), (int)getConfig().getTimeout()); "/", getConfig().getCookieDomain(), cookieTimeout);
} }
} }
} }
// 返回这个token对应的专属session // 返回这个token对应的专属session
return getSessionBySessionId(getKeyTokenSession(getTokenValue()), isCreate); return getSessionBySessionId(splicingKeyTokenSession(getTokenValue()), isCreate);
} }
/** /**
@@ -614,7 +614,7 @@ public class StpLogic {
return; return;
} }
// 将[最后操作时间]标记为当前时间戳 // 将[最后操作时间]标记为当前时间戳
SaTokenManager.getSaTokenDao().set(getKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()), getConfig().getTimeout()); SaTokenManager.getSaTokenDao().set(splicingKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()), getConfig().getTimeout());
} }
/** /**
@@ -627,7 +627,7 @@ public class StpLogic {
return; return;
} }
// 删除[最后操作时间] // 删除[最后操作时间]
SaTokenManager.getSaTokenDao().delete(getKeyLastActivityTime(tokenValue)); SaTokenManager.getSaTokenDao().delete(splicingKeyLastActivityTime(tokenValue));
// 清除标记 // 清除标记
SaTokenManager.getSaTokenServlet().getRequest().removeAttribute(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY); SaTokenManager.getSaTokenServlet().getRequest().removeAttribute(SaTokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY);
} }
@@ -679,7 +679,7 @@ public class StpLogic {
if(tokenValue == null || getConfig().getActivityTimeout() == SaTokenDao.NEVER_EXPIRE) { if(tokenValue == null || getConfig().getActivityTimeout() == SaTokenDao.NEVER_EXPIRE) {
return; return;
} }
SaTokenManager.getSaTokenDao().update(getKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis())); SaTokenManager.getSaTokenDao().update(splicingKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()));
} }
/** /**
@@ -699,7 +699,7 @@ public class StpLogic {
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getTokenTimeout() { public long getTokenTimeout() {
return SaTokenManager.getSaTokenDao().getTimeout(getKeyTokenValue(getTokenValue())); return SaTokenManager.getSaTokenDao().getTimeout(splicingKeyTokenValue(getTokenValue()));
} }
/** /**
@@ -708,7 +708,7 @@ public class StpLogic {
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getTokenTimeoutByLoginId(Object loginId) { public long getTokenTimeoutByLoginId(Object loginId) {
return SaTokenManager.getSaTokenDao().getTimeout(getKeyTokenValue(getTokenValueByLoginId(loginId))); return SaTokenManager.getSaTokenDao().getTimeout(splicingKeyTokenValue(getTokenValueByLoginId(loginId)));
} }
/** /**
@@ -725,7 +725,7 @@ public class StpLogic {
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getSessionTimeoutByLoginId(Object loginId) { public long getSessionTimeoutByLoginId(Object loginId) {
return SaTokenManager.getSaTokenDao().getSessionTimeout(getKeySession(loginId)); return SaTokenManager.getSaTokenDao().getSessionTimeout(splicingKeySession(loginId));
} }
/** /**
@@ -742,7 +742,7 @@ public class StpLogic {
* @return token剩余有效时间 * @return token剩余有效时间
*/ */
public long getTokenSessionTimeoutByTokenValue(String tokenValue) { public long getTokenSessionTimeoutByTokenValue(String tokenValue) {
return SaTokenManager.getSaTokenDao().getSessionTimeout(getKeyTokenSession(tokenValue)); return SaTokenManager.getSaTokenDao().getSessionTimeout(splicingKeyTokenSession(tokenValue));
} }
/** /**
@@ -769,7 +769,7 @@ public class StpLogic {
} }
// ------ 开始查询 // ------ 开始查询
// 获取相关数据 // 获取相关数据
String keyLastActivityTime = getKeyLastActivityTime(tokenValue); String keyLastActivityTime = splicingKeyLastActivityTime(tokenValue);
String lastActivityTimeString = SaTokenManager.getSaTokenDao().get(keyLastActivityTime); String lastActivityTimeString = SaTokenManager.getSaTokenDao().get(keyLastActivityTime);
// 查不到,返回-2 // 查不到,返回-2
if(lastActivityTimeString == null) { if(lastActivityTimeString == null) {
@@ -1015,7 +1015,7 @@ public class StpLogic {
* @return token集合 * @return token集合
*/ */
public List<String> searchTokenValue(String keyword, int start, int size) { public List<String> searchTokenValue(String keyword, int start, int size) {
return SaTokenManager.getSaTokenDao().searchData(getKeyTokenValue(""), keyword, start, size); return SaTokenManager.getSaTokenDao().searchData(splicingKeyTokenValue(""), keyword, start, size);
} }
/** /**
@@ -1026,7 +1026,7 @@ public class StpLogic {
* @return sessionId集合 * @return sessionId集合
*/ */
public List<String> searchSessionId(String keyword, int start, int size) { public List<String> searchSessionId(String keyword, int start, int size) {
return SaTokenManager.getSaTokenDao().searchData(getKeySession(""), keyword, start, size); return SaTokenManager.getSaTokenDao().searchData(splicingKeySession(""), keyword, start, size);
} }
/** /**
@@ -1037,7 +1037,7 @@ public class StpLogic {
* @return sessionId集合 * @return sessionId集合
*/ */
public List<String> searchTokenSessionId(String keyword, int start, int size) { public List<String> searchTokenSessionId(String keyword, int start, int size) {
return SaTokenManager.getSaTokenDao().searchData(getKeyTokenSession(""), keyword, start, size); return SaTokenManager.getSaTokenDao().searchData(splicingKeyTokenSession(""), keyword, start, size);
} }
@@ -1047,7 +1047,7 @@ public class StpLogic {
* 获取key客户端 tokenName * 获取key客户端 tokenName
* @return key * @return key
*/ */
public String getKeyTokenName() { public String splicingKeyTokenName() {
return getConfig().getTokenName(); return getConfig().getTokenName();
} }
@@ -1056,7 +1056,7 @@ public class StpLogic {
* @param tokenValue token值 * @param tokenValue token值
* @return key * @return key
*/ */
public String getKeyTokenValue(String tokenValue) { public String splicingKeyTokenValue(String tokenValue) {
return getConfig().getTokenName() + ":" + loginKey + ":token:" + tokenValue; return getConfig().getTokenName() + ":" + loginKey + ":token:" + tokenValue;
} }
@@ -1065,7 +1065,7 @@ public class StpLogic {
* @param loginId 账号id * @param loginId 账号id
* @return key * @return key
*/ */
public String getKeySession(Object loginId) { public String splicingKeySession(Object loginId) {
return getConfig().getTokenName() + ":" + loginKey + ":session:" + loginId; return getConfig().getTokenName() + ":" + loginKey + ":session:" + loginId;
} }
@@ -1074,7 +1074,7 @@ public class StpLogic {
* @param tokenValue token值 * @param tokenValue token值
* @return key * @return key
*/ */
public String getKeyTokenSession(String tokenValue) { public String splicingKeyTokenSession(String tokenValue) {
return getConfig().getTokenName() + ":" + loginKey + ":token-session:" + tokenValue; return getConfig().getTokenName() + ":" + loginKey + ":token-session:" + tokenValue;
} }
@@ -1083,7 +1083,7 @@ public class StpLogic {
* @param tokenValue token值 * @param tokenValue token值
* @return key * @return key
*/ */
public String getKeyLastActivityTime(String tokenValue) { public String splicingKeyLastActivityTime(String tokenValue) {
return getConfig().getTokenName() + ":" + loginKey + ":last-activity:" + tokenValue; return getConfig().getTokenName() + ":" + loginKey + ":last-activity:" + tokenValue;
} }
@@ -1091,7 +1091,7 @@ public class StpLogic {
* 在进行身份切换时使用的存储key * 在进行身份切换时使用的存储key
* @return key * @return key
*/ */
public String getKeySwitch() { public String splicingKeySwitch() {
return SaTokenConsts.SWITCH_TO_SAVE_KEY + getLoginKey(); return SaTokenConsts.SWITCH_TO_SAVE_KEY + getLoginKey();
} }
@@ -1099,7 +1099,7 @@ public class StpLogic {
* 如果token为本次请求新创建的则以此字符串为key存储在当前request中 * 如果token为本次请求新创建的则以此字符串为key存储在当前request中
* @return key * @return key
*/ */
public String getKeyJustCreatedSave() { public String splicingKeyJustCreatedSave() {
return SaTokenConsts.JUST_CREATED_SAVE_KEY + getLoginKey(); return SaTokenConsts.JUST_CREATED_SAVE_KEY + getLoginKey();
} }
@@ -1184,14 +1184,14 @@ public class StpLogic {
* @param loginId 指定loginId * @param loginId 指定loginId
*/ */
public void switchTo(Object loginId) { public void switchTo(Object loginId) {
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(getKeySwitch(), loginId); SaTokenManager.getSaTokenServlet().getRequest().setAttribute(splicingKeySwitch(), loginId);
} }
/** /**
* 结束临时切换身份 * 结束临时切换身份
*/ */
public void endSwitch() { public void endSwitch() {
SaTokenManager.getSaTokenServlet().getRequest().removeAttribute(getKeySwitch()); SaTokenManager.getSaTokenServlet().getRequest().removeAttribute(splicingKeySwitch());
} }
/** /**
@@ -1199,7 +1199,7 @@ public class StpLogic {
* @return 是否正处于[身份临时切换]中 * @return 是否正处于[身份临时切换]中
*/ */
public boolean isSwitch() { public boolean isSwitch() {
return SaTokenManager.getSaTokenServlet().getRequest().getAttribute(getKeySwitch()) != null; return SaTokenManager.getSaTokenServlet().getRequest().getAttribute(splicingKeySwitch()) != null;
} }
/** /**
@@ -1207,7 +1207,7 @@ public class StpLogic {
* @return 返回[身份临时切换]的loginId * @return 返回[身份临时切换]的loginId
*/ */
public Object getSwitchLoginId() { public Object getSwitchLoginId() {
return SaTokenManager.getSaTokenServlet().getRequest().getAttribute(getKeySwitch()); return SaTokenManager.getSaTokenServlet().getRequest().getAttribute(splicingKeySwitch());
} }
/** /**

View File

@@ -75,10 +75,10 @@ public class StpUtil {
/** /**
* 在当前会话上登录id, 并指定登录设备 * 在当前会话上登录id, 并指定登录设备
* @param loginId 登录id建议的类型long | int | String * @param loginId 登录id建议的类型long | int | String
* @param isTempCookie 是否为临时Cookie * @param isLastingCookie 是否为持久Cookie
*/ */
public void setLoginId(Object loginId, boolean isTempCookie) { public static void setLoginId(Object loginId, boolean isLastingCookie) {
stpLogic.setLoginId(loginId, isTempCookie); stpLogic.setLoginId(loginId, isLastingCookie);
} }
/** /**

View File

@@ -13,7 +13,7 @@ public class SaTokenConsts {
/** /**
* sa-token 版本号 * sa-token 版本号
*/ */
public static final String VERSION_NO = "v1.13.0"; public static final String VERSION_NO = "v1.14.0";
/** /**
* sa-token 开源地址 * sa-token 开源地址

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-parent</artifactId> <artifactId>sa-token-parent</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</parent> </parent>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-parent</artifactId> <artifactId>sa-token-parent</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</parent> </parent>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@@ -16,7 +16,7 @@
<!-- 定义sa-token版本号 --> <!-- 定义sa-token版本号 -->
<properties> <properties>
<sa-token-version>1.13.0</sa-token-version> <sa-token-version>1.14.0</sa-token-version>
</properties> </properties>
<dependencies> <dependencies>

View File

@@ -48,9 +48,9 @@ public class SaTokenJwtUtil {
*/ */
public static String createToken(Object loginId) { public static String createToken(Object loginId) {
// 判断,不可使用默认秘钥 // 判断,不可使用默认秘钥
if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1d3")) { // if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1dd")) {
throw new SaTokenException("请更换秘钥"); // throw new SaTokenException("请更换秘钥");
} // }
// 在这里你可以使用官方提供的claim方法构建载荷也可以使用setPayload自定义载荷但是两者不可一起使用 // 在这里你可以使用官方提供的claim方法构建载荷也可以使用setPayload自定义载荷但是两者不可一起使用
JwtBuilder builder = Jwts.builder() JwtBuilder builder = Jwts.builder()
.setHeaderParam("type", "JWT") .setHeaderParam("type", "JWT")
@@ -128,7 +128,7 @@ public class SaTokenJwtUtil {
SaTokenConfig config = getConfig(); SaTokenConfig config = getConfig();
// ------ 2、生成一个token // ------ 2、生成一个token
String tokenValue = createTokenValue(loginId); String tokenValue = createTokenValue(loginId);
request.setAttribute(getKeyJustCreatedSave(), tokenValue); // 将token保存到本次request里 request.setAttribute(splicingKeyJustCreatedSave(), tokenValue); // 将token保存到本次request里
if(config.getIsReadCookie() == true){ // cookie注入 if(config.getIsReadCookie() == true){ // cookie注入
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", config.getCookieDomain(), (int)config.getTimeout()); SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", config.getCookieDomain(), (int)config.getTimeout());
} }

View File

@@ -16,6 +16,8 @@
<properties> <properties>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version> <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<!-- 定义sa-token版本号 -->
<sa-token-version>1.14.0</sa-token-version>
</properties> </properties>
<dependencies> <dependencies>
@@ -30,7 +32,7 @@
<dependency> <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId> <artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.13.0</version> <version>${sa-token-version}</version>
</dependency> </dependency>
<!-- OkHttps网络请求库 http://okhttps.ejlchina.com/ --> <!-- OkHttps网络请求库 http://okhttps.ejlchina.com/ -->

View File

@@ -16,6 +16,8 @@
<properties> <properties>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version> <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<!-- 定义sa-token版本号 -->
<sa-token-version>1.14.0</sa-token-version>
</properties> </properties>
<dependencies> <dependencies>
@@ -30,21 +32,21 @@
<dependency> <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId> <artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.13.0</version> <version>${sa-token-version}</version>
</dependency> </dependency>
<!-- sa-token 实现 oauth2.0 --> <!-- sa-token 实现 oauth2.0 -->
<dependency> <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-oauth2</artifactId> <artifactId>sa-token-oauth2</artifactId>
<version>1.13.0-alpha</version> <version>1.14.0-alpha</version>
</dependency> </dependency>
<!-- sa-token整合redis (使用jackson序列化方式) --> <!-- sa-token整合redis (使用jackson序列化方式) -->
<!-- <dependency> <!-- <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId> <artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.13.0</version> <version>${sa-token-version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>

View File

@@ -16,7 +16,7 @@
<!-- 定义sa-token版本号 --> <!-- 定义sa-token版本号 -->
<properties> <properties>
<sa-token-version>1.13.0</sa-token-version> <sa-token-version>1.14.0</sa-token-version>
</properties> </properties>
<dependencies> <dependencies>

View File

@@ -8,7 +8,7 @@ import cn.dev33.satoken.SaTokenManager;
@SpringBootApplication @SpringBootApplication
public class SaTokenDemoApplication { public class SaTokenDemoApplication {
public static void main(String[] args) { public static void main(String[] args) throws Exception {
SpringApplication.run(SaTokenDemoApplication.class, args); SpringApplication.run(SaTokenDemoApplication.class, args);
System.out.println("\n启动成功sa-token配置如下" + SaTokenManager.getConfig()); System.out.println("\n启动成功sa-token配置如下" + SaTokenManager.getConfig());
} }

View File

@@ -21,7 +21,7 @@ import cn.dev33.satoken.exception.NotRoleException;
@RestControllerAdvice // 可指定包前缀,比如:(basePackages = "com.pj.admin") @RestControllerAdvice // 可指定包前缀,比如:(basePackages = "com.pj.admin")
public class GlobalException { public class GlobalException {
// 在每个控制器之前触发的操作 // 在当前类每个方法进入之前触发的操作
@ModelAttribute @ModelAttribute
public void get(HttpServletRequest request) throws IOException { public void get(HttpServletRequest request) throws IOException {

View File

@@ -5,7 +5,6 @@ import java.util.List;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -20,7 +19,6 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckRole; import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaMode; import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.session.SaSessionCustomUtil; import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
@@ -232,29 +230,25 @@ public class TestController {
return AjaxJson.getSuccess(); return AjaxJson.getSuccess();
} }
// 测试指定设备登录 浏览器访问: http://localhost:8081/test/loginByDevice
@Autowired @RequestMapping("loginByDevice")
TestService TestService; public AjaxJson loginByDevice() {
System.out.println("--------------");
// 测试AOP注解鉴权 http://localhost:8081/test/testAOP StpUtil.setLoginId(10001, "PC");
@RequestMapping("testAOP") return AjaxJson.getSuccessData("登录成功");
public AjaxJson testAOP() {
System.out.println("testAOP");
TestService.getList();
return AjaxJson.getSuccess();
} }
// 测试 浏览器访问: http://localhost:8081/test/test // 测试 浏览器访问: http://localhost:8081/test/test
@RequestMapping("test") @RequestMapping("test")
public AjaxJson test(HttpServletResponse response) { public AjaxJson test(HttpServletResponse response) {
// StpUtil.getTokenSession().logout(); System.out.println("进来了");
// StpUtil.logoutByLoginId(10001); // StpUtil.setLoginId(10001, new SaLoginModel()
// StpUtil.setLoginId(10001); // .setDevice("PC") // 此次登录的客户端设备标识, 用于[同端互斥登录]时指定此次登录的设备名称
StpUtil.setLoginId(10001, new SaLoginModel().setIsTempCookie(true)); // .setIsLastingCookie(true) // 是否为持久Cookie临时Cookie在浏览器关闭时会自动删除持久Cookie在重新打开后依然存在
StpUtil.getLoginId(); // .setTimeout(60 * 60 * 24 * 7) // 指定此次登录token的有效期, 单位:秒 如未指定自动取全局配置的timeout值
// );
return AjaxJson.getSuccess(); return AjaxJson.getSuccess("访问成功");
} }
// 测试 浏览器访问: http://localhost:8081/test/test2 // 测试 浏览器访问: http://localhost:8081/test/test2

View File

@@ -1,25 +0,0 @@
package com.pj.test;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import cn.dev33.satoken.annotation.SaCheckLogin;
/**
* 用来测试AOP注解鉴权
* @author kong
*
*/
@Service
public class TestService {
@SaCheckLogin
public List<String> getList() {
System.out.println("getList");
return new ArrayList<String>();
}
}

View File

@@ -1,11 +1,11 @@
<p align="center"> <p align="center">
<img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150"> <img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150">
</p> </p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.13.0</h1> <h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.14.0</h1>
<h4 align="center">这可能是史上功能最全的Java权限认证框架</h4> <h4 align="center">这可能是史上功能最全的Java权限认证框架</h4>
<h4 align="center"> <h4 align="center">
<a href="https://gitee.com/sz6/sa-token/stargazers"><img src="https://gitee.com/sz6/sa-token/badge/star.svg"></a> <a href="https://gitee.com/sz6/sa-token/stargazers"><img src="https://gitee.com/sz6/sa-token/badge/star.svg"></a>
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.13.0-2B9939"></a> <a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.14.0-2B9939"></a>
<a href="https://github.com/click33/sa-token/stargazers"><img src="https://img.shields.io/github/stars/click33/sa-token"></a> <a href="https://github.com/click33/sa-token/stargazers"><img src="https://img.shields.io/github/stars/click33/sa-token"></a>
<a href="https://github.com/click33/sa-token/watchers"><img src="https://img.shields.io/github/watchers/click33/sa-token"></a> <a href="https://github.com/click33/sa-token/watchers"><img src="https://img.shields.io/github/watchers/click33/sa-token"></a>
<a href="https://github.com/click33/sa-token/network/members"><img src="https://img.shields.io/github/forks/click33/sa-token"></a> <a href="https://github.com/click33/sa-token/network/members"><img src="https://img.shields.io/github/forks/click33/sa-token"></a>
@@ -55,7 +55,7 @@ sa-token是一个轻量级Java权限认证框架主要解决登录认证
- **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权 - **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
- **花式token生成** —— 内置六种token风格还可自定义token生成策略 - **花式token生成** —— 内置六种token风格还可自定义token生成策略
- **注解式鉴权** —— 优雅的将鉴权与业务代码分离 - **注解式鉴权** —— 优雅的将鉴权与业务代码分离
- **路由拦截式鉴权** —— 设定全局路由拦截,并排除指定路由 - **路由拦截式鉴权** —— 根据路由拦截鉴权可适配restful模式
- **自动续签** —— 提供两种token过期策略灵活搭配使用还可自动续签 - **自动续签** —— 提供两种token过期策略灵活搭配使用还可自动续签
- **会话治理** —— 提供方便灵活的会话查询接口 - **会话治理** —— 提供方便灵活的会话查询接口
- **组件自动注入** —— 零配置与Spring等框架集成 - **组件自动注入** —— 零配置与Spring等框架集成
@@ -87,7 +87,8 @@ StpUtil.checkLogin();
@SaCheckPermission("user:add") @SaCheckPermission("user:add")
@RequestMapping("/user/insert") @RequestMapping("/user/insert")
public String insert(SysUser user) { public String insert(SysUser user) {
return "用户增加"; // ...
return "用户增加";
} }
``` ```
@@ -185,10 +186,12 @@ sa-token秉承着开放的思想欢迎大家贡献代码为框架添砖加
## 交流群 ## 交流群
QQ交流群[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) QQ交流群[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM)
![扫码加群](https://color-test.oss-cn-qingdao.aliyuncs.com/sa-token/qq-group.png ':size=150') ![扫码加群](https://color-test.oss-cn-qingdao.aliyuncs.com/sa-token/qq-group.png ':size=230')
**微信群** **微信群**
![微信群](https://images.gitee.com/uploads/images/2021/0216/170729_25fcde8b_8073399.jpeg "sa-token-wx.jpg") ![微信群](https://dev33-test.oss-cn-beijing.aliyuncs.com/sa-token/i-wx-qr.png ':size=230')
(扫码添加微信备注sa-token邀您加入群聊)
<br> <br>

View File

@@ -19,6 +19,8 @@
- [花式token](/use/token-style) - [花式token](/use/token-style)
- [框架配置](/use/config) - [框架配置](/use/config)
- [会话治理](/use/search-session) - [会话治理](/use/search-session)
- [记住我模式](/use/remember-me)
- [密码加密](/use/password-secure)
- **进阶** - **进阶**
- [集群、分布式](/senior/dcs) - [集群、分布式](/senior/dcs)
@@ -33,6 +35,7 @@
- **附录** - **附录**
- [未登录场景值](/fun/not-login-scene) - [未登录场景值](/fun/not-login-scene)
- [token有效期详解](/fun/token-timeout) - [token有效期详解](/fun/token-timeout)
- [Session模型详解](/fun/session-model)
- [TokenInfo参数详解](/fun/token-info) - [TokenInfo参数详解](/fun/token-info)

View File

@@ -0,0 +1,21 @@
# Session模型详解
---
`sa-token`中, `Session` 分为三种, 分别是:
- `User-Session`: 指的是框架为每个`loginId`分配的`Session`
- `Token-Session`: 指的是框架为每个`token`分配的`Session`
- `自定义Session`: 指的是以一个`特定的值`作为SessionId来分配的`Session`
User-Session和Token-Session到底有什么不同下面这张图可以解释两者的区别
![../static/session-model.png](../static/session-model.png)
简而言之:
- `Token-Session` 以token为主只要token不同那么对应的Session对象就不同
- `User-Session` 以UserId为主只要token指向的UserId一致那么对应的Session对象就一致
- `自定义Session` 以特定的key为主不同key对应不同的Session对象

View File

@@ -21,6 +21,7 @@
<nav> <nav>
<select onchange="location.href=this.value"> <select onchange="location.href=this.value">
<option value="http://sa-token.dev33.cn/doc/index.html">最新版</option> <option value="http://sa-token.dev33.cn/doc/index.html">最新版</option>
<option value="http://sa-token.dev33.cn/v/v1.13.0/doc/index.html">v1.13.0</option>
<option value="http://sa-token.dev33.cn/v/v1.12.1/doc/index.html">v1.12.1</option> <option value="http://sa-token.dev33.cn/v/v1.12.1/doc/index.html">v1.12.1</option>
<option value="http://sa-token.dev33.cn/v/v1.12.0/doc/index.html">v1.12.0</option> <option value="http://sa-token.dev33.cn/v/v1.12.0/doc/index.html">v1.12.0</option>
<option value="http://sa-token.dev33.cn/v/v1.11.0/doc/index.html">v1.11.0</option> <option value="http://sa-token.dev33.cn/v/v1.11.0/doc/index.html">v1.11.0</option>
@@ -42,7 +43,7 @@
</div> </div>
<script> <script>
var name = '<img style="width: 50px; height: 50px; vertical-align: middle;" src="logo.png" alt="logo" /> '; var name = '<img style="width: 50px; height: 50px; vertical-align: middle;" src="logo.png" alt="logo" /> ';
name += '<b style="font-size: 24px; vertical-align: middle;">sa-token</b> <sub>v1.13.0</sub>' name += '<b style="font-size: 24px; vertical-align: middle;">sa-token</b> <sub>v1.14.0</sub>'
window.$docsify = { window.$docsify = {
name: name, // 名字 name: name, // 名字
repo: 'https://github.com/click33/sa-token', // github地址 repo: 'https://github.com/click33/sa-token', // github地址

View File

@@ -49,7 +49,6 @@
可能是404了SpringBoot环境下如果访问接口404后会被重定向到`/error`,然后被再次拦截,如果是其它原因,欢迎加群反馈 可能是404了SpringBoot环境下如果访问接口404后会被重定向到`/error`,然后被再次拦截,如果是其它原因,欢迎加群反馈
### 权限可以做成动态的吗? ### 权限可以做成动态的吗?
权限本来就是动态的只有jwt那种模式才是非动态的 权限本来就是动态的只有jwt那种模式才是非动态的
@@ -71,9 +70,18 @@
步骤:先在配置文件里将`tokenSessionCheckLogin`配置为`false`,然后通过`StpUtil.getTokenSession()`获取Session 步骤:先在配置文件里将`tokenSessionCheckLogin`配置为`false`,然后通过`StpUtil.getTokenSession()`获取Session
### 我只使用header来传输token还需要打开Cookie模式吗
不需要如果只使用header来传输token可以在配置文件关闭Cookie模式`isReadCookie=false`
### 我想让用户修改密码后立即掉线重新登录,应该怎么做?
框架内置 [强制指定账号下线] 的APi在执行修改密码逻辑之后调用此API即可: `StpUtil.logout()`
### 还是有不明白到的地方? ### 还是有不明白到的地方?
请在`github`提交`issues`或者加入qq群交流群链接在[首页](README?id=交流群) 请在`github`提交`issues`或者加入qq群交流群链接在[首页](README?id=交流群)
### 我能为这个框架贡献代码吗? ### 我能为这个框架贡献代码吗?
**可以**请参照首页的提交pr步骤 [贡献代码](README?id=贡献代码) **可以**请参照首页的提交pr步骤 [贡献代码](README?id=贡献代码)

View File

@@ -17,6 +17,8 @@
[[sa-admin] - 一个多窗口后台模板,流畅、易上手、提高生产力](https://gitee.com/ejlchina-zhxu/okhttps) [[sa-admin] - 一个多窗口后台模板,流畅、易上手、提高生产力](https://gitee.com/ejlchina-zhxu/okhttps)
[[vue-next-admin] - 一套为开发者快速开发准备的基于 vue2.x 越看越精彩的后台管理系统一站式平台模板](https://gitee.com/lyt-top/vue-admin-wonderful)
<br> <br>
虚位以待... 虚位以待...

View File

@@ -1,5 +1,22 @@
# 更新日志 # 更新日志
### 2021-3-12 @v1.14.0
- 新增:新增`SaLoginModel`登录参数Model适配 [记住我] 模式 **[重要]**
- 新增:新增 `StpUtil.setLoginId()` 时指定token有效期可灵活控制用户的一次登录免验证时长
- 新增新增Cookie时间判断`timeout`设置为-1时`Cookie`有效期将为`Integer.MAX_VALUE` **[重要]**
- 新增新增密码加密工具类可快速MD5、SHA1、SHA256、AES、RSA加密 **[重要]**
- 新增:新增 OAuth2.0 模块 **[重要]**
- 新增:`SaTokenConfig`配置类所有set方法支持链式调用
- 新增:`SaOAuth2Config` sa-token oauth2 配置类所有set方法新增支持链式调用
- 优化:`StpLogic`类所有`getKey`方法重名为`splicingKey`,更语义化的函数名称
- 新增:`IsRunFunction`新增`noExe`函数,用于指定当`isRun`值为`false`时执行的函数
- 新增:`SaSession`新增数据存取值操作API
- 优化:优化`SaTokenDao`接口增加Object操作API
- 优化jwt示例`createToken`方法去除默认秘钥判断,只在启动项目时打印警告
- 文档:常见问题新增示例(修改密码后如何立即掉线)
- 文档:权限认证文档新增[如何把权限精确搭到按钮级]示例说明
- 文档:优化文档,部分模块添加图片说明
### 2021-2-9 @v1.13.0 ### 2021-2-9 @v1.13.0
- 优化:优化源码注释与文档 - 优化:优化源码注释与文档

View File

@@ -9,14 +9,14 @@
<dependency> <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId> <artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</dependency> </dependency>
``` ```
## Gradle依赖 ## Gradle依赖
Gradle用户引入依赖 Gradle用户引入依赖
``` ```
implementation 'cn.dev33:sa-token-spring-boot-starter:1.13.0' implementation 'cn.dev33:sa-token-spring-boot-starter:1.14.0'
``` ```
@@ -34,8 +34,12 @@ implementation 'cn.dev33:sa-token-spring-boot-starter:1.13.0'
├── sa-token-dao-redis // sa-token整合redis (使用jdk默认序列化方式) ├── sa-token-dao-redis // sa-token整合redis (使用jdk默认序列化方式)
├── sa-token-dao-redis-jackson // sa-token整合redis (使用jackson序列化方式) ├── sa-token-dao-redis-jackson // sa-token整合redis (使用jackson序列化方式)
├── sa-token-spring-aop // sa-token整合SpringAOP 注解鉴权 ├── sa-token-spring-aop // sa-token整合SpringAOP 注解鉴权
├── sa-token-oauth2 // sa-token实现 OAuth2.0 模块(内测暂未发布)
├── sa-token-demo-springboot // sa-token示例 ├── sa-token-demo-springboot // sa-token示例
├── sa-token-demo-jwt // sa-token集成jwt示例 ├── sa-token-demo-jwt // sa-token集成jwt示例
├── sa-token-demo-oauth2 // sa-token集成OAuth2.0模块示例
├── sa-token-demo-oauth2-client // OAuth2.0 客户端
├── sa-token-demo-oauth2-server // OAuth2.0 服务端
├── sa-token-doc // sa-token开发文档 ├── sa-token-doc // sa-token开发文档
├──pom.xml ├──pom.xml
``` ```
@@ -46,7 +50,7 @@ implementation 'cn.dev33:sa-token-spring-boot-starter:1.13.0'
## jar包下载 ## jar包下载
[点击下载sa-token-1.6.0.jar](https://oss.dev33.cn/sa-token/sa-token-1.6.0.jar) [点击下载sa-token-1.6.0.jar](https://oss.dev33.cn/sa-token/sa-token-1.6.0.jar)
(注意:当前仅提供`v1.6.0`版本jar包下载更多版本请前往maven中央仓库获取) (注意:当前仅提供`v1.6.0`版本jar包下载更多版本请前往maven中央仓库获取[直达链接](https://search.maven.org/search?q=sa-token))

View File

@@ -19,7 +19,7 @@
<dependency> <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId> <artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</dependency> </dependency>
``` ```

BIN
sa-token-doc/doc/static/kickout.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
sa-token-doc/doc/static/login-view.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
sa-token-doc/doc/static/not-jur.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
sa-token-doc/doc/static/not-login.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -21,7 +21,7 @@ sa-token内置两种模式完成注解鉴权分别是`AOP模式`和`拦截器
<dependency> <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-aop</artifactId> <artifactId>sa-token-spring-aop</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</dependency> </dependency>
``` ```

View File

@@ -9,13 +9,12 @@ Sa-token默认将会话数据保存在内存中此模式读写速度最快
### 1. sa-token 整合 Redis (使用jdk默认序列化方式) ### 1. sa-token 整合 Redis (使用jdk默认序列化方式)
(注意: 整合Redis只需要引入对应的pom依赖即可所有上层API保持不变)
``` xml ``` xml
<!-- sa-token整合redis (使用jdk默认序列化方式) --> <!-- sa-token整合redis (使用jdk默认序列化方式) -->
<dependency> <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis</artifactId> <artifactId>sa-token-dao-redis</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</dependency> </dependency>
``` ```
优点兼容性好缺点Session序列化后基本不可读对开发者来讲等同于乱码 优点兼容性好缺点Session序列化后基本不可读对开发者来讲等同于乱码
@@ -27,7 +26,7 @@ Sa-token默认将会话数据保存在内存中此模式读写速度最快
<dependency> <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId> <artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</dependency> </dependency>
``` ```
优点Session序列化后可读性强可灵活手动修改缺点兼容性稍差 优点Session序列化后可读性强可灵活手动修改缺点兼容性稍差
@@ -35,7 +34,10 @@ Sa-token默认将会话数据保存在内存中此模式读写速度最快
<br> <br>
**请注意无论使用哪种序列化方式你都必须为项目提供一个Redis实例化方案例如** ### 集成Redis请注意
**1. 无论使用哪种序列化方式你都必须为项目提供一个Redis实例化方案例如**
``` xml ``` xml
<!-- 提供redis连接池 --> <!-- 提供redis连接池 -->
<dependency> <dependency>
@@ -44,6 +46,12 @@ Sa-token默认将会话数据保存在内存中此模式读写速度最快
</dependency> </dependency>
``` ```
**2. 引入了依赖我还需要为Redis配置连接信息吗** <br>
需要只有项目初始化了正确的Redis实例`sa-token`才可以使用Redis进行数据持久化参考[application-dev.yml](https://gitee.com/sz6/sa-plus/blob/master/sp-server/src/main/resources/application-dev.yml)
**3. 集成Redis后是我额外手动保存数据还是框架自动保存** <br>
框架自动保存。集成`Redis`只需要引入对应的`pom依赖`即可框架所有上层API保持不变
<br><br> <br><br>

View File

@@ -8,7 +8,9 @@
有,就让你通过。没有?那么禁止访问! 有,就让你通过。没有?那么禁止访问!
再往底了说,就是每个账号都会拥有一个权限码集合,我来验证这个集合中是否包含指定的权限码 <br/> 再往底了说,就是每个账号都会拥有一个权限码集合,我来验证这个集合中是否包含指定的权限码 <br/>
例如:当前账号拥有权限码集合:`["user:add", "user:delete", "user:get"]`,这时候我来验证权 `"user:update"`,则结果就是:**验证失败,禁止访问** 例如:当前账号拥有权限码集合:`["user:add", "user:delete", "user:get"]`,这时候我来验证权 `"user:update"`,则结果就是:**验证失败,禁止访问**
![无权限](../static/not-jur.png)
所以现在问题的核心就是: 所以现在问题的核心就是:
1. 如何获取一个账号所拥有的的权限码集合 1. 如何获取一个账号所拥有的的权限码集合

View File

@@ -1,6 +1,8 @@
# 踢人下线 # 踢人下线
所谓踢人下线,核心操作就是找到其指定`loginId`对应的`token`,并设置其失效 所谓踢人下线,核心操作就是找到其指定`loginId`对应的`token`,并设置其失效
![踢下线](../static/kickout.png)
--- ---

View File

@@ -7,6 +7,7 @@
所谓登录验证,说白了就是限制某些接口只有登录后才能访问(如:查询我的账号资料) <br> 所谓登录验证,说白了就是限制某些接口只有登录后才能访问(如:查询我的账号资料) <br>
那么判断一个会话是否登录的依据是什么?当然是登录成功后框架给你做个标记!然后在需要鉴权的接口里检查此标记,有标记者视为已登录,无标记者视为未登录! 那么判断一个会话是否登录的依据是什么?当然是登录成功后框架给你做个标记!然后在需要鉴权的接口里检查此标记,有标记者视为已登录,无标记者视为未登录!
![暂未登录](../static/not-login.png)
### 登录与注销 ### 登录与注销

View File

@@ -43,7 +43,7 @@
``` java ``` java
// 底层的 StpLogic 对象 // 底层的 StpLogic 对象
public static StpLogic stpLogic = new StpLogic("login") { public static StpLogic stpLogic = new StpLogic("user") {
// 重写 `getTokenName` 函数,返回一个与 `StpUtil` 不同的token名称, 防止冲突 // 重写 `getTokenName` 函数,返回一个与 `StpUtil` 不同的token名称, 防止冲突
@Override @Override
public String getTokenName() { public String getTokenName() {

View File

@@ -0,0 +1,83 @@
# 密码加密
严格来讲,密码加密不属于 [权限认证] 的范畴,但是对于大多数系统来讲,密码加密又是安全认证不可或缺的部分,
所以,应大家要求,`sa-token``v1.14版本`添加密码加密模块,该模块非常简单,仅仅封装了一些常见的加密算法
### 摘要加密
md5、sha1、sha256
``` java
// md5加密
SaSecureUtil.md5("123456");
// sha1加密
SaSecureUtil.sha1("123456");
// sha256加密
SaSecureUtil.sha256("123456");
// md5加盐加密: md5(md5(str) + md5(salt))
SaSecureUtil.md5BySalt("123456", "salt");
```
### 对称加密
AES加密
``` java
// 定义秘钥和明文
String key = "123456";
String text = "sa-token 一个轻量级java权限认证框架";
// 加密
String ciphertext = SaSecureUtil.aesEncrypt(key, text);
System.out.println("AES加密后" + ciphertext);
// 解密
String text2 = SaSecureUtil.aesDecrypt(key, ciphertext);
System.out.println("AES解密后" + text2);
```
### 非对称加密
RSA加密
``` java
// 定义私钥和公钥
String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAO+wmt01pwm9lHMdq7A8gkEigk0XKMfjv+4IjAFhWCSiTeP7dtlnceFJbkWxvbc7Qo3fCOpwmfcskwUc3VSgyiJkNJDs9ivPbvlt8IU2bZ+PBDxYxSCJFrgouVOpAr8ar/b6gNuYTi1vt3FkGtSjACFb002/68RKUTye8/tdcVilAgMBAAECgYA1COmrSqTUJeuD8Su9ChZ0HROhxR8T45PjMmbwIz7ilDsR1+E7R4VOKPZKW4Kz2VvnklMhtJqMs4MwXWunvxAaUFzQTTg2Fu/WU8Y9ha14OaWZABfChMZlpkmpJW9arKmI22ZuxCEsFGxghTiJQ3tK8npj5IZq5vk+6mFHQ6aJAQJBAPghz91Dpuj+0bOUfOUmzi22obWCBncAD/0CqCLnJlpfOoa9bOcXSusGuSPuKy5KiGyblHMgKI6bq7gcM2DWrGUCQQD3SkOcmia2s/6i7DUEzMKaB0bkkX4Ela/xrfV+A3GzTPv9bIBamu0VIHznuiZbeNeyw7sVo4/GTItq/zn2QJdBAkEA8xHsVoyXTVeShaDIWJKTFyT5dJ1TR++/udKIcuiNIap34tZdgGPI+EM1yoTduBM7YWlnGwA9urW0mj7F9e9WIQJAFjxqSfmeg40512KP/ed/lCQVXtYqU7U2BfBTg8pBfhLtEcOg4wTNTroGITwe2NjL5HovJ2n2sqkNXEio6Ji0QQJAFLW1Kt80qypMqot+mHhS+0KfdOpaKeMWMSR4Ij5VfE63WzETEeWAMQESxzhavN1WOTb3/p6icgcVbgPQBaWhGg==";
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvsJrdNacJvZRzHauwPIJBIoJNFyjH47/uCIwBYVgkok3j+3bZZ3HhSW5Fsb23O0KN3wjqcJn3LJMFHN1UoMoiZDSQ7PYrz275bfCFNm2fjwQ8WMUgiRa4KLlTqQK/Gq/2+oDbmE4tb7dxZBrUowAhW9NNv+vESlE8nvP7XXFYpQIDAQAB";
// 文本
String text = "sa-token 一个轻量级java权限认证框架";
// 使用公钥加密
String ciphertext = SaSecureUtil.rsaEncryptByPublic(publicKey, text);
System.out.println("公钥加密后:" + ciphertext);
// 使用私钥解密
String text2 = SaSecureUtil.rsaDecryptByPrivate(privateKey, ciphertext);
System.out.println("私钥解密后:" + text2);
```
你可能会有疑问,私钥和公钥这么长的一大串,我怎么弄出来,手写吗?当然不是,调用以下方法生成即可
``` java
// 生成一对公钥和私钥其中Map对象 (private=私钥, public=公钥)
System.out.println(SaSecureUtil.rsaGenerateKeyPair());
```
### Base64编码与解码
``` java
// 文本
String text = "sa-token 一个轻量级java权限认证框架";
// 使用Base64编码
String base64Text = SaBase64Util.encode(text);
System.out.println("Base64编码后" + base64Text);
// 使用Base64解码
String text2 = SaBase64Util.decode(base64Text);
System.out.println("Base64解码后" + text2);
```
<br>
如需更多加密算法请提交pr

View File

@@ -0,0 +1,81 @@
# [记住我]模式
---
如下图所示,一般网站的登录界面都会有一个 [ 记住我 ] 按钮,当你勾选它后,即时你关闭浏览器再次打开网站,也依然会处于登录状态,无须重复验证密码
![../static/login-view.png](../static/login-view.png)
那么在sa-token中如何做到 [ 记住我 ] 功能呢?
### 在sa-token中实现记住我功能
sa-token的登录授权**默认就是`[记住我]`模式**,为了实现`[非记住我]`模式, 你需要在登录时如下设置:
``` java
// 设置登录账号id为10001第二个参数指定是否为[记住我]当此值为false后关闭浏览器后再次打开需要重新登录
StpUtil.setLoginId(10001, false);
```
那么sa-token实现`[记住我]`的具体原理是?
### 实现原理
Cookie作为浏览器提供的默认会话跟踪机制其生命周期有两种形式分别是
- 临时Cookie有效期为本次会话只要关闭浏览器窗口Cookie就会消失
- 永久Cookie有效期为一个具体的时间在时间未到期之前即使用户关闭了浏览器Cookie也不会消失
利用Cookie的此特性我们便可以轻松实现 [记住我] 模式:
- 勾选[记住我]按钮时:调用`StpUtil.setLoginId(10001, true)`,在浏览器写入一个`永久Cookie`储存token此时用户即使重启浏览器token依然有效
- 不勾选[记住我]按钮时:调用`StpUtil.setLoginId(10001, false)`,在浏览器写入一个`临时Cookie`储存token此时用户在重启浏览器后token便会消失导致会话失效
### 前后台分离模式下如何实现[记住我]?
此时机智的你😏很快发现一个问题Cookie虽好却无法在前后端分离环境下使用那是不是代表上述方案在APP、小程序等环境中无效
准确的讲答案是肯定的任何基于Cookie的认证方案在前后台分离环境下都会失效原因在于这些客户端默认没有实现Cookie功能不过好在这些客户端一般都提供了替代方案
唯一遗憾的是此场景中token的生命周期需要我们在前端手动控制
以经典跨端框架 [uni-app](https://uniapp.dcloud.io/) 为例,我们可以使用如下方式达到同样的效果:
``` js
// 使用本地存储保存token达到 [永久Cookie] 的效果
uni.setStorageSync("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");
// 使用globalData保存token达到 [临时Cookie] 的效果
getApp().globalData.satoken = "xxxx-xxxx-xxxx-xxxx-xxx";
```
如果你决定在PC浏览器环境下进行前后台分离模式开发那么更加简单
``` js
// 使用 localStorage 保存token达到 [永久Cookie] 的效果
localStorage.setItem("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");
// 使用 sessionStorage 保存token达到 [临时Cookie] 的效果
sessionStorage.setItem("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");
```
Remember me, it's too easy!
### 登录时指定token有效期
登录时不仅可以指定是否为`[记住我]`模式还可以指定一个特定的时间作为token有效时长如下示例
``` java
// 示例1
// 指定token有效期(单位: 秒)如下所示token七天有效
StpUtil.setLoginId(10001, new SaLoginModel().setTimeout(60 * 60 * 24 * 7));
// ----------------------- 示例2所有参数
// `SaLoginModel`为登录参数Model其有诸多参数决定登录时的各种逻辑例如
StpUtil.setLoginId(10001, new SaLoginModel()
.setDevice("PC") // 此次登录的客户端设备标识, 用于[同端互斥登录]时指定此次登录的设备名称
.setIsLastingCookie(true) // 是否为持久Cookie临时Cookie在浏览器关闭时会自动删除持久Cookie在重新打开后依然存在
.setTimeout(60 * 60 * 24 * 7) // 指定此次登录token的有效期, 单位:秒 如未指定自动取全局配置的timeout值
);
```

View File

@@ -78,8 +78,26 @@ public class MySaTokenConfig implements WebMvcConfigurer {
} }
``` ```
## 4、多账号模式下使用方式
很简单,把StpUtil类换成新的权限验证类(比如多账号验证示例里面的StpUserUtil)即可其它调用方法不变
## 4、完整的示例 ``` java
@Configuration
public class MySaTokenConfig implements WebMvcConfigurer {
// 注册sa-token的所有拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaRouteInterceptor((request, response, handler)->{
// 根据路由划分模块,不同模块不同鉴权
SaRouterUtil.match("/user/**", () -> StpUserUtil.checkPermission("user"));
SaRouterUtil.match("/admin/**", () -> StpUtil.checkPermission("admin"));
})).addPathPatterns("/**");
}
}
```
## 5、完整示例
最终的代码,可能会类似于下面的样子: 最终的代码,可能会类似于下面的样子:
``` java ``` java
@@ -120,3 +138,5 @@ public class MySaTokenConfig implements WebMvcConfigurer {

View File

@@ -9,6 +9,8 @@ Session是会话中专业的数据缓存组件通过`Session`我们可以很
- `Token-Session`: 指的是框架为每个`token`分配的`Session` - `Token-Session`: 指的是框架为每个`token`分配的`Session`
- `自定义Session`: 指的是以一个`特定的值`作为SessionId来分配的`Session` - `自定义Session`: 指的是以一个`特定的值`作为SessionId来分配的`Session`
> 有关User-Session与Token-Session的详细区别请参考[Session模型详解](/fun/session-model)
### User-Session ### User-Session
有关账号Session的API如下 有关账号Session的API如下

View File

@@ -41,7 +41,7 @@
<!-- 内容部分 --> <!-- 内容部分 -->
<div class="main-box"> <div class="main-box">
<div class="content-box"> <div class="content-box">
<h1>sa-token<small>v1.13.0</small></h1> <h1>sa-token<small>v1.14.0</small></h1>
<div class="sub-title">这可能是史上功能最全的java权限认证框架</div> <div class="sub-title">这可能是史上功能最全的java权限认证框架</div>
<!-- <p>0配置开箱即用低学习成本</p> --> <!-- <p>0配置开箱即用低学习成本</p> -->
<p>登录验证、权限验证、Session会话、踢人下线、集成Redis、分布式会话、单点登录、前后台分离、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...</p> <p>登录验证、权限验证、Session会话、踢人下线、集成Redis、分布式会话、单点登录、前后台分离、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...</p>

View File

@@ -9,3 +9,4 @@ sa-token-oauth2 模块是 sa-token 实现 oauth2.0 的部分,目前该模块
3. 根据控制台打印,访问测试地址即可:[http://localhost:8002/login.html](http://localhost:8002/login.html) 3. 根据控制台打印,访问测试地址即可:[http://localhost:8002/login.html](http://localhost:8002/login.html)
可结合代码注释学习查看 可结合代码注释学习查看

View File

@@ -7,13 +7,13 @@
<parent> <parent>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-parent</artifactId> <artifactId>sa-token-parent</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</parent> </parent>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>sa-token-dao-redis</name> <name>sa-token-dao-redis</name>
<artifactId>sa-token-oauth2</artifactId> <artifactId>sa-token-oauth2</artifactId>
<version>1.13.0-alpha</version> <version>1.14.0-alpha</version>
<description>sa-token realization oauth2.0</description> <description>sa-token realization oauth2.0</description>
<dependencies> <dependencies>

View File

@@ -32,9 +32,11 @@ public class SaOAuth2Config {
/** /**
* @param codeTimeout 要设置的 codeTimeout * @param codeTimeout 要设置的 codeTimeout
* @return 对象自身
*/ */
public void setCodeTimeout(long codeTimeout) { public SaOAuth2Config setCodeTimeout(long codeTimeout) {
this.codeTimeout = codeTimeout; this.codeTimeout = codeTimeout;
return this;
} }
/** /**
@@ -46,9 +48,11 @@ public class SaOAuth2Config {
/** /**
* @param accessTokenTimeout 要设置的 accessTokenTimeout * @param accessTokenTimeout 要设置的 accessTokenTimeout
* @return 对象自身
*/ */
public void setAccessTokenTimeout(long accessTokenTimeout) { public SaOAuth2Config setAccessTokenTimeout(long accessTokenTimeout) {
this.accessTokenTimeout = accessTokenTimeout; this.accessTokenTimeout = accessTokenTimeout;
return this;
} }
/** /**
@@ -60,9 +64,11 @@ public class SaOAuth2Config {
/** /**
* @param refreshTokenTimeout 要设置的 refreshTokenTimeout * @param refreshTokenTimeout 要设置的 refreshTokenTimeout
* @return 对象自身
*/ */
public void setRefreshTokenTimeout(long refreshTokenTimeout) { public SaOAuth2Config setRefreshTokenTimeout(long refreshTokenTimeout) {
this.refreshTokenTimeout = refreshTokenTimeout; this.refreshTokenTimeout = refreshTokenTimeout;
return this;
} }

View File

@@ -523,7 +523,7 @@ public interface SaOAuth2Interface {
} }
/** /**
* 获取key[Client&账号]最新授权码记录, 持久化使用的key * 获取key[Client and 账号]最新授权码记录, 持久化使用的key
* @param loginId 账号id * @param loginId 账号id
* @param clientId 应用id * @param clientId 应用id
* @return key * @return key

View File

@@ -51,6 +51,7 @@ public class SaOAuth2Util {
* @param clientId 应用id * @param clientId 应用id
* @param scope 权限 * @param scope 权限
* @param loginId 账号id * @param loginId 账号id
* @return 是否已经授权
*/ */
public static boolean isGrant(Object loginId, String clientId, String scope) { public static boolean isGrant(Object loginId, String clientId, String scope) {
return SaOAuth2Manager.getInterface().isGrant(loginId, clientId, scope); return SaOAuth2Manager.getInterface().isGrant(loginId, clientId, scope);

View File

@@ -50,6 +50,7 @@ public class RequestAuthModel {
/** /**
* @param clientId 要设置的 clientId * @param clientId 要设置的 clientId
* @return 对象自身
*/ */
public RequestAuthModel setClientId(String clientId) { public RequestAuthModel setClientId(String clientId) {
this.clientId = clientId; this.clientId = clientId;
@@ -65,6 +66,7 @@ public class RequestAuthModel {
/** /**
* @param scope 要设置的 scope * @param scope 要设置的 scope
* @return 对象自身
*/ */
public RequestAuthModel setScope(String scope) { public RequestAuthModel setScope(String scope) {
this.scope = scope; this.scope = scope;
@@ -80,6 +82,7 @@ public class RequestAuthModel {
/** /**
* @param loginId 要设置的 loginId * @param loginId 要设置的 loginId
* @return 对象自身
*/ */
public RequestAuthModel setLoginId(Object loginId) { public RequestAuthModel setLoginId(Object loginId) {
this.loginId = loginId; this.loginId = loginId;
@@ -95,6 +98,7 @@ public class RequestAuthModel {
/** /**
* @param redirectUri 要设置的 redirectUri * @param redirectUri 要设置的 redirectUri
* @return 对象自身
*/ */
public RequestAuthModel setRedirectUri(String redirectUri) { public RequestAuthModel setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri; this.redirectUri = redirectUri;
@@ -110,6 +114,7 @@ public class RequestAuthModel {
/** /**
* @param responseType 要设置的 responseType * @param responseType 要设置的 responseType
* @return 对象自身
*/ */
public RequestAuthModel setResponseType(String responseType) { public RequestAuthModel setResponseType(String responseType) {
this.responseType = responseType; this.responseType = responseType;
@@ -125,6 +130,7 @@ public class RequestAuthModel {
/** /**
* @param state 要设置的 state * @param state 要设置的 state
* @return 对象自身
*/ */
public RequestAuthModel setState(String state) { public RequestAuthModel setState(String state) {
this.state = state; this.state = state;
@@ -133,6 +139,7 @@ public class RequestAuthModel {
/** /**
* 检查此Model参数是否有效 * 检查此Model参数是否有效
* @return 对象自身
*/ */
public RequestAuthModel checkModel() { public RequestAuthModel checkModel() {
if(SaTokenInsideUtil.isEmpty(clientId)) { if(SaTokenInsideUtil.isEmpty(clientId)) {

View File

@@ -26,7 +26,7 @@ public class ScopeModel {
} }
/** /**
* 构造一个 * 构造一个
* @param id 权限id * @param name 权限名称
* @param introduce 权限详细介绍 * @param introduce 权限详细介绍
*/ */
public ScopeModel(String name, String introduce) { public ScopeModel(String name, String introduce) {

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-parent</artifactId> <artifactId>sa-token-parent</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</parent> </parent>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-parent</artifactId> <artifactId>sa-token-parent</artifactId>
<version>1.13.0</version> <version>1.14.0</version>
</parent> </parent>
<packaging>jar</packaging> <packaging>jar</packaging>