Compare commits
201 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 28513efffa | |||
| a333355b46 | |||
| 163053d6fa | |||
| 369d3ffb5b | |||
| 62acc8e852 | |||
| aac6ef9a3b | |||
| 68521356ae | |||
| a2c40b7ae7 | |||
| 0f84c4440f | |||
| 6048fe3e90 | |||
| f9bd57c884 | |||
| 1cd2f8ab1b | |||
| 5d62a9aeb1 | |||
| 0d4830eb7d | |||
| 9e47461a0a | |||
| be7a043196 | |||
| 2c6523a143 | |||
| 116f7d0976 | |||
| 1074394fb2 | |||
| 50b19b84d7 | |||
| 29b86891db | |||
| 91d90dd901 | |||
| 978c8dd71e | |||
| 9ffc85e749 | |||
| b4590bfbc1 | |||
| 3d0fffbc9a | |||
| 0163044a7c | |||
| ce277bfbc2 | |||
| 3ea86f1584 | |||
| 8767bd00d5 | |||
| e34200efb4 | |||
| 1bc07e94c8 | |||
| d618257dd8 | |||
| b84ce6144c | |||
| 7f33d6114a | |||
| b3567e4cd7 | |||
| ebeafebc3d | |||
| dd3ab971ac | |||
| 62add1edc2 | |||
| f13f128b65 | |||
| 58e21566f6 | |||
| 62674497d3 | |||
| 5cf282ca5d | |||
| 1260b83129 | |||
| 9eb10eae11 | |||
| cdb1b0c540 | |||
| db75d5a19b | |||
| cd9f835713 | |||
| f4272c3ad4 | |||
| 2286b02881 | |||
| 9f194e73e2 | |||
| f2d0fae8a4 | |||
| 1fa72d32f2 | |||
| 6ffd14973f | |||
| 939568c7b1 | |||
| 4550ad74c8 | |||
| d4d4bd129d | |||
| f9f10477eb | |||
| 7a53b5cb00 | |||
| e2463b7e03 | |||
| ebec86f0de | |||
| cf2f64ce78 | |||
| 05c8a6b6f2 | |||
| c50f55983a | |||
| 4ab6bdd66a | |||
| b481589e85 | |||
| a77042b891 | |||
| 254be3b3e5 | |||
| 2c20e7baa0 | |||
| 85951637ec | |||
| cda9542cbd | |||
| 03fed93422 | |||
| 079f87ef4f | |||
| 8cbb0a8646 | |||
| ad4f2fee7e | |||
| 5ad5a51426 | |||
| c813f2d41f | |||
| 1337922620 | |||
| 03c9fc14f1 | |||
| 33790c62b7 | |||
| 54ecb04ce0 | |||
| 25a649df95 | |||
| 4fee7774e6 | |||
| 138db72fbe | |||
| b862377333 | |||
| 76f58a3a6d | |||
| e54b9e6f85 | |||
| 18483373f6 | |||
| 2e4c1da6dc | |||
| 25cdfc3e55 | |||
| 42b07671ba | |||
| 022a26af10 | |||
| 698b457abe | |||
| ce53c35067 | |||
| a256249fb5 | |||
| 4552b8a9fd | |||
| 96afba67fd | |||
| b221b82610 | |||
| 113a4238b7 | |||
| 981b51faaa | |||
| 6351cd5dd5 | |||
| 801c3344d1 | |||
| 61f9b0b158 | |||
| 6b85c779ba | |||
| 31eb525f4a | |||
| 7cff4cc47c | |||
| bd0443c3cf | |||
| 658284bed3 | |||
| b8aacfe625 | |||
| 6b80afe806 | |||
| d3113ed6c9 | |||
| 8ad2e3db96 | |||
| 2b18084cbc | |||
| c830ed77ca | |||
| 652e6172af | |||
| a1bab9e747 | |||
| 060ed055d0 | |||
| 068f838165 | |||
| eb1ec5676c | |||
| 1bc0f51845 | |||
| daff33df13 | |||
| 41231a87d1 | |||
| 9e45312924 | |||
| a8a702fe13 | |||
| 50624d0f47 | |||
| 4807429bf1 | |||
| 2bddb0df89 | |||
| 3929b16dfb | |||
| 2e7c0ac590 | |||
| a38029e060 | |||
| c4160e4fd3 | |||
| 4003de98c6 | |||
| fb017d9718 | |||
| 657623f910 | |||
| 7369977241 | |||
| bb5c378f48 | |||
| f9ec6e6487 | |||
| 82591e397f | |||
| 73d4fe5071 | |||
| a3677c4afc | |||
| 9bffe60112 | |||
| 99dffba4eb | |||
| 634a3be1aa | |||
| 7740ab0a3f | |||
| 6d26761fd5 | |||
| 4a91553a77 | |||
| 30dad24302 | |||
| ed7b1cef69 | |||
| 17235e0d2c | |||
| 8a72c69474 | |||
| 3bec16627e | |||
| d491f4083f | |||
| 643118177a | |||
| cf6632df79 | |||
| 474a560691 | |||
| 040777be7c | |||
| 977ab3ed40 | |||
| b409df78b3 | |||
| 22141193d4 | |||
| d7f7a89af2 | |||
| e5f751d004 | |||
| d107b6b341 | |||
| dc1e01509d | |||
| f260f6028a | |||
| 4ba55ff687 | |||
| 3e0ae863e5 | |||
| 826760f160 | |||
| dc6b0ba061 | |||
| ba5784abec | |||
| b7dba13cab | |||
| 713e11482c | |||
| 356e65f749 | |||
| e254167398 | |||
| 7b2d402acb | |||
| 0e16f76700 | |||
| 3e09e68b6e | |||
| 022e286904 | |||
| 7acf5e9790 | |||
| 4fb1ea96fb | |||
| 32bb677e26 | |||
| 9aa6e5db42 | |||
| 854774a1f1 | |||
| 73e2b2d593 | |||
| fd8d0f6d92 | |||
| a0715b8aea | |||
| 9a58702eb3 | |||
| cff04c331c | |||
| 23a85700af | |||
| 97bc0211d7 | |||
| 6ccb8ea38b | |||
| 765c4ae9f2 | |||
| 23da73f032 | |||
| 99a45be22f | |||
| d029a28488 | |||
| 5dc1aeb774 | |||
| 9bd367b877 | |||
| 3a4c1ef8f0 | |||
| 2e5b166ab9 | |||
| 37d38ba2b4 | |||
| d6e11ce5d9 | |||
| 96c279f06c |
@@ -0,0 +1,18 @@
|
||||
### 使用版本:
|
||||
请提供一下版本号
|
||||
|
||||
|
||||
### 报错信息:
|
||||
请提供报错的详细信息
|
||||
|
||||
|
||||
### 希望结果:
|
||||
相比于已发生的报错,您希望看到什么样的运行结果
|
||||
|
||||
|
||||
### 复现步骤:
|
||||
如果复现步骤比较复杂,请将 demo 上传到 git 并留下地址
|
||||
|
||||
|
||||
备注:您提供的信息越充足,我们将越能快速的定位错误
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
### 使用版本:
|
||||
请提供一下版本号
|
||||
|
||||
|
||||
### 报错信息:
|
||||
请提供报错的详细信息
|
||||
|
||||
|
||||
### 希望结果:
|
||||
相比于已发生的报错,您希望看到什么样的运行结果
|
||||
|
||||
|
||||
### 复现步骤:
|
||||
如果复现步骤比较复杂,请将 demo 上传到 git 并留下地址
|
||||
|
||||
|
||||
备注:您提供的信息越充足,我们将越能快速的定位错误
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://gitee.com/dromara/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.26.0</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.29.0</h1>
|
||||
<h4 align="center">一个轻量级 Java 权限认证框架,让鉴权变得简单、优雅!</h4>
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg"></a>
|
||||
<a href="https://gitee.com/dromara/sa-token/members"><img src="https://gitee.com/dromara/sa-token/badge/fork.svg"></a>
|
||||
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg?theme=gvp"></a>
|
||||
<a href="https://gitee.com/dromara/sa-token/members"><img src="https://gitee.com/dromara/sa-token/badge/fork.svg?theme=gvp"></a>
|
||||
<a href="https://github.com/dromara/sa-token/stargazers"><img src="https://img.shields.io/github/stars/dromara/sa-token?style=flat-square&logo=GitHub"></a>
|
||||
<a href="https://github.com/dromara/sa-token/network/members"><img src="https://img.shields.io/github/forks/dromara/sa-token?style=flat-square&logo=GitHub"></a>
|
||||
<a href="https://github.com/dromara/sa-token/watchers"><img src="https://img.shields.io/github/watchers/dromara/sa-token?style=flat-square&logo=GitHub"></a>
|
||||
@@ -18,7 +18,10 @@
|
||||
## 前言:
|
||||
- [在线文档:http://sa-token.dev33.cn/](http://sa-token.dev33.cn/)
|
||||
|
||||
- 我们将会尽力讲解每个功能的设计原因、应用场景,用心阅读文档,你学习到的将不止是 `Sa-Token` 框架本身,更是绝大多数场景下权限设计的最佳实践。
|
||||
- 注:学习测试请拉取 master 分支,dev 为正在开发的分支,有很多特性并不稳定。
|
||||
|
||||
- 开源不易,点个 star 鼓励一下吧!
|
||||
|
||||
|
||||
## Sa-Token 介绍
|
||||
|
||||
@@ -56,33 +59,32 @@ public String insert(SysUser user) {
|
||||
|
||||
将某个账号踢下线(待到对方再次访问系统时会抛出`NotLoginException`异常)
|
||||
``` java
|
||||
// 使账号id为 10001 的会话强制注销登录
|
||||
StpUtil.logoutByLoginId(10001);
|
||||
// 将账号id为 10001 的会话踢下线
|
||||
StpUtil.kickout(10001);
|
||||
```
|
||||
|
||||
在 Sa-Token 中,绝大多数功能都可以 **一行代码** 完成:
|
||||
``` java
|
||||
StpUtil.login(10001); // 标记当前会话登录的账号id
|
||||
StpUtil.getLoginId(); // 获取当前会话登录的账号id
|
||||
StpUtil.isLogin(); // 获取当前会话是否已经登录, 返回true或false
|
||||
StpUtil.logout(); // 当前会话注销登录
|
||||
StpUtil.logoutByLoginId(10001); // 让账号为10001的会话注销登录(踢人下线)
|
||||
StpUtil.hasRole("super-admin"); // 查询当前账号是否含有指定角色标识, 返回true或false
|
||||
StpUtil.hasPermission("user:add"); // 查询当前账号是否含有指定权限, 返回true或false
|
||||
StpUtil.getSession(); // 获取当前账号id的Session
|
||||
StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session
|
||||
StpUtil.login(10001); // 标记当前会话登录的账号id
|
||||
StpUtil.getLoginId(); // 获取当前会话登录的账号id
|
||||
StpUtil.isLogin(); // 获取当前会话是否已经登录, 返回true或false
|
||||
StpUtil.logout(); // 当前会话注销登录
|
||||
StpUtil.kickout(10001); // 将账号为10001的会话踢下线
|
||||
StpUtil.hasRole("super-admin"); // 查询当前账号是否含有指定角色标识, 返回true或false
|
||||
StpUtil.hasPermission("user:add"); // 查询当前账号是否含有指定权限, 返回true或false
|
||||
StpUtil.getSession(); // 获取当前账号id的Session
|
||||
StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session
|
||||
StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌值
|
||||
StpUtil.login(10001, "PC"); // 指定设备标识登录,常用于“同端互斥登录”
|
||||
StpUtil.logoutByLoginId(10001, "PC"); // 指定设备标识进行强制注销 (不同端不受影响)
|
||||
StpUtil.openSafe(120); // 在当前会话开启二级认证,有效期为120秒
|
||||
StpUtil.checkSafe(); // 校验当前会话是否处于二级认证有效期内,校验失败会抛出异常
|
||||
StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
StpUtil.login(10001, "PC"); // 指定设备标识登录,常用于“同端互斥登录”
|
||||
StpUtil.kickout(10001, "PC"); // 指定账号指定设备标识踢下线 (不同端不受影响)
|
||||
StpUtil.openSafe(120); // 在当前会话开启二级认证,有效期为120秒
|
||||
StpUtil.checkSafe(); // 校验当前会话是否处于二级认证有效期内,校验失败会抛出异常
|
||||
StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
```
|
||||
|
||||
即使不运行测试,相信您也能意会到绝大多数 API 的用法。
|
||||
|
||||
|
||||
|
||||
## Sa-Token 功能一览
|
||||
|
||||
- **登录认证** —— 单端登录、多端登录、同端互斥登录、七天内免登录
|
||||
@@ -117,44 +119,37 @@ StpUtil.switchTo(10044); // 将当前会话身份临时切换
|
||||
|
||||
|
||||
## Sa-Token-SSO 单点登录
|
||||
对于单点登录,网上教程大多以CAS模式为主,其实对于不同的系统架构,实现单点登录的步骤也大为不同,Sa-Token由简入难将其划分为三种模式:
|
||||
网上的单点登录教程大多以CAS流程为主,其实对于不同的系统架构,实现单点登录的步骤也大为不同,Sa-Token由简入难将其划分为三种模式:
|
||||
|
||||
| 系统架构 | 采用模式 | 简介 | 文档链接 |
|
||||
| :-------- | :-------- | :-------- | :-------- |
|
||||
| 前端同域 + 后端同 Redis | 模式一 | 共享Cookie同步会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type1)、[示例](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-sso1) |
|
||||
| 前端不同域 + 后端同 Redis | 模式二 | URL重定向传播会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type2)、[示例](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-sso2-server) |
|
||||
| 前端不同域 + 后端 不同Redis | 模式三 | Http请求获取会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type3)、[示例](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-sso3-server) |
|
||||
| 前端同域 + 后端同 Redis | 模式一 | 共享Cookie同步会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type1)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso1-client) |
|
||||
| 前端不同域 + 后端同 Redis | 模式二 | URL重定向传播会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type2)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso2-client) |
|
||||
| 前端不同域 + 后端 不同Redis | 模式三 | Http请求获取会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type3)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso3-client) |
|
||||
|
||||
|
||||
1. 前端同域:就是指多个系统可以部署在同一个主域名之下,比如:`c1.domain.com`、`c2.domain.com`、`c3.domain.com`
|
||||
2. 后端同Redis:就是指多个系统可以连接同一个Redis,其它的缓存数据中心亦可。PS:这里并不需要把所有项目的数据都放在同一个Redis中,Sa-Token提供了 **`[权限缓存与业务缓存分离]`** 的解决方案,详情戳:[Alone独立Redis插件](http://sa-token.dev33.cn/doc/index.html#/plugin/alone-redis)
|
||||
2. 后端同Redis:就是指多个系统可以连接同一个Redis。PS:这里并不需要把所有项目的数据都放在同一个Redis中,Sa-Token提供了 **`[权限缓存与业务缓存分离]`** 的解决方案,详情戳:[Alone独立Redis插件](http://sa-token.dev33.cn/doc/index.html#/plugin/alone-redis)
|
||||
3. 如果既无法做到前端同域,也无法做到后端同Redis,那么只能走模式三,Http请求获取会话(Sa-Token对SSO提供了完整的封装,你只需要按照示例从文档上复制几段代码便可以轻松集成)
|
||||
4. 技术选型一定要根据系统架构对症下药,切不可胡乱选择
|
||||
|
||||
|
||||
## Sa-Token-SSO 特性
|
||||
1. API简单易用,文档介绍详细,且提供直接可用的集成示例
|
||||
2. 支持三种模式,不论是否跨域、是否共享Redis、是否前后端分离,都可以完美解决
|
||||
3. 安全性高:内置域名校验、Ticket校验、秘钥校验等,杜绝`Ticket劫持`、`Token窃取`等常见攻击手段(文档讲述攻击原理和防御手段)
|
||||
4. 不丢参数:笔者曾试验多个单点登录框架,均有参数丢失的情况,比如重定向之前是:`http://a.com?id=1&name=2`,登录成功之后就变成了:`http://a.com?id=1`,Sa-Token-SSO内有专门的算法保证了参数不丢失,登录成功之后原路返回页面
|
||||
5. 无缝集成:由于Sa-Token本身就是一个权限认证框架,因此你可以只用一个框架同时解决`权限认证` + `单点登录`问题,让你不再到处搜索:xxx单点登录与xxx权限认证如何整合……
|
||||
6. 高可定制:Sa-Token-SSO模块对代码架构侵入性极低,结合Sa-Token本身的路由拦截特性,你可以非常轻松的定制化开发
|
||||
|
||||
|
||||
## Sa-Token-OAuth2.0 授权登录
|
||||
Sa-OAuth2 模块基于 [RFC-6749 标准](https://tools.ietf.org/html/rfc6749) 编写,通过Sa-OAuth2你可以非常轻松的实现系统的OAuth2.0授权认证
|
||||
|
||||
1. 授权码(Authorization Code):OAuth2.0标准授权步骤,Server端向Client端下放Code码,Client端再用Code码换取授权Token
|
||||
2. 隐藏式(Implicit):无法使用授权码模式时的备用选择,Server端使用URL重定向方式直接将Token下放到Client端页面
|
||||
3. 密码式(Password):Client直接拿着用户的账号密码换取授权Token
|
||||
4. 客户端凭证(Client Credentials):Server端针对Client级别的Token,代表应用自身的资源授权
|
||||
| 授权模式 | 简介 |
|
||||
| :-------- | :-------- |
|
||||
| 授权码(Authorization Code) | OAuth2.0 标准授权步骤,Server 端向 Client 端下放 Code 码,Client 端再用 Code 码换取授权 Token |
|
||||
| 隐藏式(Implicit) | 无法使用授权码模式时的备用选择,Server 端使用 URL 重定向方式直接将 Token 下放到 Client 端页面 |
|
||||
| 密码式(Password) | Client直接拿着用户的账号密码换取授权 Token |
|
||||
| 客户端凭证(Client Credentials)| Server 端针对 Client 级别的 Token,代表应用自身的资源授权 |
|
||||
|
||||
详细参考文档:[http://sa-token.dev33.cn/doc/index.html#/oauth2/readme](http://sa-token.dev33.cn/doc/index.html#/oauth2/readme)
|
||||
|
||||
|
||||
|
||||
## Sa-Token 功能结构图
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
@@ -172,17 +167,29 @@ Sa-OAuth2 模块基于 [RFC-6749 标准](https://tools.ietf.org/html/rfc6749)
|
||||
|
||||
- **[ dcy-fast ]**:[ 一个基于 SpringBoot + Sa-Token + Mybatis-Plus 的后台管理系统,前端vue-element-admin,并且内置代码生成器](https://gitee.com/dcy421/dcy-fast)
|
||||
|
||||
- **[ helio-starters ]**:[ 基于JDK15 + Spring Boot 2.4 + Sa-Token + Mybatis-Plus的单体Boot版脚手架和微服务Cloud版脚手架,带有配套后台管理前端模板及代码生成器](https://gitee.com/uncarbon97/helio-starters)
|
||||
- **[ helio-starters ]**:[ 单体 Boot 版脚手架 + 微服务 Cloud 版脚手架,带有配套后台管理前端模板及代码生成器](https://gitee.com/uncarbon97/helio-starters)
|
||||
|
||||
- **[ sa-token-plugin ]**:[Sa-Token第三方插件实现,基于Sa-Token-Core,提供一些与官方不同实现机制的的插件集合,作为Sa-Token开源生态的补充](https://gitee.com/bootx/sa-token-plugin)
|
||||
|
||||
- **[ easy-admin ]**:[一个基于SpringBoot2 + Sa-Token + Mybatis-Plus + Snakerflow + Layui 的后台管理系统,灵活多变可前后端分离,也可单体,内置代码生成器、权限管理、工作流引擎等](https://gitee.com/lakernote/easy-admin)
|
||||
|
||||
- **[ RuoYi-Vue-Plus ]**:[基于 RuoYi-Vue 集成 SaToken + Lombok + Mybatis-Plus + Undertow + knife4j + Hutool + Feign 重写所有原生业务 定期与 RuoYi-Vue 同步](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/)
|
||||
|
||||
- **[ falser-cloud ]**: [基于 SpringCloud Alibaba + SpringCloud gateway + SpringBoot + Sa-Token + vue-admin-template + Nacos + Rabbit MQ + Redis 的一个后台管理系统,前后端分离,权限管理,菜单管理,数据字典,停车场系统管理等功能](https://gitee.com/falser/falser-cloud)
|
||||
|
||||
- **[ bootx-platform ]**:[集成sa-token和sa-token-plugin并深度定制认证模块,包含多级别数据范围权限、数据自动加解密、数据脱敏、超级查询器、以及支付收单、消息通知等准商用功能的开源免费开发脚手架项目](https://gitee.com/bootx/bootx-platform)
|
||||
|
||||
- **[ QForum-Core ]**:[QForum 论坛系统官方核心,可拓展性强、轻量级、高性能、前后端分离,基于 SpringBoot2 + Sa-Token + Mybatis-Plus](https://github.com/Project-QForum/QForum-Core/)
|
||||
|
||||
- **[ ExciteCMS-Layui ]**:[ExciteCMS 快速开发脚手架:一款后端基于 SpringBoot2 + Sa-Token + Mybatis-Plus,前端基于 Layuimini 的内容管理系统,具备RBAC、日志管理、代码生成等功能,并集成常用的支付、OSS等第三方服务,拥有详细的开发文档](https://gitee.com/ExciteTeam/ExciteCMS-SpringBoot-Layui)
|
||||
|
||||
如果您的项目使用了Sa-Token,欢迎提交pr
|
||||
|
||||
## 友情链接
|
||||
- **[ OkHttps ]**:[ 一个轻量级http通信框架,API设计无比优雅,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
|
||||
- **[ Bean Searcher ]**:[ 比 MyBatis 效率快 100 倍的条件检索引擎,天生支持联表,使一行代码实现复杂列表检索成为可能!](https://github.com/ejlchina/bean-searcher)
|
||||
|
||||
- **[ 小诺快速开发平台 ]**:[ 基于SpringBoot2 + AntDesignVue全新快速开发平台,同时拥有三个版本](https://xiaonuo.vip/index#pricing)
|
||||
|
||||
- **[ Jpom ]**:[ 简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件](https://gitee.com/dromara/Jpom)
|
||||
@@ -190,16 +197,23 @@ Sa-OAuth2 模块基于 [RFC-6749 标准](https://tools.ietf.org/html/rfc6749)
|
||||
- **[ TLog ]**:[ 一个轻量级的分布式日志标记追踪神器](https://gitee.com/dromara/TLog)
|
||||
|
||||
|
||||
## 贡献者名单
|
||||
感谢每一个为 Sa-Token 贡献代码的小伙伴
|
||||
|
||||
[](https://giteye.net/chart/CGZ7GT8E)
|
||||
|
||||
|
||||
## 交流群
|
||||
QQ交流群:1002350610 [点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM)
|
||||
QQ交流群:1群:1002350610 (已满) 、
|
||||
2群:614714762 [点击加入](https://jq.qq.com/?_wv=1027&k=b759RZrL)
|
||||
|
||||
微信交流群:
|
||||
|
||||

|
||||
<!--
|
||||
<!--  -->
|
||||
|
||||

|
||||
|
||||
(扫码添加微信,备注:sa-token,邀您加入群聊)
|
||||
-->
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
+24
-10
@@ -37,11 +37,11 @@ cd sa-token-demo-alone-redis
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-sso1
|
||||
cd sa-token-demo-thymeleaf
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-sso1-server
|
||||
cd sa-token-demo-sso-server
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
@@ -49,29 +49,43 @@ cd sa-token-demo-sso1-client
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-sso2-server
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-sso2-client
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-sso3-server
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-sso3-client
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-jwt
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-jwt
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
|
||||
cd sa-token-demo-dubbo-provider
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-dubbo-consumer
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
|
||||
cd ..
|
||||
|
||||
:: test clean
|
||||
|
||||
cd sa-token-test
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
|
||||
|
||||
|
||||
:: 最后打印
|
||||
echo;
|
||||
echo;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.26.0</version>
|
||||
<version>1.29.0</version>
|
||||
|
||||
<!-- 项目介绍 -->
|
||||
<name>sa-token</name>
|
||||
@@ -21,6 +21,7 @@
|
||||
<module>sa-token-core</module>
|
||||
<module>sa-token-starter</module>
|
||||
<module>sa-token-plugin</module>
|
||||
<!-- <module>sa-token-test</module> -->
|
||||
<!-- <module>sa-token-demo/sa-token-demo-solon</module> -->
|
||||
</modules>
|
||||
|
||||
@@ -36,7 +37,7 @@
|
||||
|
||||
<!-- 一些属性 -->
|
||||
<properties>
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
<sa-token-version>1.29.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.26.0</version>
|
||||
<version>1.29.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.config.SaTokenConfigFactory;
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.context.SaTokenContextDefaultImpl;
|
||||
import cn.dev33.satoken.context.second.SaTokenSecondContext;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
@@ -18,21 +19,22 @@ import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.temp.SaTempInterface;
|
||||
import cn.dev33.satoken.temp.SaTempDefaultImpl;
|
||||
import cn.dev33.satoken.temp.SaTempInterface;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
/**
|
||||
* 管理 Sa-Token 所有接口对象
|
||||
* 管理 Sa-Token 所有全局组件
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class SaManager {
|
||||
|
||||
/**
|
||||
* 配置文件 Bean
|
||||
*/
|
||||
public static SaTokenConfig config;
|
||||
public volatile static SaTokenConfig config;
|
||||
public static void setConfig(SaTokenConfig config) {
|
||||
SaManager.config = config;
|
||||
if(config.getIsPrint()) {
|
||||
@@ -55,7 +57,7 @@ public class SaManager {
|
||||
/**
|
||||
* 持久化 Bean
|
||||
*/
|
||||
private static SaTokenDao saTokenDao;
|
||||
private volatile static SaTokenDao saTokenDao;
|
||||
public static void setSaTokenDao(SaTokenDao saTokenDao) {
|
||||
if((SaManager.saTokenDao instanceof SaTokenDaoDefaultImpl)) {
|
||||
((SaTokenDaoDefaultImpl)SaManager.saTokenDao).endRefreshThread();
|
||||
@@ -76,7 +78,7 @@ public class SaManager {
|
||||
/**
|
||||
* 权限认证 Bean
|
||||
*/
|
||||
private static StpInterface stpInterface;
|
||||
private volatile static StpInterface stpInterface;
|
||||
public static void setStpInterface(StpInterface stpInterface) {
|
||||
SaManager.stpInterface = stpInterface;
|
||||
}
|
||||
@@ -94,7 +96,7 @@ public class SaManager {
|
||||
/**
|
||||
* 框架行为 Bean
|
||||
*/
|
||||
private static SaTokenAction saTokenAction;
|
||||
private volatile static SaTokenAction saTokenAction;
|
||||
public static void setSaTokenAction(SaTokenAction saTokenAction) {
|
||||
SaManager.saTokenAction = saTokenAction;
|
||||
}
|
||||
@@ -110,27 +112,55 @@ public class SaManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 容器操作 Bean
|
||||
* 上下文Context Bean
|
||||
*/
|
||||
private static SaTokenContext saTokenContext;
|
||||
private volatile static SaTokenContext saTokenContext;
|
||||
public static void setSaTokenContext(SaTokenContext saTokenContext) {
|
||||
SaManager.saTokenContext = saTokenContext;
|
||||
}
|
||||
public static SaTokenContext getSaTokenContext() {
|
||||
if (saTokenContext == null) {
|
||||
synchronized (SaManager.class) {
|
||||
if (saTokenContext == null) {
|
||||
setSaTokenContext(new SaTokenContextDefaultImpl());
|
||||
}
|
||||
return saTokenContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 二级Context
|
||||
*/
|
||||
private volatile static SaTokenSecondContext saTokenSecondContext;
|
||||
public static SaTokenSecondContext getSaTokenSecondContext() {
|
||||
return saTokenSecondContext;
|
||||
}
|
||||
public static void setSaTokenSecondContext(SaTokenSecondContext saTokenSecondContext) {
|
||||
SaManager.saTokenSecondContext = saTokenSecondContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个可用的SaTokenContext
|
||||
* @return /
|
||||
*/
|
||||
public static SaTokenContext getSaTokenContextOrSecond() {
|
||||
|
||||
// s1. 一级Context可用时返回一级Context
|
||||
if(saTokenContext != null) {
|
||||
if(saTokenSecondContext == null || saTokenContext.isValid()) {
|
||||
// 因为 isValid 是一个耗时操作,所以此处假定:二级Context为null的情况下无需验证一级Context有效性
|
||||
// 这样可以提升6倍左右的上下文获取速度
|
||||
return saTokenContext;
|
||||
}
|
||||
}
|
||||
return saTokenContext;
|
||||
|
||||
// s2. 一级Context不可用时判断二级Context是否可用
|
||||
if(saTokenSecondContext != null && saTokenSecondContext.isValid()) {
|
||||
return saTokenSecondContext;
|
||||
}
|
||||
|
||||
// s3. 都不行,就返回默认的 Context
|
||||
return SaTokenContextDefaultImpl.defaultContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 侦听器 Bean
|
||||
*/
|
||||
private static SaTokenListener saTokenListener;
|
||||
private volatile static SaTokenListener saTokenListener;
|
||||
public static void setSaTokenListener(SaTokenListener saTokenListener) {
|
||||
SaManager.saTokenListener = saTokenListener;
|
||||
}
|
||||
@@ -148,7 +178,7 @@ public class SaManager {
|
||||
/**
|
||||
* 临时令牌验证模块 Bean
|
||||
*/
|
||||
private static SaTempInterface saTemp;
|
||||
private volatile static SaTempInterface saTemp;
|
||||
public static void setSaTemp(SaTempInterface saTemp) {
|
||||
SaManager.saTemp = saTemp;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package cn.dev33.satoken.action;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* Sa-Token 逻辑代理接口
|
||||
* <h1> v1.27+ 此接口已废弃,目前版本暂时向下兼容,请及时更换为 SaStrategy </h1>
|
||||
* <p>Sa-Token 逻辑代理接口 </p>
|
||||
* <p>此接口将会代理框架内部的一些关键性逻辑,方便开发者进行按需重写</p>
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public interface SaTokenAction {
|
||||
|
||||
/**
|
||||
@@ -42,4 +45,10 @@ public interface SaTokenAction {
|
||||
*/
|
||||
public void checkMethodAnnotation(Method method);
|
||||
|
||||
/**
|
||||
* 从指定元素校验注解
|
||||
* @param target /
|
||||
*/
|
||||
public void validateAnnotation(AnnotatedElement target);
|
||||
|
||||
}
|
||||
|
||||
@@ -13,14 +13,17 @@ import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaCheckSafe;
|
||||
import cn.dev33.satoken.basic.SaBasicUtil;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
/**
|
||||
* Sa-Token 逻辑代理接口 [默认实现类]
|
||||
* <h1> v1.27+ 此接口已废弃,目前版本暂时向下兼容,请及时更换为 SaStrategy </h1>
|
||||
* <p> Sa-Token 逻辑代理接口 [默认实现类] </p>
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public class SaTokenActionDefaultImpl implements SaTokenAction {
|
||||
|
||||
/**
|
||||
@@ -110,37 +113,38 @@ public class SaTokenActionDefaultImpl implements SaTokenAction {
|
||||
* 从指定元素校验注解
|
||||
* @param target see note
|
||||
*/
|
||||
protected void validateAnnotation(AnnotatedElement target) {
|
||||
public void validateAnnotation(AnnotatedElement target) {
|
||||
|
||||
// 校验 @SaCheckLogin 注解
|
||||
if(target.isAnnotationPresent(SaCheckLogin.class)) {
|
||||
SaCheckLogin at = target.getAnnotation(SaCheckLogin.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
SaCheckLogin checkLogin = (SaCheckLogin) SaStrategy.me.getAnnotation.apply(target, SaCheckLogin.class);
|
||||
if(checkLogin != null) {
|
||||
SaManager.getStpLogic(checkLogin.type()).checkByAnnotation(checkLogin);
|
||||
}
|
||||
|
||||
|
||||
// 校验 @SaCheckRole 注解
|
||||
if(target.isAnnotationPresent(SaCheckRole.class)) {
|
||||
SaCheckRole at = target.getAnnotation(SaCheckRole.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
SaCheckRole checkRole = (SaCheckRole) SaStrategy.me.getAnnotation.apply(target, SaCheckRole.class);
|
||||
if(checkRole != null) {
|
||||
SaManager.getStpLogic(checkRole.type()).checkByAnnotation(checkRole);
|
||||
}
|
||||
|
||||
|
||||
// 校验 @SaCheckPermission 注解
|
||||
if(target.isAnnotationPresent(SaCheckPermission.class)) {
|
||||
SaCheckPermission at = target.getAnnotation(SaCheckPermission.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
SaCheckPermission checkPermission = (SaCheckPermission) SaStrategy.me.getAnnotation.apply(target, SaCheckPermission.class);
|
||||
if(checkPermission != null) {
|
||||
SaManager.getStpLogic(checkPermission.type()).checkByAnnotation(checkPermission);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckSafe 注解
|
||||
if(target.isAnnotationPresent(SaCheckSafe.class)) {
|
||||
SaCheckSafe at = target.getAnnotation(SaCheckSafe.class);
|
||||
SaManager.getStpLogic(null).checkByAnnotation(at);
|
||||
SaCheckSafe checkSafe = (SaCheckSafe) SaStrategy.me.getAnnotation.apply(target, SaCheckSafe.class);
|
||||
if(checkSafe != null) {
|
||||
SaManager.getStpLogic(checkSafe.type()).checkByAnnotation(checkSafe);
|
||||
}
|
||||
|
||||
|
||||
// 校验 @SaCheckBasic 注解
|
||||
if(target.isAnnotationPresent(SaCheckBasic.class)) {
|
||||
SaCheckBasic at = target.getAnnotation(SaCheckBasic.class);
|
||||
SaBasicUtil.check(at.realm(), at.account());
|
||||
SaCheckBasic checkBasic = (SaCheckBasic) SaStrategy.me.getAnnotation.apply(target, SaCheckBasic.class);
|
||||
if(checkBasic != null) {
|
||||
SaBasicUtil.check(checkBasic.realm(), checkBasic.account());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public @interface SaCheckBasic {
|
||||
String realm() default SaBasicTemplate.DEFAULT_REALM;
|
||||
|
||||
/**
|
||||
* 需要校验的账号密码
|
||||
* 需要校验的账号密码,格式形如 sa:123456
|
||||
* @return see note
|
||||
*/
|
||||
String account() default "";
|
||||
|
||||
@@ -33,4 +33,21 @@ public @interface SaCheckPermission {
|
||||
*/
|
||||
String type() default "";
|
||||
|
||||
/**
|
||||
* 在权限认证不通过时的次要选择,两者只要其一认证成功即可通过校验
|
||||
*
|
||||
* <p>
|
||||
* 例1:@SaCheckPermission(value="user-add", orRole="admin"),
|
||||
* 代表本次请求只要具有 user-add权限 或 admin角色 其一即可通过校验
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 例2: orRole = {"admin", "manager", "staff"},具有三个角色其一即可 <br>
|
||||
* 例3: orRole = {"admin, manager, staff"},必须三个角色同时具备
|
||||
* </p>
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
String[] orRole() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -15,4 +15,10 @@ import java.lang.annotation.Target;
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
public @interface SaCheckSafe {
|
||||
|
||||
/**
|
||||
* 多账号体系下所属的账号体系标识
|
||||
* @return see note
|
||||
*/
|
||||
String type() default "";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package cn.dev33.satoken.config;
|
||||
|
||||
/**
|
||||
* Sa-Token Cookie写入 相关配置
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaCookieConfig {
|
||||
|
||||
/**
|
||||
* 域(写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景)
|
||||
*/
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 路径
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 是否只在 https 协议下有效
|
||||
*/
|
||||
private Boolean secure = false;
|
||||
|
||||
/**
|
||||
* 是否禁止 js 操作 Cookie
|
||||
*/
|
||||
private Boolean httpOnly = false;
|
||||
|
||||
/**
|
||||
* 第三方限制级别(Strict=完全禁止,Lax=部分允许,None=不限制)
|
||||
*/
|
||||
private String sameSite;
|
||||
|
||||
/**
|
||||
* @return 域 (写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景)
|
||||
*/
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param domain 域 (写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookieConfig setDomain(String domain) {
|
||||
this.domain = domain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 路径
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path 路径
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookieConfig setPath(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否只在 https 协议下有效
|
||||
*/
|
||||
public Boolean getSecure() {
|
||||
return secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param secure 是否只在 https 协议下有效
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookieConfig setSecure(Boolean secure) {
|
||||
this.secure = secure;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否禁止 js 操作 Cookie
|
||||
*/
|
||||
public Boolean getHttpOnly() {
|
||||
return httpOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param httpOnly 是否禁止 js 操作 Cookie
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookieConfig setHttpOnly(Boolean httpOnly) {
|
||||
this.httpOnly = httpOnly;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 第三方限制级别(Strict=完全禁止,Lax=部分允许,None=不限制)
|
||||
*/
|
||||
public String getSameSite() {
|
||||
return sameSite;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sameSite 第三方限制级别(Strict=完全禁止,Lax=部分允许,None=不限制)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookieConfig setSameSite(String sameSite) {
|
||||
this.sameSite = sameSite;
|
||||
return this;
|
||||
}
|
||||
|
||||
// toString
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaCookieConfig [domain=" + domain + ", path=" + path + ", secure=" + secure + ", httpOnly=" + httpOnly
|
||||
+ ", sameSite=" + sameSite + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO 单点登录模块 配置Model
|
||||
* Sa-Token SSO 单点登录模块 配置类 Model
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@@ -18,8 +18,11 @@ public class SaSsoConfig implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6541180061782004705L;
|
||||
|
||||
|
||||
// ----------------- Server端相关配置
|
||||
|
||||
/**
|
||||
* Ticket有效期 (单位: 秒)
|
||||
* Ticket有效期 (单位: 秒)
|
||||
*/
|
||||
public long ticketTimeout = 60 * 5;
|
||||
|
||||
@@ -27,48 +30,77 @@ public class SaSsoConfig implements Serializable {
|
||||
* 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
*/
|
||||
public String allowUrl = "*";
|
||||
|
||||
/**
|
||||
* 是否打开单点注销功能
|
||||
*/
|
||||
public Boolean isSlo = true;
|
||||
|
||||
/**
|
||||
* 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
* 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
*/
|
||||
public Boolean isHttp = false;
|
||||
|
||||
/**
|
||||
* 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
*/
|
||||
public String secretkey;
|
||||
|
||||
|
||||
// ----------------- Client端相关配置
|
||||
|
||||
/**
|
||||
* SSO-Server端 单点登录地址
|
||||
* 配置 Server 端单点登录授权地址
|
||||
*/
|
||||
public String authUrl;
|
||||
|
||||
/**
|
||||
* SSO-Server端 Ticket校验地址
|
||||
* 是否打开单点注销功能
|
||||
*/
|
||||
// public Boolean isSlo = true; // 同上
|
||||
|
||||
/**
|
||||
* 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
*/
|
||||
// public Boolean isHttp = false; // 同上
|
||||
|
||||
/**
|
||||
* 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
*/
|
||||
// public String secretkey; // 同上
|
||||
|
||||
/**
|
||||
* 配置 Server 端的 ticket 校验地址
|
||||
*/
|
||||
public String checkTicketUrl;
|
||||
|
||||
/**
|
||||
* SSO-Server端 单点注销地址
|
||||
* 配置 Server 端查询 userinfo 地址
|
||||
*/
|
||||
public String userinfoUrl;
|
||||
|
||||
/**
|
||||
* 配置 Server 端单点注销地址
|
||||
*/
|
||||
public String sloUrl;
|
||||
|
||||
/**
|
||||
* SSO-Client端 当前Client端的单点注销回调URL (为空时自动获取)
|
||||
* 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
*/
|
||||
public String ssoLogoutCall;
|
||||
|
||||
/**
|
||||
* SSO-Server端 账号资料查询地址
|
||||
*/
|
||||
public String userinfoUrl;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return Ticket有效期 (单位: 秒)
|
||||
* @return Ticket有效期 (单位: 秒)
|
||||
*/
|
||||
public long getTicketTimeout() {
|
||||
return ticketTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ticketTimeout Ticket有效期 (单位: 秒)
|
||||
* @param ticketTimeout Ticket有效期 (单位: 秒)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setTicketTimeout(long ticketTimeout) {
|
||||
@@ -93,14 +125,46 @@ public class SaSsoConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
* @return 是否打开单点注销功能
|
||||
*/
|
||||
public Boolean getIsSlo() {
|
||||
return isSlo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isSlo 是否打开单点注销功能
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setIsSlo(Boolean isSlo) {
|
||||
this.isSlo = isSlo;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
*/
|
||||
public Boolean getIsHttp() {
|
||||
return isHttp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setIsHttp(Boolean isHttp) {
|
||||
this.isHttp = isHttp;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
*/
|
||||
public String getSecretkey() {
|
||||
return secretkey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param secretkey 调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
* @param secretkey 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setSecretkey(String secretkey) {
|
||||
@@ -109,14 +173,14 @@ public class SaSsoConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SSO-Server端 单点登录地址
|
||||
* @return 配置的 Server 端单点登录授权地址
|
||||
*/
|
||||
public String getAuthUrl() {
|
||||
return authUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authUrl SSO-Server端 单点登录地址
|
||||
* @param authUrl 配置 Server 端单点登录授权地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setAuthUrl(String authUrl) {
|
||||
@@ -125,14 +189,14 @@ public class SaSsoConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SSO-Server端Ticket校验地址
|
||||
* @return 配置的 Server 端的 ticket 校验地址
|
||||
*/
|
||||
public String getCheckTicketUrl() {
|
||||
return checkTicketUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param checkTicketUrl SSO-Server端Ticket校验地址
|
||||
* @param checkTicketUrl 配置 Server 端的 ticket 校验地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setCheckTicketUrl(String checkTicketUrl) {
|
||||
@@ -141,14 +205,30 @@ public class SaSsoConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SSO-Server端单点注销地址
|
||||
* @return 配置的 Server 端查询 userinfo 地址
|
||||
*/
|
||||
public String getUserinfoUrl() {
|
||||
return userinfoUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userinfoUrl 配置 Server 端查询 userinfo 地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setUserinfoUrl(String userinfoUrl) {
|
||||
this.userinfoUrl = userinfoUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置 Server 端单点注销地址
|
||||
*/
|
||||
public String getSloUrl() {
|
||||
return sloUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sloUrl SSO-Server端单点注销地址
|
||||
* @param sloUrl 配置 Server 端单点注销地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setSloUrl(String sloUrl) {
|
||||
@@ -157,14 +237,14 @@ public class SaSsoConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SSO-Client端 当前Client端的单点注销回调URL (为空时自动获取)
|
||||
* @return 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
*/
|
||||
public String getSsoLogoutCall() {
|
||||
return ssoLogoutCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ssoLogoutCall SSO-Client端 当前Client端的单点注销回调URL (为空时自动获取)
|
||||
* @param ssoLogoutCall 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setSsoLogoutCall(String ssoLogoutCall) {
|
||||
@@ -172,30 +252,14 @@ public class SaSsoConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SSO-Server端 账号资料查询地址
|
||||
*/
|
||||
public String getUserinfoUrl() {
|
||||
return userinfoUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userinfoUrl SSO-Server端 账号资料查询地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setUserinfoUrl(String userinfoUrl) {
|
||||
this.userinfoUrl = userinfoUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaSsoConfig [ticketTimeout=" + ticketTimeout + ", allowUrl=" + allowUrl + ", secretkey=" + secretkey
|
||||
+ ", authUrl=" + authUrl + ", checkTicketUrl=" + checkTicketUrl + ", sloUrl=" + sloUrl
|
||||
+ ", ssoLogoutCall=" + ssoLogoutCall + ", userinfoUrl=" + userinfoUrl + ", isHttp=" + isHttp + ", isSlo=" + isSlo + "]";
|
||||
return "SaSsoConfig [ticketTimeout=" + ticketTimeout + ", allowUrl=" + allowUrl + ", isSlo=" + isSlo
|
||||
+ ", isHttp=" + isHttp + ", secretkey=" + secretkey + ", authUrl=" + authUrl + ", checkTicketUrl="
|
||||
+ checkTicketUrl + ", userinfoUrl=" + userinfoUrl + ", sloUrl=" + sloUrl + ", ssoLogoutCall="
|
||||
+ ssoLogoutCall + "]";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 以数组形式写入允许的授权回调地址
|
||||
* @param url 所有集合
|
||||
@@ -206,50 +270,6 @@ public class SaSsoConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
// -------------------- SaSsoHandle 相关配置 --------------------
|
||||
|
||||
/**
|
||||
* 是否使用http请求校验ticket值
|
||||
*/
|
||||
public Boolean isHttp = false;
|
||||
|
||||
/**
|
||||
* 是否打开单点注销
|
||||
*/
|
||||
public Boolean isSlo = false;
|
||||
|
||||
|
||||
/**
|
||||
* @return isHttp 是否使用http请求校验ticket值
|
||||
*/
|
||||
public Boolean getIsHttp() {
|
||||
return isHttp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isHttp 是否使用http请求校验ticket值
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setIsHttp(Boolean isHttp) {
|
||||
this.isHttp = isHttp;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否打开单点注销
|
||||
*/
|
||||
public Boolean getIsSlo() {
|
||||
return isSlo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isSlo 是否打开单点注销
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setIsSlo(Boolean isSlo) {
|
||||
this.isSlo = isSlo;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// -------------------- SaSsoHandle 所有回调函数 --------------------
|
||||
@@ -312,4 +332,7 @@ public class SaSsoConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -53,9 +53,6 @@ public class SaTokenConfig implements Serializable {
|
||||
/** 是否打开自动续签 (如果此值为true, 框架会在每次直接或间接调用getLoginId()时进行一次过期检查与续签操作) */
|
||||
private Boolean autoRenew = true;
|
||||
|
||||
/** 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景 */
|
||||
private String cookieDomain;
|
||||
|
||||
/** token前缀, 格式样例(satoken: Bearer xxxx-xxxx-xxxx-xxxx) */
|
||||
private String tokenPrefix;
|
||||
|
||||
@@ -66,7 +63,7 @@ public class SaTokenConfig implements Serializable {
|
||||
private Boolean isLog = false;
|
||||
|
||||
/**
|
||||
* jwt秘钥 (只有集成 sa-token-temp-jwt 模块时此参数才会生效)
|
||||
* jwt秘钥 (只有集成 jwt 模块时此参数才会生效)
|
||||
*/
|
||||
private String jwtSecretKey;
|
||||
|
||||
@@ -83,7 +80,14 @@ public class SaTokenConfig implements Serializable {
|
||||
/** 配置当前项目的网络访问地址 */
|
||||
private String currDomain;
|
||||
|
||||
/** 是否校验Id-Token(部分rpc插件有效) */
|
||||
private Boolean checkIdToken = false;
|
||||
|
||||
/**
|
||||
* Cookie配置对象
|
||||
*/
|
||||
public SaCookieConfig cookie = new SaCookieConfig();
|
||||
|
||||
/**
|
||||
* SSO单点登录配置对象
|
||||
*/
|
||||
@@ -286,22 +290,6 @@ public class SaTokenConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景
|
||||
*/
|
||||
public String getCookieDomain() {
|
||||
return cookieDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cookieDomain 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setCookieDomain(String cookieDomain) {
|
||||
this.cookieDomain = cookieDomain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token前缀, 格式样例(satoken: Bearer xxxx-xxxx-xxxx-xxxx)
|
||||
*/
|
||||
@@ -351,14 +339,14 @@ public class SaTokenConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return jwt秘钥 (只有集成 sa-token-temp-jwt 模块时此参数才会生效)
|
||||
* @return jwt秘钥 (只有集成 jwt 模块时此参数才会生效)
|
||||
*/
|
||||
public String getJwtSecretKey() {
|
||||
return jwtSecretKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jwtSecretKey jwt秘钥 (只有集成 sa-token-temp-jwt 模块时此参数才会生效)
|
||||
* @param jwtSecretKey jwt秘钥 (只有集成 jwt 模块时此参数才会生效)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setJwtSecretKey(String jwtSecretKey) {
|
||||
@@ -413,6 +401,22 @@ public class SaTokenConfig implements Serializable {
|
||||
this.currDomain = currDomain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否校验Id-Token(部分rpc插件有效)
|
||||
*/
|
||||
public Boolean getCheckIdToken() {
|
||||
return checkIdToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param checkIdToken 是否校验Id-Token(部分rpc插件有效)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setCheckIdToken(Boolean checkIdToken) {
|
||||
this.checkIdToken = checkIdToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SSO单点登录配置对象
|
||||
@@ -423,23 +427,58 @@ public class SaTokenConfig implements Serializable {
|
||||
|
||||
/**
|
||||
* @param sso SSO单点登录配置对象
|
||||
* @return 对象自身
|
||||
*/
|
||||
public void setSso(SaSsoConfig sso) {
|
||||
public SaTokenConfig setSso(SaSsoConfig sso) {
|
||||
this.sso = sso;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Cookie 全局配置对象
|
||||
*/
|
||||
public SaCookieConfig getCookie() {
|
||||
return cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cookie Cookie 全局配置对象
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setCookie(SaCookieConfig cookie) {
|
||||
this.cookie = cookie;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaTokenConfig [tokenName=" + tokenName + ", timeout=" + timeout + ", activityTimeout=" + activityTimeout
|
||||
+ ", isConcurrent=" + isConcurrent + ", isShare=" + isShare + ", isReadBody=" + isReadBody
|
||||
+ ", isReadHead=" + isReadHead + ", isReadCookie=" + isReadCookie + ", tokenStyle=" + tokenStyle
|
||||
+ ", dataRefreshPeriod=" + dataRefreshPeriod + ", tokenSessionCheckLogin=" + tokenSessionCheckLogin
|
||||
+ ", autoRenew=" + autoRenew + ", cookieDomain=" + cookieDomain + ", tokenPrefix=" + tokenPrefix
|
||||
+ ", isPrint=" + isPrint + ", isLog=" + isLog + ", jwtSecretKey=" + jwtSecretKey + ", idTokenTimeout="
|
||||
+ idTokenTimeout + ", basic=" + basic + ", currDomain=" + currDomain + ", sso=" + sso + "]";
|
||||
return "SaTokenConfig ["
|
||||
+ "tokenName=" + tokenName
|
||||
+ ", timeout=" + timeout
|
||||
+ ", activityTimeout=" + activityTimeout
|
||||
+ ", isConcurrent=" + isConcurrent
|
||||
+ ", isShare=" + isShare
|
||||
+ ", isReadBody=" + isReadBody
|
||||
+ ", isReadHead=" + isReadHead
|
||||
+ ", isReadCookie=" + isReadCookie
|
||||
+ ", tokenStyle=" + tokenStyle
|
||||
+ ", dataRefreshPeriod=" + dataRefreshPeriod
|
||||
+ ", tokenSessionCheckLogin=" + tokenSessionCheckLogin
|
||||
+ ", autoRenew=" + autoRenew
|
||||
+ ", tokenPrefix=" + tokenPrefix
|
||||
+ ", isPrint=" + isPrint
|
||||
+ ", isLog=" + isLog
|
||||
+ ", jwtSecretKey=" + jwtSecretKey
|
||||
+ ", idTokenTimeout=" + idTokenTimeout
|
||||
+ ", basic=" + basic
|
||||
+ ", currDomain=" + currDomain
|
||||
+ ", checkIdToken=" + checkIdToken
|
||||
+ ", sso=" + sso
|
||||
+ ", cookie=" + cookie
|
||||
+ "]";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 setIsConcurrent() ,使用方式保持不变 </h1>
|
||||
* @param allowConcurrentLogin see note
|
||||
@@ -456,10 +495,31 @@ public class SaTokenConfig implements Serializable {
|
||||
* @param isV see note
|
||||
* @return see note
|
||||
*/
|
||||
@Deprecated
|
||||
public SaTokenConfig setIsV(Boolean isV) {
|
||||
this.isPrint = isV;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 getCookie().getDomain() ,使用方式保持不变 </h1>
|
||||
* @return 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景
|
||||
*/
|
||||
@Deprecated
|
||||
public String getCookieDomain() {
|
||||
return getCookie().getDomain();
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 getCookie().setDomain() ,使用方式保持不变 </h1>
|
||||
* @param cookieDomain 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景
|
||||
* @return 对象自身
|
||||
*/
|
||||
@Deprecated
|
||||
public SaTokenConfig setCookieDomain(String cookieDomain) {
|
||||
this.getCookie().setDomain(cookieDomain);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.Properties;
|
||||
/**
|
||||
* Sa-Token配置文件的构建工厂类
|
||||
* <p>
|
||||
* 只有在非IOC环境下才会用到此类
|
||||
* 用于手动读取配置文件初始化 SaTokenConfig 对象,只有在非IOC环境下你才会用到此类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
|
||||
@@ -11,6 +11,15 @@ import cn.dev33.satoken.context.model.SaStorage;
|
||||
*
|
||||
*/
|
||||
public class SaHolder {
|
||||
|
||||
/**
|
||||
* 获取当前请求的 SaTokenContext
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public static SaTokenContext getContext() {
|
||||
return SaManager.getSaTokenContextOrSecond();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的 [Request] 对象
|
||||
@@ -18,7 +27,7 @@ public class SaHolder {
|
||||
* @return see note
|
||||
*/
|
||||
public static SaRequest getRequest() {
|
||||
return SaManager.getSaTokenContext().getRequest();
|
||||
return SaManager.getSaTokenContextOrSecond().getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,7 +36,7 @@ public class SaHolder {
|
||||
* @return see note
|
||||
*/
|
||||
public static SaResponse getResponse() {
|
||||
return SaManager.getSaTokenContext().getResponse();
|
||||
return SaManager.getSaTokenContextOrSecond().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,7 +45,7 @@ public class SaHolder {
|
||||
* @return see note
|
||||
*/
|
||||
public static SaStorage getStorage() {
|
||||
return SaManager.getSaTokenContext().getStorage();
|
||||
return SaManager.getSaTokenContextOrSecond().getStorage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,4 +41,12 @@ public interface SaTokenContext {
|
||||
*/
|
||||
public boolean matchPath(String pattern, String path);
|
||||
|
||||
/**
|
||||
* 此上下文是否有效
|
||||
* @return /
|
||||
*/
|
||||
public default boolean isValid() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-2
@@ -6,11 +6,22 @@ import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
/**
|
||||
* Sa-Token 上下文处理器 [默认实现类]
|
||||
* Sa-Token 上下文处理器 [默认实现类]
|
||||
*
|
||||
* <p>
|
||||
* 一般情况下框架会为你自动注入合适的上下文处理器,如果代码断点走到了此默认实现类,
|
||||
* 说明你引入的依赖有问题或者错误的调用了Sa-Token的API, 请在[在线开发文档 → 附录 → 常见问题排查] 中按照提示进行排查
|
||||
* </p>
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenContextDefaultImpl implements SaTokenContext {
|
||||
|
||||
/**
|
||||
* 默认的上下文处理器对象
|
||||
*/
|
||||
public static SaTokenContext defaultContext = new SaTokenContextDefaultImpl();
|
||||
|
||||
/**
|
||||
* 默认的错误提示语
|
||||
@@ -50,5 +61,4 @@ public class SaTokenContextDefaultImpl implements SaTokenContext {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+11
@@ -6,6 +6,12 @@ import cn.dev33.satoken.context.model.SaStorage;
|
||||
|
||||
/**
|
||||
* Sa-Token 上下文处理器 [ThreadLocal版本]
|
||||
*
|
||||
* <p>
|
||||
* 使用 [ThreadLocal版本] 上下文处理器需要在全局过滤器或者拦截器内率先调用
|
||||
* SaTokenContextForThreadLocalStorage.setBox(req,res, sto) 初始化上下文
|
||||
* </p>
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@@ -31,4 +37,9 @@ public class SaTokenContextForThreadLocal implements SaTokenContext {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return SaTokenContextForThreadLocalStorage.getBox() != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -7,6 +7,7 @@ import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
/**
|
||||
* Sa-Token 上下文处理器 [ThreadLocal版本] ---- 对象存储器
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
package cn.dev33.satoken.context.model;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
/**
|
||||
* Cookie Model
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaCookie {
|
||||
|
||||
/**
|
||||
* 写入响应头时使用的key
|
||||
*/
|
||||
public static final String HEADER_NAME = "Set-Cookie";
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 有效时长 (单位:秒),-1代表为临时Cookie 浏览器关闭后自动删除
|
||||
*/
|
||||
private int maxAge = -1;
|
||||
|
||||
/**
|
||||
* 域
|
||||
*/
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 路径
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 是否只在 https 协议下有效
|
||||
*/
|
||||
private Boolean secure = false;
|
||||
|
||||
/**
|
||||
* 是否禁止 js 操作 Cookie
|
||||
*/
|
||||
private Boolean httpOnly = false;
|
||||
|
||||
/**
|
||||
* 第三方限制级别(Strict=完全禁止,Lax=部分允许,None=不限制)
|
||||
*/
|
||||
private String sameSite;
|
||||
|
||||
|
||||
/**
|
||||
* 构造一个
|
||||
*/
|
||||
public SaCookie() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个
|
||||
* @param name 名字
|
||||
* @param value 值
|
||||
*/
|
||||
public SaCookie(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return 名称
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 名称
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookie setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 值
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value 值
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookie setValue(String value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 有效时长 (单位:秒),-1代表为临时Cookie 浏览器关闭后自动删除
|
||||
*/
|
||||
public int getMaxAge() {
|
||||
return maxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxAge 有效时长 (单位:秒),-1代表为临时Cookie 浏览器关闭后自动删除
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookie setMaxAge(int maxAge) {
|
||||
this.maxAge = maxAge;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 域
|
||||
*/
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param domain 域
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookie setDomain(String domain) {
|
||||
this.domain = domain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 路径
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path 路径
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookie setPath(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否只在 https 协议下有效
|
||||
*/
|
||||
public Boolean getSecure() {
|
||||
return secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param secure 是否只在 https 协议下有效
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookie setSecure(Boolean secure) {
|
||||
this.secure = secure;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否禁止 js 操作 Cookie
|
||||
*/
|
||||
public Boolean getHttpOnly() {
|
||||
return httpOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param httpOnly 是否禁止 js 操作 Cookie
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookie setHttpOnly(Boolean httpOnly) {
|
||||
this.httpOnly = httpOnly;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 第三方限制级别(Strict=完全禁止,Lax=部分允许,None=不限制)
|
||||
*/
|
||||
public String getSameSite() {
|
||||
return sameSite;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sameSite 第三方限制级别(Strict=完全禁止,Lax=部分允许,None=不限制)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaCookie setSameSite(String sameSite) {
|
||||
this.sameSite = sameSite;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// toString
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaCookie [name=" + name + ", value=" + value + ", maxAge=" + maxAge + ", domain=" + domain + ", path=" + path
|
||||
+ ", secure=" + secure + ", httpOnly=" + httpOnly + ", sameSite="
|
||||
+ sameSite + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一下
|
||||
*/
|
||||
public void builde() {
|
||||
if(path == null) {
|
||||
path = "/";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为响应头 Set-Cookie 参数需要的值
|
||||
* @return /
|
||||
*/
|
||||
public String toHeaderValue() {
|
||||
this.builde();
|
||||
|
||||
if(SaFoxUtil.isEmpty(name)) {
|
||||
throw new SaTokenException("name不能为空");
|
||||
}
|
||||
if(value != null && value.indexOf(";") > -1) {
|
||||
throw new SaTokenException("无效Value:" + value);
|
||||
}
|
||||
|
||||
// Set-Cookie: name=value; Max-Age=100000; Expires=Tue, 05-Oct-2021 20:28:17 GMT; Domain=localhost; Path=/; Secure; HttpOnly; SameSite=Lax
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(name + "=" + value);
|
||||
|
||||
if(maxAge >= 0) {
|
||||
sb.append("; Max-Age=" + maxAge);
|
||||
String expires;
|
||||
if(maxAge == 0) {
|
||||
expires = Instant.EPOCH.atOffset(ZoneOffset.UTC).format(DateTimeFormatter.RFC_1123_DATE_TIME);
|
||||
} else {
|
||||
expires = OffsetDateTime.now().plusSeconds(maxAge).format(DateTimeFormatter.RFC_1123_DATE_TIME);
|
||||
}
|
||||
sb.append("; Expires=" + expires);
|
||||
}
|
||||
if(SaFoxUtil.isEmpty(domain) == false) {
|
||||
sb.append("; Domain=" + domain);
|
||||
}
|
||||
if(SaFoxUtil.isEmpty(path) == false) {
|
||||
sb.append("; Path=" + path);
|
||||
}
|
||||
if(secure) {
|
||||
sb.append("; Secure");
|
||||
}
|
||||
if(httpOnly) {
|
||||
sb.append("; HttpOnly");
|
||||
}
|
||||
if(SaFoxUtil.isEmpty(sameSite) == false) {
|
||||
sb.append("; sameSite=" + sameSite);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,22 +17,34 @@ public interface SaResponse {
|
||||
* 删除指定Cookie
|
||||
* @param name Cookie名称
|
||||
*/
|
||||
public void deleteCookie(String name);
|
||||
|
||||
public default void deleteCookie(String name) {
|
||||
addCookie(name, null, null, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入指定Cookie
|
||||
* 写入指定Cookie
|
||||
* @param name Cookie名称
|
||||
* @param value Cookie值
|
||||
* @param path Cookie路径
|
||||
* @param domain Cookie的作用域
|
||||
* @param timeout 过期时间 (秒)
|
||||
*/
|
||||
public void addCookie(String name, String value, String path, String domain, int timeout);
|
||||
|
||||
public default void addCookie(String name, String value, String path, String domain, int timeout) {
|
||||
this.addCookie(new SaCookie(name, value).setPath(path).setDomain(domain).setMaxAge(timeout));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应状态码
|
||||
* 写入指定Cookie
|
||||
* @param cookie Cookie-Model
|
||||
*/
|
||||
public default void addCookie(SaCookie cookie) {
|
||||
this.addHeader(SaCookie.HEADER_NAME, cookie.toHeaderValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应状态码
|
||||
* @param sc 响应状态码
|
||||
* @return 对象自身
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaResponse setStatus(int sc);
|
||||
|
||||
@@ -43,6 +55,14 @@ public interface SaResponse {
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaResponse setHeader(String name, String value);
|
||||
|
||||
/**
|
||||
* 在响应头里添加一个值
|
||||
* @param name 名字
|
||||
* @param value 值
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaResponse addHeader(String name, String value);
|
||||
|
||||
/**
|
||||
* 在响应头写入 [Server] 服务器名称
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.dev33.satoken.context.second;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
|
||||
/**
|
||||
* Sa-Token 二级Context - 基础接口
|
||||
*
|
||||
* <p> (利用继承机制实现区别 [一级Context] 与 [二级Context] 的目的)
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaTokenSecondContext extends SaTokenContext {
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package cn.dev33.satoken.context.second;
|
||||
|
||||
/**
|
||||
* Sa-Token 二级Context - 创建器
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SaTokenSecondContextCreator {
|
||||
|
||||
/**
|
||||
* 创建一个二级 Context
|
||||
* @return /
|
||||
*/
|
||||
public SaTokenSecondContext create();
|
||||
|
||||
}
|
||||
@@ -99,7 +99,7 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
if(getKeyTimeout(key) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
// 无动作
|
||||
dataMap.put(key, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,7 +176,7 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
/**
|
||||
* 是否继续执行数据清理的线程标记
|
||||
*/
|
||||
public boolean refreshFlag;
|
||||
public volatile boolean refreshFlag;
|
||||
|
||||
|
||||
/**
|
||||
@@ -224,7 +224,7 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
}
|
||||
}
|
||||
});
|
||||
refreshThread.start();
|
||||
this.refreshThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
/**
|
||||
* 一个异常:代表 API 已被禁用
|
||||
* @author kong
|
||||
*/
|
||||
public class ApiDisabledException extends SaTokenException {
|
||||
|
||||
/**
|
||||
* 序列化版本号
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130133L;
|
||||
|
||||
/** 异常提示语 */
|
||||
public static final String BE_MESSAGE = "This API is disabled";
|
||||
|
||||
/**
|
||||
* 一个异常:代表 API 已被禁用
|
||||
*/
|
||||
public ApiDisabledException() {
|
||||
super(BE_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 一个异常:代表 API 已被禁用
|
||||
* @param message 异常描述
|
||||
*/
|
||||
public ApiDisabledException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package cn.dev33.satoken.exception;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
/**
|
||||
* 一个异常:代表会话未能通过登录认证
|
||||
* @author kong
|
||||
@@ -24,23 +26,23 @@ public class NotLoginException extends SaTokenException {
|
||||
|
||||
/** 表示未提供token */
|
||||
public static final String NOT_TOKEN = "-1";
|
||||
public static final String NOT_TOKEN_MESSAGE = "未提供token";
|
||||
public static final String NOT_TOKEN_MESSAGE = "未提供Token";
|
||||
|
||||
/** 表示token无效 */
|
||||
public static final String INVALID_TOKEN = "-2";
|
||||
public static final String INVALID_TOKEN_MESSAGE = "token无效";
|
||||
public static final String INVALID_TOKEN_MESSAGE = "Token无效";
|
||||
|
||||
/** 表示token已过期 */
|
||||
public static final String TOKEN_TIMEOUT = "-3";
|
||||
public static final String TOKEN_TIMEOUT_MESSAGE = "token已过期";
|
||||
public static final String TOKEN_TIMEOUT_MESSAGE = "Token已过期";
|
||||
|
||||
/** 表示token已被顶下线 */
|
||||
public static final String BE_REPLACED = "-4";
|
||||
public static final String BE_REPLACED_MESSAGE = "token已被顶下线";
|
||||
public static final String BE_REPLACED_MESSAGE = "Token已被顶下线";
|
||||
|
||||
/** 表示token已被踢下线 */
|
||||
public static final String KICK_OUT = "-5";
|
||||
public static final String KICK_OUT_MESSAGE = "token已被踢下线";
|
||||
public static final String KICK_OUT_MESSAGE = "Token已被踢下线";
|
||||
|
||||
/** 默认的提示语 */
|
||||
public static final String DEFAULT_MESSAGE = "当前会话未登录";
|
||||
@@ -99,6 +101,17 @@ public class NotLoginException extends SaTokenException {
|
||||
* @return 构建完毕的异常对象
|
||||
*/
|
||||
public static NotLoginException newInstance(String loginType, String type) {
|
||||
return newInstance(loginType, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法构建一个NotLoginException
|
||||
* @param loginType 账号类型
|
||||
* @param type 账号类型
|
||||
* @param token 引起异常的Token值
|
||||
* @return 构建完毕的异常对象
|
||||
*/
|
||||
public static NotLoginException newInstance(String loginType, String type, String token) {
|
||||
String message = null;
|
||||
if(NOT_TOKEN.equals(type)) {
|
||||
message = NOT_TOKEN_MESSAGE;
|
||||
@@ -118,6 +131,9 @@ public class NotLoginException extends SaTokenException {
|
||||
else {
|
||||
message = DEFAULT_MESSAGE;
|
||||
}
|
||||
if(SaFoxUtil.isEmpty(token) == false) {
|
||||
message = message + ":" + token;
|
||||
}
|
||||
return new NotLoginException(message, loginType, type);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
/**
|
||||
* Sa-Token框架内部逻辑发生错误抛出的异常
|
||||
* (自定义此异常方便开发者在做全局异常处理时分辨异常类型)
|
||||
@@ -42,4 +44,26 @@ public class SaTokenException extends RuntimeException {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果flag==true,则抛出message异常
|
||||
* @param flag 标记
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void throwBy(boolean flag, String message) {
|
||||
if(flag) {
|
||||
throw new SaTokenException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果value==null或者isEmpty,则抛出message异常
|
||||
* @param value 值
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void throwByNull(Object value, String message) {
|
||||
if(SaFoxUtil.isEmpty(value)) {
|
||||
throw new SaTokenException(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
/**
|
||||
* 一个异常:代表停止路由匹配
|
||||
* 一个异常:代表停止路由匹配,进入Controller
|
||||
*
|
||||
* @author kong
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.dev33.satoken.fun;
|
||||
|
||||
/**
|
||||
* 设定一个函数,并传入一个参数,方便在Lambda表达式下的函数式编程
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SaParamFunction<T> {
|
||||
|
||||
/**
|
||||
* 执行的方法
|
||||
* @param r 传入的参数
|
||||
*/
|
||||
public void run(T r);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.dev33.satoken.fun;
|
||||
|
||||
/**
|
||||
* 设定一个函数,传入一个参数,并返回一个值,方便在Lambda表达式下的函数式编程
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SaParamRetFunction<T, R> {
|
||||
|
||||
/**
|
||||
* 执行的方法
|
||||
* @param param 传入的参数
|
||||
* @return 返回值
|
||||
*/
|
||||
public R run(T param);
|
||||
|
||||
}
|
||||
@@ -147,14 +147,14 @@ public class SaIdTemplate {
|
||||
* @return Token
|
||||
*/
|
||||
public String createToken() {
|
||||
return SaFoxUtil.getRandomString(60);
|
||||
return SaFoxUtil.getRandomString(64);
|
||||
}
|
||||
|
||||
|
||||
// -------------------- 拼接key
|
||||
|
||||
/**
|
||||
* 拼接key:Id-Token的存储key
|
||||
* 拼接key:Id-Token 的存储 key
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTokenSaveKey() {
|
||||
@@ -162,7 +162,7 @@ public class SaIdTemplate {
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接key:Id-Token的存储key
|
||||
* 拼接key:次级 Id-Token 的存储 key
|
||||
* @return key
|
||||
*/
|
||||
public String splicingPastTokenSaveKey() {
|
||||
|
||||
@@ -27,22 +27,20 @@ public interface SaTokenListener {
|
||||
public void doLogout(String loginType, Object loginId, String tokenValue);
|
||||
|
||||
/**
|
||||
* 每次被踢下线时触发
|
||||
* @param loginType 账号类别
|
||||
* @param loginId 账号id
|
||||
* @param tokenValue token值
|
||||
* @param device 设备标识
|
||||
* 每次被踢下线时触发
|
||||
* @param loginType 账号类别
|
||||
* @param loginId 账号id
|
||||
* @param tokenValue token值
|
||||
*/
|
||||
public void doLogoutByLoginId(String loginType, Object loginId, String tokenValue, String device);
|
||||
public void doKickout(String loginType, Object loginId, String tokenValue);
|
||||
|
||||
/**
|
||||
* 每次被顶下线时触发
|
||||
* @param loginType 账号类别
|
||||
* @param loginId 账号id
|
||||
* @param tokenValue token值
|
||||
* @param device 设备标识
|
||||
*/
|
||||
public void doReplaced(String loginType, Object loginId, String tokenValue, String device);
|
||||
public void doReplaced(String loginType, Object loginId, String tokenValue);
|
||||
|
||||
/**
|
||||
* 每次被封禁时触发
|
||||
|
||||
+5
-5
@@ -26,23 +26,23 @@ public class SaTokenListenerDefaultImpl implements SaTokenListener {
|
||||
*/
|
||||
@Override
|
||||
public void doLogout(String loginType, Object loginId, String tokenValue) {
|
||||
println("账号[" + loginId + "]注销成功");
|
||||
println("账号[" + loginId + "]注销成功 (Token=" + tokenValue + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次被踢下线时触发
|
||||
*/
|
||||
@Override
|
||||
public void doLogoutByLoginId(String loginType, Object loginId, String tokenValue, String device) {
|
||||
println("账号[" + loginId + "]被踢下线 (终端: " + device + ")");
|
||||
public void doKickout(String loginType, Object loginId, String tokenValue) {
|
||||
println("账号[" + loginId + "]被踢下线 (Token=" + tokenValue + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次被顶下线时触发
|
||||
*/
|
||||
@Override
|
||||
public void doReplaced(String loginType, Object loginId, String tokenValue, String device) {
|
||||
println("账号[" + loginId + "]被顶下线 (终端: " + device + ")");
|
||||
public void doReplaced(String loginType, Object loginId, String tokenValue) {
|
||||
println("账号[" + loginId + "]被顶下线 (Token=" + tokenValue + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package cn.dev33.satoken.router;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
/**
|
||||
* Http 请求各种请求类型的枚举表示
|
||||
*
|
||||
* <p> 参考:Spring - HttpMethod
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public enum SaHttpMethod {
|
||||
|
||||
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE, CONNECT,
|
||||
|
||||
/**
|
||||
* 代表全部请求方式
|
||||
*/
|
||||
ALL;
|
||||
|
||||
private static final Map<String, SaHttpMethod> map = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (SaHttpMethod reqMethod : values()) {
|
||||
map.put(reqMethod.name(), reqMethod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* String 转 enum
|
||||
* @param method 请求类型
|
||||
* @return ReqMethod 对象
|
||||
*/
|
||||
public static SaHttpMethod toEnum(String method) {
|
||||
if(method == null) {
|
||||
throw new SaTokenException("无效Method:" + method);
|
||||
}
|
||||
SaHttpMethod reqMethod = map.get(method.toUpperCase());
|
||||
if(reqMethod == null) {
|
||||
throw new SaTokenException("无效Method:" + method);
|
||||
}
|
||||
return reqMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* String[] 转 enum[]
|
||||
* @param methods 请求类型数组
|
||||
* @return ReqMethod 对象
|
||||
*/
|
||||
public static SaHttpMethod[] toEnumArray(String... methods) {
|
||||
SaHttpMethod [] arr = new SaHttpMethod[methods.length];
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
arr[i] = SaHttpMethod.toEnum(methods[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package cn.dev33.satoken.router;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.exception.BackResultException;
|
||||
import cn.dev33.satoken.exception.StopMatchException;
|
||||
import cn.dev33.satoken.fun.IsRunFunction;
|
||||
import cn.dev33.satoken.fun.SaFunction;
|
||||
import cn.dev33.satoken.fun.SaParamFunction;
|
||||
import cn.dev33.satoken.fun.SaParamRetFunction;
|
||||
|
||||
/**
|
||||
* 路由匹配操作工具类
|
||||
@@ -26,7 +26,7 @@ public class SaRouter {
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatch(String pattern, String path) {
|
||||
return SaManager.getSaTokenContext().matchPath(pattern, path);
|
||||
return SaManager.getSaTokenContextOrSecond().matchPath(pattern, path);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,6 +36,9 @@ public class SaRouter {
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatch(List<String> patterns, String path) {
|
||||
if(patterns == null) {
|
||||
return false;
|
||||
}
|
||||
for (String pattern : patterns) {
|
||||
if(isMatch(pattern, path)) {
|
||||
return true;
|
||||
@@ -44,6 +47,44 @@ public class SaRouter {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配
|
||||
* @param patterns 路由匹配符数组
|
||||
* @param path 被匹配的路由
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatch(String[] patterns, String path) {
|
||||
if(patterns == null) {
|
||||
return false;
|
||||
}
|
||||
for (String pattern : patterns) {
|
||||
if(isMatch(pattern, path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Http请求方法匹配
|
||||
* @param methods Http请求方法断言数组
|
||||
* @param methodString Http请求方法
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatch(SaHttpMethod[] methods, String methodString) {
|
||||
if(methods == null) {
|
||||
return false;
|
||||
}
|
||||
for (SaHttpMethod method : methods) {
|
||||
if(method == SaHttpMethod.ALL || (method != null && method.toString().equalsIgnoreCase(methodString))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------ 使用当前URI匹配
|
||||
|
||||
/**
|
||||
* 路由匹配 (使用当前URI)
|
||||
* @param pattern 路由匹配符
|
||||
@@ -61,40 +102,233 @@ public class SaRouter {
|
||||
public static boolean isMatchCurrURI(List<String> patterns) {
|
||||
return isMatch(patterns, SaHolder.getRequest().getRequestPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配 (使用当前URI)
|
||||
* @param patterns 路由匹配符数组
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatchCurrURI(String[] patterns) {
|
||||
return isMatch(patterns, SaHolder.getRequest().getRequestPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Http请求方法匹配 (使用当前请求方式)
|
||||
* @param methods Http请求方法断言数组
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatchCurrMethod(SaHttpMethod[] methods) {
|
||||
return isMatch(methods, SaHolder.getRequest().getMethod());
|
||||
}
|
||||
|
||||
|
||||
// -------------------- 执行相关 --------------------
|
||||
// -------------------- 开始匹配 --------------------
|
||||
|
||||
/**
|
||||
* 初始化一个SaRouterStaff,开始匹配
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff newMatch() {
|
||||
return new SaRouterStaff();
|
||||
}
|
||||
|
||||
// ----------------- path匹配
|
||||
|
||||
/**
|
||||
* 路由匹配
|
||||
* @param patterns 路由匹配符集合
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff match(String... patterns) {
|
||||
return new SaRouterStaff().match(patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配排除
|
||||
* @param patterns 路由匹配符排除数组
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff notMatch(String... patterns) {
|
||||
return new SaRouterStaff().notMatch(patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配
|
||||
* @param patterns 路由匹配符集合
|
||||
* @return 对象自身
|
||||
*/
|
||||
public static SaRouterStaff match(List<String> patterns) {
|
||||
return new SaRouterStaff().match(patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配排除
|
||||
* @param patterns 路由匹配符排除集合
|
||||
* @return 对象自身
|
||||
*/
|
||||
public static SaRouterStaff notMatch(List<String> patterns) {
|
||||
return new SaRouterStaff().notMatch(patterns);
|
||||
}
|
||||
|
||||
// ----------------- Method匹配
|
||||
|
||||
/**
|
||||
* Http请求方式匹配 (Enum)
|
||||
* @param methods Http请求方法断言数组
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff match(SaHttpMethod... methods) {
|
||||
return new SaRouterStaff().match(methods);
|
||||
}
|
||||
|
||||
/**
|
||||
* Http请求方法匹配排除 (Enum)
|
||||
* @param methods Http请求方法断言排除数组
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff notMatch(SaHttpMethod... methods) {
|
||||
return new SaRouterStaff().notMatch(methods);
|
||||
}
|
||||
|
||||
/**
|
||||
* Http请求方法匹配 (String)
|
||||
* @param methods Http请求方法断言数组
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff matchMethod(String... methods) {
|
||||
return new SaRouterStaff().matchMethod(methods);
|
||||
}
|
||||
|
||||
/**
|
||||
* Http请求方法匹配排除 (String)
|
||||
* @param methods Http请求方法断言排除数组
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff notMatchMethod(String... methods) {
|
||||
return new SaRouterStaff().notMatchMethod(methods);
|
||||
}
|
||||
|
||||
// ----------------- 条件匹配
|
||||
|
||||
/**
|
||||
* 根据 boolean 值进行匹配
|
||||
* @param flag boolean值
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff match(boolean flag) {
|
||||
return new SaRouterStaff().match(flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 boolean 值进行匹配排除
|
||||
* @param flag boolean值
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff notMatch(boolean flag) {
|
||||
return new SaRouterStaff().notMatch(flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自定义方法进行匹配 (lazy)
|
||||
* @param fun 自定义方法
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff match(SaParamRetFunction<Object, Boolean> fun) {
|
||||
return new SaRouterStaff().match(fun);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自定义方法进行匹配排除 (lazy)
|
||||
* @param fun 自定义排除方法
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff notMatch(SaParamRetFunction<Object, Boolean> fun) {
|
||||
return new SaRouterStaff().notMatch(fun);
|
||||
}
|
||||
|
||||
|
||||
// -------------------- 直接指定check函数 --------------------
|
||||
|
||||
/**
|
||||
* 路由匹配,如果匹配成功则执行认证函数
|
||||
* @param pattern 路由匹配符
|
||||
* @param function 要执行的方法
|
||||
* @param fun 要执行的校验方法
|
||||
* @return /
|
||||
*/
|
||||
public static void match(String pattern, SaFunction function) {
|
||||
if(isMatchCurrURI(pattern)) {
|
||||
function.run();
|
||||
}
|
||||
public static SaRouterStaff match(String pattern, SaFunction fun) {
|
||||
return new SaRouterStaff().match(pattern, fun);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配,如果匹配成功则执行认证函数
|
||||
* @param pattern 路由匹配符
|
||||
* @param fun 要执行的校验方法
|
||||
* @return /
|
||||
*/
|
||||
public static SaRouterStaff match(String pattern, SaParamFunction<SaRouterStaff> fun) {
|
||||
return new SaRouterStaff().match(pattern, fun);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配 (并指定排除匹配符),如果匹配成功则执行认证函数
|
||||
* @param pattern 路由匹配符
|
||||
* @param excludePattern 要排除的路由匹配符
|
||||
* @param function 要执行的方法
|
||||
* @param fun 要执行的方法
|
||||
* @return /
|
||||
*/
|
||||
public static void match(String pattern, String excludePattern, SaFunction function) {
|
||||
if(isMatchCurrURI(pattern)) {
|
||||
if(isMatchCurrURI(excludePattern) == false) {
|
||||
function.run();
|
||||
}
|
||||
}
|
||||
public static SaRouterStaff match(String pattern, String excludePattern, SaFunction fun) {
|
||||
return new SaRouterStaff().match(pattern, excludePattern, fun);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配 (并指定排除匹配符),如果匹配成功则执行认证函数
|
||||
* @param pattern 路由匹配符
|
||||
* @param excludePattern 要排除的路由匹配符
|
||||
* @param fun 要执行的方法
|
||||
* @return /
|
||||
*/
|
||||
public static SaRouterStaff match(String pattern, String excludePattern, SaParamFunction<SaRouterStaff> fun) {
|
||||
return new SaRouterStaff().match(pattern, excludePattern, fun);
|
||||
}
|
||||
|
||||
|
||||
// -------------------- 提前退出 --------------------
|
||||
|
||||
/**
|
||||
* 停止匹配,跳出函数 (在多个匹配链中一次性跳出Auth函数)
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff stop() {
|
||||
throw new StopMatchException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止匹配,结束执行,向前端返回结果
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff back() {
|
||||
throw new BackResultException("");
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止匹配,结束执行,向前端返回结果
|
||||
* @param result 要输出的结果
|
||||
* @return SaRouterStaff
|
||||
*/
|
||||
public static SaRouterStaff back(Object result) {
|
||||
throw new BackResultException(result);
|
||||
}
|
||||
|
||||
|
||||
// -------------------- 历史API兼容 --------------------
|
||||
|
||||
/**
|
||||
* <h1>本函数设计已过时,请更换为:SaRouter.match(path...).ckeck(fun) </h1>
|
||||
* 路由匹配,如果匹配成功则执行认证函数
|
||||
* @param patterns 路由匹配符集合
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
@Deprecated
|
||||
public static void match(List<String> patterns, SaFunction function) {
|
||||
if(isMatchCurrURI(patterns)) {
|
||||
function.run();
|
||||
@@ -102,11 +336,13 @@ public class SaRouter {
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1>本函数设计已过时,请更换为:SaRouter.match(path...).notMatch(path...).ckeck(fun) </h1>
|
||||
* 路由匹配 (并指定排除匹配符),如果匹配成功则执行认证函数
|
||||
* @param patterns 路由匹配符集合
|
||||
* @param excludePatterns 要排除的路由匹配符集合
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
@Deprecated
|
||||
public static void match(List<String> patterns, List<String> excludePatterns, SaFunction function) {
|
||||
if(isMatchCurrURI(patterns)) {
|
||||
if(isMatchCurrURI(excludePatterns) == false) {
|
||||
@@ -114,41 +350,5 @@ public class SaRouter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 路由匹配,如果匹配成功则执行认证函数
|
||||
* @param patterns 路由匹配符集合
|
||||
* @return 匹配结果包装对象
|
||||
*/
|
||||
public static IsRunFunction match(String... patterns) {
|
||||
boolean matchResult = isMatch(Arrays.asList(patterns), SaHolder.getRequest().getRequestPath());
|
||||
return new IsRunFunction(matchResult);
|
||||
}
|
||||
|
||||
|
||||
// -------------------- 其它操作 --------------------
|
||||
|
||||
/**
|
||||
* 停止匹配,跳出函数 (在多个匹配链中一次性跳出Auth函数)
|
||||
*/
|
||||
public static void stop() {
|
||||
throw new StopMatchException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止匹配,结束执行,向前端返回结果
|
||||
* @param result 要输出的结果
|
||||
*/
|
||||
public static void back(Object result) {
|
||||
throw new BackResultException(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止匹配,结束执行,向前端返回结果
|
||||
*/
|
||||
public static void back() {
|
||||
throw new BackResultException("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
package cn.dev33.satoken.router;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.exception.BackResultException;
|
||||
import cn.dev33.satoken.exception.StopMatchException;
|
||||
import cn.dev33.satoken.fun.SaFunction;
|
||||
import cn.dev33.satoken.fun.SaParamFunction;
|
||||
import cn.dev33.satoken.fun.SaParamRetFunction;
|
||||
|
||||
/**
|
||||
* 路由匹配操作对象
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaRouterStaff {
|
||||
|
||||
/**
|
||||
* 是否命中的标记变量
|
||||
*/
|
||||
public boolean isHit = true;
|
||||
|
||||
/**
|
||||
* @return 是否命中
|
||||
*/
|
||||
public boolean isHit() {
|
||||
return isHit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isHit 命中标记
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff setHit(boolean isHit) {
|
||||
this.isHit = isHit;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置命中标记为 true
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff reset() {
|
||||
this.isHit = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ----------------- path匹配
|
||||
|
||||
/**
|
||||
* 路由匹配
|
||||
* @param patterns 路由匹配符数组
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff match(String... patterns) {
|
||||
if(isHit) {
|
||||
isHit = SaRouter.isMatchCurrURI(patterns);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配排除
|
||||
* @param patterns 路由匹配符排除数组
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff notMatch(String... patterns) {
|
||||
if(isHit) {
|
||||
isHit = !SaRouter.isMatchCurrURI(patterns);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配
|
||||
* @param patterns 路由匹配符集合
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff match(List<String> patterns) {
|
||||
if(isHit) {
|
||||
isHit = SaRouter.isMatchCurrURI(patterns);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配排除
|
||||
* @param patterns 路由匹配符排除集合
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff notMatch(List<String> patterns) {
|
||||
if(isHit) {
|
||||
isHit = !SaRouter.isMatchCurrURI(patterns);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// ----------------- Method匹配
|
||||
|
||||
/**
|
||||
* Http请求方法匹配 (Enum)
|
||||
* @param methods Http请求方法断言数组
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff match(SaHttpMethod... methods) {
|
||||
if(isHit) {
|
||||
isHit = SaRouter.isMatchCurrMethod(methods);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Http请求方法匹配排除 (Enum)
|
||||
* @param methods Http请求方法断言排除数组
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff notMatch(SaHttpMethod... methods) {
|
||||
if(isHit) {
|
||||
isHit = !SaRouter.isMatchCurrMethod(methods);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Http请求方法匹配 (String)
|
||||
* @param methods Http请求方法断言数组
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff matchMethod(String... methods) {
|
||||
if(isHit) {
|
||||
SaHttpMethod [] arr = SaHttpMethod.toEnumArray(methods);
|
||||
isHit = SaRouter.isMatchCurrMethod(arr);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Http请求方法匹配排除 (String)
|
||||
* @param methods Http请求方法断言排除数组
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff notMatchMethod(String... methods) {
|
||||
if(isHit) {
|
||||
SaHttpMethod [] arr = SaHttpMethod.toEnumArray(methods);
|
||||
isHit = !SaRouter.isMatchCurrMethod(arr);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ----------------- 条件匹配
|
||||
|
||||
/**
|
||||
* 根据 boolean 值进行匹配
|
||||
* @param flag boolean值
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff match(boolean flag) {
|
||||
if(isHit) {
|
||||
isHit = flag;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 boolean 值进行匹配排除
|
||||
* @param flag boolean值
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff notMatch(boolean flag) {
|
||||
if(isHit) {
|
||||
isHit = !flag;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自定义方法进行匹配 (lazy)
|
||||
* @param fun 自定义方法
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff match(SaParamRetFunction<Object, Boolean> fun) {
|
||||
if(isHit) {
|
||||
isHit = fun.run(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自定义方法进行匹配排除 (lazy)
|
||||
* @param fun 自定义排除方法
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff notMatch(SaParamRetFunction<Object, Boolean> fun) {
|
||||
if(isHit) {
|
||||
isHit = !fun.run(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ----------------- 函数校验执行
|
||||
|
||||
/**
|
||||
* 执行校验函数 (无参)
|
||||
* @param fun 要执行的函数
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff check(SaFunction fun) {
|
||||
if(isHit) {
|
||||
fun.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行校验函数 (带参)
|
||||
* @param fun 要执行的函数
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff check(SaParamFunction<SaRouterStaff> fun) {
|
||||
if(isHit) {
|
||||
fun.run(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自由匹配 ( 在free作用域里执行stop()不会跳出Auth函数,而是仅仅跳出free代码块 )
|
||||
* @param fun 要执行的函数
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff free(SaParamFunction<SaRouterStaff> fun) {
|
||||
if(isHit) {
|
||||
try {
|
||||
fun.run(this);
|
||||
} catch (StopMatchException e) {
|
||||
// 跳出 free自由匹配代码块
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ----------------- 直接指定check函数
|
||||
|
||||
/**
|
||||
* 路由匹配,如果匹配成功则执行认证函数
|
||||
* @param pattern 路由匹配符
|
||||
* @param fun 要执行的校验方法
|
||||
* @return /
|
||||
*/
|
||||
public SaRouterStaff match(String pattern, SaFunction fun) {
|
||||
return this.match(pattern).check(fun);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配,如果匹配成功则执行认证函数
|
||||
* @param pattern 路由匹配符
|
||||
* @param fun 要执行的校验方法
|
||||
* @return /
|
||||
*/
|
||||
public SaRouterStaff match(String pattern, SaParamFunction<SaRouterStaff> fun) {
|
||||
return this.match(pattern).check(fun);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配 (并指定排除匹配符),如果匹配成功则执行认证函数
|
||||
* @param pattern 路由匹配符
|
||||
* @param excludePattern 要排除的路由匹配符
|
||||
* @param fun 要执行的方法
|
||||
* @return /
|
||||
*/
|
||||
public SaRouterStaff match(String pattern, String excludePattern, SaFunction fun) {
|
||||
return this.match(pattern).notMatch(excludePattern).check(fun);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由匹配 (并指定排除匹配符),如果匹配成功则执行认证函数
|
||||
* @param pattern 路由匹配符
|
||||
* @param excludePattern 要排除的路由匹配符
|
||||
* @param fun 要执行的方法
|
||||
* @return /
|
||||
*/
|
||||
public SaRouterStaff match(String pattern, String excludePattern, SaParamFunction<SaRouterStaff> fun) {
|
||||
return this.match(pattern).notMatch(excludePattern).check(fun);
|
||||
}
|
||||
|
||||
|
||||
// ----------------- 提前退出
|
||||
|
||||
/**
|
||||
* 停止匹配,跳出函数 (在多个匹配链中一次性跳出Auth函数)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff stop() {
|
||||
if(isHit) {
|
||||
throw new StopMatchException();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止匹配,结束执行,向前端返回结果
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaRouterStaff back() {
|
||||
if(isHit) {
|
||||
throw new BackResultException("");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止匹配,结束执行,向前端返回结果
|
||||
* @return 对象自身
|
||||
* @param result 要输出的结果
|
||||
*/
|
||||
public SaRouterStaff back(Object result) {
|
||||
if(isHit) {
|
||||
throw new BackResultException(result);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -27,7 +27,7 @@ public class SaRouterUtil {
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatch(String pattern, String path) {
|
||||
return SaManager.getSaTokenContext().matchPath(pattern, path);
|
||||
return SaManager.getSaTokenContextOrSecond().matchPath(pattern, path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,533 @@
|
||||
package cn.dev33.satoken.secure;
|
||||
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* BCrypt加密算法实现。由它加密的文件可在所有支持的操作系统和处理器上进行转移。它的口令必须是8至56个字符,并将在内部被转化为448位的密钥。
|
||||
* <p>
|
||||
* 此类来自于https://github.com/jeremyh/jBCrypt/
|
||||
* <p>
|
||||
* 使用方法如下:
|
||||
* <p>
|
||||
* {@code
|
||||
* String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
|
||||
* }
|
||||
* <p>
|
||||
* 使用checkpw方法检查被加密的字符串是否与原始字符串匹配:
|
||||
* <p>
|
||||
* {@code
|
||||
* BCrypt.checkpw(candidate_password, stored_hash);
|
||||
* }
|
||||
* <p>
|
||||
* gensalt方法提供了可选参数 (log_rounds) 来定义加盐多少,也决定了加密的复杂度:
|
||||
* <p>
|
||||
* {@code
|
||||
* String strong_salt = BCrypt.gensalt(10);
|
||||
* String stronger_salt = BCrypt.gensalt(12);
|
||||
* }
|
||||
*
|
||||
* @author Damien Miller
|
||||
* @since 4.1.1
|
||||
*/
|
||||
public class BCrypt {
|
||||
// BCrypt parameters
|
||||
private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
|
||||
private static final int BCRYPT_SALT_LEN = 16;
|
||||
|
||||
// Blowfish parameters
|
||||
private static final int BLOWFISH_NUM_ROUNDS = 16;
|
||||
|
||||
// Initial contents of key schedule
|
||||
private static final int[] P_orig = {0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7,
|
||||
0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b};
|
||||
private static final int[] S_orig = {0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8,
|
||||
0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
|
||||
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 0xb4cc5c34,
|
||||
0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af,
|
||||
0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482,
|
||||
0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
|
||||
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72,
|
||||
0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4,
|
||||
0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857,
|
||||
0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
|
||||
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d,
|
||||
0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01,
|
||||
0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0,
|
||||
0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
|
||||
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb,
|
||||
0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a,
|
||||
0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d,
|
||||
0xa99f8fa1, 0x08ba4799, 0x6e85076a, 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
|
||||
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6,
|
||||
0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
|
||||
0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 0xa9446146,
|
||||
0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366,
|
||||
0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
|
||||
0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6,
|
||||
0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
|
||||
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775,
|
||||
0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
|
||||
0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829,
|
||||
0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
||||
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a,
|
||||
0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
|
||||
0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa,
|
||||
0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7,
|
||||
0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941,
|
||||
0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c,
|
||||
0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
|
||||
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c,
|
||||
0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1,
|
||||
0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e,
|
||||
0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
|
||||
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b,
|
||||
0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7,
|
||||
0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057,
|
||||
0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
|
||||
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591,
|
||||
0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df,
|
||||
0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28,
|
||||
0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
|
||||
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce,
|
||||
0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315,
|
||||
0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb,
|
||||
0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
|
||||
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9,
|
||||
0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc,
|
||||
0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b,
|
||||
0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
|
||||
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf,
|
||||
0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e,
|
||||
0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82,
|
||||
0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
|
||||
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71,
|
||||
0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df,
|
||||
0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166,
|
||||
0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
|
||||
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60,
|
||||
0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0,
|
||||
0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6};
|
||||
|
||||
// bcrypt IV: "OrpheanBeholderScryDoubt". The C implementation calls
|
||||
// this "ciphertext", but it is really plaintext or an IV. We keep
|
||||
// the name to make code comparison easier.
|
||||
static private final int[] bf_crypt_ciphertext = {0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274};
|
||||
|
||||
// Table for Base64 encoding
|
||||
static private final char[] base64_code = {'.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
|
||||
'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
|
||||
|
||||
// Table for Base64 decoding
|
||||
static private final byte[] index_64 = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1};
|
||||
|
||||
// Expanded Blowfish key
|
||||
private int[] P;
|
||||
private int[] S;
|
||||
|
||||
/**
|
||||
* Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note that this is *not* compatible with the standard MIME-base64 encoding.
|
||||
*
|
||||
* @param d the byte array to encode
|
||||
* @param len the number of bytes to encode
|
||||
* @return base64-encoded string
|
||||
* @throws IllegalArgumentException if the length is invalid
|
||||
*/
|
||||
private static String encode_base64(byte[] d, int len) throws IllegalArgumentException {
|
||||
int off = 0;
|
||||
StringBuilder rs = new StringBuilder();
|
||||
int c1, c2;
|
||||
|
||||
if (len <= 0 || len > d.length)
|
||||
throw new IllegalArgumentException("Invalid len");
|
||||
|
||||
while (off < len) {
|
||||
c1 = d[off++] & 0xff;
|
||||
rs.append(base64_code[(c1 >> 2) & 0x3f]);
|
||||
c1 = (c1 & 0x03) << 4;
|
||||
if (off >= len) {
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
break;
|
||||
}
|
||||
c2 = d[off++] & 0xff;
|
||||
c1 |= (c2 >> 4) & 0x0f;
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
c1 = (c2 & 0x0f) << 2;
|
||||
if (off >= len) {
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
break;
|
||||
}
|
||||
c2 = d[off++] & 0xff;
|
||||
c1 |= (c2 >> 6) & 0x03;
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
rs.append(base64_code[c2 & 0x3f]);
|
||||
}
|
||||
return rs.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the 3 bits base64-encoded by the specified character, range-checking againt conversion table
|
||||
*
|
||||
* @param x the base64-encoded value
|
||||
* @return the decoded value of x
|
||||
*/
|
||||
private static byte char64(char x) {
|
||||
if ((int) x > index_64.length)
|
||||
return -1;
|
||||
return index_64[x];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a string encoded using bcrypt's base64 scheme to a byte array.<br>
|
||||
* Note that this is *not* compatible with the standard MIME-base64 encoding.
|
||||
*
|
||||
* @param s the string to decode
|
||||
* @param maxolen the maximum number of bytes to decode
|
||||
* @return an array containing the decoded bytes
|
||||
* @throws IllegalArgumentException if maxolen is invalid
|
||||
*/
|
||||
private static byte[] decodeBase64(String s, int maxolen) throws IllegalArgumentException {
|
||||
final StringBuilder rs = new StringBuilder();
|
||||
int off = 0, slen = s.length(), olen = 0;
|
||||
byte[] ret;
|
||||
byte c1, c2, c3, c4, o;
|
||||
|
||||
if (maxolen <= 0)
|
||||
throw new IllegalArgumentException("Invalid maxolen");
|
||||
|
||||
while (off < slen - 1 && olen < maxolen) {
|
||||
c1 = char64(s.charAt(off++));
|
||||
c2 = char64(s.charAt(off++));
|
||||
if (c1 == -1 || c2 == -1)
|
||||
break;
|
||||
o = (byte) (c1 << 2);
|
||||
o |= (c2 & 0x30) >> 4;
|
||||
rs.append((char) o);
|
||||
if (++olen >= maxolen || off >= slen)
|
||||
break;
|
||||
c3 = char64(s.charAt(off++));
|
||||
if (c3 == -1)
|
||||
break;
|
||||
o = (byte) ((c2 & 0x0f) << 4);
|
||||
o |= (c3 & 0x3c) >> 2;
|
||||
rs.append((char) o);
|
||||
if (++olen >= maxolen || off >= slen)
|
||||
break;
|
||||
c4 = char64(s.charAt(off++));
|
||||
o = (byte) ((c3 & 0x03) << 6);
|
||||
o |= c4;
|
||||
rs.append((char) o);
|
||||
++olen;
|
||||
}
|
||||
|
||||
ret = new byte[olen];
|
||||
for (off = 0; off < olen; off++)
|
||||
ret[off] = (byte) rs.charAt(off);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blowfish encipher a single 64-bit block encoded as two 32-bit halves
|
||||
*
|
||||
* @param lr an array containing the two 32-bit half blocks
|
||||
* @param off the position in the array of the blocks
|
||||
*/
|
||||
private void encipher(int[] lr, int off) {
|
||||
int i, n, l = lr[off], r = lr[off + 1];
|
||||
|
||||
l ^= P[0];
|
||||
for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2; ) {
|
||||
// Feistel substitution on left word
|
||||
n = S[(l >> 24) & 0xff];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)];
|
||||
n += S[0x300 | (l & 0xff)];
|
||||
r ^= n ^ P[++i];
|
||||
|
||||
// Feistel substitution on right word
|
||||
n = S[(r >> 24) & 0xff];
|
||||
n += S[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)];
|
||||
n += S[0x300 | (r & 0xff)];
|
||||
l ^= n ^ P[++i];
|
||||
}
|
||||
lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];
|
||||
lr[off + 1] = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycically extract a word of key material
|
||||
*
|
||||
* @param data the string to extract the data from
|
||||
* @param offp a "pointer" (as a one-entry array) to the current offset into data
|
||||
* @return the next word of material from data
|
||||
*/
|
||||
private static int streamToWord(byte[] data, int[] offp) {
|
||||
int i;
|
||||
int word = 0;
|
||||
int off = offp[0];
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
word = (word << 8) | (data[off] & 0xff);
|
||||
off = (off + 1) % data.length;
|
||||
}
|
||||
|
||||
offp[0] = off;
|
||||
return word;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the Blowfish key schedule
|
||||
*/
|
||||
private void init_key() {
|
||||
P = P_orig.clone();
|
||||
S = S_orig.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Key the Blowfish cipher
|
||||
*
|
||||
* @param key an array containing the key
|
||||
*/
|
||||
private void key(byte[] key) {
|
||||
int i;
|
||||
int[] koffp = {0};
|
||||
int[] lr = {0, 0};
|
||||
int plen = P.length, slen = S.length;
|
||||
|
||||
for (i = 0; i < plen; i++)
|
||||
P[i] = P[i] ^ streamToWord(key, koffp);
|
||||
|
||||
for (i = 0; i < plen; i += 2) {
|
||||
encipher(lr, 0);
|
||||
P[i] = lr[0];
|
||||
P[i + 1] = lr[1];
|
||||
}
|
||||
|
||||
for (i = 0; i < slen; i += 2) {
|
||||
encipher(lr, 0);
|
||||
S[i] = lr[0];
|
||||
S[i + 1] = lr[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the "enhanced key schedule" step described by Provos and Mazieres in "A Future-Adaptable Password Scheme" http://www.openbsd.org/papers/bcrypt-paper.ps
|
||||
*
|
||||
* @param data salt information
|
||||
* @param key password information
|
||||
*/
|
||||
private void ekskey(byte[] data, byte[] key) {
|
||||
int i;
|
||||
int[] koffp = {0};
|
||||
int[] doffp = {0};
|
||||
int[] lr = {0, 0};
|
||||
int plen = P.length, slen = S.length;
|
||||
|
||||
for (i = 0; i < plen; i++)
|
||||
P[i] = P[i] ^ streamToWord(key, koffp);
|
||||
|
||||
for (i = 0; i < plen; i += 2) {
|
||||
lr[0] ^= streamToWord(data, doffp);
|
||||
lr[1] ^= streamToWord(data, doffp);
|
||||
encipher(lr, 0);
|
||||
P[i] = lr[0];
|
||||
P[i + 1] = lr[1];
|
||||
}
|
||||
|
||||
for (i = 0; i < slen; i += 2) {
|
||||
lr[0] ^= streamToWord(data, doffp);
|
||||
lr[1] ^= streamToWord(data, doffp);
|
||||
encipher(lr, 0);
|
||||
S[i] = lr[0];
|
||||
S[i + 1] = lr[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密密文
|
||||
*
|
||||
* @param password 明文密码
|
||||
* @param salt 加盐
|
||||
* @param log_rounds hash中叠加的对数
|
||||
* @param cdata 加密数据
|
||||
* @return 加密后的密文
|
||||
*/
|
||||
public byte[] crypt(byte[] password, byte[] salt, int log_rounds, int[] cdata) {
|
||||
int rounds, i, j;
|
||||
int clen = cdata.length;
|
||||
byte[] ret;
|
||||
|
||||
if (log_rounds < 4 || log_rounds > 30)
|
||||
throw new IllegalArgumentException("Bad number of rounds");
|
||||
rounds = 1 << log_rounds;
|
||||
if (salt.length != BCRYPT_SALT_LEN)
|
||||
throw new IllegalArgumentException("Bad salt length");
|
||||
|
||||
init_key();
|
||||
ekskey(salt, password);
|
||||
for (i = 0; i != rounds; i++) {
|
||||
key(password);
|
||||
key(salt);
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
for (j = 0; j < (clen >> 1); j++)
|
||||
encipher(cdata, j << 1);
|
||||
}
|
||||
|
||||
ret = new byte[clen * 4];
|
||||
for (i = 0, j = 0; i < clen; i++) {
|
||||
ret[j++] = (byte) ((cdata[i] >> 24) & 0xff);
|
||||
ret[j++] = (byte) ((cdata[i] >> 16) & 0xff);
|
||||
ret[j++] = (byte) ((cdata[i] >> 8) & 0xff);
|
||||
ret[j++] = (byte) (cdata[i] & 0xff);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成密文,使用长度为10的加盐方式
|
||||
*
|
||||
* @param password 需要加密的明文
|
||||
* @return 密文
|
||||
*/
|
||||
public static String hashpw(String password) {
|
||||
return hashpw(password, gensalt());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成密文
|
||||
*
|
||||
* @param password 需要加密的明文
|
||||
* @param salt 盐,使用{@link #gensalt()} 生成
|
||||
* @return 密文
|
||||
*/
|
||||
public static String hashpw(String password, String salt) {
|
||||
BCrypt bcrypt;
|
||||
String real_salt;
|
||||
byte[] saltb;
|
||||
byte[] hashed;
|
||||
char minor = (char) 0;
|
||||
int rounds, off;
|
||||
StringBuilder rs = new StringBuilder();
|
||||
|
||||
if (salt.charAt(0) != '$' || salt.charAt(1) != '2')
|
||||
throw new IllegalArgumentException("Invalid salt version");
|
||||
if (salt.charAt(2) == '$')
|
||||
off = 3;
|
||||
else {
|
||||
minor = salt.charAt(2);
|
||||
// pr#1560@Github
|
||||
// 修正一个在Blowfish实现上的安全风险
|
||||
if ((minor != 'a' && minor != 'x' && minor != 'y' && minor != 'b') || salt.charAt(3) != '$')
|
||||
throw new IllegalArgumentException("Invalid salt revision");
|
||||
off = 4;
|
||||
}
|
||||
|
||||
// Extract number of rounds
|
||||
if (salt.charAt(off + 2) > '$')
|
||||
throw new IllegalArgumentException("Missing salt rounds");
|
||||
rounds = Integer.parseInt(salt.substring(off, off + 2));
|
||||
|
||||
real_salt = salt.substring(off + 3, off + 25);
|
||||
byte[] passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes(StandardCharsets.UTF_8);
|
||||
saltb = decodeBase64(real_salt, BCRYPT_SALT_LEN);
|
||||
|
||||
bcrypt = new BCrypt();
|
||||
hashed = bcrypt.crypt(passwordb, saltb, rounds, bf_crypt_ciphertext.clone());
|
||||
|
||||
rs.append("$2");
|
||||
if (minor >= 'a')
|
||||
rs.append(minor);
|
||||
rs.append("$");
|
||||
if (rounds < 10)
|
||||
rs.append("0");
|
||||
if (rounds > 30) {
|
||||
throw new IllegalArgumentException("rounds exceeds maximum (30)");
|
||||
}
|
||||
rs.append(rounds);
|
||||
rs.append("$");
|
||||
rs.append(encode_base64(saltb, saltb.length));
|
||||
rs.append(encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1));
|
||||
return rs.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成盐
|
||||
*
|
||||
* @param log_rounds hash中叠加的2的对数 - the work factor therefore increases as 2**log_rounds.
|
||||
* @param random {@link SecureRandom}
|
||||
* @return an encoded salt value
|
||||
*/
|
||||
public static String gensalt(int log_rounds, SecureRandom random) {
|
||||
final StringBuilder rs = new StringBuilder();
|
||||
byte[] rnd = new byte[BCRYPT_SALT_LEN];
|
||||
|
||||
random.nextBytes(rnd);
|
||||
|
||||
rs.append("$2a$");
|
||||
if (log_rounds < 10)
|
||||
rs.append("0");
|
||||
if (log_rounds > 30) {
|
||||
throw new IllegalArgumentException("log_rounds exceeds maximum (30)");
|
||||
}
|
||||
rs.append(log_rounds);
|
||||
rs.append("$");
|
||||
rs.append(encode_base64(rnd, rnd.length));
|
||||
return rs.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成盐
|
||||
*
|
||||
* @param log_rounds the log2 of the number of rounds of hashing to apply - the work factor therefore increases as 2**log_rounds.
|
||||
* @return 盐
|
||||
*/
|
||||
public static String gensalt(int log_rounds) {
|
||||
return gensalt(log_rounds, new SecureRandom());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成盐
|
||||
*
|
||||
* @return 盐
|
||||
*/
|
||||
public static String gensalt() {
|
||||
return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查明文密码文本是否匹配加密后的文本
|
||||
*
|
||||
* @param plaintext 需要验证的明文密码
|
||||
* @param hashed 密文
|
||||
* @return 是否匹配
|
||||
*/
|
||||
public static boolean checkpw(String plaintext, String hashed) {
|
||||
byte[] hashed_bytes;
|
||||
byte[] try_bytes;
|
||||
|
||||
String try_pw;
|
||||
try {
|
||||
try_pw = hashpw(plaintext, hashed);
|
||||
} catch (Exception ignore) {
|
||||
// 生成密文时错误直接返回false issue#1377@Github
|
||||
return false;
|
||||
}
|
||||
hashed_bytes = hashed.getBytes(StandardCharsets.UTF_8);
|
||||
try_bytes = try_pw.getBytes(StandardCharsets.UTF_8);
|
||||
if (hashed_bytes.length != try_bytes.length) {
|
||||
return false;
|
||||
}
|
||||
byte ret = 0;
|
||||
for (int i = 0; i < try_bytes.length; i++)
|
||||
ret |= hashed_bytes[i] ^ try_bytes[i];
|
||||
return ret == 0;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.fun.SaRetFunction;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
@@ -20,7 +21,17 @@ import cn.dev33.satoken.util.SaFoxUtil;
|
||||
public class SaSession implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 在 Session 上存储角色时建议使用的key
|
||||
*/
|
||||
public static final String ROLE_LIST = "ROLE_LIST";
|
||||
|
||||
/**
|
||||
* 在 Session 上存储权限时建议使用的key
|
||||
*/
|
||||
public static final String PERMISSION_LIST = "PERMISSION_LIST";
|
||||
|
||||
/** 此Session的id */
|
||||
private String id;
|
||||
|
||||
@@ -139,6 +150,16 @@ public class SaSession implements Serializable {
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个token签名
|
||||
*
|
||||
* @param tokenValue token值
|
||||
* @param device 设备标识
|
||||
*/
|
||||
public void addTokenSign(String tokenValue, String device) {
|
||||
addTokenSign(new TokenSign(tokenValue, device));
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除一个token签名
|
||||
*
|
||||
@@ -196,8 +217,10 @@ public class SaSession implements Serializable {
|
||||
* @param minTimeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void updateMinTimeout(long minTimeout) {
|
||||
if(getTimeout() < minTimeout) {
|
||||
SaManager.getSaTokenDao().updateSessionTimeout(this.id, minTimeout);
|
||||
long min = trans(minTimeout);
|
||||
long curr = trans(getTimeout());
|
||||
if(curr < min) {
|
||||
updateTimeout(minTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,12 +229,21 @@ public class SaSession implements Serializable {
|
||||
* @param maxTimeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void updateMaxTimeout(long maxTimeout) {
|
||||
if(getTimeout() > maxTimeout) {
|
||||
SaManager.getSaTokenDao().updateSessionTimeout(this.id, maxTimeout);
|
||||
long max = trans(maxTimeout);
|
||||
long curr = trans(getTimeout());
|
||||
if(curr > max) {
|
||||
updateTimeout(maxTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* value为 -1 时返回 Long.MAX_VALUE,否则原样返回
|
||||
* @param value /
|
||||
* @return /
|
||||
*/
|
||||
protected long trans(long value) {
|
||||
return value == SaTokenDao.NEVER_EXPIRE ? Long.MAX_VALUE : value;
|
||||
}
|
||||
|
||||
// ----------------------- 存取值 (类型转换)
|
||||
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
package cn.dev33.satoken.session;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
|
||||
/**
|
||||
* 自定义Session工具类
|
||||
* 自定义 Session 工具类
|
||||
*
|
||||
* <p>样例:
|
||||
* <pre>
|
||||
* // 在一处代码写入数据
|
||||
* SaSession session = SaSessionCustomUtil.getSessionById("role-" + 1001);
|
||||
* session.set("count", 1);
|
||||
*
|
||||
* // 在另一处代码获取数据
|
||||
* SaSession session = SaSessionCustomUtil.getSessionById("role-" + 1001);
|
||||
* int count = session.getInt("count");
|
||||
* System.out.println("count=" + count);
|
||||
* </pre>
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
@@ -11,12 +24,12 @@ import cn.dev33.satoken.SaManager;
|
||||
public class SaSessionCustomUtil {
|
||||
|
||||
/**
|
||||
* 添加上指定前缀,防止恶意伪造Session
|
||||
* 添加上指定前缀,防止恶意伪造Session
|
||||
*/
|
||||
public static String sessionKey = "custom";
|
||||
|
||||
/**
|
||||
* 拼接Key: 自定义Session的Id
|
||||
* 拼接Key: 自定义Session的Id
|
||||
*
|
||||
* @param sessionId 会话id
|
||||
* @return sessionId
|
||||
@@ -45,7 +58,7 @@ public class SaSessionCustomUtil {
|
||||
public static SaSession getSessionById(String sessionId, boolean isCreate) {
|
||||
SaSession session = SaManager.getSaTokenDao().getSession(splicingSessionKey(sessionId));
|
||||
if (session == null && isCreate) {
|
||||
session = SaManager.getSaTokenAction().createSession(splicingSessionKey(sessionId));
|
||||
session = SaStrategy.me.createSession.apply(splicingSessionKey(sessionId));
|
||||
SaManager.getSaTokenDao().setSession(session, SaManager.getConfig().getTimeout());
|
||||
}
|
||||
return session;
|
||||
|
||||
@@ -3,7 +3,7 @@ package cn.dev33.satoken.session;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Token签名 Model
|
||||
* Token 签名 Model
|
||||
*
|
||||
* 挂在到SaSession上的token签名
|
||||
*
|
||||
@@ -43,7 +43,7 @@ public class TokenSign implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token value
|
||||
* @return token值
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
|
||||
@@ -48,6 +48,9 @@ public class SaSsoConsts {
|
||||
/** back参数名称 */
|
||||
public static String back = "back";
|
||||
|
||||
/** mode参数名称 */
|
||||
public static String mode = "mode";
|
||||
|
||||
/** loginId参数名称 */
|
||||
public static String loginId = "loginId";
|
||||
|
||||
@@ -70,6 +73,12 @@ public class SaSsoConsts {
|
||||
|
||||
/** 表示自己 */
|
||||
public static final String SELF = "self";
|
||||
|
||||
/** 表示简单模式(SSO模式一) */
|
||||
public static final String MODE_SIMPLE = "simple";
|
||||
|
||||
/** 表示ticket模式(SSO模式二和模式三) */
|
||||
public static final String MODE_TICKET = "ticket";
|
||||
|
||||
/** 表示请求没有得到任何有效处理 {msg: "not handle"} */
|
||||
public static final String NOT_HANDLE = "{\"msg\": \"not handle\"}";
|
||||
|
||||
@@ -20,7 +20,7 @@ import cn.dev33.satoken.util.SaResult;
|
||||
public class SaSsoHandle {
|
||||
|
||||
/**
|
||||
* 处理所有Server端请求
|
||||
* 处理Server端所有请求
|
||||
* @return 处理结果
|
||||
*/
|
||||
public static Object serverRequest() {
|
||||
@@ -46,8 +46,13 @@ public class SaSsoHandle {
|
||||
return ssoCheckTicket();
|
||||
}
|
||||
|
||||
// SSO-Server端:单点注销
|
||||
if(req.isPath(Api.ssoLogout) && cfg.isSlo) {
|
||||
// SSO-Server端:单点注销 [模式一] (不带loginId参数)
|
||||
if(req.isPath(Api.ssoLogout) && cfg.isSlo && req.hasParam(ParamName.loginId) == false) {
|
||||
return ssoServerLogoutType1();
|
||||
}
|
||||
|
||||
// SSO-Server端:单点注销 [模式三] (带loginId参数)
|
||||
if(req.isPath(Api.ssoLogout) && cfg.isHttp && cfg.isSlo && req.hasParam(ParamName.loginId)) {
|
||||
return ssoServerLogout();
|
||||
}
|
||||
|
||||
@@ -66,14 +71,24 @@ public class SaSsoHandle {
|
||||
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||
|
||||
// ---------- 此处两种情况分开处理:
|
||||
// 情况1:在SSO认证中心尚未登录,则先去登登录
|
||||
// ---------- 此处有两种情况分开处理:
|
||||
// ---- 情况1:在SSO认证中心尚未登录,需要先去登录
|
||||
if(stpLogic.isLogin() == false) {
|
||||
return cfg.notLoginView.get();
|
||||
}
|
||||
// 情况2:在SSO认证中心已经登录,开始构建授权重定向地址,下放ticket
|
||||
String redirectUrl = SaSsoUtil.buildRedirectUrl(stpLogic.getLoginId(), req.getParam(ParamName.redirect));
|
||||
return res.redirect(redirectUrl);
|
||||
// ---- 情况2:在SSO认证中心已经登录,需要重定向回 Client 端,而这又分为两种方式:
|
||||
String mode = req.getParam(ParamName.mode, "");
|
||||
|
||||
// 方式1:直接重定向回Client端 (mode=simple)
|
||||
if(mode.equals(SaSsoConsts.MODE_SIMPLE)) {
|
||||
String redirect = req.getParam(ParamName.redirect);
|
||||
SaSsoUtil.checkRedirectUrl(redirect);
|
||||
return res.redirect(redirect);
|
||||
} else {
|
||||
// 方式2:带着ticket参数重定向回Client端 (mode=ticket)
|
||||
String redirectUrl = SaSsoUtil.buildRedirectUrl(stpLogic.getLoginId(), req.getParam(ParamName.redirect));
|
||||
return res.redirect(redirectUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +127,24 @@ public class SaSsoHandle {
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销
|
||||
* SSO-Server端:单点注销 [模式一]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public static Object ssoServerLogoutType1() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||
|
||||
// 开始处理
|
||||
stpLogic.logout();
|
||||
|
||||
// 返回
|
||||
return ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销 [模式三]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public static Object ssoServerLogout() {
|
||||
@@ -126,7 +158,6 @@ public class SaSsoHandle {
|
||||
String secretkey = req.getParam(ParamName.secretkey);
|
||||
|
||||
// 遍历通知Client端注销会话
|
||||
// SaSsoUtil.singleLogout(secretkey, loginId, url -> cfg.sendHttp.apply(url));
|
||||
// step.1 校验秘钥
|
||||
SaSsoUtil.checkSecretkey(secretkey);
|
||||
|
||||
@@ -134,7 +165,7 @@ public class SaSsoHandle {
|
||||
SaSsoUtil.forEachSloUrl(loginId, url -> cfg.sendHttp.apply(url));
|
||||
|
||||
// step.3 Server端注销
|
||||
stpLogic.logoutByTokenValue(stpLogic.getTokenValueByLoginId(loginId));
|
||||
stpLogic.logout(loginId);
|
||||
|
||||
// 完成
|
||||
return SaSsoConsts.OK;
|
||||
@@ -142,7 +173,7 @@ public class SaSsoHandle {
|
||||
|
||||
|
||||
/**
|
||||
* 处理所有Client端请求
|
||||
* 处理Client端所有请求
|
||||
* @return 处理结果
|
||||
*/
|
||||
public static Object clientRequest() {
|
||||
@@ -206,20 +237,8 @@ public class SaSsoHandle {
|
||||
return res.redirect(serverAuthUrl);
|
||||
} else {
|
||||
// ------- 1、校验ticket,获取账号id
|
||||
Object loginId = null;
|
||||
if(cfg.isHttp) {
|
||||
// 方式1:使用http请求校验ticket
|
||||
String ssoLogoutCall = null;
|
||||
if(cfg.isSlo) {
|
||||
ssoLogoutCall = SaHolder.getRequest().getUrl().replace(Api.ssoLogin, Api.ssoLogoutCall);
|
||||
}
|
||||
String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall);
|
||||
Object body = cfg.sendHttp.apply(checkUrl);
|
||||
loginId = (SaFoxUtil.isEmpty(body) ? null : body);
|
||||
} else {
|
||||
// 方式2:直连Redis校验ticket
|
||||
loginId = SaSsoUtil.checkTicket(ticket);
|
||||
}
|
||||
Object loginId = checkTicket(ticket, Api.ssoLogin);
|
||||
|
||||
// Be: 如果开发者自定义了处理逻辑
|
||||
if(cfg.ticketResultHandle != null) {
|
||||
return cfg.ticketResultHandle.apply(loginId, back);
|
||||
@@ -307,7 +326,7 @@ public class SaSsoHandle {
|
||||
/*
|
||||
* 三种情况:
|
||||
* 1. 有back参数,值为SELF -> 回退一级并刷新
|
||||
* 2. 有back参数,值为url -> 跳转back地址
|
||||
* 2. 有back参数,值为url -> 跳转到此url地址
|
||||
* 3. 无back参数 -> 返回json数据
|
||||
*/
|
||||
String back = req.getParam(ParamName.back);
|
||||
@@ -321,4 +340,28 @@ public class SaSsoHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装:校验ticket,取出loginId
|
||||
* @param ticket ticket码
|
||||
* @param currUri 当前路由的uri,用于计算单点注销回调地址
|
||||
* @return loginId
|
||||
*/
|
||||
public static Object checkTicket(String ticket, String currUri) {
|
||||
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||
// --------- 两种模式
|
||||
if(cfg.isHttp) {
|
||||
// 模式三:使用http请求校验ticket
|
||||
String ssoLogoutCall = null;
|
||||
if(cfg.isSlo) {
|
||||
ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, Api.ssoLogoutCall);
|
||||
}
|
||||
String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall);
|
||||
Object body = cfg.sendHttp.apply(checkUrl);
|
||||
return (SaFoxUtil.isEmpty(body) ? null : body);
|
||||
} else {
|
||||
// 模式二:直连Redis校验ticket
|
||||
return SaSsoUtil.checkTicket(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.sso.SaSsoConsts.ParamName;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
/**
|
||||
@@ -213,7 +214,7 @@ public class SaSsoTemplate {
|
||||
|
||||
// 3、是否在[允许地址列表]之中
|
||||
List<String> authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(","));
|
||||
if(SaManager.getSaTokenAction().hasElement(authUrlList, url) == false) {
|
||||
if(SaStrategy.me.hasElement.apply(authUrlList, url) == false) {
|
||||
throw new SaTokenException("非法redirect:" + url);
|
||||
}
|
||||
|
||||
@@ -327,14 +328,17 @@ public class SaSsoTemplate {
|
||||
* @param fun 调用方法
|
||||
*/
|
||||
public void forEachSloUrl(Object loginId, CallSloUrlFunction fun) {
|
||||
SaSession session = stpLogic.getSessionByLoginId(loginId, false);
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String secretkey = SaManager.getConfig().getSso().getSecretkey();
|
||||
Set<String> urlSet = stpLogic.getSessionByLoginId(loginId).get(SaSsoConsts.SLO_CALLBACK_SET_KEY,
|
||||
() -> new HashSet<String>());
|
||||
|
||||
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, () -> new HashSet<String>());
|
||||
for (String url : urlSet) {
|
||||
// 拼接:login参数、秘钥参数
|
||||
url = SaFoxUtil.joinParam(url, ParamName.loginId, loginId);
|
||||
url = SaFoxUtil.joinParam(url, ParamName.secretkey, secretkey);
|
||||
url = SaFoxUtil.joinParam(url, ParamName.secretkey, secretkey);
|
||||
// 调用
|
||||
fun.run(url);
|
||||
}
|
||||
@@ -408,7 +412,7 @@ public class SaSsoTemplate {
|
||||
* @author kong
|
||||
*/
|
||||
@FunctionalInterface
|
||||
static interface CallSloUrlFunction{
|
||||
public static interface CallSloUrlFunction{
|
||||
/**
|
||||
* 调用function
|
||||
* @param url 注销回调URL
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* 快速构建 调用 `StpUtil.login()` 时的 [配置参数 Model ]
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaLoginConfig {
|
||||
|
||||
/**
|
||||
* @param device 此次登录的客户端设备标识
|
||||
* @return SaLoginModel配置对象
|
||||
*/
|
||||
public static SaLoginModel setDevice(String device) {
|
||||
return create().setDevice(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isLastingCookie 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public static SaLoginModel setIsLastingCookie(Boolean isLastingCookie) {
|
||||
return create().setIsLastingCookie(isLastingCookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeout 指定此次登录token的有效期, 单位:秒 (如未指定,自动取全局配置的timeout值)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public static SaLoginModel setTimeout(Long timeout) {
|
||||
return create().setTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param extraData 扩展信息(只在jwt模式下生效)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public static SaLoginModel setExtraData(Map<String, Object> extraData) {
|
||||
return create().setExtraData(extraData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param token 预定Token(预定本次登录生成的Token值)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public static SaLoginModel setToken(String token) {
|
||||
return create().setToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入扩展数据(只在jwt模式下生效)
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return 对象自身
|
||||
*/
|
||||
public static SaLoginModel setExtra(String key, Object value) {
|
||||
return create().setExtra(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法获取一个 SaLoginModel 对象
|
||||
* @return SaLoginModel 对象
|
||||
*/
|
||||
public static SaLoginModel create() {
|
||||
return new SaLoginModel();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
@@ -12,7 +15,6 @@ import cn.dev33.satoken.util.SaTokenConsts;
|
||||
*/
|
||||
public class SaLoginModel {
|
||||
|
||||
|
||||
/**
|
||||
* 此次登录的客户端设备标识
|
||||
*/
|
||||
@@ -28,16 +30,26 @@ public class SaLoginModel {
|
||||
*/
|
||||
public Long timeout;
|
||||
|
||||
/**
|
||||
* 扩展信息(只在jwt模式下生效)
|
||||
*/
|
||||
public Map<String, Object> extraData;
|
||||
|
||||
/**
|
||||
* 预定Token(预定本次登录生成的Token值)
|
||||
*/
|
||||
public String token;
|
||||
|
||||
|
||||
/**
|
||||
* @return 参考 {@link #device}
|
||||
* @return 此次登录的客户端设备标识
|
||||
*/
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param device 参考 {@link #device}
|
||||
* @param device 此次登录的客户端设备标识
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel setDevice(String device) {
|
||||
@@ -46,14 +58,14 @@ public class SaLoginModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 参考 {@link #isLastingCookie}
|
||||
* @return 参考 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在)
|
||||
*/
|
||||
public Boolean getIsLastingCookie() {
|
||||
return isLastingCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isLastingCookie 参考 {@link #isLastingCookie}
|
||||
* @param isLastingCookie 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel setIsLastingCookie(Boolean isLastingCookie) {
|
||||
@@ -62,14 +74,14 @@ public class SaLoginModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 参考 {@link #timeout}
|
||||
* @return 指定此次登录token的有效期, 单位:秒 (如未指定,自动取全局配置的timeout值)
|
||||
*/
|
||||
public Long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeout 参考 {@link #timeout}
|
||||
* @param timeout 指定此次登录token的有效期, 单位:秒 (如未指定,自动取全局配置的timeout值)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel setTimeout(long timeout) {
|
||||
@@ -77,6 +89,75 @@ public class SaLoginModel {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 扩展信息(只在jwt模式下生效)
|
||||
*/
|
||||
public Map<String, Object> getExtraData() {
|
||||
return extraData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param extraData 扩展信息(只在jwt模式下生效)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel setExtraData(Map<String, Object> extraData) {
|
||||
this.extraData = extraData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 预定Token(预定本次登录生成的Token值)
|
||||
*/
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param token 预定Token(预定本次登录生成的Token值)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel setToken(String token) {
|
||||
this.token = token;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* toString
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaLoginModel [device=" + device + ", isLastingCookie=" + isLastingCookie + ", timeout=" + timeout
|
||||
+ ", extraData=" + extraData + ", token=" + token + "]";
|
||||
}
|
||||
|
||||
// ------ 附加方法
|
||||
|
||||
|
||||
/**
|
||||
* 写入扩展数据(只在jwt模式下生效)
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel setExtra(String key, Object value) {
|
||||
if(this.extraData == null) {
|
||||
this.extraData = new LinkedHashMap<>();
|
||||
}
|
||||
this.extraData.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扩展数据(只在jwt模式下生效)
|
||||
* @param key 键
|
||||
* @return 扩展数据的值
|
||||
*/
|
||||
public Object getExtra(String key) {
|
||||
if(this.extraData == null) {
|
||||
return null;
|
||||
}
|
||||
return this.extraData.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Cookie时长
|
||||
@@ -91,6 +172,16 @@ public class SaLoginModel {
|
||||
return (int)(long)timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取device参数,如果为null,则返回默认值
|
||||
*/
|
||||
public String getDeviceOrDefault() {
|
||||
if(device == null) {
|
||||
return SaTokenConsts.DEFAULT_LOGIN_DEVICE;
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建对象,初始化默认值
|
||||
* @return 对象自身
|
||||
@@ -105,9 +196,9 @@ public class SaLoginModel {
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel build(SaTokenConfig config) {
|
||||
if(device == null) {
|
||||
device = SaTokenConsts.DEFAULT_LOGIN_DEVICE;
|
||||
}
|
||||
// if(device == null) {
|
||||
// device = SaTokenConsts.DEFAULT_LOGIN_DEVICE;
|
||||
// }
|
||||
if(isLastingCookie == null) {
|
||||
isLastingCookie = true;
|
||||
}
|
||||
@@ -125,12 +216,13 @@ public class SaLoginModel {
|
||||
return new SaLoginModel();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* toString
|
||||
* 更换为 getDeviceOrDefault()
|
||||
* @return /
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaLoginModel [device=" + device + ", isLastingCookie=" + isLastingCookie + ", timeout=" + timeout + "]";
|
||||
@Deprecated
|
||||
public String getDeviceOrDefalut() {
|
||||
return getDeviceOrDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,11 +2,12 @@ package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.fun.SaFunction;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* Sa-Token 权限验证工具类
|
||||
* Sa-Token 权限认证工具类
|
||||
* @author kong
|
||||
*/
|
||||
public class StpUtil {
|
||||
@@ -29,6 +30,16 @@ public class StpUtil {
|
||||
return stpLogic.getLoginType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置 StpLogic 对象
|
||||
* @param stpLogic /
|
||||
*/
|
||||
public static void setStpLogic(StpLogic stpLogic) {
|
||||
StpUtil.stpLogic = stpLogic;
|
||||
// 防止自定义 stpLogic 被覆盖
|
||||
SaManager.putStpLogic(stpLogic);
|
||||
}
|
||||
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
@@ -40,6 +51,14 @@ public class StpUtil {
|
||||
return stpLogic.getTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话写入当前TokenValue
|
||||
* @param tokenValue token值
|
||||
*/
|
||||
public static void setTokenValue(String tokenValue){
|
||||
stpLogic.setTokenValue(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话写入当前TokenValue
|
||||
* @param tokenValue token值
|
||||
@@ -57,6 +76,14 @@ public class StpUtil {
|
||||
return stpLogic.getTokenValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前TokenValue (不裁剪前缀)
|
||||
* @return /
|
||||
*/
|
||||
public static String getTokenValueNotCut(){
|
||||
return stpLogic.getTokenValueNotCut();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的Token信息
|
||||
* @return token信息
|
||||
@@ -68,6 +95,8 @@ public class StpUtil {
|
||||
|
||||
// =================== 登录相关操作 ===================
|
||||
|
||||
// --- 登录
|
||||
|
||||
/**
|
||||
* 会话登录
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
@@ -102,16 +131,56 @@ public class StpUtil {
|
||||
public static void login(Object id, SaLoginModel loginModel) {
|
||||
stpLogic.login(id, loginModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定账号id的登录会话
|
||||
* @param id 登录id,建议的类型:(long | int | String)
|
||||
* @return 返回会话令牌
|
||||
*/
|
||||
public static String createLoginSession(Object id) {
|
||||
return stpLogic.createLoginSession(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定账号id的登录会话
|
||||
* @param id 登录id,建议的类型:(long | int | String)
|
||||
* @param loginModel 此次登录的参数Model
|
||||
* @return 返回会话令牌
|
||||
*/
|
||||
public static String createLoginSession(Object id, SaLoginModel loginModel) {
|
||||
return stpLogic.createLoginSession(id, loginModel);
|
||||
}
|
||||
|
||||
// --- 注销
|
||||
|
||||
/**
|
||||
* 会话注销
|
||||
* 会话注销
|
||||
*/
|
||||
public static void logout() {
|
||||
stpLogic.logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据指定Token
|
||||
* 会话注销,根据账号id
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void logout(Object loginId) {
|
||||
stpLogic.logout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id 和 设备标识
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
*/
|
||||
public static void logout(Object loginId, String device) {
|
||||
stpLogic.logout(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据指定 Token
|
||||
*
|
||||
* @param tokenValue 指定token
|
||||
*/
|
||||
public static void logoutByTokenValue(String tokenValue) {
|
||||
@@ -119,24 +188,48 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
|
||||
* 踢人下线,根据账号id
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void logoutByLoginId(Object loginId) {
|
||||
stpLogic.logoutByLoginId(loginId);
|
||||
public static void kickout(Object loginId) {
|
||||
stpLogic.kickout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人下线,根据账号id 和 设备标识
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表踢出所有设备)
|
||||
*/
|
||||
public static void kickout(Object loginId, String device) {
|
||||
stpLogic.kickout(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id and 设备标识 (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* 踢人下线,根据指定 Token
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param tokenValue 指定token
|
||||
*/
|
||||
public static void logoutByLoginId(Object loginId, String device) {
|
||||
stpLogic.logoutByLoginId(loginId, device);
|
||||
public static void kickoutByTokenValue(String tokenValue) {
|
||||
stpLogic.kickoutByTokenValue(tokenValue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 顶人下线,根据账号id 和 设备标识
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表顶替所有设备)
|
||||
*/
|
||||
public static void replaced(Object loginId, String device) {
|
||||
stpLogic.replaced(loginId, device);
|
||||
}
|
||||
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
@@ -212,9 +305,18 @@ public class StpUtil {
|
||||
public static Object getLoginIdByToken(String tokenValue) {
|
||||
return stpLogic.getLoginIdByToken(tokenValue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取Token扩展信息(只在jwt模式下有效)
|
||||
* @param key 键值
|
||||
* @return 对应的扩展数据
|
||||
*/
|
||||
public static Object getExtra(String key) {
|
||||
return stpLogic.getExtra(key);
|
||||
}
|
||||
|
||||
// =================== session相关 ===================
|
||||
|
||||
// =================== User-Session 相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回
|
||||
@@ -262,7 +364,7 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
|
||||
// =================== token专属session ===================
|
||||
// =================== Token-Session 相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定Token-Session,如果Session尚未创建,则新建并返回
|
||||
@@ -282,7 +384,7 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
|
||||
// =================== [临时过期] 验证相关 ===================
|
||||
// =================== [临时有效期] 验证相关 ===================
|
||||
|
||||
/**
|
||||
* 检查当前token 是否已经[临时过期],如果已经过期则抛出异常
|
||||
@@ -304,7 +406,7 @@ public class StpUtil {
|
||||
// =================== 过期时间相关 ===================
|
||||
|
||||
/**
|
||||
* 获取当前登录者的token剩余有效时间 (单位: 秒)
|
||||
* 获取当前登录者的 token 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getTokenTimeout() {
|
||||
@@ -312,7 +414,7 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录者的Session剩余有效时间 (单位: 秒)
|
||||
* 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getSessionTimeout() {
|
||||
@@ -320,7 +422,7 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前token的专属Session剩余有效时间 (单位: 秒)
|
||||
* 获取当前 Token-Session 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getTokenSessionTimeout() {
|
||||
@@ -328,19 +430,60 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前token[临时过期]剩余有效时间 (单位: 秒)
|
||||
* @return token[临时过期]剩余有效时间
|
||||
* 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
|
||||
* @return token [临时过期] 剩余有效时间
|
||||
*/
|
||||
public static long getTokenActivityTimeout() {
|
||||
return stpLogic.getTokenActivityTimeout();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对当前 Token 的 timeout 值进行续期
|
||||
* @param timeout 要修改成为的有效时间 (单位: 秒)
|
||||
*/
|
||||
public static void renewTimeout(long timeout) {
|
||||
stpLogic.renewTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对指定 Token 的 timeout 值进行续期
|
||||
* @param tokenValue 指定token
|
||||
* @param timeout 要修改成为的有效时间 (单位: 秒)
|
||||
*/
|
||||
public static void renewTimeout(String tokenValue, long timeout) {
|
||||
stpLogic.renewTimeout(tokenValue, timeout);
|
||||
}
|
||||
|
||||
// =================== 角色验证操作 ===================
|
||||
|
||||
/**
|
||||
* 获取:当前账号的角色集合
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getRoleList() {
|
||||
return stpLogic.getRoleList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:指定账号的角色集合
|
||||
* @param loginId 指定账号id
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getRoleList(Object loginId) {
|
||||
return stpLogic.getRoleList(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定账号id是否含有角色标识, 返回true或false
|
||||
* 判断:当前账号是否拥有指定角色, 返回true或false
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
*/
|
||||
public static boolean hasRole(String role) {
|
||||
return stpLogic.hasRole(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:指定账号是否含有指定角色标识, 返回true或false
|
||||
* @param loginId 账号id
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
@@ -350,16 +493,25 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定角色标识, 返回true或false
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
* 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
|
||||
* @param roleArray 角色标识数组
|
||||
* @return true或false
|
||||
*/
|
||||
public static boolean hasRole(String role) {
|
||||
return stpLogic.hasRole(role);
|
||||
public static boolean hasRoleAnd(String... roleArray){
|
||||
return stpLogic.hasRoleAnd(roleArray);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
|
||||
* 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
|
||||
* @param roleArray 角色标识数组
|
||||
* @return true或false
|
||||
*/
|
||||
public static boolean hasRoleOr(String... roleArray){
|
||||
return stpLogic.hasRoleOr(roleArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
|
||||
* @param role 角色标识
|
||||
*/
|
||||
public static void checkRole(String role) {
|
||||
@@ -367,7 +519,7 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
|
||||
* 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
|
||||
* @param roleArray 角色标识数组
|
||||
*/
|
||||
public static void checkRoleAnd(String... roleArray){
|
||||
@@ -375,18 +527,44 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
|
||||
* 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
|
||||
* @param roleArray 角色标识数组
|
||||
*/
|
||||
public static void checkRoleOr(String... roleArray){
|
||||
stpLogic.checkRoleOr(roleArray);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== 权限验证操作 ===================
|
||||
|
||||
/**
|
||||
* 获取:当前账号的权限码集合
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getPermissionList() {
|
||||
return stpLogic.getPermissionList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:指定账号的权限码集合
|
||||
* @param loginId 指定账号id
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getPermissionList(Object loginId) {
|
||||
return stpLogic.getPermissionList(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定账号id是否含有指定权限, 返回true或false
|
||||
* 判断:当前账号是否含有指定权限, 返回true或false
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(String permission) {
|
||||
return stpLogic.hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:指定账号id是否含有指定权限, 返回true或false
|
||||
* @param loginId 账号id
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
@@ -396,16 +574,25 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限, 返回true或false
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
* 判断:当前账号是否含有指定权限, [指定多个,必须全部具有]
|
||||
* @param permissionArray 权限码数组
|
||||
* @return true 或 false
|
||||
*/
|
||||
public static boolean hasPermission(String permission) {
|
||||
return stpLogic.hasPermission(permission);
|
||||
}
|
||||
public static boolean hasPermissionAnd(String... permissionArray){
|
||||
return stpLogic.hasPermissionAnd(permissionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
* 判断:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
* @param permissionArray 权限码数组
|
||||
* @return true 或 false
|
||||
*/
|
||||
public static boolean hasPermissionOr(String... permissionArray){
|
||||
return stpLogic.hasPermissionOr(permissionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
* @param permission 权限码
|
||||
*/
|
||||
public static void checkPermission(String permission) {
|
||||
@@ -413,7 +600,7 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 [指定多个,必须全部验证通过]
|
||||
* 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
|
||||
* @param permissionArray 权限码数组
|
||||
*/
|
||||
public static void checkPermissionAnd(String... permissionArray) {
|
||||
@@ -421,7 +608,7 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
* 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
* @param permissionArray 权限码数组
|
||||
*/
|
||||
public static void checkPermissionOr(String... permissionArray) {
|
||||
@@ -637,6 +824,7 @@ public class StpUtil {
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 获取当前StpLogin的loginKey
|
||||
* @return 当前StpLogin的loginKey
|
||||
*/
|
||||
@@ -647,6 +835,7 @@ public class StpUtil {
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
*/
|
||||
@@ -657,6 +846,7 @@ public class StpUtil {
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param device 设备标识
|
||||
@@ -668,6 +858,7 @@ public class StpUtil {
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
@@ -679,6 +870,7 @@ public class StpUtil {
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定所有登录参数Model
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param loginModel 此次登录的参数Model
|
||||
@@ -688,4 +880,29 @@ public class StpUtil {
|
||||
stpLogic.login(loginId, loginModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 会话注销,根据账号id (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
@Deprecated
|
||||
public static void logoutByLoginId(Object loginId) {
|
||||
stpLogic.kickout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 会话注销,根据账号id and 设备标识 (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void logoutByLoginId(Object loginId, String device) {
|
||||
stpLogic.kickout(loginId, device);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
package cn.dev33.satoken.strategy;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* Sa-Token 策略对象
|
||||
* <p>
|
||||
* 此类统一定义框架内的一些关键性逻辑算法,方便开发者进行按需重写,例:
|
||||
* </p>
|
||||
* <pre>
|
||||
// SaStrategy全局单例,所有方法都用以下形式重写
|
||||
SaStrategy.me.setCreateToken((loginId, loginType) -》 {
|
||||
// 自定义Token生成的算法
|
||||
return "xxxx";
|
||||
});
|
||||
* </pre>
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public final class SaStrategy {
|
||||
|
||||
private SaStrategy() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 SaStrategy 对象的单例引用
|
||||
*/
|
||||
public static final SaStrategy me = new SaStrategy();
|
||||
|
||||
//
|
||||
// 所有策略
|
||||
//
|
||||
|
||||
/**
|
||||
* 创建 Token 的策略
|
||||
* <p> 参数 [账号id, 账号类型]
|
||||
*/
|
||||
public BiFunction<Object, String, String> createToken = (loginId, loginType) -> {
|
||||
return SaManager.getSaTokenAction().createToken(loginId, loginType);
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建 Session 的策略
|
||||
* <p> 参数 [SessionId]
|
||||
*/
|
||||
public Function<String, SaSession> createSession = (sessionId) -> {
|
||||
return SaManager.getSaTokenAction().createSession(sessionId);
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断:集合中是否包含指定元素(模糊匹配)
|
||||
* <p> 参数 [集合, 元素]
|
||||
*/
|
||||
public BiFunction<List<String>, String, Boolean> hasElement = (list, element) -> {
|
||||
return SaManager.getSaTokenAction().hasElement(list, element);
|
||||
};
|
||||
|
||||
/**
|
||||
* 对一个 [Method] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数 [Method句柄]
|
||||
*/
|
||||
public Consumer<Method> checkMethodAnnotation = (method) -> {
|
||||
|
||||
// 先校验 Method 所属 Class 上的注解
|
||||
me.checkElementAnnotation.accept(method.getDeclaringClass());
|
||||
|
||||
// 再校验 Method 上的注解
|
||||
me.checkElementAnnotation.accept(method);
|
||||
};
|
||||
|
||||
/**
|
||||
* 对一个 [元素] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数 [element元素]
|
||||
*/
|
||||
public Consumer<AnnotatedElement> checkElementAnnotation = (element) -> {
|
||||
// 为了兼容旧版本
|
||||
SaManager.getSaTokenAction().validateAnnotation(element);
|
||||
};
|
||||
|
||||
/**
|
||||
* 从元素上获取注解(注解鉴权内部实现)
|
||||
* <p> 参数 [element元素,要获取的注解类型]
|
||||
*/
|
||||
public BiFunction<AnnotatedElement, Class<? extends Annotation> , Annotation> getAnnotation = (element, annotationClass)->{
|
||||
// 默认使用jdk的注解处理器
|
||||
return element.getAnnotation(annotationClass);
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// 重写策略 set连缀风格
|
||||
//
|
||||
|
||||
/**
|
||||
* 重写创建 Token 的策略
|
||||
* <p> 参数 [账号id, 账号类型]
|
||||
* @param createToken /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaStrategy setCreateToken(BiFunction<Object, String, String> createToken) {
|
||||
this.createToken = createToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写创建 Session 的策略
|
||||
* <p> 参数 [SessionId]
|
||||
* @param createSession /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaStrategy setCreateSession(Function<String, SaSession> createSession) {
|
||||
this.createSession = createSession;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:集合中是否包含指定元素(模糊匹配)
|
||||
* <p> 参数 [集合, 元素]
|
||||
* @param hasElement /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaStrategy setHasElement(BiFunction<List<String>, String, Boolean> hasElement) {
|
||||
this.hasElement = hasElement;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对一个 [Method] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数 [Method句柄]
|
||||
* @param checkMethodAnnotation /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaStrategy setCheckMethodAnnotation(Consumer<Method> checkMethodAnnotation) {
|
||||
this.checkMethodAnnotation = checkMethodAnnotation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对一个 [元素] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数 [element元素]
|
||||
* @param checkElementAnnotation /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaStrategy setCheckElementAnnotation(Consumer<AnnotatedElement> checkElementAnnotation) {
|
||||
this.checkElementAnnotation = checkElementAnnotation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从元素上获取注解(注解鉴权内部实现)
|
||||
* <p> 参数 [element元素,要获取的注解类型]
|
||||
* @param getAnnotation /
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaStrategy setGetAnnotation(BiFunction<AnnotatedElement, Class<? extends Annotation> , Annotation> getAnnotation) {
|
||||
this.getAnnotation = getAnnotation;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.dev33.satoken.temp;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
/**
|
||||
@@ -19,7 +20,7 @@ public interface SaTempInterface {
|
||||
public default String createToken(Object value, long timeout) {
|
||||
|
||||
// 生成 token
|
||||
String token = SaManager.getSaTokenAction().createToken(null, null);
|
||||
String token = SaStrategy.me.createToken.apply(null, null);
|
||||
|
||||
// 持久化映射关系
|
||||
String key = splicingKeyTempToken(token);
|
||||
@@ -60,6 +61,15 @@ public interface SaTempInterface {
|
||||
String key = splicingKeyTempToken(token);
|
||||
return SaManager.getSaTokenDao().getObjectTimeout(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个 token
|
||||
* @param token 指定token
|
||||
*/
|
||||
public default void deleteToken(String token) {
|
||||
String key = splicingKeyTempToken(token);
|
||||
SaManager.getSaTokenDao().deleteObject(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取映射关系的持久化key
|
||||
|
||||
@@ -48,5 +48,13 @@ public class SaTempUtil {
|
||||
public static long getTimeout(String token) {
|
||||
return SaManager.getSaTemp().getTimeout(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个 token
|
||||
* @param token 指定token
|
||||
*/
|
||||
public static void deleteToken(String token) {
|
||||
SaManager.getSaTemp().deleteToken(token);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -404,4 +404,51 @@ public class SaFoxUtil {
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* String 转 Array,按照逗号切割
|
||||
* @param str 字符串
|
||||
* @return 数组
|
||||
*/
|
||||
public static String[] convertStringToArray(String str) {
|
||||
List<String> list = convertStringToList(str);
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Array 转 String,按照逗号切割
|
||||
* @param arr 数组
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String convertArrayToString(String[] arr) {
|
||||
if(arr == null || arr.length == 0) {
|
||||
return "";
|
||||
}
|
||||
return String.join(",", arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个空集合
|
||||
* @param <T> 集合类型
|
||||
* @return 空集合
|
||||
*/
|
||||
public static <T>List<T> emptyList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* String数组转集合
|
||||
* @param strs String数组
|
||||
* @return 集合
|
||||
*/
|
||||
public static List<String> toList(String... strs) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (String str : strs) {
|
||||
list.add(str);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.util.Map;
|
||||
* code=状态码 <br>
|
||||
* msg=描述信息 <br>
|
||||
* data=携带对象 <br>
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ public class SaTokenConsts {
|
||||
/**
|
||||
* Sa-Token 当前版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.26.0";
|
||||
public static final String VERSION_NO = "v1.29.0";
|
||||
|
||||
/**
|
||||
* Sa-Token 开源地址
|
||||
@@ -30,7 +30,12 @@ public class SaTokenConsts {
|
||||
/**
|
||||
* 常量key标记: 如果token为本次请求新创建的,则以此字符串为key存储在当前request中
|
||||
*/
|
||||
public static final String JUST_CREATED_SAVE_KEY = "JUST_CREATED_SAVE_KEY_";
|
||||
public static final String JUST_CREATED = "JUST_CREATED_";
|
||||
|
||||
/**
|
||||
* 常量key标记: 如果token为本次请求新创建的,则以此字符串为key存储在当前request中(不拼接前缀,纯Token)
|
||||
*/
|
||||
public static final String JUST_CREATED_NOT_PREFIX = "JUST_CREATED_NOT_PREFIX_";
|
||||
|
||||
/**
|
||||
* 常量key标记: 如果本次请求已经验证过[无操作过期], 则以此值存储在当前request中
|
||||
@@ -97,5 +102,14 @@ public class SaTokenConsts {
|
||||
* 切面、拦截器、过滤器等各种组件的注册优先级顺序
|
||||
*/
|
||||
public static final int ASSEMBLY_ORDER = -100;
|
||||
|
||||
|
||||
// =================== 废弃 ===================
|
||||
|
||||
/**
|
||||
* 请更换为 JUST_CREATED
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String JUST_CREATED_SAVE_KEY = JUST_CREATED;
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -15,11 +15,11 @@ sa-token:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 2
|
||||
# Redis服务器地址
|
||||
host: 49.235.117.153
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password: Kdfsjia.d.1212dsa
|
||||
# password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
target/
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
|
||||
/.idea/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
/.apt_generated/
|
||||
/.apt_generated_tests/
|
||||
@@ -0,0 +1,76 @@
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.pj</groupId>
|
||||
<artifactId>sa-token-demo-dubbo-consumer</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<!-- 指定一些属性 -->
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot Web模块 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token整合 Redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Dubbo -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
<version>2.7.11</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Dubbo 注册到 Nacos -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-nacos</artifactId>
|
||||
<version>2.7.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>1.4.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Dubbo -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-context-dubbo</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.pj;
|
||||
|
||||
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Dubbo 服务消费端
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@EnableDubbo
|
||||
@SpringBootApplication
|
||||
public class ConsumerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConsumerApplication.class, args);
|
||||
System.out.println("ConsumerApplication 启动成功");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.pj.more;
|
||||
|
||||
public interface DemoService {
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
void doLogin(Object loginId);
|
||||
|
||||
/**
|
||||
* 判断是否登录,打印状态
|
||||
*/
|
||||
void isLogin(String str);
|
||||
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.pj.more;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@RestController
|
||||
public class TestController {
|
||||
|
||||
@DubboReference
|
||||
private DemoService demoService;
|
||||
|
||||
// Consumer端登录,状态传播到Provider端
|
||||
@RequestMapping("test")
|
||||
public String test() {
|
||||
demoService.isLogin("----------- 登录前 ");
|
||||
|
||||
StpUtil.login(10001);
|
||||
|
||||
demoService.isLogin("----------- 登录后 ");
|
||||
|
||||
return "ok";
|
||||
}
|
||||
|
||||
// Provider端登录,状态回传到Consumer端
|
||||
@RequestMapping("test2")
|
||||
public String test2() {
|
||||
System.out.println("----------- 登录前 ");
|
||||
System.out.println("Token值:" + StpUtil.getTokenValue());
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
|
||||
demoService.doLogin(10002);
|
||||
|
||||
System.out.println("----------- 登录后 ");
|
||||
System.out.println("Token值:" + StpUtil.getTokenValue());
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
|
||||
return "ok";
|
||||
}
|
||||
|
||||
// Consumer端登录,状态在Consumer端保持
|
||||
@RequestMapping("test3")
|
||||
public String test3() {
|
||||
System.out.println("----------- 登录前 ");
|
||||
System.out.println("Token值:" + StpUtil.getTokenValue());
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
|
||||
StpUtil.login(10003);
|
||||
demoService.isLogin("----------- Provider状态");
|
||||
|
||||
System.out.println("----------- 登录后 ");
|
||||
System.out.println("Token值:" + StpUtil.getTokenValue());
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
|
||||
return "ok";
|
||||
}
|
||||
|
||||
// Provider端登录,状态在Provider端保持
|
||||
@RequestMapping("test4")
|
||||
public String test4() {
|
||||
// 登录
|
||||
demoService.doLogin(10004);
|
||||
|
||||
// 打印一下
|
||||
demoService.isLogin("----------- 会话信息 ");
|
||||
|
||||
return "ok";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
server:
|
||||
# 端口号
|
||||
port: 8081
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间
|
||||
|
||||
dubbo:
|
||||
application:
|
||||
# 服务名称
|
||||
name: dubbo-consumer-demo
|
||||
registry:
|
||||
# 注册中心地址
|
||||
address: nacos://127.0.0.1:8001
|
||||
@@ -0,0 +1,13 @@
|
||||
target/
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
|
||||
/.idea/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
/.apt_generated/
|
||||
/.apt_generated_tests/
|
||||
@@ -0,0 +1,76 @@
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.pj</groupId>
|
||||
<artifactId>sa-token-demo-dubbo-provider</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<!-- 指定一些属性 -->
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot Web模块 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token整合 Redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Dubbo -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
<version>2.7.11</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Dubbo 注册到 Nacos -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-nacos</artifactId>
|
||||
<version>2.7.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>1.4.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Dubbo -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-context-dubbo</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.pj;
|
||||
|
||||
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Dubbo 服务提供端
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@EnableDubbo
|
||||
@SpringBootApplication
|
||||
public class ProviderApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ProviderApplication.class, args);
|
||||
System.out.println("ProviderApplication 启动成功");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.pj.more;
|
||||
|
||||
public interface DemoService {
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
void doLogin(Object loginId);
|
||||
|
||||
/**
|
||||
* 判断是否登录,打印状态
|
||||
*/
|
||||
void isLogin(String str);
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.pj.more;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboService;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@DubboService()
|
||||
public class DemoServiceImpl implements DemoService {
|
||||
|
||||
@Override
|
||||
public void doLogin(Object loginId) {
|
||||
StpUtil.login(loginId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isLogin(String str) {
|
||||
System.out.println(str);
|
||||
System.out.println("Token值:" + StpUtil.getTokenValue());
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
server:
|
||||
# 端口号
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
|
||||
# Dubbo
|
||||
dubbo:
|
||||
# 服务名
|
||||
application:
|
||||
name: dubbo-provider-demo
|
||||
# 扫描包
|
||||
scan:
|
||||
base-packages: com.pj
|
||||
# 注册中心地址
|
||||
registry:
|
||||
address: nacos://127.0.0.1:8001
|
||||
# 协议
|
||||
protocol:
|
||||
name: dubbo
|
||||
port: 12345
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -26,52 +26,32 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- jwt -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token整合redis (使用jdk默认序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<!-- Sa-Token 整合 jwt -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<artifactId>sa-token-jwt</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合redis (使用jackson序列化方式) -->
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- 提供redis连接池 -->
|
||||
<!-- <dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合SpringAOP实现注解鉴权 -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-aop</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -79,12 +59,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- test -->
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<!-- <version>2.3.1</version> -->
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ public class SaTokenJwtDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenJwtDemoApplication.class, args);
|
||||
System.out.println("\n启动成功:sa-token配置如下:" + SaManager.getConfig());
|
||||
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
|
||||
import cn.dev33.satoken.jwt.StpLogicJwtForStyle;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
|
||||
|
||||
/**
|
||||
* [Sa-Token 权限认证] 配置类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 注册Sa-Token 的拦截器,打开注解式鉴权功能
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册注解拦截器
|
||||
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sa-Token 整合 jwt
|
||||
*/
|
||||
@Bean
|
||||
public StpLogic getStpLogicJwt() {
|
||||
return new StpLogicJwtForStyle();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
package com.pj.satoken.jwt;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
|
||||
@Component
|
||||
public class SaTokenJwtUtil {
|
||||
|
||||
/**
|
||||
* 秘钥 (随便手打几个字母就好了)
|
||||
*/
|
||||
public static final String BASE64_SECURITY = "79e7c69681b8270162386e6daa53d1dd";
|
||||
|
||||
/**
|
||||
* token有效期 (单位: 秒)
|
||||
*/
|
||||
public static final long TIMEOUT = 60 * 60 * 2;
|
||||
|
||||
|
||||
public static final String LOGIN_ID_KEY = "loginId";
|
||||
|
||||
|
||||
/**
|
||||
* 根据userId生成token
|
||||
* @param loginId 账号id
|
||||
* @param base64Security 秘钥
|
||||
* @return jwt-token
|
||||
*/
|
||||
public static String createToken(Object loginId) {
|
||||
// 判断,不可使用默认秘钥
|
||||
// if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1dd")) {
|
||||
// throw new SaTokenException("请更换秘钥");
|
||||
// }
|
||||
// 在这里你可以使用官方提供的claim方法构建载荷,也可以使用setPayload自定义载荷,但是两者不可一起使用
|
||||
JwtBuilder builder = Jwts.builder()
|
||||
.setHeaderParam("type", "JWT")
|
||||
.claim(LOGIN_ID_KEY, loginId)
|
||||
.setIssuedAt(new Date()) // 签发日期
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 1000 * TIMEOUT)) // 有效截止日期
|
||||
.signWith(SignatureAlgorithm.HS256, BASE64_SECURITY.getBytes()); // 加密算法
|
||||
//生成JWT
|
||||
return builder.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个jwt里面解析出Claims
|
||||
* @param tokenValue token值
|
||||
* @param base64Security 秘钥
|
||||
* @return Claims对象
|
||||
*/
|
||||
public static Claims getClaims(String tokenValue) {
|
||||
// System.out.println(tokenValue);
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(BASE64_SECURITY.getBytes())
|
||||
.parseClaimsJws(tokenValue).getBody();
|
||||
return claims;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个jwt里面解析loginId
|
||||
* @param tokenValue token值
|
||||
* @param base64Security 秘钥
|
||||
* @return loginId
|
||||
*/
|
||||
public static String getLoginId(String tokenValue) {
|
||||
try {
|
||||
Object loginId = getClaims(tokenValue).get(LOGIN_ID_KEY);
|
||||
if(loginId == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(loginId);
|
||||
} catch (ExpiredJwtException e) {
|
||||
// throw NotLoginException.newInstance(StpUtil.TYPE, NotLoginException.TOKEN_TIMEOUT);
|
||||
return NotLoginException.TOKEN_TIMEOUT;
|
||||
} catch (MalformedJwtException e) {
|
||||
throw NotLoginException.newInstance(StpUtil.stpLogic.loginType, NotLoginException.INVALID_TOKEN);
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static {
|
||||
|
||||
// 判断秘钥
|
||||
if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1dd")) {
|
||||
String warn = "-------------------------------------\n";
|
||||
warn += "请更换JWT秘钥,不要使用示例默认秘钥\n";
|
||||
warn += "-------------------------------------";
|
||||
System.err.println(warn);
|
||||
}
|
||||
|
||||
// 提前调用一下方法,促使其属性初始化
|
||||
StpUtil.getLoginType();
|
||||
|
||||
// 修改默认实现
|
||||
StpUtil.stpLogic = new StpLogic("login") {
|
||||
|
||||
// 重写 (随机生成一个tokenValue)
|
||||
@Override
|
||||
public String createTokenValue(Object loginId) {
|
||||
return SaTokenJwtUtil.createToken(loginId);
|
||||
}
|
||||
|
||||
// 重写 (在当前会话上登录id )
|
||||
@Override
|
||||
public void login(Object loginId, SaLoginModel loginModel) {
|
||||
// ------ 1、获取相应对象
|
||||
SaStorage storage = SaManager.getSaTokenContext().getStorage();
|
||||
SaTokenConfig config = getConfig();
|
||||
// ------ 2、生成一个token
|
||||
String tokenValue = createTokenValue(loginId);
|
||||
storage.set(splicingKeyJustCreatedSave(), tokenValue); // 将token保存到本次request里
|
||||
if(config.getIsReadCookie() == true){ // cookie注入
|
||||
SaManager.getSaTokenContext().getResponse().addCookie(getTokenName(), tokenValue, "/", config.getCookieDomain(), (int)config.getTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
// 重写 (获取指定token对应的登录id)
|
||||
@Override
|
||||
public String getLoginIdNotHandle(String tokenValue) {
|
||||
try {
|
||||
return SaTokenJwtUtil.getLoginId(tokenValue);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 重写 (当前会话注销登录)
|
||||
@Override
|
||||
public void logout() {
|
||||
// 如果连token都没有,那么无需执行任何操作
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return;
|
||||
}
|
||||
// 如果打开了cookie模式,把cookie清除掉
|
||||
if(getConfig().getIsReadCookie() == true){
|
||||
SaManager.getSaTokenContext().getResponse().deleteCookie(getTokenName());
|
||||
}
|
||||
}
|
||||
|
||||
// 重写 (获取指定key的session)
|
||||
@Override
|
||||
public SaSession getSessionBySessionId(String sessionId, boolean isCreate) {
|
||||
throw new SaTokenException("jwt has not session");
|
||||
}
|
||||
|
||||
// 重写 (获取当前登录者的token剩余有效时间 (单位: 秒))
|
||||
@Override
|
||||
public long getTokenTimeout() {
|
||||
// 如果没有token
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
// 开始取值
|
||||
Claims claims = null;
|
||||
try {
|
||||
claims = SaTokenJwtUtil.getClaims(tokenValue);
|
||||
} catch (Exception e) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
if(claims == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
Date expiration = claims.getExpiration();
|
||||
if(expiration == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
return (expiration.getTime() - System.currentTimeMillis()) / 1000;
|
||||
}
|
||||
|
||||
// 重写 (返回当前token的登录设备)
|
||||
@Override
|
||||
public String getLoginDevice() {
|
||||
return SaTokenConsts.DEFAULT_LOGIN_DEVICE;
|
||||
}
|
||||
|
||||
// 重写 (获取当前会话的token信息)
|
||||
@Override
|
||||
public SaTokenInfo getTokenInfo() {
|
||||
SaTokenInfo info = new SaTokenInfo();
|
||||
info.tokenName = getTokenName();
|
||||
info.tokenValue = getTokenValue();
|
||||
info.isLogin = isLogin();
|
||||
info.loginId = getLoginIdDefaultNull();
|
||||
info.loginType = getLoginType();
|
||||
info.tokenTimeout = getTokenTimeout();
|
||||
// info.sessionTimeout = getSessionTimeout();
|
||||
// info.tokenSessionTimeout = getTokenSessionTimeout();
|
||||
// info.tokenActivityTimeout = getTokenActivityTimeout();
|
||||
info.loginDevice = getLoginDevice();
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
@@ -21,15 +18,6 @@ import cn.dev33.satoken.exception.NotRoleException;
|
||||
@RestControllerAdvice // 可指定包前缀,比如:(basePackages = "com.pj.admin")
|
||||
public class GlobalException {
|
||||
|
||||
// 在每个控制器之前触发的操作
|
||||
@ModelAttribute
|
||||
public void get(HttpServletRequest request) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
|
||||
@@ -55,46 +43,5 @@ public class GlobalException {
|
||||
|
||||
// 返回给前端
|
||||
return aj;
|
||||
|
||||
// 输出到客户端
|
||||
// response.setContentType("application/json; charset=utf-8"); // http说明,我要返回JSON对象
|
||||
// response.getWriter().print(new ObjectMapper().writeValueAsString(aj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 全局异常拦截(拦截项目中的NotLoginException异常)
|
||||
// @ExceptionHandler(NotLoginException.class)
|
||||
// public AjaxJson handlerNotLoginException(NotLoginException nle, HttpServletRequest request, HttpServletResponse response)
|
||||
// throws Exception {
|
||||
//
|
||||
// // 打印堆栈,以供调试
|
||||
// nle.printStackTrace();
|
||||
//
|
||||
// // 判断场景值,定制化异常信息
|
||||
// String message = "";
|
||||
// if(nle.getType().equals(NotLoginException.NOT_TOKEN)) {
|
||||
// message = "未提供token";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
|
||||
// message = "token无效";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
|
||||
// message = "token已过期";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.BE_REPLACED)) {
|
||||
// message = "token已被顶下线";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.KICK_OUT)) {
|
||||
// message = "token已被踢下线";
|
||||
// }
|
||||
// else {
|
||||
// message = "当前会话未登录";
|
||||
// }
|
||||
//
|
||||
// // 返回给前端
|
||||
// return AjaxJson.getError(message);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@@ -22,8 +23,6 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
@RequestMapping("/test/")
|
||||
public class TestJwtController {
|
||||
|
||||
|
||||
|
||||
// 测试登录接口, 浏览器访问: http://localhost:8081/test/login
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login(@RequestParam(defaultValue="10001") String id) {
|
||||
@@ -51,7 +50,7 @@ public class TestJwtController {
|
||||
System.out.println(tokenInfo);
|
||||
return AjaxJson.getSuccessData(tokenInfo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 测试会话session接口, 浏览器访问: http://localhost:8081/test/session
|
||||
@RequestMapping("session")
|
||||
@@ -70,11 +69,12 @@ public class TestJwtController {
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
@SaCheckLogin
|
||||
public AjaxJson test() {
|
||||
System.out.println();
|
||||
System.out.println("--------------进入请求--------------");
|
||||
StpUtil.login(10001);
|
||||
System.out.println(StpUtil.getTokenInfo().getTokenValue());
|
||||
System.out.println(StpUtil.getExtra("username"));
|
||||
System.out.println(StpUtil.getExtra("nick"));
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ sa-token:
|
||||
is-share: true
|
||||
# token风格
|
||||
token-style: uuid
|
||||
# jwt秘钥
|
||||
jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
+2
-1
@@ -21,7 +21,8 @@ public class SaOAuth2TemplateImpl extends SaOAuth2Template {
|
||||
.setClientId("10001")
|
||||
.setClientSecret("aaaa-bbbb-cccc-dddd-eeee")
|
||||
.setAllowUrl("*")
|
||||
.setContractScope("userinfo");
|
||||
.setContractScope("userinfo")
|
||||
.setIsAutoMode(true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-web</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<version>1.6.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
|
||||
@@ -185,7 +185,7 @@ public class TestController {
|
||||
// 先登录上
|
||||
StpUtil.login(10001);
|
||||
// 踢下线
|
||||
StpUtil.logoutByLoginId(10001);
|
||||
StpUtil.kickout(10001);
|
||||
// 再尝试获取
|
||||
StpUtil.getLoginId();
|
||||
// 返回
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.26.0</sa-token-version>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import cn.dev33.satoken.SaManager;
|
||||
public class SaTokenDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenDemoApplication.class, args);
|
||||
SpringApplication.run(SaTokenDemoApplication.class, args);
|
||||
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
|
||||
}
|
||||
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.pj.current;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.DisableLoginException;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class GlobalException {
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@ResponseBody
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
System.out.println("全局异常---------------");
|
||||
e.printStackTrace();
|
||||
|
||||
// 不同异常返回不同状态码
|
||||
AjaxJson aj = null;
|
||||
if (e instanceof NotLoginException) { // 如果是未登录异常
|
||||
NotLoginException ee = (NotLoginException) e;
|
||||
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
|
||||
}
|
||||
else if(e instanceof NotRoleException) { // 如果是角色异常
|
||||
NotRoleException ee = (NotRoleException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||
}
|
||||
else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
}
|
||||
else if(e instanceof DisableLoginException) { // 如果是被封禁异常
|
||||
DisableLoginException ee = (DisableLoginException) e;
|
||||
aj = AjaxJson.getNotJur("账号被封禁:" + ee.getDisableTime() + "秒后解封");
|
||||
}
|
||||
else { // 普通异常, 输出:500 + 异常信息
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回给前端
|
||||
return aj;
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.pj.current;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 处理 404
|
||||
* @author kong
|
||||
*/
|
||||
@RestController
|
||||
public class NotFoundHandle implements ErrorController {
|
||||
|
||||
@Override
|
||||
public String getErrorPath() {
|
||||
return "/error";
|
||||
}
|
||||
|
||||
@RequestMapping("/error")
|
||||
public Object error(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
response.setStatus(200);
|
||||
return SaResult.get(404, "not found", null);
|
||||
}
|
||||
|
||||
}
|
||||
+21
-9
@@ -1,7 +1,9 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@@ -10,6 +12,7 @@ import com.pj.util.AjaxJson;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.filter.SaServletFilter;
|
||||
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,18 +22,18 @@ import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
|
||||
*/
|
||||
@Configuration
|
||||
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
|
||||
|
||||
/**
|
||||
* 注册sa-token的拦截器,打开注解式鉴权功能
|
||||
* 注册Sa-Token 的拦截器,打开注解式鉴权功能
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册注解拦截器
|
||||
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**").excludePathPatterns("");
|
||||
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 [sa-token全局过滤器]
|
||||
* 注册 [Sa-Token 全局过滤器]
|
||||
*/
|
||||
@Bean
|
||||
public SaServletFilter getSaServletFilter() {
|
||||
@@ -40,10 +43,9 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
.addInclude("/**")// .addExclude("/favicon.ico")
|
||||
|
||||
// 认证函数: 每次请求执行
|
||||
.setAuth(r -> {
|
||||
// System.out.println("---------- sa全局认证");
|
||||
|
||||
// SaRouter.match("/test/test", () -> new Object());
|
||||
.setAuth(obj -> {
|
||||
// System.out.println("---------- sa全局认证 " + SaHolder.getRequest().getRequestPath());
|
||||
|
||||
})
|
||||
|
||||
// 异常处理函数:每次认证函数发生异常时执行此函数
|
||||
@@ -69,5 +71,15 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 重写 Sa-Token 框架内部算法策略
|
||||
*/
|
||||
@Autowired
|
||||
public void rewriteSaStrategy() {
|
||||
// 重写Sa-Token的注解处理器,增加注解合并功能
|
||||
SaStrategy.me.getAnnotation = (element, annotationClass) -> {
|
||||
return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package com.pj.satoken.at;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaCheckSafe;
|
||||
|
||||
/**
|
||||
* 继承Sa-Token行为Bean默认实现, 重写部分逻辑
|
||||
*/
|
||||
//@Component
|
||||
public class MySaTokenAction extends SaTokenActionDefaultImpl {
|
||||
|
||||
/**
|
||||
* 重写Sa-Token的注解处理器,加强注解合并功能
|
||||
* @param target see note
|
||||
*/
|
||||
@Override
|
||||
protected void validateAnnotation(AnnotatedElement target) {
|
||||
|
||||
// 校验 @SaCheckLogin 注解
|
||||
if(AnnotatedElementUtils.isAnnotated(target, SaCheckLogin.class)) {
|
||||
SaCheckLogin at = AnnotatedElementUtils.getMergedAnnotation(target, SaCheckLogin.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckRole 注解
|
||||
if(AnnotatedElementUtils.isAnnotated(target, SaCheckRole.class)) {
|
||||
SaCheckRole at = AnnotatedElementUtils.getMergedAnnotation(target, SaCheckRole.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckPermission 注解
|
||||
if(AnnotatedElementUtils.isAnnotated(target, SaCheckPermission.class)) {
|
||||
SaCheckPermission at = AnnotatedElementUtils.getMergedAnnotation(target, SaCheckPermission.class);
|
||||
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckSafe 注解
|
||||
if(AnnotatedElementUtils.isAnnotated(target, SaCheckSafe.class)) {
|
||||
SaCheckSafe at = AnnotatedElementUtils.getMergedAnnotation(target, SaCheckSafe.class);
|
||||
SaManager.getStpLogic(null).checkByAnnotation(at);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+239
-48
@@ -2,19 +2,18 @@ package com.pj.satoken.at;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.fun.SaFunction;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* Sa-Token 权限验证工具类 (User版)
|
||||
* Sa-Token 权限认证工具类 (User版)
|
||||
* @author kong
|
||||
*/
|
||||
@Component
|
||||
public class StpUserUtil {
|
||||
|
||||
/**
|
||||
@@ -35,6 +34,16 @@ public class StpUserUtil {
|
||||
return stpLogic.getLoginType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置 StpLogic 对象
|
||||
* @param stpLogic /
|
||||
*/
|
||||
public static void setStpLogic(StpLogic stpLogic) {
|
||||
StpUtil.stpLogic = stpLogic;
|
||||
// 防止自定义 stpLogic 被覆盖
|
||||
SaManager.putStpLogic(stpLogic);
|
||||
}
|
||||
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
@@ -46,6 +55,14 @@ public class StpUserUtil {
|
||||
return stpLogic.getTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话写入当前TokenValue
|
||||
* @param tokenValue token值
|
||||
*/
|
||||
public static void setTokenValue(String tokenValue){
|
||||
stpLogic.setTokenValue(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话写入当前TokenValue
|
||||
* @param tokenValue token值
|
||||
@@ -63,6 +80,14 @@ public class StpUserUtil {
|
||||
return stpLogic.getTokenValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前TokenValue (不裁剪前缀)
|
||||
* @return /
|
||||
*/
|
||||
public static String getTokenValueNotCut(){
|
||||
return stpLogic.getTokenValueNotCut();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的Token信息
|
||||
* @return token信息
|
||||
@@ -74,6 +99,8 @@ public class StpUserUtil {
|
||||
|
||||
// =================== 登录相关操作 ===================
|
||||
|
||||
// --- 登录
|
||||
|
||||
/**
|
||||
* 会话登录
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
@@ -108,16 +135,56 @@ public class StpUserUtil {
|
||||
public static void login(Object id, SaLoginModel loginModel) {
|
||||
stpLogic.login(id, loginModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定账号id的登录会话
|
||||
* @param id 登录id,建议的类型:(long | int | String)
|
||||
* @return 返回会话令牌
|
||||
*/
|
||||
public static String createLoginSession(Object id) {
|
||||
return stpLogic.createLoginSession(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定账号id的登录会话
|
||||
* @param id 登录id,建议的类型:(long | int | String)
|
||||
* @param loginModel 此次登录的参数Model
|
||||
* @return 返回会话令牌
|
||||
*/
|
||||
public static String createLoginSession(Object id, SaLoginModel loginModel) {
|
||||
return stpLogic.createLoginSession(id, loginModel);
|
||||
}
|
||||
|
||||
// --- 注销
|
||||
|
||||
/**
|
||||
* 会话注销
|
||||
* 会话注销
|
||||
*/
|
||||
public static void logout() {
|
||||
stpLogic.logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据指定Token
|
||||
* 会话注销,根据账号id
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void logout(Object loginId) {
|
||||
stpLogic.logout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id 和 设备标识
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
*/
|
||||
public static void logout(Object loginId, String device) {
|
||||
stpLogic.logout(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据指定 Token
|
||||
*
|
||||
* @param tokenValue 指定token
|
||||
*/
|
||||
public static void logoutByTokenValue(String tokenValue) {
|
||||
@@ -125,24 +192,48 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
|
||||
* 踢人下线,根据账号id
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void logoutByLoginId(Object loginId) {
|
||||
stpLogic.logoutByLoginId(loginId);
|
||||
public static void kickout(Object loginId) {
|
||||
stpLogic.kickout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人下线,根据账号id 和 设备标识
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表踢出所有设备)
|
||||
*/
|
||||
public static void kickout(Object loginId, String device) {
|
||||
stpLogic.kickout(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id & 设备标识 (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* 踢人下线,根据指定 Token
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param tokenValue 指定token
|
||||
*/
|
||||
public static void logoutByLoginId(Object loginId, String device) {
|
||||
stpLogic.logoutByLoginId(loginId, device);
|
||||
public static void kickoutByTokenValue(String tokenValue) {
|
||||
stpLogic.kickoutByTokenValue(tokenValue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 顶人下线,根据账号id 和 设备标识
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表顶替所有设备)
|
||||
*/
|
||||
public static void replaced(Object loginId, String device) {
|
||||
stpLogic.replaced(loginId, device);
|
||||
}
|
||||
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
@@ -220,7 +311,7 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
|
||||
// =================== session相关 ===================
|
||||
// =================== User-Session 相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回
|
||||
@@ -268,7 +359,7 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
|
||||
// =================== token专属session ===================
|
||||
// =================== Token-Session 相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定Token-Session,如果Session尚未创建,则新建并返回
|
||||
@@ -288,7 +379,7 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
|
||||
// =================== [临时过期] 验证相关 ===================
|
||||
// =================== [临时有效期] 验证相关 ===================
|
||||
|
||||
/**
|
||||
* 检查当前token 是否已经[临时过期],如果已经过期则抛出异常
|
||||
@@ -310,7 +401,7 @@ public class StpUserUtil {
|
||||
// =================== 过期时间相关 ===================
|
||||
|
||||
/**
|
||||
* 获取当前登录者的token剩余有效时间 (单位: 秒)
|
||||
* 获取当前登录者的 token 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getTokenTimeout() {
|
||||
@@ -318,7 +409,7 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录者的Session剩余有效时间 (单位: 秒)
|
||||
* 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getSessionTimeout() {
|
||||
@@ -326,7 +417,7 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前token的专属Session剩余有效时间 (单位: 秒)
|
||||
* 获取当前 Token-Session 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getTokenSessionTimeout() {
|
||||
@@ -334,8 +425,8 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前token[临时过期]剩余有效时间 (单位: 秒)
|
||||
* @return token[临时过期]剩余有效时间
|
||||
* 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
|
||||
* @return token [临时过期] 剩余有效时间
|
||||
*/
|
||||
public static long getTokenActivityTimeout() {
|
||||
return stpLogic.getTokenActivityTimeout();
|
||||
@@ -345,8 +436,34 @@ public class StpUserUtil {
|
||||
|
||||
// =================== 角色验证操作 ===================
|
||||
|
||||
/**
|
||||
* 获取:当前账号的角色集合
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getRoleList() {
|
||||
return stpLogic.getRoleList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:指定账号的角色集合
|
||||
* @param loginId 指定账号id
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getRoleList(Object loginId) {
|
||||
return stpLogic.getRoleList(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定账号id是否含有角色标识, 返回true或false
|
||||
* 判断:当前账号是否拥有指定角色, 返回true或false
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
*/
|
||||
public static boolean hasRole(String role) {
|
||||
return stpLogic.hasRole(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:指定账号是否含有指定角色标识, 返回true或false
|
||||
* @param loginId 账号id
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
@@ -356,16 +473,25 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定角色标识, 返回true或false
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
* 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
|
||||
* @param roleArray 角色标识数组
|
||||
* @return true或false
|
||||
*/
|
||||
public static boolean hasRole(String role) {
|
||||
return stpLogic.hasRole(role);
|
||||
public static boolean hasRoleAnd(String... roleArray){
|
||||
return stpLogic.hasRoleAnd(roleArray);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
|
||||
* 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
|
||||
* @param roleArray 角色标识数组
|
||||
* @return true或false
|
||||
*/
|
||||
public static boolean hasRoleOr(String... roleArray){
|
||||
return stpLogic.hasRoleOr(roleArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
|
||||
* @param role 角色标识
|
||||
*/
|
||||
public static void checkRole(String role) {
|
||||
@@ -373,7 +499,7 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
|
||||
* 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
|
||||
* @param roleArray 角色标识数组
|
||||
*/
|
||||
public static void checkRoleAnd(String... roleArray){
|
||||
@@ -381,18 +507,44 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
|
||||
* 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
|
||||
* @param roleArray 角色标识数组
|
||||
*/
|
||||
public static void checkRoleOr(String... roleArray){
|
||||
stpLogic.checkRoleOr(roleArray);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== 权限验证操作 ===================
|
||||
|
||||
/**
|
||||
* 获取:当前账号的权限码集合
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getPermissionList() {
|
||||
return stpLogic.getPermissionList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:指定账号的权限码集合
|
||||
* @param loginId 指定账号id
|
||||
* @return /
|
||||
*/
|
||||
public static List<String> getPermissionList(Object loginId) {
|
||||
return stpLogic.getPermissionList(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定账号id是否含有指定权限, 返回true或false
|
||||
* 判断:当前账号是否含有指定权限, 返回true或false
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(String permission) {
|
||||
return stpLogic.hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:指定账号id是否含有指定权限, 返回true或false
|
||||
* @param loginId 账号id
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
@@ -402,16 +554,25 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限, 返回true或false
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
* 判断:当前账号是否含有指定权限, [指定多个,必须全部具有]
|
||||
* @param permissionArray 权限码数组
|
||||
* @return true 或 false
|
||||
*/
|
||||
public static boolean hasPermission(String permission) {
|
||||
return stpLogic.hasPermission(permission);
|
||||
}
|
||||
public static boolean hasPermissionAnd(String... permissionArray){
|
||||
return stpLogic.hasPermissionAnd(permissionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
* 判断:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
* @param permissionArray 权限码数组
|
||||
* @return true 或 false
|
||||
*/
|
||||
public static boolean hasPermissionOr(String... permissionArray){
|
||||
return stpLogic.hasPermissionOr(permissionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
* @param permission 权限码
|
||||
*/
|
||||
public static void checkPermission(String permission) {
|
||||
@@ -419,7 +580,7 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 [指定多个,必须全部验证通过]
|
||||
* 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
|
||||
* @param permissionArray 权限码数组
|
||||
*/
|
||||
public static void checkPermissionAnd(String... permissionArray) {
|
||||
@@ -427,7 +588,7 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
* 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
* @param permissionArray 权限码数组
|
||||
*/
|
||||
public static void checkPermissionOr(String... permissionArray) {
|
||||
@@ -602,7 +763,7 @@ public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* 在当前会话 开启二级认证
|
||||
* @param timeout 维持时间 (单位: 秒)
|
||||
* @param safeTime 维持时间 (单位: 秒)
|
||||
*/
|
||||
public static void openSafe(long safeTime) {
|
||||
stpLogic.openSafe(safeTime);
|
||||
@@ -625,7 +786,7 @@ public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
|
||||
* @return
|
||||
* @return 剩余有效时间
|
||||
*/
|
||||
public static long getSafeTime() {
|
||||
return stpLogic.getSafeTime();
|
||||
@@ -643,6 +804,7 @@ public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 获取当前StpLogin的loginKey
|
||||
* @return 当前StpLogin的loginKey
|
||||
*/
|
||||
@@ -653,6 +815,7 @@ public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
*/
|
||||
@@ -663,6 +826,7 @@ public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param device 设备标识
|
||||
@@ -674,6 +838,7 @@ public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
@@ -685,6 +850,7 @@ public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定所有登录参数Model
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param loginModel 此次登录的参数Model
|
||||
@@ -694,4 +860,29 @@ public class StpUserUtil {
|
||||
stpLogic.login(loginId, loginModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 会话注销,根据账号id (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
@Deprecated
|
||||
public static void logoutByLoginId(Object loginId) {
|
||||
stpLogic.kickout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 会话注销,根据账号id and 设备标识 (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void logoutByLoginId(Object loginId, String device) {
|
||||
stpLogic.kickout(loginId, device);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckBasic;
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaCheckSafe;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 注解鉴权测试
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/at/")
|
||||
public class AtController {
|
||||
|
||||
// 登录认证,登录之后才可以进入方法 ---- http://localhost:8081/at/checkLogin
|
||||
@SaCheckLogin
|
||||
@RequestMapping("checkLogin")
|
||||
public SaResult checkLogin() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 权限认证,具备user-add权限才可以进入方法 ---- http://localhost:8081/at/checkPermission
|
||||
@SaCheckPermission("user-add")
|
||||
@RequestMapping("checkPermission")
|
||||
public SaResult checkPermission() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 权限认证,同时具备所有权限才可以进入 ---- http://localhost:8081/at/checkPermissionAnd
|
||||
@SaCheckPermission({"user-add", "user-delete", "user-update"})
|
||||
@RequestMapping("checkPermissionAnd")
|
||||
public SaResult checkPermissionAnd() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 权限认证,只要具备其中一个就可以进入 ---- http://localhost:8081/at/checkPermissionOr
|
||||
@SaCheckPermission(value = {"user-add", "user-delete", "user-update"}, mode = SaMode.OR)
|
||||
@RequestMapping("checkPermissionOr")
|
||||
public SaResult checkPermissionOr() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 角色认证,只有具备admin角色才可以进入 ---- http://localhost:8081/at/checkRole
|
||||
@SaCheckRole("admin")
|
||||
@RequestMapping("checkRole")
|
||||
public SaResult checkRole() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 完成二级认证 ---- http://localhost:8081/at/openSafe
|
||||
@RequestMapping("openSafe")
|
||||
public SaResult openSafe() {
|
||||
StpUtil.openSafe(200); // 打开二级认证,有效期为200秒
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 通过二级认证后才可以进入 ---- http://localhost:8081/at/checkSafe
|
||||
@SaCheckSafe
|
||||
@RequestMapping("checkSafe")
|
||||
public SaResult checkSafe() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 通过Basic认证后才可以进入 ---- http://localhost:8081/at/checkBasic
|
||||
@SaCheckBasic(account = "sa:123456")
|
||||
@RequestMapping("checkBasic")
|
||||
public SaResult checkBasic() {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
}
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.DisableLoginException;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@ControllerAdvice // 可指定包前缀,比如:(basePackages = "com.pj.admin")
|
||||
public class GlobalException {
|
||||
|
||||
// 在当前类每个方法进入之前触发的操作
|
||||
@ModelAttribute
|
||||
public void get(HttpServletRequest request) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@ResponseBody
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
System.out.println("全局异常---------------");
|
||||
e.printStackTrace();
|
||||
|
||||
// 不同异常返回不同状态码
|
||||
AjaxJson aj = null;
|
||||
if (e instanceof NotLoginException) { // 如果是未登录异常
|
||||
NotLoginException ee = (NotLoginException) e;
|
||||
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
|
||||
} else if(e instanceof NotRoleException) { // 如果是角色异常
|
||||
NotRoleException ee = (NotRoleException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||
} else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
} else if(e instanceof DisableLoginException) { // 如果是被封禁异常
|
||||
DisableLoginException ee = (DisableLoginException) e;
|
||||
aj = AjaxJson.getNotJur("账号被封禁:" + ee.getDisableTime() + "秒后解封");
|
||||
} else { // 普通异常, 输出:500 + 异常信息
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回给前端
|
||||
return aj;
|
||||
|
||||
// 输出到客户端
|
||||
// response.setContentType("application/json; charset=utf-8"); // http说明,我要返回JSON对象
|
||||
// response.getWriter().print(new ObjectMapper().writeValueAsString(aj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 全局异常拦截(拦截项目中的NotLoginException异常)
|
||||
// @ExceptionHandler(NotLoginException.class)
|
||||
// public AjaxJson handlerNotLoginException(NotLoginException nle, HttpServletRequest request, HttpServletResponse response)
|
||||
// throws Exception {
|
||||
//
|
||||
// // 打印堆栈,以供调试
|
||||
// nle.printStackTrace();
|
||||
//
|
||||
// // 判断场景值,定制化异常信息
|
||||
// String message = "";
|
||||
// if(nle.getType().equals(NotLoginException.NOT_TOKEN)) {
|
||||
// message = "未提供token";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
|
||||
// message = "token无效";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
|
||||
// message = "token已过期";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.BE_REPLACED)) {
|
||||
// message = "token已被顶下线";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.KICK_OUT)) {
|
||||
// message = "token已被踢下线";
|
||||
// }
|
||||
// else {
|
||||
// message = "当前会话未登录";
|
||||
// }
|
||||
//
|
||||
// // 返回给前端
|
||||
// return AjaxJson.getError(message);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 登录测试
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/acc/")
|
||||
public class LoginController {
|
||||
|
||||
// 测试登录 ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456
|
||||
@RequestMapping("doLogin")
|
||||
public SaResult doLogin(String name, String pwd) {
|
||||
// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
|
||||
if("zhang".equals(name) && "123456".equals(pwd)) {
|
||||
StpUtil.login(10001);
|
||||
return SaResult.ok("登录成功");
|
||||
}
|
||||
return SaResult.error("登录失败");
|
||||
}
|
||||
|
||||
// 查询登录状态 ---- http://localhost:8081/acc/isLogin
|
||||
@RequestMapping("isLogin")
|
||||
public SaResult isLogin() {
|
||||
return SaResult.ok("是否登录:" + StpUtil.isLogin());
|
||||
}
|
||||
|
||||
// 查询 Token 信息 ---- http://localhost:8081/acc/tokenInfo
|
||||
@RequestMapping("tokenInfo")
|
||||
public SaResult tokenInfo() {
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
}
|
||||
|
||||
// 测试注销 ---- http://localhost:8081/acc/logout
|
||||
@RequestMapping("logout")
|
||||
public SaResult logout() {
|
||||
StpUtil.logout();
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
}
|
||||
+3
-5
@@ -186,7 +186,7 @@ public class TestController {
|
||||
// 先登录上
|
||||
StpUtil.login(10001);
|
||||
// 踢下线
|
||||
StpUtil.logoutByLoginId(10001);
|
||||
StpUtil.kickout(10001);
|
||||
// 再尝试获取
|
||||
StpUtil.getLoginId();
|
||||
// 返回
|
||||
@@ -235,12 +235,11 @@ public class TestController {
|
||||
return AjaxJson.getSuccessData("登录成功");
|
||||
}
|
||||
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
System.out.println("进来了");
|
||||
return AjaxJson.getSuccess();
|
||||
System.out.println("------------进来了");
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test2
|
||||
@@ -249,5 +248,4 @@ public class TestController {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 登录测试
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/user/")
|
||||
public class UserController {
|
||||
|
||||
// 测试登录,浏览器访问: http://localhost:8081/user/doLogin?username=zhang&password=123456
|
||||
@RequestMapping("doLogin")
|
||||
public String doLogin(String username, String password) {
|
||||
// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
|
||||
if("zhang".equals(username) && "123456".equals(password)) {
|
||||
StpUtil.login(10001);
|
||||
return "登录成功";
|
||||
}
|
||||
return "登录失败";
|
||||
}
|
||||
|
||||
// 查询登录状态,浏览器访问: http://localhost:8081/user/isLogin
|
||||
@RequestMapping("isLogin")
|
||||
public String isLogin(String username, String password) {
|
||||
return "当前会话是否登录:" + StpUtil.isLogin();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,7 @@ sa-token:
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: false
|
||||
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
|
||||
+2
-2
@@ -42,9 +42,9 @@ sa.ajax = function(url, data, successFn) {
|
||||
// ----------------------------------- 相关事件 -----------------------------------
|
||||
|
||||
// 检查当前是否已经登录,如果已登录则直接开始跳转,如果未登录则等待用户输入账号密码
|
||||
sa.ajax("/getRedirectUrl", {redirect: getParam('redirect', '')}, function(res) {
|
||||
sa.ajax("/getRedirectUrl", {redirect: getParam('redirect', ''), mode: getParam('mode', '')}, function(res) {
|
||||
if(res.code == 200) {
|
||||
// redirect地址有效,开始跳转
|
||||
// 已登录,并且redirect地址有效,开始跳转
|
||||
location.href = decodeURIComponent(res.data);
|
||||
} else if(res.code == 401) {
|
||||
console.log('未登录');
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user