mirror of
https://gitee.com/dromara/sa-token.git
synced 2026-02-27 16:50:24 +08:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9ad71db66 | ||
|
|
9fe0e1f97b | ||
|
|
9f6717f8c0 | ||
|
|
af2d04ca1f | ||
|
|
64e6b58a05 | ||
|
|
275b3cec6f | ||
|
|
e92ff9fb7d | ||
|
|
cffd462cd8 | ||
|
|
9ca2cad5e6 | ||
|
|
a076c8f8fc | ||
|
|
dbddd05a6e | ||
|
|
18760010fc | ||
|
|
9151f9a5c8 | ||
|
|
cdaf9fa65a | ||
|
|
c316746f0d | ||
|
|
0695ee6cdf | ||
|
|
3716b874e3 | ||
|
|
255a15033e | ||
|
|
ad70bb0300 | ||
|
|
8c35d838ff | ||
|
|
1eae6861d1 | ||
|
|
bbcc016fc7 | ||
|
|
c62f9482df | ||
|
|
d2c0ac3f52 | ||
|
|
51b76f07f4 | ||
|
|
0b32a1f552 |
15
README.md
15
README.md
@@ -1,11 +1,11 @@
|
||||
<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">
|
||||
</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">
|
||||
<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/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>
|
||||
@@ -87,7 +87,8 @@ StpUtil.checkLogin();
|
||||
@SaCheckPermission("user:add")
|
||||
@RequestMapping("/user/insert")
|
||||
public String insert(SysUser user) {
|
||||
return "用户增加";
|
||||
// ...
|
||||
return "用户增加";
|
||||
}
|
||||
```
|
||||
|
||||
@@ -185,10 +186,12 @@ sa-token秉承着开放的思想,欢迎大家贡献代码,为框架添砖加
|
||||
## 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM)
|
||||
|
||||

|
||||

|
||||
|
||||
**微信群** :
|
||||
**微信群**
|
||||
|
||||

|
||||

|
||||
|
||||
(扫码添加微信,备注:sa-token,邀您加入群聊)
|
||||
|
||||
<br>
|
||||
|
||||
4
pom.xml
4
pom.xml
@@ -8,7 +8,7 @@
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
|
||||
<!-- 项目介绍 -->
|
||||
<name>sa-token</name>
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
<!-- 一些属性 -->
|
||||
<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>
|
||||
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public class SaTokenActionDefaultImpl implements SaTokenAction {
|
||||
|
||||
|
||||
/**
|
||||
* 生成一个token
|
||||
* 根据一定的算法生成一个token
|
||||
*/
|
||||
@Override
|
||||
public String createToken(Object loginId, String loginKey) {
|
||||
|
||||
@@ -23,14 +23,27 @@ public class IsRunFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据变量决定是否执行此函数
|
||||
*
|
||||
* 当 isRun == true 时执行此函数
|
||||
* @param function 函数
|
||||
* @return 对象自身
|
||||
*/
|
||||
public void exe(SaFunction function) {
|
||||
public IsRunFunction exe(SaFunction function) {
|
||||
if (isRun) {
|
||||
function.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 isRun == false 时执行此函数
|
||||
* @param function 函数
|
||||
* @return 对象自身
|
||||
*/
|
||||
public IsRunFunction noExe(SaFunction function) {
|
||||
if (!isRun) {
|
||||
function.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -226,6 +226,38 @@ public class SaSession implements Serializable {
|
||||
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("");
|
||||
}
|
||||
|
||||
|
||||
// ----------------------- 一些操作
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ public class SaSessionCustomUtil {
|
||||
* @param sessionId 会话id
|
||||
* @return sessionId
|
||||
*/
|
||||
public static String getSessionKey(String sessionId) {
|
||||
public static String splicingSessionKey(String sessionId) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + sessionKey + ":session:" + sessionId;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class SaSessionCustomUtil {
|
||||
* @return 是否存在
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
session = SaTokenManager.getSaTokenAction().createSession(sessionId);
|
||||
SaTokenManager.getSaTokenDao().setSession(session, SaTokenManager.getConfig().getTimeout());
|
||||
@@ -67,7 +67,7 @@ public class SaSessionCustomUtil {
|
||||
* @param sessionId 指定key
|
||||
*/
|
||||
public static void deleteSessionById(String sessionId) {
|
||||
SaTokenManager.getSaTokenDao().deleteSession(getSessionKey(sessionId));
|
||||
SaTokenManager.getSaTokenDao().deleteSession(splicingSessionKey(sessionId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@ package cn.dev33.satoken.stp;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
/**
|
||||
* 调用StpUtil.setLogin()时的配置 Model
|
||||
* 调用 `StpUtil.setLogin()` 时的 [配置参数 Model ]
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@@ -17,16 +18,16 @@ public class SaLoginModel {
|
||||
*/
|
||||
public String device;
|
||||
|
||||
/**
|
||||
* 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在)
|
||||
*/
|
||||
public Boolean isLastingCookie;
|
||||
|
||||
/**
|
||||
* 指定此次登录token的有效期, 单位:秒 (如未指定,自动取全局配置的timeout值)
|
||||
*/
|
||||
public Long timeout;
|
||||
|
||||
/**
|
||||
* 是否为临时Cookie(临时Cookie会在浏览器关闭时自动删除)
|
||||
*/
|
||||
public Boolean isTempCookie;
|
||||
|
||||
|
||||
/**
|
||||
* @return device
|
||||
@@ -44,6 +45,22 @@ public class SaLoginModel {
|
||||
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
|
||||
*/
|
||||
@@ -60,22 +77,21 @@ public class SaLoginModel {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isTempCookie
|
||||
*/
|
||||
public Boolean getIsTempCookie() {
|
||||
return isTempCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isTempCookie 要设置的 isTempCookie
|
||||
* @return 对象自身
|
||||
* @return cookie时长
|
||||
*/
|
||||
public SaLoginModel setIsTempCookie(Boolean isTempCookie) {
|
||||
this.isTempCookie = isTempCookie;
|
||||
return this;
|
||||
public int getCookieTimeout() {
|
||||
if(isLastingCookie == false) {
|
||||
return -1;
|
||||
}
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
return (int)(long)timeout;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建对象,初始化默认值
|
||||
* @return 对象自身
|
||||
@@ -93,12 +109,12 @@ public class SaLoginModel {
|
||||
if(device == null) {
|
||||
device = SaTokenConsts.DEFAULT_LOGIN_DEVICE;
|
||||
}
|
||||
if(isLastingCookie == null) {
|
||||
isLastingCookie = true;
|
||||
}
|
||||
if(timeout == null) {
|
||||
timeout = config.getTimeout();
|
||||
}
|
||||
if(isTempCookie == null) {
|
||||
isTempCookie = false;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -112,4 +128,15 @@ public class SaLoginModel {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* toString
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaLoginModel [device=" + device + ", isLastingCookie=" + isLastingCookie + ", timeout=" + timeout + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class StpLogic {
|
||||
* @return 此StpLogic的token名称
|
||||
*/
|
||||
public String getTokenName() {
|
||||
return getKeyTokenName();
|
||||
return splicingKeyTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,8 +96,8 @@ public class StpLogic {
|
||||
String tokenValue = null;
|
||||
|
||||
// 1. 尝试从request里读取
|
||||
if(request.getAttribute(getKeyJustCreatedSave()) != null) {
|
||||
tokenValue = String.valueOf(request.getAttribute(getKeyJustCreatedSave()));
|
||||
if(request.getAttribute(splicingKeyJustCreatedSave()) != null) {
|
||||
tokenValue = String.valueOf(request.getAttribute(splicingKeyJustCreatedSave()));
|
||||
}
|
||||
// 2. 尝试从请求体里面读取
|
||||
if(tokenValue == null && config.getIsReadBody()){
|
||||
@@ -161,10 +161,10 @@ public class StpLogic {
|
||||
/**
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param isTempCookie 是否为临时Cookie
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
*/
|
||||
public void setLoginId(Object loginId, boolean isTempCookie) {
|
||||
setLoginId(loginId, new SaLoginModel().setIsTempCookie(isTempCookie));
|
||||
public void setLoginId(Object loginId, boolean isLastingCookie) {
|
||||
setLoginId(loginId, new SaLoginModel().setIsLastingCookie(isLastingCookie));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,7 +197,7 @@ public class StpLogic {
|
||||
for (TokenSign tokenSign : tokenSignList) {
|
||||
if(tokenSign.getDevice().equals(loginModel.getDevice())) {
|
||||
// 1. 将此token 标记为已顶替
|
||||
dao.update(getKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED);
|
||||
dao.update(splicingKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED);
|
||||
// 2. 清理掉[token-最后操作时间]
|
||||
clearLastActivity(tokenSign.getValue());
|
||||
// 3. 清理账号session上的token签名记录
|
||||
@@ -226,17 +226,16 @@ public class StpLogic {
|
||||
|
||||
// ------ 4. 持久化其它数据
|
||||
// token -> uid
|
||||
dao.set(getKeyTokenValue(tokenValue), String.valueOf(loginId), loginModel.getTimeout());
|
||||
dao.set(splicingKeyTokenValue(tokenValue), String.valueOf(loginId), loginModel.getTimeout());
|
||||
// 将token保存到本次request里
|
||||
request.setAttribute(getKeyJustCreatedSave(), tokenValue);
|
||||
request.setAttribute(splicingKeyJustCreatedSave(), tokenValue);
|
||||
// 写入 [最后操作时间]
|
||||
setLastActivityToNow(tokenValue);
|
||||
// 注入Cookie
|
||||
if(config.getIsReadCookie() == true){
|
||||
HttpServletResponse response = SaTokenManager.getSaTokenServlet().getResponse();
|
||||
int cookieTimeout = loginModel.getIsTempCookie() ? -1 : (int)(long)loginModel.getTimeout();
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
SaTokenManager.getSaTokenDao().delete(getKeyTokenValue(tokenValue));
|
||||
SaTokenManager.getSaTokenDao().delete(splicingKeyTokenValue(tokenValue));
|
||||
|
||||
// 3. 尝试清理账号session上的token签名 (如果为null或已被标记为异常, 那么无需继续执行 )
|
||||
SaSession session = getSessionByLoginId(loginId, false);
|
||||
@@ -313,7 +312,7 @@ public class StpLogic {
|
||||
// 2. 清理掉[token-最后操作时间]
|
||||
clearLastActivity(tokenValue);
|
||||
// 3. 标记:已被踢下线
|
||||
SaTokenManager.getSaTokenDao().update(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT);
|
||||
SaTokenManager.getSaTokenDao().update(splicingKeyTokenValue(tokenValue), NotLoginException.KICK_OUT);
|
||||
// 4. 清理账号session上的token签名
|
||||
session.removeTokenSign(tokenValue);
|
||||
}
|
||||
@@ -475,7 +474,7 @@ public class StpLogic {
|
||||
* @return loginId
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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会话
|
||||
*/
|
||||
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会话
|
||||
*/
|
||||
public SaSession getTokenSessionByToken(String tokenValue) {
|
||||
return getSessionBySessionId(getKeyTokenSession(tokenValue), true);
|
||||
return getSessionBySessionId(splicingKeyTokenSession(tokenValue), true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -579,18 +578,19 @@ public class StpLogic {
|
||||
// 随机一个token送给Ta
|
||||
tokenValue = createTokenValue(null);
|
||||
// Request做上标记
|
||||
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(getKeyJustCreatedSave(), tokenValue);
|
||||
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(splicingKeyJustCreatedSave(), tokenValue);
|
||||
// 写入 [最后操作时间]
|
||||
setLastActivityToNow(tokenValue);
|
||||
// cookie注入
|
||||
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,
|
||||
"/", getConfig().getCookieDomain(), (int)getConfig().getTimeout());
|
||||
"/", getConfig().getCookieDomain(), cookieTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 返回这个token对应的专属session
|
||||
return getSessionBySessionId(getKeyTokenSession(getTokenValue()), isCreate);
|
||||
return getSessionBySessionId(splicingKeyTokenSession(getTokenValue()), isCreate);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -614,7 +614,7 @@ public class StpLogic {
|
||||
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;
|
||||
}
|
||||
// 删除[最后操作时间]
|
||||
SaTokenManager.getSaTokenDao().delete(getKeyLastActivityTime(tokenValue));
|
||||
SaTokenManager.getSaTokenDao().delete(splicingKeyLastActivityTime(tokenValue));
|
||||
// 清除标记
|
||||
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) {
|
||||
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剩余有效时间
|
||||
*/
|
||||
public long getTokenTimeout() {
|
||||
return SaTokenManager.getSaTokenDao().getTimeout(getKeyTokenValue(getTokenValue()));
|
||||
return SaTokenManager.getSaTokenDao().getTimeout(splicingKeyTokenValue(getTokenValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -708,7 +708,7 @@ public class StpLogic {
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
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剩余有效时间
|
||||
*/
|
||||
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剩余有效时间
|
||||
*/
|
||||
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);
|
||||
// 查不到,返回-2
|
||||
if(lastActivityTimeString == null) {
|
||||
@@ -1015,7 +1015,7 @@ public class StpLogic {
|
||||
* @return token集合
|
||||
*/
|
||||
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集合
|
||||
*/
|
||||
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集合
|
||||
*/
|
||||
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
|
||||
* @return key
|
||||
*/
|
||||
public String getKeyTokenName() {
|
||||
public String splicingKeyTokenName() {
|
||||
return getConfig().getTokenName();
|
||||
}
|
||||
|
||||
@@ -1056,7 +1056,7 @@ public class StpLogic {
|
||||
* @param tokenValue token值
|
||||
* @return key
|
||||
*/
|
||||
public String getKeyTokenValue(String tokenValue) {
|
||||
public String splicingKeyTokenValue(String tokenValue) {
|
||||
return getConfig().getTokenName() + ":" + loginKey + ":token:" + tokenValue;
|
||||
}
|
||||
|
||||
@@ -1065,7 +1065,7 @@ public class StpLogic {
|
||||
* @param loginId 账号id
|
||||
* @return key
|
||||
*/
|
||||
public String getKeySession(Object loginId) {
|
||||
public String splicingKeySession(Object loginId) {
|
||||
return getConfig().getTokenName() + ":" + loginKey + ":session:" + loginId;
|
||||
}
|
||||
|
||||
@@ -1074,7 +1074,7 @@ public class StpLogic {
|
||||
* @param tokenValue token值
|
||||
* @return key
|
||||
*/
|
||||
public String getKeyTokenSession(String tokenValue) {
|
||||
public String splicingKeyTokenSession(String tokenValue) {
|
||||
return getConfig().getTokenName() + ":" + loginKey + ":token-session:" + tokenValue;
|
||||
}
|
||||
|
||||
@@ -1083,7 +1083,7 @@ public class StpLogic {
|
||||
* @param tokenValue token值
|
||||
* @return key
|
||||
*/
|
||||
public String getKeyLastActivityTime(String tokenValue) {
|
||||
public String splicingKeyLastActivityTime(String tokenValue) {
|
||||
return getConfig().getTokenName() + ":" + loginKey + ":last-activity:" + tokenValue;
|
||||
}
|
||||
|
||||
@@ -1091,7 +1091,7 @@ public class StpLogic {
|
||||
* 在进行身份切换时,使用的存储key
|
||||
* @return key
|
||||
*/
|
||||
public String getKeySwitch() {
|
||||
public String splicingKeySwitch() {
|
||||
return SaTokenConsts.SWITCH_TO_SAVE_KEY + getLoginKey();
|
||||
}
|
||||
|
||||
@@ -1099,7 +1099,7 @@ public class StpLogic {
|
||||
* 如果token为本次请求新创建的,则以此字符串为key存储在当前request中
|
||||
* @return key
|
||||
*/
|
||||
public String getKeyJustCreatedSave() {
|
||||
public String splicingKeyJustCreatedSave() {
|
||||
return SaTokenConsts.JUST_CREATED_SAVE_KEY + getLoginKey();
|
||||
}
|
||||
|
||||
@@ -1184,14 +1184,14 @@ public class StpLogic {
|
||||
* @param loginId 指定loginId
|
||||
*/
|
||||
public void switchTo(Object loginId) {
|
||||
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(getKeySwitch(), loginId);
|
||||
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(splicingKeySwitch(), loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束临时切换身份
|
||||
*/
|
||||
public void endSwitch() {
|
||||
SaTokenManager.getSaTokenServlet().getRequest().removeAttribute(getKeySwitch());
|
||||
SaTokenManager.getSaTokenServlet().getRequest().removeAttribute(splicingKeySwitch());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1199,7 +1199,7 @@ public class StpLogic {
|
||||
* @return 是否正处于[身份临时切换]中
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public Object getSwitchLoginId() {
|
||||
return SaTokenManager.getSaTokenServlet().getRequest().getAttribute(getKeySwitch());
|
||||
return SaTokenManager.getSaTokenServlet().getRequest().getAttribute(splicingKeySwitch());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,10 +75,10 @@ public class StpUtil {
|
||||
/**
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param isTempCookie 是否为临时Cookie
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
*/
|
||||
public void setLoginId(Object loginId, boolean isTempCookie) {
|
||||
stpLogic.setLoginId(loginId, isTempCookie);
|
||||
public static void setLoginId(Object loginId, boolean isLastingCookie) {
|
||||
stpLogic.setLoginId(loginId, isLastingCookie);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,7 @@ public class SaTokenConsts {
|
||||
/**
|
||||
* sa-token 版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.13.0";
|
||||
public static final String VERSION_NO = "v1.14.0";
|
||||
|
||||
/**
|
||||
* sa-token 开源地址
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.13.0</sa-token-version>
|
||||
<sa-token-version>1.14.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -48,9 +48,9 @@ public class SaTokenJwtUtil {
|
||||
*/
|
||||
public static String createToken(Object loginId) {
|
||||
// 判断,不可使用默认秘钥
|
||||
if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1d3")) {
|
||||
throw new SaTokenException("请更换秘钥");
|
||||
}
|
||||
// if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1dd")) {
|
||||
// throw new SaTokenException("请更换秘钥");
|
||||
// }
|
||||
// 在这里你可以使用官方提供的claim方法构建载荷,也可以使用setPayload自定义载荷,但是两者不可一起使用
|
||||
JwtBuilder builder = Jwts.builder()
|
||||
.setHeaderParam("type", "JWT")
|
||||
@@ -128,7 +128,7 @@ public class SaTokenJwtUtil {
|
||||
SaTokenConfig config = getConfig();
|
||||
// ------ 2、生成一个token
|
||||
String tokenValue = createTokenValue(loginId);
|
||||
request.setAttribute(getKeyJustCreatedSave(), tokenValue); // 将token保存到本次request里
|
||||
request.setAttribute(splicingKeyJustCreatedSave(), tokenValue); // 将token保存到本次request里
|
||||
if(config.getIsReadCookie() == true){ // cookie注入
|
||||
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", config.getCookieDomain(), (int)config.getTimeout());
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<sa-token-version>1.14.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -30,7 +32,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OkHttps网络请求库: http://okhttps.ejlchina.com/ -->
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<sa-token-version>1.14.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -30,21 +32,21 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 实现 oauth2.0 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-oauth2</artifactId>
|
||||
<version>1.13.0-alpha</version>
|
||||
<version>1.14.0-alpha</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token整合redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.13.0</sa-token-version>
|
||||
<sa-token-version>1.14.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -8,7 +8,7 @@ import cn.dev33.satoken.SaTokenManager;
|
||||
@SpringBootApplication
|
||||
public class SaTokenDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(SaTokenDemoApplication.class, args);
|
||||
System.out.println("\n启动成功:sa-token配置如下:" + SaTokenManager.getConfig());
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import cn.dev33.satoken.exception.NotRoleException;
|
||||
@RestControllerAdvice // 可指定包前缀,比如:(basePackages = "com.pj.admin")
|
||||
public class GlobalException {
|
||||
|
||||
// 在每个控制器之前触发的操作
|
||||
// 在当前类每个方法进入之前触发的操作
|
||||
@ModelAttribute
|
||||
public void get(HttpServletRequest request) throws IOException {
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.util.List;
|
||||
|
||||
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.RequestParam;
|
||||
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.SaMode;
|
||||
import cn.dev33.satoken.session.SaSessionCustomUtil;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@@ -232,29 +230,25 @@ public class TestController {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
TestService TestService;
|
||||
|
||||
// 测试AOP注解鉴权: http://localhost:8081/test/testAOP
|
||||
@RequestMapping("testAOP")
|
||||
public AjaxJson testAOP() {
|
||||
System.out.println("testAOP");
|
||||
TestService.getList();
|
||||
return AjaxJson.getSuccess();
|
||||
// 测试指定设备登录 浏览器访问: http://localhost:8081/test/loginByDevice
|
||||
@RequestMapping("loginByDevice")
|
||||
public AjaxJson loginByDevice() {
|
||||
System.out.println("--------------");
|
||||
StpUtil.setLoginId(10001, "PC");
|
||||
return AjaxJson.getSuccessData("登录成功");
|
||||
}
|
||||
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test(HttpServletResponse response) {
|
||||
// StpUtil.getTokenSession().logout();
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
// StpUtil.setLoginId(10001);
|
||||
StpUtil.setLoginId(10001, new SaLoginModel().setIsTempCookie(true));
|
||||
StpUtil.getLoginId();
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
System.out.println("进来了");
|
||||
// StpUtil.setLoginId(10001, new SaLoginModel()
|
||||
// .setDevice("PC") // 此次登录的客户端设备标识, 用于[同端互斥登录]时指定此次登录的设备名称
|
||||
// .setIsLastingCookie(true) // 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在)
|
||||
// .setTimeout(60 * 60 * 24 * 7) // 指定此次登录token的有效期, 单位:秒 (如未指定,自动取全局配置的timeout值)
|
||||
// );
|
||||
return AjaxJson.getSuccess("访问成功");
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test2
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
<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">
|
||||
</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">
|
||||
<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/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>
|
||||
@@ -55,7 +55,7 @@ sa-token是一个轻量级Java权限认证框架,主要解决:登录认证
|
||||
- **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- **路由拦截式鉴权** —— 设定全局路由拦截,并排除指定路由
|
||||
- **路由拦截式鉴权** —— 根据路由拦截鉴权,可适配restful模式
|
||||
- **自动续签** —— 提供两种token过期策略,灵活搭配使用,还可自动续签
|
||||
- **会话治理** —— 提供方便灵活的会话查询接口
|
||||
- **组件自动注入** —— 零配置与Spring等框架集成
|
||||
@@ -87,7 +87,8 @@ StpUtil.checkLogin();
|
||||
@SaCheckPermission("user:add")
|
||||
@RequestMapping("/user/insert")
|
||||
public String insert(SysUser user) {
|
||||
return "用户增加";
|
||||
// ...
|
||||
return "用户增加";
|
||||
}
|
||||
```
|
||||
|
||||
@@ -185,10 +186,12 @@ sa-token秉承着开放的思想,欢迎大家贡献代码,为框架添砖加
|
||||
## 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM)
|
||||
|
||||

|
||||

|
||||
|
||||
**微信群** :
|
||||
**微信群**
|
||||
|
||||

|
||||

|
||||
|
||||
(扫码添加微信,备注:sa-token,邀您加入群聊)
|
||||
|
||||
<br>
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
- [花式token](/use/token-style)
|
||||
- [框架配置](/use/config)
|
||||
- [会话治理](/use/search-session)
|
||||
- [记住我模式](/use/remember-me)
|
||||
- [密码加密](/use/password-secure)
|
||||
|
||||
- **进阶**
|
||||
- [集群、分布式](/senior/dcs)
|
||||
@@ -33,6 +35,7 @@
|
||||
- **附录**
|
||||
- [未登录场景值](/fun/not-login-scene)
|
||||
- [token有效期详解](/fun/token-timeout)
|
||||
- [Session模型详解](/fun/session-model)
|
||||
- [TokenInfo参数详解](/fun/token-info)
|
||||
|
||||
|
||||
|
||||
21
sa-token-doc/doc/fun/session-model.md
Normal file
21
sa-token-doc/doc/fun/session-model.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Session模型详解
|
||||
|
||||
---
|
||||
|
||||
在`sa-token`中, `Session` 分为三种, 分别是:
|
||||
- `User-Session`: 指的是框架为每个`loginId`分配的`Session`
|
||||
- `Token-Session`: 指的是框架为每个`token`分配的`Session`
|
||||
- `自定义Session`: 指的是以一个`特定的值`作为SessionId,来分配的`Session`
|
||||
|
||||
|
||||
User-Session和Token-Session到底有什么不同?下面这张图可以解释两者的区别
|
||||
|
||||

|
||||
|
||||
简而言之:
|
||||
- `Token-Session` 以token为主,只要token不同,那么对应的Session对象就不同
|
||||
- `User-Session` 以UserId为主,只要token指向的UserId一致,那么对应的Session对象就一致
|
||||
- `自定义Session` 以特定的key为主,不同key对应不同的Session对象
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<nav>
|
||||
<select onchange="location.href=this.value">
|
||||
<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.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>
|
||||
@@ -42,7 +43,7 @@
|
||||
</div>
|
||||
<script>
|
||||
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 = {
|
||||
name: name, // 名字
|
||||
repo: 'https://github.com/click33/sa-token', // github地址
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
可能是404了,SpringBoot环境下如果访问接口404后,会被重定向到`/error`,然后被再次拦截,如果是其它原因,欢迎加群反馈
|
||||
|
||||
|
||||
|
||||
### 权限可以做成动态的吗?
|
||||
权限本来就是动态的,只有jwt那种模式才是非动态的
|
||||
|
||||
@@ -71,9 +70,18 @@
|
||||
步骤:先在配置文件里将`tokenSessionCheckLogin`配置为`false`,然后通过`StpUtil.getTokenSession()`获取Session
|
||||
|
||||
|
||||
### 我只使用header来传输token,还需要打开Cookie模式吗?
|
||||
不需要,如果只使用header来传输token,可以在配置文件关闭Cookie模式,例:`isReadCookie=false`
|
||||
|
||||
|
||||
### 我想让用户修改密码后立即掉线重新登录,应该怎么做?
|
||||
框架内置 [强制指定账号下线] 的APi,在执行修改密码逻辑之后调用此API即可: `StpUtil.logout()`
|
||||
|
||||
|
||||
### 还是有不明白到的地方?
|
||||
请在`github`提交`issues`,或者加入qq群交流(群链接在[首页](README?id=交流群))
|
||||
|
||||
|
||||
### 我能为这个框架贡献代码吗?
|
||||
**可以**,请参照首页的提交pr步骤 ,[贡献代码](README?id=贡献代码)
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
[[sa-admin] - 一个多窗口后台模板,流畅、易上手、提高生产力](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
|
||||
[[vue-next-admin] - 一套为开发者快速开发准备的基于 vue2.x 越看越精彩的后台管理系统一站式平台模板](https://gitee.com/lyt-top/vue-admin-wonderful)
|
||||
|
||||
|
||||
<br>
|
||||
虚位以待...
|
||||
|
||||
@@ -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
|
||||
- 优化:优化源码注释与文档
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## 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-jackson // sa-token整合redis (使用jackson序列化方式)
|
||||
├── sa-token-spring-aop // sa-token整合SpringAOP 注解鉴权
|
||||
├── sa-token-oauth2 // sa-token实现 OAuth2.0 模块(内测暂未发布)
|
||||
├── sa-token-demo-springboot // sa-token示例
|
||||
├── 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开发文档
|
||||
├──pom.xml
|
||||
```
|
||||
@@ -46,7 +50,7 @@ implementation 'cn.dev33:sa-token-spring-boot-starter:1.13.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))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
BIN
sa-token-doc/doc/static/kickout.png
vendored
Normal file
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
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
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
BIN
sa-token-doc/doc/static/not-login.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
sa-token-doc/doc/static/session-model.png
vendored
Normal file
BIN
sa-token-doc/doc/static/session-model.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -21,7 +21,7 @@ sa-token内置两种模式完成注解鉴权,分别是`AOP模式`和`拦截器
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-aop</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -9,13 +9,12 @@ Sa-token默认将会话数据保存在内存中,此模式读写速度最快,
|
||||
|
||||
|
||||
### 1. sa-token 整合 Redis (使用jdk默认序列化方式)
|
||||
(注意: 整合Redis只需要引入对应的pom依赖即可,所有上层API保持不变)
|
||||
``` xml
|
||||
<!-- sa-token整合redis (使用jdk默认序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</dependency>
|
||||
```
|
||||
优点:兼容性好,缺点:Session序列化后基本不可读,对开发者来讲等同于乱码
|
||||
@@ -27,7 +26,7 @@ Sa-token默认将会话数据保存在内存中,此模式读写速度最快,
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</dependency>
|
||||
```
|
||||
优点:Session序列化后可读性强,可灵活手动修改,缺点:兼容性稍差
|
||||
@@ -35,7 +34,10 @@ Sa-token默认将会话数据保存在内存中,此模式读写速度最快,
|
||||
|
||||
<br>
|
||||
|
||||
**请注意,无论使用哪种序列化方式,你都必须为项目提供一个Redis实例化方案,例如:**
|
||||
### 集成Redis请注意
|
||||
|
||||
|
||||
**1. 无论使用哪种序列化方式,你都必须为项目提供一个Redis实例化方案,例如:**
|
||||
``` xml
|
||||
<!-- 提供redis连接池 -->
|
||||
<dependency>
|
||||
@@ -44,6 +46,12 @@ Sa-token默认将会话数据保存在内存中,此模式读写速度最快,
|
||||
</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>
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
有,就让你通过。没有?那么禁止访问!
|
||||
|
||||
再往底了说,就是每个账号都会拥有一个权限码集合,我来验证这个集合中是否包含指定的权限码 <br/>
|
||||
例如:当前账号拥有权限码集合:`["user:add", "user:delete", "user:get"]`,这时候我来验证权 `"user:update"`,则结果就是:**验证失败,禁止访问**
|
||||
例如:当前账号拥有权限码集合:`["user:add", "user:delete", "user:get"]`,这时候我来验证权限 `"user:update"`,则其结果就是:**验证失败,禁止访问**
|
||||
|
||||

|
||||
|
||||
所以现在问题的核心就是:
|
||||
1. 如何获取一个账号所拥有的的权限码集合
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# 踢人下线
|
||||
所谓踢人下线,核心操作就是找到其指定`loginId`对应的`token`,并设置其失效
|
||||
|
||||

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

|
||||
|
||||
|
||||
### 登录与注销
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
``` java
|
||||
// 底层的 StpLogic 对象
|
||||
public static StpLogic stpLogic = new StpLogic("login") {
|
||||
public static StpLogic stpLogic = new StpLogic("user") {
|
||||
// 重写 `getTokenName` 函数,返回一个与 `StpUtil` 不同的token名称, 防止冲突
|
||||
@Override
|
||||
public String getTokenName() {
|
||||
|
||||
83
sa-token-doc/doc/use/password-secure.md
Normal file
83
sa-token-doc/doc/use/password-secure.md
Normal 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
|
||||
81
sa-token-doc/doc/use/remember-me.md
Normal file
81
sa-token-doc/doc/use/remember-me.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# [记住我]模式
|
||||
---
|
||||
|
||||
如下图所示,一般网站的登录界面都会有一个 [ 记住我 ] 按钮,当你勾选它后,即时你关闭浏览器再次打开网站,也依然会处于登录状态,无须重复验证密码
|
||||
|
||||

|
||||
|
||||
那么在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值)
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -120,3 +138,5 @@ public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ Session是会话中专业的数据缓存组件,通过`Session`我们可以很
|
||||
- `Token-Session`: 指的是框架为每个`token`分配的`Session`
|
||||
- `自定义Session`: 指的是以一个`特定的值`作为SessionId,来分配的`Session`
|
||||
|
||||
> 有关User-Session与Token-Session的详细区别,请参考:[Session模型详解](/fun/session-model)
|
||||
|
||||
|
||||
### User-Session
|
||||
有关账号Session的API如下:
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<!-- 内容部分 -->
|
||||
<div class="main-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>
|
||||
<!-- <p>0配置开箱即用,低学习成本</p> -->
|
||||
<p>登录验证、权限验证、Session会话、踢人下线、集成Redis、分布式会话、单点登录、前后台分离、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...</p>
|
||||
|
||||
@@ -9,3 +9,4 @@ sa-token-oauth2 模块是 sa-token 实现 oauth2.0 的部分,目前该模块
|
||||
3. 根据控制台打印,访问测试地址即可:[http://localhost:8002/login.html](http://localhost:8002/login.html)
|
||||
|
||||
可结合代码注释学习查看
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sa-token-dao-redis</name>
|
||||
<artifactId>sa-token-oauth2</artifactId>
|
||||
<version>1.13.0-alpha</version>
|
||||
<version>1.14.0-alpha</version>
|
||||
<description>sa-token realization oauth2.0</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -32,9 +32,11 @@ public class SaOAuth2Config {
|
||||
|
||||
/**
|
||||
* @param codeTimeout 要设置的 codeTimeout
|
||||
* @return 对象自身
|
||||
*/
|
||||
public void setCodeTimeout(long codeTimeout) {
|
||||
public SaOAuth2Config setCodeTimeout(long codeTimeout) {
|
||||
this.codeTimeout = codeTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,9 +48,11 @@ public class SaOAuth2Config {
|
||||
|
||||
/**
|
||||
* @param accessTokenTimeout 要设置的 accessTokenTimeout
|
||||
* @return 对象自身
|
||||
*/
|
||||
public void setAccessTokenTimeout(long accessTokenTimeout) {
|
||||
public SaOAuth2Config setAccessTokenTimeout(long accessTokenTimeout) {
|
||||
this.accessTokenTimeout = accessTokenTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,9 +64,11 @@ public class SaOAuth2Config {
|
||||
|
||||
/**
|
||||
* @param refreshTokenTimeout 要设置的 refreshTokenTimeout
|
||||
* @return 对象自身
|
||||
*/
|
||||
public void setRefreshTokenTimeout(long refreshTokenTimeout) {
|
||||
public SaOAuth2Config setRefreshTokenTimeout(long refreshTokenTimeout) {
|
||||
this.refreshTokenTimeout = refreshTokenTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -523,7 +523,7 @@ public interface SaOAuth2Interface {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取key:[Client&账号]最新授权码记录, 持久化使用的key
|
||||
* 获取key:[Client and 账号]最新授权码记录, 持久化使用的key
|
||||
* @param loginId 账号id
|
||||
* @param clientId 应用id
|
||||
* @return key
|
||||
|
||||
@@ -51,6 +51,7 @@ public class SaOAuth2Util {
|
||||
* @param clientId 应用id
|
||||
* @param scope 权限
|
||||
* @param loginId 账号id
|
||||
* @return 是否已经授权
|
||||
*/
|
||||
public static boolean isGrant(Object loginId, String clientId, String scope) {
|
||||
return SaOAuth2Manager.getInterface().isGrant(loginId, clientId, scope);
|
||||
|
||||
@@ -50,6 +50,7 @@ public class RequestAuthModel {
|
||||
|
||||
/**
|
||||
* @param clientId 要设置的 clientId
|
||||
* @return 对象自身
|
||||
*/
|
||||
public RequestAuthModel setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
@@ -65,6 +66,7 @@ public class RequestAuthModel {
|
||||
|
||||
/**
|
||||
* @param scope 要设置的 scope
|
||||
* @return 对象自身
|
||||
*/
|
||||
public RequestAuthModel setScope(String scope) {
|
||||
this.scope = scope;
|
||||
@@ -80,6 +82,7 @@ public class RequestAuthModel {
|
||||
|
||||
/**
|
||||
* @param loginId 要设置的 loginId
|
||||
* @return 对象自身
|
||||
*/
|
||||
public RequestAuthModel setLoginId(Object loginId) {
|
||||
this.loginId = loginId;
|
||||
@@ -95,6 +98,7 @@ public class RequestAuthModel {
|
||||
|
||||
/**
|
||||
* @param redirectUri 要设置的 redirectUri
|
||||
* @return 对象自身
|
||||
*/
|
||||
public RequestAuthModel setRedirectUri(String redirectUri) {
|
||||
this.redirectUri = redirectUri;
|
||||
@@ -110,6 +114,7 @@ public class RequestAuthModel {
|
||||
|
||||
/**
|
||||
* @param responseType 要设置的 responseType
|
||||
* @return 对象自身
|
||||
*/
|
||||
public RequestAuthModel setResponseType(String responseType) {
|
||||
this.responseType = responseType;
|
||||
@@ -125,6 +130,7 @@ public class RequestAuthModel {
|
||||
|
||||
/**
|
||||
* @param state 要设置的 state
|
||||
* @return 对象自身
|
||||
*/
|
||||
public RequestAuthModel setState(String state) {
|
||||
this.state = state;
|
||||
@@ -133,6 +139,7 @@ public class RequestAuthModel {
|
||||
|
||||
/**
|
||||
* 检查此Model参数是否有效
|
||||
* @return 对象自身
|
||||
*/
|
||||
public RequestAuthModel checkModel() {
|
||||
if(SaTokenInsideUtil.isEmpty(clientId)) {
|
||||
|
||||
@@ -26,7 +26,7 @@ public class ScopeModel {
|
||||
}
|
||||
/**
|
||||
* 构造一个
|
||||
* @param id 权限id
|
||||
* @param name 权限名称
|
||||
* @param introduce 权限详细介绍
|
||||
*/
|
||||
public ScopeModel(String name, String introduce) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<version>1.14.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user