Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 600cc98e20 | |||
| 2d3a262e2f | |||
| 6a677ca779 | |||
| dc5bfb6d84 | |||
| fbff086ed9 | |||
| 1028ac0fe6 | |||
| 4abb72bb65 | |||
| 79cb976439 | |||
| f26424537f | |||
| 2328f9d654 | |||
| 22826dac86 | |||
| bbce343a01 | |||
| 8ed9166abf | |||
| a1ec710efd | |||
| ae9ba2d1fd | |||
| 97a5fb2f40 | |||
| 128ab7614e | |||
| 7f0a3aa1c6 | |||
| 69d01e3e6e | |||
| 82ee90f712 | |||
| 9733c8777a | |||
| 4ff6a87ef5 | |||
| 7ffe6cb0e6 | |||
| dbba90d846 | |||
| 064ef4f12c | |||
| a8688cc07f | |||
| 4678e34203 | |||
| 81c0200981 |
@@ -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" style="margin-bottom: 10px;">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.8.0</h1>
|
||||
<h4 align="center">一个JavaWeb轻量级权限认证框架,功能全面,上手简单</h4>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.11.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.8.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.11.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>
|
||||
@@ -16,7 +16,7 @@
|
||||
---
|
||||
|
||||
|
||||
## 😘 在线资料
|
||||
## 在线资料
|
||||
|
||||
- [官网首页:http://sa-token.dev33.cn/](http://sa-token.dev33.cn/)
|
||||
|
||||
@@ -24,11 +24,30 @@
|
||||
|
||||
- [需求提交:我们深知一个优秀的项目需要海纳百川,点我在线提交需求](http://sa-app.dev33.cn/wall.html?name=sa-token)
|
||||
|
||||
- [开源不易,求鼓励,点个star吧](https://github.com/click33/sa-token)
|
||||
- [开源不易,求鼓励,点个star吧](###)
|
||||
|
||||
|
||||
## ⭐ sa-token是什么?
|
||||
**sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,有多简单呢?以登录验证为例,你只需要:**
|
||||
## sa-token是什么?
|
||||
sa-token是一个JavaWeb轻量级权限认证框架,主要解决项目中登录认证、权限认证、Session会话等一系列由此衍生的权限相关业务
|
||||
|
||||
在架构设计上`sa-token`拒绝引入复杂的概念,以实际业务需求为第一目标进行定向突破,例如踢人下线、自动续签、同端互斥登录等常见业务在框架内均可以一行代码调用实现,简单粗暴,拒绝复杂!
|
||||
|
||||
对于传统Session会话模型的N多难题,例如难以分布式、水平扩展性差,难以兼容前后台分离环境,多会话管理混乱等,
|
||||
`sa-token`独创了以账号为主的`Id-Session`模式,同时又兼容了传统以token为主的`Token-Session`模式,两者彼此独立,互不干扰,
|
||||
让你在进行会话管理时可以如鱼得水,在`sa-toekn`的强力加持下,权限问题将不再成为业务逻辑的瓶颈!
|
||||
|
||||
总的来说,与其它权限认证框架相比,`sa-token`具有以下优势:
|
||||
1. 上手简单:可零配置启动框架,能自动化的均已自动化,不让你费脑子
|
||||
2. 功能强大:能集成的功能全部集成,不让你用个框架还要自己给框架打各种补丁
|
||||
3. API简单易用:同样的一个功能,可能在别的框架中需要上百行代码,但是在sa-token中统统一行代码调个方法即可解决
|
||||
4. 组件易于扩展:框架中几乎所有组件都提供了对应的扩展接口,90%以上的逻辑都是可以被按需重写的
|
||||
|
||||
有了sa-token,是时候和那些老旧权限框架说拜拜了!
|
||||
|
||||
|
||||
## 代码示例
|
||||
|
||||
sa-token的API调用非常简单,有多简单呢?以登录验证为例,你只需要:
|
||||
|
||||
``` java
|
||||
// 在登录时写入当前会话的账号id
|
||||
@@ -38,17 +57,7 @@ StpUtil.setLoginId(10001);
|
||||
StpUtil.checkLogin();
|
||||
```
|
||||
|
||||
|
||||
**没有复杂的封装!不要任何的配置!只需这两行简单的调用,即可轻松完成系统登录鉴权!**
|
||||
|
||||
|
||||
## 🔥 框架设计思想
|
||||
与其它权限认证框架相比,`sa-token`尽力保证两点:
|
||||
- 上手简单:能自动化的配置全部自动化,不让你费脑子
|
||||
- 功能强大:能涵盖的功能全部涵盖,不让你用个框架还要自己给框架打各种补丁
|
||||
|
||||
|
||||
**如果上面的示例能够证明`sa-token`的简单,那么以下API则可以证明`sa-token`的强大**
|
||||
如果上面的示例能够证明`sa-token`的简单,那么以下API则可以证明`sa-token`的强大
|
||||
``` java
|
||||
StpUtil.setLoginId(10001); // 标记当前会话登录的账号id
|
||||
StpUtil.getLoginId(); // 获取当前会话登录的账号id
|
||||
@@ -60,15 +69,15 @@ StpUtil.hasPermission("user:add"); // 查询当前账号是否含有指定权
|
||||
StpUtil.getSession(); // 获取当前账号id的Session
|
||||
StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session
|
||||
StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌值
|
||||
StpUtil.setLoginId(10001, "PC"); // 指定设备标识登录
|
||||
StpUtil.logoutByLoginId(10001, "PC"); // 指定设备标识进行强制注销 (不同端不受影响)
|
||||
```
|
||||
**sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档**
|
||||
sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档
|
||||
|
||||
|
||||
|
||||
|
||||
## 💦️️ 涵盖功能
|
||||
## 涵盖功能
|
||||
- **登录验证** —— 轻松登录鉴权,并提供五种细分场景值
|
||||
- **权限验证** —— 拦截违规调用,不同角色不同授权
|
||||
- **权限验证** —— 适配RBAC模型,不同角色不同授权
|
||||
- **Session会话** —— 专业的数据缓存中心
|
||||
- **踢人下线** —— 将违规用户立刻清退下线
|
||||
- **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
@@ -78,32 +87,37 @@ StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌
|
||||
- **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- **自动续签** —— 提供两种token过期策略,灵活搭配使用,还可自动续签
|
||||
- **同端互斥登录** —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录
|
||||
- **组件自动注入** —— 零配置与Spring等框架集成
|
||||
- **会话治理** —— 提供方便灵活的会话查询接口
|
||||
- **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
|
||||
|
||||
|
||||
## 🔨 贡献代码
|
||||
sa-token欢迎大家贡献代码,为框架添砖加瓦
|
||||
1. 在github上fork一份到自己的仓库
|
||||
## 参与贡献
|
||||
众人拾柴火焰高,sa-token秉承着开放的思想,欢迎大家贡献代码,为框架添砖加瓦
|
||||
|
||||
1. 在gitee或者github上fork一份代码到自己的仓库
|
||||
2. clone自己的仓库到本地电脑
|
||||
3. 在本地电脑修改、commit、push
|
||||
4. 提交pr(点击:New Pull Request)
|
||||
5. 等待合并
|
||||
|
||||
作者寄语:参与贡献不光只有提交代码一个选择,点一个star、提一个issues都是对开源项目的促进,
|
||||
如果框架帮助到了你,欢迎你把框架推荐给你的朋友、同事使用,为sa-token的推广做一份贡献
|
||||
|
||||
## 🌱 建议贡献的地方
|
||||
- 修复源码现有bug,或增加新的实用功能
|
||||
- 完善在线文档,或者修复现有错误之处
|
||||
- 更多demo示例:比如SSM版搭建步骤
|
||||
|
||||
## 建议贡献的地方
|
||||
- 修复源码现有bug,或优化代码架构,或增加新的实用功能
|
||||
- 完善在线文档,或者修复现有描述错误之处
|
||||
- 更多的第三方框架集成方案,更多的demo示例:比如SSM版搭建步骤
|
||||
- 您可以参考项目issues与需求墙进行贡献
|
||||
- 如果更新实用功能,可在文档友情链接处留下自己的推广链接
|
||||
|
||||
|
||||
## 🚀 友情链接
|
||||
## 友情链接
|
||||
[**[ okhttps ]** 一个轻量级http通信框架,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
|
||||
|
||||
## 😎 交流群
|
||||
## 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) ,欢迎你的加入
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
|
||||
<!-- 项目介绍 -->
|
||||
<name>sa-token</name>
|
||||
@@ -22,6 +22,7 @@
|
||||
<module>sa-token-spring-boot-starter</module>
|
||||
<module>sa-token-dao-redis</module>
|
||||
<module>sa-token-dao-redis-jackson</module>
|
||||
<module>sa-token-spring-aop</module>
|
||||
</modules>
|
||||
|
||||
<!-- 开源协议 apache 2.0 -->
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
@@ -8,14 +10,14 @@ import cn.dev33.satoken.session.SaSession;
|
||||
*/
|
||||
public interface SaTokenDao {
|
||||
|
||||
|
||||
/** 常量,表示一个key永不过期 (在一个key被标注为永远不过期时返回此值) */
|
||||
public static final Long NEVER_EXPIRE = -1L;
|
||||
|
||||
/** 常量,表示系统中不存在这个缓存 (在对不存在的key获取剩余存活时间时返回此值) */
|
||||
public static final Long NOT_VALUE_EXPIRE = -2L;
|
||||
|
||||
|
||||
|
||||
// --------------------- token相关 ---------------------
|
||||
|
||||
/**
|
||||
* 根据key获取value,如果没有,则返回空
|
||||
@@ -58,7 +60,9 @@ public interface SaTokenDao {
|
||||
* @param timeout 过期时间
|
||||
*/
|
||||
public void updateTimeout(String key, long timeout);
|
||||
|
||||
|
||||
// --------------------- Session相关 ---------------------
|
||||
|
||||
/**
|
||||
* 根据指定key的Session,如果没有,则返回空
|
||||
@@ -101,7 +105,17 @@ public interface SaTokenDao {
|
||||
public void updateSessionTimeout(String sessionId, long timeout);
|
||||
|
||||
|
||||
|
||||
// --------------------- 会话管理 ---------------------
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
* @param prefix 前缀
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package cn.dev33.satoken.dao;
|
||||
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -10,6 +11,7 @@ import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaTaskUtil;
|
||||
import cn.dev33.satoken.util.SaTaskUtil.FunctionRunClass;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token持久层默认的实现类 , 基于内存Map
|
||||
@@ -207,6 +209,14 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
|
||||
|
||||
|
||||
// --------------------- 会话管理
|
||||
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size) {
|
||||
return SaTokenInsideUtil.searchList(expireMap.keySet(), prefix, keyword, start, size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -31,18 +31,18 @@ public class SaTokenInfo {
|
||||
/** token专属session剩余有效时间 (单位: 秒) */
|
||||
public long tokenSessionTimeout;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* token剩余无操作有效时间
|
||||
*/
|
||||
public long tokenActivityTimeout;
|
||||
|
||||
|
||||
/** 当前登录设备 */
|
||||
public String loginDevice;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return tokenName token名称
|
||||
* @return tokenName
|
||||
*/
|
||||
public String getTokenName() {
|
||||
return tokenName;
|
||||
@@ -50,11 +50,9 @@ public class SaTokenInfo {
|
||||
|
||||
/**
|
||||
* @param tokenName 要设置的 tokenName
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenInfo setTokenName(String tokenName) {
|
||||
public void setTokenName(String tokenName) {
|
||||
this.tokenName = tokenName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,11 +64,9 @@ public class SaTokenInfo {
|
||||
|
||||
/**
|
||||
* @param tokenValue 要设置的 tokenValue
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenInfo setTokenValue(String tokenValue) {
|
||||
public void setTokenValue(String tokenValue) {
|
||||
this.tokenValue = tokenValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,11 +78,9 @@ public class SaTokenInfo {
|
||||
|
||||
/**
|
||||
* @param isLogin 要设置的 isLogin
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenInfo setIsLogin(Boolean isLogin) {
|
||||
public void setIsLogin(Boolean isLogin) {
|
||||
this.isLogin = isLogin;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,11 +92,9 @@ public class SaTokenInfo {
|
||||
|
||||
/**
|
||||
* @param loginId 要设置的 loginId
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenInfo setLoginId(Object loginId) {
|
||||
public void setLoginId(Object loginId) {
|
||||
this.loginId = loginId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,13 +106,11 @@ public class SaTokenInfo {
|
||||
|
||||
/**
|
||||
* @param loginKey 要设置的 loginKey
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenInfo setLoginKey(String loginKey) {
|
||||
public void setLoginKey(String loginKey) {
|
||||
this.loginKey = loginKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return tokenTimeout
|
||||
*/
|
||||
@@ -130,11 +120,9 @@ public class SaTokenInfo {
|
||||
|
||||
/**
|
||||
* @param tokenTimeout 要设置的 tokenTimeout
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenInfo setTokenTimeout(long tokenTimeout) {
|
||||
public void setTokenTimeout(long tokenTimeout) {
|
||||
this.tokenTimeout = tokenTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,11 +134,9 @@ public class SaTokenInfo {
|
||||
|
||||
/**
|
||||
* @param sessionTimeout 要设置的 sessionTimeout
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenInfo setSessionTimeout(long sessionTimeout) {
|
||||
public void setSessionTimeout(long sessionTimeout) {
|
||||
this.sessionTimeout = sessionTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,13 +148,11 @@ public class SaTokenInfo {
|
||||
|
||||
/**
|
||||
* @param tokenSessionTimeout 要设置的 tokenSessionTimeout
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenInfo setTokenSessionTimeout(long tokenSessionTimeout) {
|
||||
public void setTokenSessionTimeout(long tokenSessionTimeout) {
|
||||
this.tokenSessionTimeout = tokenSessionTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return tokenActivityTimeout
|
||||
*/
|
||||
@@ -178,11 +162,23 @@ public class SaTokenInfo {
|
||||
|
||||
/**
|
||||
* @param tokenActivityTimeout 要设置的 tokenActivityTimeout
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenInfo setTokenActivityTimeout(long tokenActivityTimeout) {
|
||||
public void setTokenActivityTimeout(long tokenActivityTimeout) {
|
||||
this.tokenActivityTimeout = tokenActivityTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return loginDevice
|
||||
*/
|
||||
public String getLoginDevice() {
|
||||
return loginDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loginDevice 要设置的 loginDevice
|
||||
*/
|
||||
public void setLoginDevice(String loginDevice) {
|
||||
this.loginDevice = loginDevice;
|
||||
}
|
||||
|
||||
|
||||
@@ -193,8 +189,9 @@ public class SaTokenInfo {
|
||||
return "SaTokenInfo [tokenName=" + tokenName + ", tokenValue=" + tokenValue + ", isLogin=" + isLogin
|
||||
+ ", loginId=" + loginId + ", loginKey=" + loginKey + ", tokenTimeout=" + tokenTimeout
|
||||
+ ", sessionTimeout=" + sessionTimeout + ", tokenSessionTimeout=" + tokenSessionTimeout
|
||||
+ ", tokenActivityTimeout=" + tokenActivityTimeout + "]";
|
||||
+ ", tokenActivityTimeout=" + tokenActivityTimeout + ", loginDevice=" + loginDevice + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -208,6 +205,4 @@ public class SaTokenInfo {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -9,6 +10,10 @@ import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
@@ -17,7 +22,6 @@ import cn.dev33.satoken.exception.NotRoleException;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.session.TokenSign;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token 权限验证,逻辑实现类
|
||||
@@ -119,6 +123,7 @@ public class StpLogic {
|
||||
info.sessionTimeout = getSessionTimeout();
|
||||
info.tokenSessionTimeout = getTokenSessionTimeout();
|
||||
info.tokenActivityTimeout = getTokenActivityTimeout();
|
||||
info.loginDevice = getLoginDevice();
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -130,11 +135,23 @@ public class StpLogic {
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
*/
|
||||
public void setLoginId(Object loginId) {
|
||||
setLoginId(loginId, SaTokenConsts.DEFAULT_LOGIN_DEVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话上登录id
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param device 设备标识
|
||||
*/
|
||||
public void setLoginId(Object loginId, String device) {
|
||||
|
||||
// ------ 0、如果当前会话已经登录上了此LoginId,则立即返回
|
||||
// ------ 0、如果当前会话已经登录上了此LoginId,且登录设备相同,则立即返回
|
||||
Object loggedId = getLoginIdDefaultNull();
|
||||
if(loggedId != null && loggedId.toString().equals(loginId.toString())) {
|
||||
return;
|
||||
String loggedDevice = getLoginDevice();
|
||||
if(loggedDevice != null && loggedDevice.equals(device)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ------ 1、获取相应对象
|
||||
@@ -148,18 +165,20 @@ public class StpLogic {
|
||||
if(config.getAllowConcurrentLogin() == true) {
|
||||
// 如果配置为共享token, 则尝试从session签名记录里取出token
|
||||
if(config.getIsShare() == true) {
|
||||
tokenValue = getTokenValueByLoginId(loginId);
|
||||
tokenValue = getTokenValueByLoginId(loginId, device);
|
||||
}
|
||||
} else {
|
||||
// --- 如果不允许并发登录
|
||||
// 如果此时[id-session]不为null,说明此账号在其他地正在登录,现在需要先把其它地的token标记为被顶下线
|
||||
// 如果此时[id-session]不为null,说明此账号在其他地正在登录,现在需要先把其它地的同设备token标记为被顶下线
|
||||
SaSession session = getSessionByLoginId(loginId, false);
|
||||
if(session != null) {
|
||||
List<String> tokenValueList = getTokenValueListByLoginId(loginId);
|
||||
for (String token : tokenValueList) {
|
||||
dao.updateValue(getKeyTokenValue(token), NotLoginException.BE_REPLACED); // 1. 将此token 标记为已顶替
|
||||
clearLastActivity(token); // 2. 清理掉[token-最后操作时间]
|
||||
session.removeTokenSign(token); // 3. 清理账号session上的token签名
|
||||
List<TokenSign> tokenSignList = session.getTokenSignList();
|
||||
for (TokenSign tokenSign : tokenSignList) {
|
||||
if(tokenSign.getDevice().equals(device)) {
|
||||
dao.updateValue(getKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED); // 1. 将此token 标记为已顶替
|
||||
clearLastActivity(tokenSign.getValue()); // 2. 清理掉[token-最后操作时间]
|
||||
session.removeTokenSign(tokenSign.getValue()); // 3. 清理账号session上的token签名记录
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,7 +195,7 @@ public class StpLogic {
|
||||
dao.updateSessionTimeout(session.getId(), config.getTimeout());
|
||||
}
|
||||
// 在session上记录token签名
|
||||
session.addTokenSign(new TokenSign(tokenValue, SaTokenConsts.DEFAULT_LOGIN_DEVICE));
|
||||
session.addTokenSign(new TokenSign(tokenValue, device));
|
||||
|
||||
// ------ 4. 持久化其它数据
|
||||
dao.setValue(getKeyTokenValue(tokenValue), String.valueOf(loginId), config.getTimeout()); // token -> uid
|
||||
@@ -235,6 +254,16 @@ public class StpLogic {
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public void logoutByLoginId(Object loginId) {
|
||||
logoutByLoginId(loginId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定loginId指定设备的会话注销登录(踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
*/
|
||||
public void logoutByLoginId(Object loginId, String device) {
|
||||
// 先获取这个账号的[id-session], 如果为null,则不执行任何操作
|
||||
SaSession session = getSessionByLoginId(loginId);
|
||||
if(session == null) {
|
||||
@@ -244,20 +273,21 @@ public class StpLogic {
|
||||
// 循环token签名列表,开始删除相关信息
|
||||
List<TokenSign> tokenSignList = session.getTokenSignList();
|
||||
for (TokenSign tokenSign : tokenSignList) {
|
||||
// 1. 获取token
|
||||
String tokenValue = tokenSign.getValue();
|
||||
// 2. 清理掉[token-最后操作时间]
|
||||
clearLastActivity(tokenValue);
|
||||
// 3. 标记:已被踢下线
|
||||
SaTokenManager.getSaTokenDao().updateValue(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT); // 标记:已被踢下线
|
||||
// 4. 清理账号session上的token签名
|
||||
session.removeTokenSign(tokenValue);
|
||||
if(device == null || tokenSign.getDevice().equals(device)) {
|
||||
// 1. 获取token
|
||||
String tokenValue = tokenSign.getValue();
|
||||
// 2. 清理掉[token-最后操作时间]
|
||||
clearLastActivity(tokenValue);
|
||||
// 3. 标记:已被踢下线
|
||||
SaTokenManager.getSaTokenDao().updateValue(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT); // 标记:已被踢下线
|
||||
// 4. 清理账号session上的token签名
|
||||
session.removeTokenSign(tokenValue);
|
||||
}
|
||||
}
|
||||
// 尝试注销session
|
||||
session.logoutByTokenSignCountToZero();
|
||||
}
|
||||
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
@@ -310,7 +340,7 @@ public class StpLogic {
|
||||
return loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 获取当前会话登录id, 如果未登录,则返回默认值
|
||||
* @param <T> 返回类型
|
||||
* @param defaultValue 默认值
|
||||
@@ -336,7 +366,7 @@ public class StpLogic {
|
||||
return (T)loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 获取当前会话登录id, 如果未登录,则返回null
|
||||
* @return 账号id
|
||||
*/
|
||||
@@ -367,7 +397,7 @@ public class StpLogic {
|
||||
return String.valueOf(getLoginId());
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 获取当前会话登录id, 并转换为int
|
||||
* @return 账号id
|
||||
*/
|
||||
@@ -379,7 +409,7 @@ public class StpLogic {
|
||||
return Integer.valueOf(String.valueOf(getLoginId()));
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 获取当前会话登录id, 并转换为long
|
||||
* @return 账号id
|
||||
*/
|
||||
@@ -424,6 +454,15 @@ public class StpLogic {
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的session, 如果session尚未创建,则返回null
|
||||
* @param sessionId sessionId
|
||||
* @return session对象
|
||||
*/
|
||||
public SaSession getSessionBySessionId(String sessionId) {
|
||||
return getSessionBySessionId(sessionId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的session, 如果session尚未创建,isCreate=是否新建并返回
|
||||
* @param loginId 账号id
|
||||
@@ -495,7 +534,13 @@ public class StpLogic {
|
||||
// 如果配置忽略token登录校验,则必须保证token不为null (token为null的时候随机创建一个)
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null || Objects.equals(tokenValue, "")) {
|
||||
setLoginId(SaTokenInsideUtil.getMarking28());
|
||||
// 随机一个token送给ta
|
||||
tokenValue = createTokenValue(null);
|
||||
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(SaTokenConsts.JUST_CREATED_SAVE_KEY, tokenValue);
|
||||
setLastActivityToNow(tokenValue); // 写入 [最后操作时间]
|
||||
if(getConfig().getIsReadCookie() == true){ // cookie注入
|
||||
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue, "/", (int)getConfig().getTimeout());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 返回这个token对应的专属session
|
||||
@@ -513,7 +558,7 @@ public class StpLogic {
|
||||
|
||||
// =================== [临时过期] 验证相关 ===================
|
||||
|
||||
/**
|
||||
/**
|
||||
* 写入指定token的 [最后操作时间] 为当前时间戳
|
||||
* @param tokenValue 指定token
|
||||
*/
|
||||
@@ -601,7 +646,6 @@ public class StpLogic {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== 过期时间相关 ===================
|
||||
|
||||
/**
|
||||
@@ -827,7 +871,6 @@ public class StpLogic {
|
||||
|
||||
// =================== id 反查token 相关操作 ===================
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定loginId的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
@@ -836,16 +879,38 @@ public class StpLogic {
|
||||
* @return token值
|
||||
*/
|
||||
public String getTokenValueByLoginId(Object loginId) {
|
||||
List<String> tokenValueList = getTokenValueListByLoginId(loginId);
|
||||
return getTokenValueByLoginId(loginId, SaTokenConsts.DEFAULT_LOGIN_DEVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId指定设备端的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @return token值
|
||||
*/
|
||||
public String getTokenValueByLoginId(Object loginId, String device) {
|
||||
List<String> tokenValueList = getTokenValueListByLoginId(loginId, device);
|
||||
return tokenValueList.size() == 0 ? null : tokenValueList.get(tokenValueList.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的tokenValue
|
||||
* 获取指定loginId的tokenValue集合
|
||||
* @param loginId 账号id
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public List<String> getTokenValueListByLoginId(Object loginId) {
|
||||
return getTokenValueListByLoginId(loginId, SaTokenConsts.DEFAULT_LOGIN_DEVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId指定设备端的tokenValue 集合
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public List<String> getTokenValueListByLoginId(Object loginId, String device) {
|
||||
// 如果session为null的话直接返回空集合
|
||||
SaSession session = getSessionByLoginId(loginId, false);
|
||||
if(session == null) {
|
||||
@@ -855,11 +920,78 @@ public class StpLogic {
|
||||
List<TokenSign> tokenSignList = session.getTokenSignList();
|
||||
List<String> tokenValueList = new ArrayList<>();
|
||||
for (TokenSign tokenSign : tokenSignList) {
|
||||
tokenValueList.add(tokenSign.getValue());
|
||||
if(tokenSign.getDevice().equals(device)) {
|
||||
tokenValueList.add(tokenSign.getValue());
|
||||
}
|
||||
}
|
||||
return tokenValueList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前token的登录设备
|
||||
* @return 当前令牌的登录设备
|
||||
*/
|
||||
public String getLoginDevice() {
|
||||
// 如果没有token,直接返回
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return null;
|
||||
}
|
||||
// 如果还未登录,直接返回 null
|
||||
if(isLogin() == false) {
|
||||
return null;
|
||||
}
|
||||
// 如果session为null的话直接返回 null
|
||||
SaSession session = getSession(false);
|
||||
if(session == null) {
|
||||
return null;
|
||||
}
|
||||
// 遍历解析
|
||||
List<TokenSign> tokenSignList = session.getTokenSignList();
|
||||
for (TokenSign tokenSign : tokenSignList) {
|
||||
if(tokenSign.getValue().equals(tokenValue)) {
|
||||
return tokenSign.getDevice();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// =================== 会话管理 ===================
|
||||
|
||||
/**
|
||||
* 根据条件查询token
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return token集合
|
||||
*/
|
||||
public List<String> searchTokenValue(String keyword, int start, int size) {
|
||||
return SaTokenManager.getSaTokenDao().searchData(getKeyTokenValue(""), keyword, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询SessionId
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public List<String> searchSessionId(String keyword, int start, int size) {
|
||||
return SaTokenManager.getSaTokenDao().searchData(getKeySession(""), keyword, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询token专属Session的Id
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public List<String> searchTokenSessionId(String keyword, int start, int size) {
|
||||
return SaTokenManager.getSaTokenDao().searchData(getKeyTokenSession(""), keyword, start, size);
|
||||
}
|
||||
|
||||
|
||||
// =================== 返回相应key ===================
|
||||
|
||||
@@ -870,6 +1002,7 @@ public class StpLogic {
|
||||
public String getKeyTokenName() {
|
||||
return getConfig().getTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取key: tokenValue 持久化 token-id
|
||||
* @param tokenValue token值
|
||||
@@ -878,6 +1011,7 @@ public class StpLogic {
|
||||
public String getKeyTokenValue(String tokenValue) {
|
||||
return getConfig().getTokenName() + ":" + loginKey + ":token:" + tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取key: session 持久化
|
||||
* @param loginId 账号id
|
||||
@@ -886,6 +1020,7 @@ public class StpLogic {
|
||||
public String getKeySession(Object loginId) {
|
||||
return getConfig().getTokenName() + ":" + loginKey + ":session:" + loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取key: tokenValue的专属session
|
||||
* @param tokenValue token值
|
||||
@@ -894,6 +1029,7 @@ public class StpLogic {
|
||||
public String getKeyTokenSession(String tokenValue) {
|
||||
return getConfig().getTokenName() + ":" + loginKey + ":token-session:" + tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取key: 指定token的最后操作时间 持久化
|
||||
* @param tokenValue token值
|
||||
@@ -915,8 +1051,66 @@ public class StpLogic {
|
||||
return SaTokenManager.getConfig();
|
||||
}
|
||||
|
||||
|
||||
// =================== 注解鉴权 ===================
|
||||
|
||||
|
||||
/**
|
||||
* 对一个Method对象进行注解检查(注解鉴权内部实现)
|
||||
* @param method Method对象
|
||||
*/
|
||||
public void checkMethodAnnotation(Method method) {
|
||||
|
||||
// ----------- 验证登录
|
||||
if(method.isAnnotationPresent(SaCheckLogin.class) || method.getDeclaringClass().isAnnotationPresent(SaCheckLogin.class)) {
|
||||
this.checkLogin();
|
||||
}
|
||||
|
||||
// ----------- 验证角色
|
||||
// 验证方法上的
|
||||
SaCheckRole scr = method.getAnnotation(SaCheckRole.class);
|
||||
if(scr != null) {
|
||||
String[] roleArray = scr.value();
|
||||
if(scr.mode() == SaMode.AND) {
|
||||
this.checkRoleAnd(roleArray);
|
||||
} else {
|
||||
this.checkRoleOr(roleArray);
|
||||
}
|
||||
}
|
||||
// 验证类上的
|
||||
scr = method.getDeclaringClass().getAnnotation(SaCheckRole.class);
|
||||
if(scr != null) {
|
||||
String[] roleArray = scr.value();
|
||||
if(scr.mode() == SaMode.AND) {
|
||||
this.checkRoleAnd(roleArray);
|
||||
} else {
|
||||
this.checkRoleOr(roleArray);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------- 验证权限
|
||||
// 验证方法上的
|
||||
SaCheckPermission scp = method.getAnnotation(SaCheckPermission.class);
|
||||
if(scp != null) {
|
||||
String[] permissionArray = scp.value();
|
||||
if(scp.mode() == SaMode.AND) {
|
||||
this.checkPermissionAnd(permissionArray);
|
||||
} else {
|
||||
this.checkPermissionOr(permissionArray);
|
||||
}
|
||||
}
|
||||
// 验证类上的
|
||||
scp = method.getDeclaringClass().getAnnotation(SaCheckPermission.class);
|
||||
if(scp != null) {
|
||||
String[] permissionArray = scp.value();
|
||||
if(scp.mode() == SaMode.AND) {
|
||||
this.checkPermissionAnd(permissionArray);
|
||||
} else {
|
||||
this.checkPermissionOr(permissionArray);
|
||||
}
|
||||
}
|
||||
|
||||
// 验证通过
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,15 @@ public class StpUtil {
|
||||
stpLogic.setLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话上登录id
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param device 设备标识
|
||||
*/
|
||||
public static void setLoginId(Object loginId, String device) {
|
||||
stpLogic.setLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话注销登录
|
||||
*/
|
||||
@@ -85,6 +94,16 @@ public class StpUtil {
|
||||
stpLogic.logoutByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定loginId指定设备的会话注销登录(踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
*/
|
||||
public static void logoutByLoginId(Object loginId, String device) {
|
||||
stpLogic.logoutByLoginId(loginId, device);
|
||||
}
|
||||
|
||||
|
||||
// 查询相关
|
||||
|
||||
@@ -175,6 +194,15 @@ public class StpUtil {
|
||||
return stpLogic.getSessionByLoginId(loginId, isCreate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的session, 如果session尚未创建,则返回null
|
||||
* @param sessionId sessionId
|
||||
* @return session对象
|
||||
*/
|
||||
public static SaSession getSessionBySessionId(String sessionId) {
|
||||
return stpLogic.getSessionBySessionId(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的session,如果session尚未创建,则新建并返回
|
||||
* @param loginId 账号id
|
||||
@@ -381,15 +409,81 @@ public class StpUtil {
|
||||
public static String getTokenValueByLoginId(Object loginId) {
|
||||
return stpLogic.getTokenValueByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId指定设备端的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @return token值
|
||||
*/
|
||||
public static String getTokenValueByLoginId(Object loginId, String device) {
|
||||
return stpLogic.getTokenValueByLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的tokenValue
|
||||
* 获取指定loginId的tokenValue集合
|
||||
* @param loginId 账号id
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public static List<String> getTokenValueListByLoginId(Object loginId) {
|
||||
return stpLogic.getTokenValueListByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId指定设备端的tokenValue集合
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
|
||||
return stpLogic.getTokenValueListByLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前token的登录设备
|
||||
* @return 当前令牌的登录设备
|
||||
*/
|
||||
public static String getLoginDevice() {
|
||||
return stpLogic.getLoginDevice();
|
||||
}
|
||||
|
||||
|
||||
// =================== 会话管理 ===================
|
||||
|
||||
/**
|
||||
* 根据条件查询token
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return token集合
|
||||
*/
|
||||
public static List<String> searchTokenValue(String keyword, int start, int size) {
|
||||
return stpLogic.searchTokenValue(keyword, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询SessionId
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public static List<String> searchSessionId(String keyword, int start, int size) {
|
||||
return stpLogic.searchSessionId(keyword, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询token专属Session的Id
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public static List<String> searchTokenSessionId(String keyword, int start, int size) {
|
||||
return stpLogic.searchTokenSessionId(keyword, start, size);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public class SaTokenConsts {
|
||||
/**
|
||||
* sa-token 版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.8.0";
|
||||
public static final String VERSION_NO = "v1.11.0";
|
||||
|
||||
/**
|
||||
* sa-token 开源地址
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* sa-token 工具类
|
||||
* sa-token 内部代码工具类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@@ -49,4 +53,58 @@ public class SaTokenInsideUtil {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从集合里查询数据
|
||||
* @param dataList 数据集合
|
||||
* @param prefix 前缀
|
||||
* @param keyword 关键字
|
||||
* @param start 起始位置 (-1代表查询所有)
|
||||
* @param size 获取条数
|
||||
* @return 符合条件的新数据集合
|
||||
*/
|
||||
public static List<String> searchList(Collection<String> dataList, String prefix, String keyword, int start, int size) {
|
||||
if(prefix == null) {
|
||||
prefix = "";
|
||||
}
|
||||
if(keyword == null) {
|
||||
keyword = "";
|
||||
}
|
||||
// 挑选出所有符合条件的
|
||||
List<String> list = new ArrayList<String>();
|
||||
Iterator<String> keys = dataList.iterator();
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
if(key.startsWith(prefix) && key.indexOf(keyword) > -1) {
|
||||
list.add(key);
|
||||
}
|
||||
}
|
||||
// 取指定段数据
|
||||
return searchList(list, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从集合里查询数据
|
||||
* @param list 数据集合
|
||||
* @param start 起始位置 (-1代表查询所有)
|
||||
* @param size 获取条数
|
||||
* @return 符合条件的新数据集合
|
||||
*/
|
||||
public static List<String> searchList(List<String> list, int start, int size) {
|
||||
// 取指定段数据
|
||||
if(start < 0) {
|
||||
return list;
|
||||
}
|
||||
int end = start + size;
|
||||
List<String> list2 = new ArrayList<String>();
|
||||
for (int i = start; i < end; i++) {
|
||||
if(i >= list.size()) {
|
||||
return list2;
|
||||
}
|
||||
list2.add(list.get(i));
|
||||
}
|
||||
return list2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
<!-- RedisTemplate 相关操作API -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<version>2.3.7.RELEASE</version>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
+36
@@ -1,6 +1,9 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -15,6 +18,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token持久层的实现类, 基于redis (to jackson)
|
||||
@@ -120,6 +124,17 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getTimeout(key);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.setValue(key, this.getValue(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@@ -179,7 +194,28 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void updateSessionTimeout(String sessionId, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getSessionTimeout(sessionId);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.saveSession(this.getSession(sessionId), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
sessionRedisTemplate.expire(sessionId, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
*/
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size) {
|
||||
Set<String> keys = stringRedisTemplate.keys(prefix + "*" + keyword + "*");
|
||||
List<String> list = new ArrayList<String>(keys);
|
||||
return SaTokenInsideUtil.searchList(list, start, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
<!-- RedisTemplate 相关操作API -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<version>2.3.7.RELEASE</version>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -11,6 +14,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token持久层的实现类, 基于redis
|
||||
@@ -101,6 +105,17 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getTimeout(key);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.setValue(key, this.getValue(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@@ -160,7 +175,31 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void updateSessionTimeout(String sessionId, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getSessionTimeout(sessionId);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.saveSession(this.getSession(sessionId), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
sessionRedisTemplate.expire(sessionId, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
*/
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size) {
|
||||
Set<String> keys = stringRedisTemplate.keys(prefix + "*" + keyword + "*");
|
||||
List<String> list = new ArrayList<String>(keys);
|
||||
return SaTokenInsideUtil.searchList(list, start, size);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -29,21 +29,21 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token整合redis (使用jdk默认序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- 提供redis连接池 -->
|
||||
@@ -51,6 +51,13 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合SpringAOP实现注解鉴权 -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-aop</artifactId>
|
||||
<version>1.11.0</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
|
||||
|
||||
/**
|
||||
* 继承sa-token行为Bean默认实现, 重写部分逻辑
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class MySaTokenAction extends SaTokenActionDefaultImpl {
|
||||
|
||||
// 重写token生成策略
|
||||
// @Override
|
||||
// public String createToken(Object loginId, String loginKey) {
|
||||
// return SaTokenInsideUtil.getRandomString(60);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
import com.pj.util.Ttime;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 压力测试
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/s-test/")
|
||||
public class StressTestController {
|
||||
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/s-test/login
|
||||
// 测试前,请先将 is-read-cookie 配置为 false
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login() {
|
||||
// StpUtil.getTokenSession().logout();
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
|
||||
int count = 10; // 循环多少轮
|
||||
int loginCount = 10000; // 每轮循环多少次
|
||||
|
||||
// 循环10次 取平均时间
|
||||
List<Double> list = new ArrayList<>();
|
||||
for (int i = 1; i <= count; i++) {
|
||||
System.out.println("\n---------------------第" + i + "轮---------------------");
|
||||
Ttime t = new Ttime().start();
|
||||
// 每次登录的次数
|
||||
for (int j = 1; j <= loginCount; j++) {
|
||||
StpUtil.setLoginId("1000" + j, "PC-" + j);
|
||||
if(j % 1000 == 0) {
|
||||
System.out.println("已登录:" + j);
|
||||
}
|
||||
}
|
||||
t.end();
|
||||
list.add((t.returnMs() + 0.0) / 1000);
|
||||
System.out.println("第" + i + "轮" + "用时:" + t.toString());
|
||||
}
|
||||
// System.out.println(((SaTokenDaoDefaultImpl)SaTokenManager.getSaTokenDao()).dataMap.size());
|
||||
|
||||
System.out.println("\n---------------------测试结果---------------------");
|
||||
System.out.println(list.size() + "次测试: " + list);
|
||||
double ss = 0;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
ss += list.get(i);
|
||||
}
|
||||
System.out.println("平均用时: " + ss / list.size());
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pj.util.AjaxJson;
|
||||
import com.pj.util.Ttime;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
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.SaTokenInfo;
|
||||
@@ -38,7 +43,8 @@ public class TestController {
|
||||
System.out.println("登录成功");
|
||||
System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("当前登录账号:" + StpUtil.getLoginId());
|
||||
System.out.println("当前登录账号:" + StpUtil.getLoginIdAsInt()); // 获取登录id并转为int
|
||||
// System.out.println("当前登录账号并转为int:" + StpUtil.getLoginIdAsInt());
|
||||
System.out.println("当前登录设备:" + StpUtil.getLoginDevice());
|
||||
// System.out.println("当前token信息:" + StpUtil.getTokenInfo());
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
@@ -151,13 +157,14 @@ public class TestController {
|
||||
|
||||
// 测试注解式鉴权, 浏览器访问: http://localhost:8081/test/atCheck
|
||||
@SaCheckLogin // 注解式鉴权:当前会话必须登录才能通过
|
||||
@SaCheckRole("super-admin") // 注解式鉴权:当前会话必须具有指定角色标识才能通过
|
||||
@SaCheckPermission("user-add") // 注解式鉴权:当前会话必须具有指定权限才能通过
|
||||
@RequestMapping("atCheck")
|
||||
public AjaxJson atCheck() {
|
||||
System.out.println("======================= 进入方法,测试注解鉴权接口 ========================= ");
|
||||
System.out.println("只有通过注解鉴权,才能进入此方法");
|
||||
StpUtil.checkActivityTimeout();
|
||||
StpUtil.updateLastActivityToNow();
|
||||
// StpUtil.checkActivityTimeout();
|
||||
// StpUtil.updateLastActivityToNow();
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
@@ -188,14 +195,49 @@ public class TestController {
|
||||
// 返回
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试登录接口, 按照设备登录, 浏览器访问: http://localhost:8081/test/login2
|
||||
@RequestMapping("login2")
|
||||
public AjaxJson login2(@RequestParam(defaultValue="10001") String id, @RequestParam(defaultValue="PC") String device) {
|
||||
StpUtil.setLoginId(id, device);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
StpUtil.getTokenSession().logout();
|
||||
|
||||
// 测试会话治理 浏览器访问: http://localhost:8081/test/search
|
||||
@RequestMapping("search")
|
||||
public AjaxJson search() {
|
||||
System.out.println("--------------");
|
||||
Ttime t = new Ttime().start();
|
||||
List<String> tokenValue = StpUtil.searchTokenValue("8feb8265f773", 0, 10);
|
||||
for (String v : tokenValue) {
|
||||
// SaSession session = StpUtil.getSessionBySessionId(sid);
|
||||
System.out.println(v);
|
||||
}
|
||||
System.out.println("用时:" + t.end().toString());
|
||||
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/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
StpUtil.getTokenSession().logout();
|
||||
StpUtil.logoutByLoginId(10001);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
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
-1
@@ -1,4 +1,4 @@
|
||||
package com.pj.test;
|
||||
package com.pj.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.pj.util;
|
||||
|
||||
|
||||
/**
|
||||
* 用于测试用时
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class Ttime {
|
||||
|
||||
private long start=0; //开始时间
|
||||
private long end=0; //结束时间
|
||||
|
||||
public static Ttime t = new Ttime(); //static快捷使用
|
||||
|
||||
/**
|
||||
* 开始计时
|
||||
* @return
|
||||
*/
|
||||
public Ttime start() {
|
||||
start=System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 结束计时
|
||||
*/
|
||||
public Ttime end() {
|
||||
end=System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回所用毫秒数
|
||||
*/
|
||||
public long returnMs() {
|
||||
return end-start;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出结果
|
||||
*/
|
||||
public void outTime() {
|
||||
System.out.println(this.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束并格式化输出结果
|
||||
*/
|
||||
public void endOutTime() {
|
||||
this.end().outTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (returnMs() + 0.0) / 1000 + "s"; // 格式化为:0.01s
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@ spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
@@ -30,7 +30,7 @@ spring:
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 1000ms
|
||||
timeout: 10000ms
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
|
||||
+47
-33
@@ -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" style="margin-bottom: 10px;">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.8.0</h1>
|
||||
<h4 align="center">一个JavaWeb轻量级权限认证框架,功能全面,上手简单</h4>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.11.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.8.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.11.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>
|
||||
@@ -16,7 +16,7 @@
|
||||
---
|
||||
|
||||
|
||||
## 😘 在线资料
|
||||
## 在线资料
|
||||
|
||||
- [官网首页:http://sa-token.dev33.cn/](http://sa-token.dev33.cn/)
|
||||
|
||||
@@ -24,11 +24,30 @@
|
||||
|
||||
- [需求提交:我们深知一个优秀的项目需要海纳百川,点我在线提交需求](http://sa-app.dev33.cn/wall.html?name=sa-token)
|
||||
|
||||
- [开源不易,求鼓励,点个star吧](https://github.com/click33/sa-token)
|
||||
- [开源不易,求鼓励,点个star吧](###)
|
||||
|
||||
|
||||
## ⭐ sa-token是什么?
|
||||
**sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,有多简单呢?以登录验证为例,你只需要:**
|
||||
## sa-token是什么?
|
||||
sa-token是一个JavaWeb轻量级权限认证框架,主要解决项目中登录认证、权限认证、Session会话等一系列由此衍生的权限相关业务
|
||||
|
||||
在架构设计上`sa-token`拒绝引入复杂的概念,以实际业务需求为第一目标进行定向突破,例如踢人下线、自动续签、同端互斥登录等常见业务在框架内均可以一行代码调用实现,简单粗暴,拒绝复杂!
|
||||
|
||||
对于传统Session会话模型的N多难题,例如难以分布式、水平扩展性差,难以兼容前后台分离环境,多会话管理混乱等,
|
||||
`sa-token`独创了以账号为主的`Id-Session`模式,同时又兼容了传统以token为主的`Token-Session`模式,两者彼此独立,互不干扰,
|
||||
让你在进行会话管理时可以如鱼得水,在`sa-toekn`的强力加持下,权限问题将不再成为业务逻辑的瓶颈!
|
||||
|
||||
总的来说,与其它权限认证框架相比,`sa-token`具有以下优势:
|
||||
1. 上手简单:可零配置启动框架,能自动化的均已自动化,不让你费脑子
|
||||
2. 功能强大:能集成的功能全部集成,不让你用个框架还要自己给框架打各种补丁
|
||||
3. API简单易用:同样的一个功能,可能在别的框架中需要上百行代码,但是在sa-token中统统一行代码调个方法即可解决
|
||||
4. 组件易于扩展:框架中几乎所有组件都提供了对应的扩展接口,90%以上的逻辑都是可以被按需重写的
|
||||
|
||||
有了sa-token,是时候和那些老旧权限框架说拜拜了!
|
||||
|
||||
|
||||
## 代码示例
|
||||
|
||||
sa-token的API调用非常简单,有多简单呢?以登录验证为例,你只需要:
|
||||
|
||||
``` java
|
||||
// 在登录时写入当前会话的账号id
|
||||
@@ -38,17 +57,7 @@ StpUtil.setLoginId(10001);
|
||||
StpUtil.checkLogin();
|
||||
```
|
||||
|
||||
|
||||
**没有复杂的封装!不要任何的配置!只需这两行简单的调用,即可轻松完成系统登录鉴权!**
|
||||
|
||||
|
||||
## 🔥 框架设计思想
|
||||
与其它权限认证框架相比,`sa-token`尽力保证两点:
|
||||
- 上手简单:能自动化的配置全部自动化,不让你费脑子
|
||||
- 功能强大:能涵盖的功能全部涵盖,不让你用个框架还要自己给框架打各种补丁
|
||||
|
||||
|
||||
**如果上面的示例能够证明`sa-token`的简单,那么以下API则可以证明`sa-token`的强大**
|
||||
如果上面的示例能够证明`sa-token`的简单,那么以下API则可以证明`sa-token`的强大
|
||||
``` java
|
||||
StpUtil.setLoginId(10001); // 标记当前会话登录的账号id
|
||||
StpUtil.getLoginId(); // 获取当前会话登录的账号id
|
||||
@@ -60,15 +69,15 @@ StpUtil.hasPermission("user:add"); // 查询当前账号是否含有指定权
|
||||
StpUtil.getSession(); // 获取当前账号id的Session
|
||||
StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session
|
||||
StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌值
|
||||
StpUtil.setLoginId(10001, "PC"); // 指定设备标识登录
|
||||
StpUtil.logoutByLoginId(10001, "PC"); // 指定设备标识进行强制注销 (不同端不受影响)
|
||||
```
|
||||
**sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档**
|
||||
sa-token的API众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档
|
||||
|
||||
|
||||
|
||||
|
||||
## 💦️️ 涵盖功能
|
||||
## 涵盖功能
|
||||
- **登录验证** —— 轻松登录鉴权,并提供五种细分场景值
|
||||
- **权限验证** —— 拦截违规调用,不同角色不同授权
|
||||
- **权限验证** —— 适配RBAC模型,不同角色不同授权
|
||||
- **Session会话** —— 专业的数据缓存中心
|
||||
- **踢人下线** —— 将违规用户立刻清退下线
|
||||
- **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
@@ -78,32 +87,37 @@ StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌
|
||||
- **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- **自动续签** —— 提供两种token过期策略,灵活搭配使用,还可自动续签
|
||||
- **同端互斥登录** —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录
|
||||
- **组件自动注入** —— 零配置与Spring等框架集成
|
||||
- **会话治理** —— 提供方便灵活的会话查询接口
|
||||
- **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
|
||||
|
||||
|
||||
## 🔨 贡献代码
|
||||
sa-token欢迎大家贡献代码,为框架添砖加瓦
|
||||
1. 在github上fork一份到自己的仓库
|
||||
## 参与贡献
|
||||
众人拾柴火焰高,sa-token秉承着开放的思想,欢迎大家贡献代码,为框架添砖加瓦
|
||||
|
||||
1. 在gitee或者github上fork一份代码到自己的仓库
|
||||
2. clone自己的仓库到本地电脑
|
||||
3. 在本地电脑修改、commit、push
|
||||
4. 提交pr(点击:New Pull Request)
|
||||
5. 等待合并
|
||||
|
||||
作者寄语:参与贡献不光只有提交代码一个选择,点一个star、提一个issues都是对开源项目的促进,
|
||||
如果框架帮助到了你,欢迎你把框架推荐给你的朋友、同事使用,为sa-token的推广做一份贡献
|
||||
|
||||
## 🌱 建议贡献的地方
|
||||
- 修复源码现有bug,或增加新的实用功能
|
||||
- 完善在线文档,或者修复现有错误之处
|
||||
- 更多demo示例:比如SSM版搭建步骤
|
||||
|
||||
## 建议贡献的地方
|
||||
- 修复源码现有bug,或优化代码架构,或增加新的实用功能
|
||||
- 完善在线文档,或者修复现有描述错误之处
|
||||
- 更多的第三方框架集成方案,更多的demo示例:比如SSM版搭建步骤
|
||||
- 您可以参考项目issues与需求墙进行贡献
|
||||
- 如果更新实用功能,可在文档友情链接处留下自己的推广链接
|
||||
|
||||
|
||||
## 🚀 友情链接
|
||||
## 友情链接
|
||||
[**[ okhttps ]** 一个轻量级http通信框架,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
|
||||
|
||||
## 😎 交流群
|
||||
## 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) ,欢迎你的加入
|
||||
|
||||
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
- [无Cookie模式(前后台分离)](/use/not-cookie)
|
||||
- [模拟他人](/use/mock-person)
|
||||
- [多账号验证](/use/many-account)
|
||||
- [同端互斥登录](/use/mutex-login)
|
||||
- [注解式鉴权](/use/at-check)
|
||||
- [花式token](/use/token-style)
|
||||
- [框架配置](/use/config)
|
||||
- [会话治理](/use/search-session)
|
||||
|
||||
- **其它**
|
||||
- [常见问题](/more/common-questions)
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Description">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="keywords" content="sa-token|sa-token框架|sa-token文档|sa-token在线文档|权限认证框架">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,功能全面,上手简单,登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、多账号体系、注解式鉴权、花式token、自动续签、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<link rel="stylesheet" href="https://unpkg.com/docsify@4.11.3/lib/themes/vue.css">
|
||||
<meta name="keywords" content="sa-token|sa-token框架|sa-token文档|sa-token在线文档|sa-token官方文档|权限认证框架">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,功能全面,上手简单,登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、多账号体系、注解式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<link rel="stylesheet" href="https://unpkg.zhimg.com/docsify@4.11.3/lib/themes/vue.css">
|
||||
<link rel="stylesheet" href="./lib/index.css">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="logo.png">
|
||||
</head>
|
||||
@@ -22,6 +22,9 @@
|
||||
<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.10.0/doc/index.html">v1.10.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.9.0/doc/index.html">v1.9.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.8.0/doc/index.html">v1.8.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.7.0/doc/index.html">v1.7.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.6.0/doc/index.html">v1.6.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.5.1/doc/index.html">v1.5.1</option>
|
||||
@@ -37,7 +40,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.8.0</sub>'
|
||||
name += '<b style="font-size: 24px; vertical-align: middle;">sa-token</b> <sub>v1.11.0</sub>'
|
||||
window.$docsify = {
|
||||
name: name, // 名字
|
||||
repo: 'https://github.com/click33/sa-token', // github地址
|
||||
@@ -75,9 +78,9 @@
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script src="https://unpkg.com/docsify@4.9.4/lib/docsify.min.js"></script>
|
||||
<script src="https://unpkg.com/docsify-copy-code@2.1.0/dist/docsify-copy-code.min.js"></script>
|
||||
<script src="https://unpkg.com/prismjs@1.19.0/components/prism-java.min.js"></script>
|
||||
<script src="https://unpkg.zhimg.com/docsify@4.9.4/lib/docsify.min.js"></script>
|
||||
<script src="https://unpkg.zhimg.com/docsify-copy-code@2.1.0/dist/docsify-copy-code.min.js"></script>
|
||||
<script src="https://unpkg.zhimg.com/prismjs@1.19.0/components/prism-java.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
# 更新日志
|
||||
|
||||
|
||||
### 2020-12-24 @v1.8.0
|
||||
### 2021-1-9 @v1.11.0
|
||||
- 新增:提供AOP注解鉴权方案 **[重要]**
|
||||
- 优化自动生成token的算法
|
||||
|
||||
|
||||
### 2021-1-9 @v1.10.0
|
||||
- 新增:提供查询所有会话方案 **[重要]**
|
||||
- 修复:修复token设置为永不过期时无法正常被顶下线的bug,感谢github用户 @zjh599245299 提出的bug
|
||||
|
||||
|
||||
### 2021-1-6 @v1.9.0
|
||||
- 优化:`spring-boot-starter-data-redis` 由 `2.3.7.RELEASE` 改为 `2.3.3.RELEASE`
|
||||
- 修复:补上注解拦截器里漏掉验证`@SaCheckRole`的bug
|
||||
- 新增:新增同端互斥登录,像QQ一样手机电脑同时在线,但是两个手机上互斥登录 **[重要]**
|
||||
|
||||
|
||||
### 2021-1-2 @v1.8.0
|
||||
- 优化:优化源码注释
|
||||
- 修复:修复部分文档错别字
|
||||
- 修复:修复项目文件夹名称错误
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -73,9 +73,51 @@
|
||||
```
|
||||
|
||||
|
||||
- mode有两种取值:
|
||||
- `SaMode.AND`, 标注一组权限,会话必须全部具有才可通过校验
|
||||
- `SaMode.OR`, 标注一组权限,会话只要具有其一即可通过校验
|
||||
mode有两种取值:
|
||||
- `SaMode.AND`, 标注一组权限,会话必须全部具有才可通过校验
|
||||
- `SaMode.OR`, 标注一组权限,会话只要具有其一即可通过校验
|
||||
|
||||
|
||||
|
||||
|
||||
## 4、AOP模式使用注解
|
||||
|
||||
使用拦截器方式,只能把注解加到`Controller层`上,那么如果我想把注解写到项目的任意位置,比如`Service层`,应该怎么办? <br>
|
||||
很简单,你只需要将拦截器模式更换为`SpringAOP模式`即可, 在`pom.xml`里添加
|
||||
|
||||
``` xml
|
||||
<!-- sa-token整合SpringAOP实现注解鉴权 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-aop</artifactId>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
然后你就可以在任意地方使用注解鉴权,例如:
|
||||
``` java
|
||||
@Service
|
||||
public class UserService {
|
||||
@SaCheckLogin
|
||||
public List<String> getList() {
|
||||
System.out.println("getList");
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**注意:拦截器模式和AOP模式不可同时集成,否则会在Controller层发生一个注解校验两次的bug**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
```
|
||||
优点:兼容性好,缺点:session序列化后基本不可读,对开发者来讲等同于乱码
|
||||
@@ -23,7 +23,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
```
|
||||
优点:session序列化后可读性强,可灵活手动修改,缺点:兼容性稍差
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
- 所谓权限验证,验证的核心就是当前账号是否拥有一个权限码
|
||||
- 有:就让你通过、没有:那么禁止访问
|
||||
- 再往底了说,就是每个账号都会拥有一个权限码集合,我来验证这个集合中是否包括我需要检测的那个权限码
|
||||
- 例如:当前账号拥有权限码集合:`[101, 102, "user-add", "user-get"]`,这时候我去验证权限码:`201`,则结果就是验证失败,禁止访问
|
||||
- 所以现在问题的核心就是,1、如何获取一个账号所拥有的的权限码集合,2、本次操作要验证的权限码是哪个
|
||||
- 例如:当前账号拥有权限码集合:`["user:add", "user:delete", "user:get"]`,这时候我去验证权限码:`"user:update"`,则结果就是验证失败,禁止访问
|
||||
- 所以现在问题的核心就是:
|
||||
1. 如何获取一个账号所拥有的的权限码集合
|
||||
2. 本次操作要验证的权限码是哪个
|
||||
|
||||
## 获取当前账号权限码集合
|
||||
因为每个项目的需求不同,其权限设计也千变万化,所以【获取当前账号权限码集合】这一操作不可能内置到框架中,
|
||||
因为每个项目的需求不同,其权限设计也千变万化,【获取当前账号权限码集合】这一操作不可能内置到框架中,
|
||||
所以`sa-token`将此操作以接口的方式暴露给你,以方便的你根据自己的业务逻辑进行重写
|
||||
|
||||
- 你需要做的就是新建一个类,重写`StpInterface`接口,例如以下代码:
|
||||
@@ -34,8 +36,9 @@ public class StpInterfaceImpl implements StpInterface {
|
||||
* 返回一个账号所拥有的权限码集合
|
||||
*/
|
||||
@Override
|
||||
public List<Object> getPermissionCodeList(Object loginId, String loginKey) {
|
||||
List<Object> list = new ArrayList<Object>(); // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
|
||||
public List<String> getPermissionCodeList(Object loginId, String loginKey) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("101");
|
||||
list.add("user-add");
|
||||
list.add("user-delete");
|
||||
@@ -50,7 +53,8 @@ public class StpInterfaceImpl implements StpInterface {
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginKey) {
|
||||
List<String> list = new ArrayList<String>(); // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("admin");
|
||||
list.add("super-admin");
|
||||
return list;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# 同端互斥登录
|
||||
|
||||
如果你经常使用腾讯QQ,就会发现它的登录有如下特点:它可以手机电脑同时在线,但是不能在两个手机上同时登录一个账号 <br/>
|
||||
同端互斥登录,指的就是像腾讯QQ一样,在同一类型设备上只允许单地点登录,在不同类型设备上允许同时在线
|
||||
|
||||
---
|
||||
|
||||
## 具体API
|
||||
|
||||
在`sa-token`中如何做到同端互斥登录? <br/>
|
||||
首先在配置文件中,将 `allowConcurrentLogin` 配置为false,然后调用登录等相关接口时声明设备标识即可:
|
||||
|
||||
|
||||
#### 指定设备标识登录
|
||||
``` java
|
||||
StpUtil.setLoginId(10001, "PC"); // 指定`账号id`和`设备标识`进行登录
|
||||
```
|
||||
调用此方法登录后,同设备的会被顶下线(不同设备不受影响),再次访问系统时会抛出 `NotLoginException` 异常,场景值=`-4`
|
||||
|
||||
|
||||
#### 指定设备标识强制注销
|
||||
``` java
|
||||
StpUtil.logoutByLoginId(10001, "PC"); // 指定`账号id`和`设备标识`进行强制注销 (踢人下线)
|
||||
```
|
||||
如果第二个参数填写null或不填,代表将这个账号id所有在线端踢下线,被踢出者再次访问系统时会抛出 `NotLoginException` 异常,场景值=`-5`
|
||||
|
||||
|
||||
#### 查询当前登录的设备标识
|
||||
``` java
|
||||
StpUtil.getLoginDevice(); // 指返回当前token的登录设备
|
||||
```
|
||||
|
||||
|
||||
#### id反查token
|
||||
``` java
|
||||
StpUtil.getTokenValueByLoginId(10001, "APP"); // 获取指定loginId指定设备端的tokenValue
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# 会话治理
|
||||
|
||||
尽管框架将大部分操作提供了简易的封装,但在一些特殊场景下,我们仍需要绕过框架,直达数据底层进行一些操作 <br>
|
||||
sa-token提供以下API助你直接操作会话列表
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 具体API
|
||||
|
||||
#### 查询所有token
|
||||
``` java
|
||||
StpUtil.searchTokenValue(String keyword, int start, int size);
|
||||
```
|
||||
|
||||
#### 查询所有账号Session会话
|
||||
``` java
|
||||
StpUtil.searchSessionId(String keyword, int start, int size);
|
||||
```
|
||||
|
||||
|
||||
#### 查询所有令牌Session会话
|
||||
``` java
|
||||
StpUtil.searchTokenSessionId(String keyword, int start, int size);
|
||||
```
|
||||
|
||||
|
||||
#### 参数详解:
|
||||
- `keyword`: 查询关键字,只有包括这个字符串的token值才会被查询出来
|
||||
- `start`: 数据开始处索引, 值为-1时代表一次性取出所有数据
|
||||
- `size`: 要获取的数据条数
|
||||
|
||||
使用示例:
|
||||
``` java
|
||||
// 查询value包括1000的所有token,结果集从第0条开始,返回10条
|
||||
List<String> tokenList = StpUtil.searchTokenValue("1000", 0, 10);
|
||||
for (String token : tokenList) {
|
||||
System.out.println(token);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
#### 注意事项:
|
||||
由于会话查询底层采用了遍历方式获取数据,当数据量过大时此操作将会比较耗时,有多耗时呢?这里提供一份参考数据:
|
||||
- 单机模式下:百万会话取出10条token平均耗时 `0.255s`
|
||||
- Redis模式下:百万会话取出10条token平均耗时 `3.322s`
|
||||
|
||||
请根据业务实际水平合理调用API
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ StpUtil.getSession(); // 获取当前账号id的Session (
|
||||
StpUtil.getSession(true); // 获取当前账号id的Session, 并决定在Session尚未创建时,是否新建并返回
|
||||
StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session
|
||||
StpUtil.getSessionByLoginId(10001, true); // 获取账号id为10001的Session, 并决定在Session尚未创建时,是否新建并返回
|
||||
StpUtil.getSessionBySessionId("xxxx-xxxx"); // 获取SessionId为xxxx-xxxx的Session, 在Session尚未创建时, 返回null
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>sa-token</title>
|
||||
<title>sa-token 官方文档</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Description">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="keywords" content="sa-token|sa-token框架|sa-token文档|sa-token在线文档|权限认证框架">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,功能全面,上手简单,登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、多账号体系、注解式鉴权、花式token、自动续签、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<link rel="stylesheet" href="https://unpkg.com/docsify@4.11.3/lib/themes/vue.css">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,功能全面,上手简单,登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、多账号体系、注解式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<link rel="stylesheet" href="https://unpkg.zhimg.com/docsify@4.11.3/lib/themes/vue.css">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="doc/logo.png">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
|
||||
@@ -43,16 +43,16 @@
|
||||
<!-- 内容部分 -->
|
||||
<div class="main-box">
|
||||
<div class="content-box">
|
||||
<h1>sa-token<small>v1.8.0</small></h1>
|
||||
<h1>sa-token<small>v1.11.0</small></h1>
|
||||
<div class="sub-title">一个JavaWeb轻量级权限认证框架,功能全面,上手简单</div>
|
||||
<!-- <p>0配置开箱即用,低学习成本</p> -->
|
||||
<p>登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、多账号体系、注解式鉴权、花式token、自动续签、Spring集成...</p>
|
||||
<p>登录验证、权限验证、Session会话、踢人下线、集成Redis、前后台分离、模拟他人账号、多账号体系、注解式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...</p>
|
||||
<p>零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有</p>
|
||||
<div class="btn-box">
|
||||
<a href="https://github.com/click33/sa-token" target="_blank">GitHub</a>
|
||||
<a href="https://gitee.com/sz6/sa-token" target="_blank">码云</a>
|
||||
<a href="http://sa-app.dev33.cn/wall.html?name=sa-token" target="_blank">需求墙</a>
|
||||
<a href="doc/" target="_self">开发文档</a>
|
||||
<a href="doc/index.html" target="_self">开发文档</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -60,7 +60,7 @@
|
||||
<footer>
|
||||
<p>
|
||||
<span class="copyright">Copyright © 2020 </span>
|
||||
<a href="http://www.miitbeian.gov.cn/" target="_blank">鲁ICP备18046274号-2</a>
|
||||
<a href="https://beian.miit.gov.cn/" target="_blank">鲁ICP备18046274号-2</a>
|
||||
QQ交流群:<a href="https://jq.qq.com/?_wv=1027&k=45H977HM" target="_blank">1002350610</a>
|
||||
<!-- QQ交流群:<a href="https://jq.qq.com/?_wv=1027&k=5DHN5Ib" target="_blank">782974737</a> -->
|
||||
</p>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.11.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sa-token-spring-aop</name>
|
||||
<artifactId>sa-token-spring-aop</artifactId>
|
||||
<description>sa-token authentication by spring-aop</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- sa-token-spring-boot-starter -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
<!-- spring-boot-starter-aop -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,67 @@
|
||||
package cn.dev33.satoken.aop;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* sa-token 基于 Spring Aop 的注解鉴权
|
||||
* @author kong
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class SaCheckAspect {
|
||||
|
||||
/**
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
public StpLogic stpLogic = null;
|
||||
|
||||
/**
|
||||
* 创建,并指定一个默认的 StpLogic
|
||||
*/
|
||||
public SaCheckAspect() {
|
||||
this.stpLogic = StpUtil.stpLogic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义AOP签名 (切入所有使用sa-token鉴权注解的方法)
|
||||
*/
|
||||
public static final String POINTCUT_SIGN = "@within(cn.dev33.satoken.annotation.SaCheckLogin) || @annotation(cn.dev33.satoken.annotation.SaCheckLogin) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckRole) || @annotation(cn.dev33.satoken.annotation.SaCheckRole) || "
|
||||
+ "@within(cn.dev33.satoken.annotation.SaCheckPermission) || @annotation(cn.dev33.satoken.annotation.SaCheckPermission)";
|
||||
|
||||
/**
|
||||
* 声明AOP签名
|
||||
*/
|
||||
@Pointcut(POINTCUT_SIGN)
|
||||
public void pointcut() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 环绕切入
|
||||
* @param joinPoint 切面对象
|
||||
* @return 底层方法执行后的返回值
|
||||
* @throws Throwable 底层方法抛出的异常
|
||||
*/
|
||||
@Around("pointcut()")
|
||||
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
// 注解鉴权
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
stpLogic.checkMethodAnnotation(signature.getMethod());
|
||||
try {
|
||||
// 执行原有逻辑
|
||||
Object obj = joinPoint.proceed();
|
||||
return obj;
|
||||
} catch (Throwable e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.aop.SaCheckAspect
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
+5
-30
@@ -1,14 +1,13 @@
|
||||
package cn.dev33.satoken.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@@ -51,34 +50,10 @@ public class SaCheckInterceptor implements HandlerInterceptor {
|
||||
if (handler instanceof HandlerMethod == false) {
|
||||
return true;
|
||||
}
|
||||
HandlerMethod method = (HandlerMethod ) handler;
|
||||
Method method = ((HandlerMethod) handler).getMethod();
|
||||
|
||||
// ----------- 验证登录
|
||||
if(method.hasMethodAnnotation(SaCheckLogin.class) || method.getBeanType().isAnnotationPresent(SaCheckLogin.class)) {
|
||||
stpLogic.checkLogin();
|
||||
}
|
||||
|
||||
// ----------- 验证权限
|
||||
// 验证方法上的
|
||||
SaCheckPermission scp = method.getMethodAnnotation(SaCheckPermission.class);
|
||||
if(scp != null) {
|
||||
String[] permissionCodeArray = scp.value();
|
||||
if(scp.mode() == SaMode.AND) {
|
||||
stpLogic.checkPermissionAnd(permissionCodeArray); // 必须全部都有
|
||||
} else {
|
||||
stpLogic.checkPermissionOr(permissionCodeArray); // 有一个就行了
|
||||
}
|
||||
}
|
||||
// 验证类上的
|
||||
scp = method.getBeanType().getAnnotation(SaCheckPermission.class);
|
||||
if(scp != null) {
|
||||
String[] permissionCodeArray = scp.value();
|
||||
if(scp.mode() == SaMode.AND) {
|
||||
stpLogic.checkPermissionAnd(permissionCodeArray); // 必须全部都有
|
||||
} else {
|
||||
stpLogic.checkPermissionOr(permissionCodeArray); // 有一个就行了
|
||||
}
|
||||
}
|
||||
// 进行验证
|
||||
stpLogic.checkMethodAnnotation(method);
|
||||
|
||||
// 通过验证
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user