Compare commits
113 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be499e011b | |||
| 3d75eca892 | |||
| 0975070508 | |||
| af2d851ddb | |||
| 7e399fb63a | |||
| a30b85fc9f | |||
| df6ea7ec4e | |||
| 1d620fa119 | |||
| abba41b6f7 | |||
| 21147043f2 | |||
| a7ef71737e | |||
| d45d404fa0 | |||
| 7f93c2086d | |||
| 443a61d404 | |||
| ed18af72b8 | |||
| de39d91b71 | |||
| 5e3795e29e | |||
| e320a8e45b | |||
| 26393d17dc | |||
| 30758464ac | |||
| a3c8b2ade2 | |||
| f5cbe0616e | |||
| 969deb9470 | |||
| cfc11d0ba8 | |||
| 210b92f3dc | |||
| 2c39fc7209 | |||
| b62e2dae24 | |||
| 0c0b08c6c3 | |||
| 7302a34de0 | |||
| 8e3bb4db6b | |||
| 4af9b4a9be | |||
| 042f383256 | |||
| 2d022516c5 | |||
| 2e82fc0592 | |||
| 1beb5afede | |||
| bcce8b4b68 | |||
| bfab1b655c | |||
| 2b2ef97dfb | |||
| 2b3a558325 | |||
| ca07fbdfc9 | |||
| 4de21e7391 | |||
| d4ee232b9b | |||
| 7199e81f6f | |||
| 651d0c37c6 | |||
| 63fa7b3eed | |||
| 4a78c8d74f | |||
| 6a3a2c20ad | |||
| 7853c1bea0 | |||
| faa63fe327 | |||
| 5f341d97ba | |||
| 20063fbd41 | |||
| 9de31236c6 | |||
| f5881f2d1c | |||
| d30509f6cc | |||
| 0900b0d06f | |||
| 897ed026e7 | |||
| 9b2c4cf127 | |||
| 7f416d9e29 | |||
| 53af6a3c20 | |||
| 5892a3907c | |||
| d6cad032ac | |||
| 072e3a5217 | |||
| 8c3cc23be7 | |||
| 10845ee849 | |||
| aa252f831c | |||
| c44ad67117 | |||
| d115d04319 | |||
| 9500c0bd56 | |||
| 563c6b7334 | |||
| ee5729e607 | |||
| 914d9c4d1e | |||
| 81daf2c300 | |||
| b5991a71ca | |||
| 88d88198f1 | |||
| b4dcf5f02d | |||
| 2abc4bab3c | |||
| 7fafb070ff | |||
| 4109dc35e2 | |||
| 357aab49d6 | |||
| 22a3620de5 | |||
| 7292ea5fca | |||
| 4ef67ee282 | |||
| fee291180b | |||
| 98bdea8e23 | |||
| b3dc39d5fd | |||
| e444b88521 | |||
| 54dcbe2a88 | |||
| 9de70bc8be | |||
| c9a1b369f2 | |||
| 5a1e1be85a | |||
| de68b9365b | |||
| 249ceceae3 | |||
| b97e199e92 | |||
| b0e92a252a | |||
| ca39ced1b1 | |||
| f596a5ea5d | |||
| 42acb14ea7 | |||
| a1e995e800 | |||
| 31c1c42cc4 | |||
| e12077a1a2 | |||
| 1d60239d22 | |||
| 8a00231000 | |||
| 4d7dd1ab3e | |||
| 3f5dd24ae2 | |||
| cce907fd20 | |||
| f05818d0ea | |||
| 40a5cdc6dc | |||
| fbc139b430 | |||
| 7e366326e4 | |||
| 6363db248a | |||
| 9c852da49b | |||
| 4784cbd103 | |||
| 40a6da10fe |
+1
-1
@@ -13,4 +13,4 @@ unpackage/
|
||||
|
||||
.idea/
|
||||
|
||||
|
||||
.flattened-pom.xml
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<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">
|
||||
<img alt="logo" src="https://sa-token.dev33.cn/doc/logo.png" width="150" height="150">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.29.0</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.30.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?theme=gvp"></a>
|
||||
@@ -28,64 +28,6 @@
|
||||
**Sa-Token** 是一个轻量级 Java 权限认证框架,主要解决:**`登录认证`**、**`权限认证`**、**`Session会话`**、**`单点登录`**、**`OAuth2.0`**、**`微服务网关鉴权`**
|
||||
等一系列权限相关问题。
|
||||
|
||||
Sa-Token 的 API 设计非常简单,有多简单呢?以登录认证为例,你只需要:
|
||||
|
||||
``` java
|
||||
// 在登录时写入当前会话的账号id
|
||||
StpUtil.login(10001);
|
||||
|
||||
// 然后在需要校验登录处调用以下方法:
|
||||
// 如果当前会话未登录,这句代码会抛出 `NotLoginException` 异常
|
||||
StpUtil.checkLogin();
|
||||
```
|
||||
|
||||
至此,我们已经借助 Sa-Token 完成登录认证!
|
||||
|
||||
此时的你小脑袋可能飘满了问号,就这么简单?自定义 Realm 呢?全局过滤器呢?我不用写各种配置文件吗?
|
||||
|
||||
没错,在 Sa-Token 中,登录认证就是如此简单,不需要任何的复杂前置工作,只需这一行简单的API调用,就可以完成会话登录认证!
|
||||
|
||||
当你受够 Shiro、SpringSecurity 等框架的三拜九叩之后,你就会明白,相对于这些传统老牌框架,Sa-Token 的 API 设计是多么的简单、优雅!
|
||||
|
||||
权限认证示例(只有具备 `user:add` 权限的会话才可以进入请求)
|
||||
``` java
|
||||
@SaCheckPermission("user:add")
|
||||
@RequestMapping("/user/insert")
|
||||
public String insert(SysUser user) {
|
||||
// ...
|
||||
return "用户增加";
|
||||
}
|
||||
```
|
||||
|
||||
将某个账号踢下线(待到对方再次访问系统时会抛出`NotLoginException`异常)
|
||||
``` java
|
||||
// 将账号id为 10001 的会话踢下线
|
||||
StpUtil.kickout(10001);
|
||||
```
|
||||
|
||||
在 Sa-Token 中,绝大多数功能都可以 **一行代码** 完成:
|
||||
``` java
|
||||
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.kickout(10001, "PC"); // 指定账号指定设备标识踢下线 (不同端不受影响)
|
||||
StpUtil.openSafe(120); // 在当前会话开启二级认证,有效期为120秒
|
||||
StpUtil.checkSafe(); // 校验当前会话是否处于二级认证有效期内,校验失败会抛出异常
|
||||
StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
```
|
||||
|
||||
即使不运行测试,相信您也能意会到绝大多数 API 的用法。
|
||||
|
||||
|
||||
## Sa-Token 功能一览
|
||||
|
||||
- **登录认证** —— 单端登录、多端登录、同端互斥登录、七天内免登录
|
||||
- **权限认证** —— 权限认证、角色认证、会话二级认证
|
||||
@@ -119,7 +61,7 @@ StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
|
||||
|
||||
## Sa-Token-SSO 单点登录
|
||||
网上的单点登录教程大多以CAS流程为主,其实对于不同的系统架构,实现单点登录的步骤也大为不同,Sa-Token由简入难将其划分为三种模式:
|
||||
Sa-Token-SSO 由简入难划分为三种模式,解决不同架构下的 SSO 接入问题:
|
||||
|
||||
| 系统架构 | 采用模式 | 简介 | 文档链接 |
|
||||
| :-------- | :-------- | :-------- | :-------- |
|
||||
@@ -131,8 +73,6 @@ StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
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)
|
||||
3. 如果既无法做到前端同域,也无法做到后端同Redis,那么只能走模式三,Http请求获取会话(Sa-Token对SSO提供了完整的封装,你只需要按照示例从文档上复制几段代码便可以轻松集成)
|
||||
4. 技术选型一定要根据系统架构对症下药,切不可胡乱选择
|
||||
|
||||
|
||||
## Sa-Token-OAuth2.0 授权登录
|
||||
Sa-OAuth2 模块基于 [RFC-6749 标准](https://tools.ietf.org/html/rfc6749) 编写,通过Sa-OAuth2你可以非常轻松的实现系统的OAuth2.0授权认证
|
||||
@@ -154,54 +94,47 @@ Sa-OAuth2 模块基于 [RFC-6749 标准](https://tools.ietf.org/html/rfc6749)
|
||||

|
||||
|
||||
|
||||
## Star 趋势
|
||||
[](https://giteye.net/chart/77YQZ6UK)
|
||||
|
||||
[](https://starchart.cc/dromara/sa-token)
|
||||
|
||||
|
||||
## 使用Sa-Token的开源项目
|
||||
- **[ sa-plus ]**:[一个基于 SpringBoot 架构的快速开发框架,内置代码生成器](https://gitee.com/click33/sa-plus)
|
||||
- [[ sa-plus ]](https://gitee.com/click33/sa-plus):一个基于 SpringBoot 架构的快速开发框架,内置代码生成器
|
||||
|
||||
- **[ jthink ]**: [一个基于 SpringBoot + Sa-Token + Thymeleaf 的博客系统](https://gitee.com/wtsoftware/jthink)
|
||||
- [[ jthink ]](https://gitee.com/wtsoftware/jthink): 一个基于 SpringBoot + Sa-Token + Thymeleaf 的博客系统
|
||||
|
||||
- **[ dcy-fast ]**:[ 一个基于 SpringBoot + Sa-Token + Mybatis-Plus 的后台管理系统,前端vue-element-admin,并且内置代码生成器](https://gitee.com/dcy421/dcy-fast)
|
||||
- [[ dcy-fast ]](https://gitee.com/dcy421/dcy-fast): 一个基于 SpringBoot + Sa-Token + Mybatis-Plus 的后台管理系统,前端vue-element-admin,并且内置代码生成器
|
||||
|
||||
- **[ helio-starters ]**:[ 单体 Boot 版脚手架 + 微服务 Cloud 版脚手架,带有配套后台管理前端模板及代码生成器](https://gitee.com/uncarbon97/helio-starters)
|
||||
- [[ helio-starters ]](https://gitee.com/uncarbon97/helio-starters): 单体 Boot 版脚手架 + 微服务 Cloud 版脚手架,带有配套后台管理前端模板及代码生成器
|
||||
|
||||
- **[ sa-token-plugin ]**:[Sa-Token第三方插件实现,基于Sa-Token-Core,提供一些与官方不同实现机制的的插件集合,作为Sa-Token开源生态的补充](https://gitee.com/bootx/sa-token-plugin)
|
||||
- [[ sa-token-plugin ]](https://gitee.com/bootx/sa-token-plugin):Sa-Token第三方插件实现,基于Sa-Token-Core,提供一些与官方不同实现机制的的插件集合,作为Sa-Token开源生态的补充
|
||||
|
||||
- **[ easy-admin ]**:[一个基于SpringBoot2 + Sa-Token + Mybatis-Plus + Snakerflow + Layui 的后台管理系统,灵活多变可前后端分离,也可单体,内置代码生成器、权限管理、工作流引擎等](https://gitee.com/lakernote/easy-admin)
|
||||
- [[ easy-admin ]](https://gitee.com/lakernote/easy-admin):一个基于SpringBoot2 + Sa-Token + Mybatis-Plus + Snakerflow + Layui 的后台管理系统,灵活多变可前后端分离,也可单体,内置代码生成器、权限管理、工作流引擎等
|
||||
|
||||
- **[ RuoYi-Vue-Plus ]**:[基于 RuoYi-Vue 集成 SaToken + Lombok + Mybatis-Plus + Undertow + knife4j + Hutool + Feign 重写所有原生业务 定期与 RuoYi-Vue 同步](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/)
|
||||
- [[ RuoYi-Vue-Plus ]](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus):重写RuoYi-Vue所有功能 集成 Sa-Token+Mybatis-Plus+Jackson+Xxl-Job+knife4j+Hutool+OSS 定期同步
|
||||
|
||||
- **[ falser-cloud ]**: [基于 SpringCloud Alibaba + SpringCloud gateway + SpringBoot + Sa-Token + vue-admin-template + Nacos + Rabbit MQ + Redis 的一个后台管理系统,前后端分离,权限管理,菜单管理,数据字典,停车场系统管理等功能](https://gitee.com/falser/falser-cloud)
|
||||
- [[ RuoYi-Cloud-Plus ]](https://gitee.com/JavaLionLi/RuoYi-Cloud-Plus):重写RuoYi-Cloud所有功能 整合 SpringCloudAlibaba Dubbo3.0 Sa-Token Mybatis-Plus MQ OSS ES Xxl-Job Docker 全方位升级 定期同步
|
||||
|
||||
- **[ bootx-platform ]**:[集成sa-token和sa-token-plugin并深度定制认证模块,包含多级别数据范围权限、数据自动加解密、数据脱敏、超级查询器、以及支付收单、消息通知等准商用功能的开源免费开发脚手架项目](https://gitee.com/bootx/bootx-platform)
|
||||
- [[ falser-cloud ]](https://gitee.com/falser/falser-cloud): 基于 SpringCloud Alibaba + SpringCloud gateway + SpringBoot + Sa-Token + vue-admin-template + Nacos + Rabbit MQ + Redis 的一个后台管理系统,前后端分离,权限管理,菜单管理,数据字典,停车场系统管理等功能
|
||||
|
||||
- **[ QForum-Core ]**:[QForum 论坛系统官方核心,可拓展性强、轻量级、高性能、前后端分离,基于 SpringBoot2 + Sa-Token + Mybatis-Plus](https://github.com/Project-QForum/QForum-Core/)
|
||||
- [[ bootx-platform ]](https://gitee.com/bootx/bootx-platform):集成sa-token和sa-token-plugin并深度定制认证模块,包含多级别数据范围权限、数据自动加解密、数据脱敏、超级查询器、以及支付收单、消息通知等准商用功能的开源免费开发脚手架项目
|
||||
|
||||
- **[ ExciteCMS-Layui ]**:[ExciteCMS 快速开发脚手架:一款后端基于 SpringBoot2 + Sa-Token + Mybatis-Plus,前端基于 Layuimini 的内容管理系统,具备RBAC、日志管理、代码生成等功能,并集成常用的支付、OSS等第三方服务,拥有详细的开发文档](https://gitee.com/ExciteTeam/ExciteCMS-SpringBoot-Layui)
|
||||
- [[ QForum-Core ]](https://github.com/Project-QForum/QForum-Core/):QForum 论坛系统官方核心,可拓展性强、轻量级、高性能、前后端分离,基于 SpringBoot2 + Sa-Token + Mybatis-Plus
|
||||
|
||||
- [[ ExciteCMS-Layui ]](https://gitee.com/ExciteTeam/ExciteCMS-SpringBoot-Layui):ExciteCMS 快速开发脚手架:一款后端基于 SpringBoot2 + Sa-Token + Mybatis-Plus,前端基于 Layuimini 的内容管理系统,具备RBAC、日志管理、代码生成等功能,并集成常用的支付、OSS等第三方服务,拥有详细的开发文档
|
||||
|
||||
- [[ 拾壹博客 ]](https://gitee.com/quequnlong/shiyi-blog):一款vue+springboot前后端分离的博客系统,博客后台管理系统使用了vue+elmentui开发,后端使用Sa-Token进行权限管理,支持动态菜单权限,动态定时任务,文件支持本地和七牛云上传,使用ElasticSearch作为全文检索服务,支持QQ、微博、码云登录。
|
||||
|
||||
如果您的项目使用了Sa-Token,欢迎提交pr
|
||||
|
||||
## 友情链接
|
||||
- **[ OkHttps ]**:[ 一个轻量级http通信框架,API设计无比优雅,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
- [[ OkHttps ]](https://gitee.com/ejlchina-zhxu/okhttps):一个轻量级http通信框架,API设计无比优雅,支持 WebSocket 以及 Stomp 协议
|
||||
|
||||
- **[ Bean Searcher ]**:[ 比 MyBatis 效率快 100 倍的条件检索引擎,天生支持联表,使一行代码实现复杂列表检索成为可能!](https://github.com/ejlchina/bean-searcher)
|
||||
- [[ Bean Searcher ]](https://github.com/ejlchina/bean-searcher):比 MyBatis 效率快 100 倍的条件检索引擎,天生支持联表,使一行代码实现复杂列表检索成为可能!
|
||||
|
||||
- **[ 小诺快速开发平台 ]**:[ 基于SpringBoot2 + AntDesignVue全新快速开发平台,同时拥有三个版本](https://xiaonuo.vip/index#pricing)
|
||||
- [[ 小诺快速开发平台 ]](https://xiaonuo.vip/index#pricing):基于SpringBoot2 + AntDesignVue全新快速开发平台,同时拥有三个版本
|
||||
|
||||
- **[ Jpom ]**:[ 简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件](https://gitee.com/dromara/Jpom)
|
||||
- [[ Jpom ]](https://gitee.com/dromara/Jpom):简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件
|
||||
|
||||
- **[ TLog ]**:[ 一个轻量级的分布式日志标记追踪神器](https://gitee.com/dromara/TLog)
|
||||
- [[ TLog ]](https://gitee.com/dromara/TLog):一个轻量级的分布式日志标记追踪神器
|
||||
|
||||
|
||||
## 贡献者名单
|
||||
感谢每一个为 Sa-Token 贡献代码的小伙伴
|
||||
|
||||
[](https://giteye.net/chart/CGZ7GT8E)
|
||||
|
||||
|
||||
## 交流群
|
||||
QQ交流群:1群:1002350610 (已满) 、
|
||||
|
||||
+18
-69
@@ -5,75 +5,24 @@ call mvn clean
|
||||
:: demo模块clean
|
||||
cd sa-token-demo
|
||||
|
||||
cd sa-token-demo-jwt
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-springboot
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-webflux
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-solon
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-oauth2-client
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-oauth2-server
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-quick-login
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-alone-redis
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-thymeleaf
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-sso-server
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-sso1-client
|
||||
call mvn clean
|
||||
cd ..
|
||||
|
||||
cd sa-token-demo-sso2-client
|
||||
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 sa-token-demo-springboot & call mvn clean & cd ..
|
||||
cd sa-token-demo-webflux & call mvn clean & cd ..
|
||||
cd sa-token-demo-solon & call mvn clean & cd ..
|
||||
cd sa-token-demo-oauth2-client & call mvn clean & cd ..
|
||||
cd sa-token-demo-oauth2-server & call mvn clean & cd ..
|
||||
cd sa-token-demo-quick-login & call mvn clean & cd ..
|
||||
cd sa-token-demo-alone-redis & call mvn clean & cd ..
|
||||
cd sa-token-demo-thymeleaf & call mvn clean & cd ..
|
||||
cd sa-token-demo-sso-server & call mvn clean & cd ..
|
||||
cd sa-token-demo-sso1-client & call mvn clean & cd ..
|
||||
cd sa-token-demo-sso2-client & call mvn clean & cd ..
|
||||
cd sa-token-demo-sso3-client & call mvn clean & cd ..
|
||||
cd sa-token-demo-sso3-client-nosdk & 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 sa-token-demo-websocket & call mvn clean & cd ..
|
||||
cd sa-token-demo-websocket-spring & call mvn clean & cd ..
|
||||
|
||||
cd ..
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.29.0</version>
|
||||
<version>${revision}</version>
|
||||
|
||||
<!-- 项目介绍 -->
|
||||
<name>sa-token</name>
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
<!-- 一些属性 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<revision>1.30.0</revision>
|
||||
<jdk.version>1.8</jdk.version>
|
||||
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
|
||||
@@ -86,6 +86,34 @@
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- 统一版本号管理 -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>flatten-maven-plugin</artifactId>
|
||||
<version>1.2.7</version>
|
||||
<configuration>
|
||||
<updatePomFile>true</updatePomFile>
|
||||
<flattenMode>resolveCiFriendliesOnly</flattenMode>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>flatten</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>flatten</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>flatten.clean</id>
|
||||
<phase>clean</phase>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.29.0</version>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ package cn.dev33.satoken;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.dev33.satoken.action.SaTokenAction;
|
||||
import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.config.SaTokenConfigFactory;
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
@@ -13,8 +11,12 @@ 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;
|
||||
import cn.dev33.satoken.json.SaJsonTemplate;
|
||||
import cn.dev33.satoken.json.SaJsonTemplateDefaultImpl;
|
||||
import cn.dev33.satoken.listener.SaTokenListener;
|
||||
import cn.dev33.satoken.listener.SaTokenListenerDefaultImpl;
|
||||
import cn.dev33.satoken.sign.SaSignTemplate;
|
||||
import cn.dev33.satoken.sign.SaSignTemplateDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
@@ -28,7 +30,6 @@ import cn.dev33.satoken.util.SaFoxUtil;
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class SaManager {
|
||||
|
||||
/**
|
||||
@@ -93,24 +94,6 @@ public class SaManager {
|
||||
return stpInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* 框架行为 Bean
|
||||
*/
|
||||
private volatile static SaTokenAction saTokenAction;
|
||||
public static void setSaTokenAction(SaTokenAction saTokenAction) {
|
||||
SaManager.saTokenAction = saTokenAction;
|
||||
}
|
||||
public static SaTokenAction getSaTokenAction() {
|
||||
if (saTokenAction == null) {
|
||||
synchronized (SaManager.class) {
|
||||
if (saTokenAction == null) {
|
||||
setSaTokenAction(new SaTokenActionDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return saTokenAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上下文Context Bean
|
||||
*/
|
||||
@@ -192,6 +175,42 @@ public class SaManager {
|
||||
}
|
||||
return saTemp;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON 转换器 Bean
|
||||
*/
|
||||
private volatile static SaJsonTemplate saJsonTemplate;
|
||||
public static void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) {
|
||||
SaManager.saJsonTemplate = saJsonTemplate;
|
||||
}
|
||||
public static SaJsonTemplate getSaJsonTemplate() {
|
||||
if (saJsonTemplate == null) {
|
||||
synchronized (SaManager.class) {
|
||||
if (saJsonTemplate == null) {
|
||||
setSaJsonTemplate(new SaJsonTemplateDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return saJsonTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数签名 Bean
|
||||
*/
|
||||
private volatile static SaSignTemplate saSignTemplate;
|
||||
public static void setSaSignTemplate(SaSignTemplate saSignTemplate) {
|
||||
SaManager.saSignTemplate = saSignTemplate;
|
||||
}
|
||||
public static SaSignTemplate getSaSignTemplate() {
|
||||
if (saSignTemplate == null) {
|
||||
synchronized (SaManager.class) {
|
||||
if (saSignTemplate == null) {
|
||||
setSaSignTemplate(new SaSignTemplateDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return saSignTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* StpLogic集合, 记录框架所有成功初始化的StpLogic
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* <h1> v1.27+ 此接口已废弃,目前版本暂时向下兼容,请及时更换为 SaStrategy </h1>
|
||||
* <p>Sa-Token 逻辑代理接口 </p>
|
||||
* <p>此接口将会代理框架内部的一些关键性逻辑,方便开发者进行按需重写</p>
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public interface SaTokenAction {
|
||||
|
||||
/**
|
||||
* 创建一个Token
|
||||
* @param loginId 账号id
|
||||
* @param loginType 账号类型
|
||||
* @return token
|
||||
*/
|
||||
public String createToken(Object loginId, String loginType);
|
||||
|
||||
/**
|
||||
* 创建一个Session
|
||||
* @param sessionId Session的Id
|
||||
* @return 创建后的Session
|
||||
*/
|
||||
public SaSession createSession(String sessionId);
|
||||
|
||||
/**
|
||||
* 判断:集合中是否包含指定元素(模糊匹配)
|
||||
* @param list 集合
|
||||
* @param element 元素
|
||||
* @return 是否包含
|
||||
*/
|
||||
public boolean hasElement(List<String> list, String element);
|
||||
|
||||
/**
|
||||
* 对一个Method对象进行注解检查(注解鉴权内部实现)
|
||||
* @param method Method对象
|
||||
*/
|
||||
public void checkMethodAnnotation(Method method);
|
||||
|
||||
/**
|
||||
* 从指定元素校验注解
|
||||
* @param target /
|
||||
*/
|
||||
public void validateAnnotation(AnnotatedElement target);
|
||||
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package cn.dev33.satoken.action;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* <h1> v1.27+ 此接口已废弃,目前版本暂时向下兼容,请及时更换为 SaStrategy </h1>
|
||||
* <p> Sa-Token 逻辑代理接口 [默认实现类] </p>
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public class SaTokenActionDefaultImpl implements SaTokenAction {
|
||||
|
||||
/**
|
||||
* 创建一个Token
|
||||
*/
|
||||
@Override
|
||||
public String createToken(Object loginId, String loginType) {
|
||||
// 根据配置的tokenStyle生成不同风格的token
|
||||
String tokenStyle = SaManager.getConfig().getTokenStyle();
|
||||
// uuid
|
||||
if(SaTokenConsts.TOKEN_STYLE_UUID.equals(tokenStyle)) {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
// 简单uuid (不带下划线)
|
||||
if(SaTokenConsts.TOKEN_STYLE_SIMPLE_UUID.equals(tokenStyle)) {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
// 32位随机字符串
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_32.equals(tokenStyle)) {
|
||||
return SaFoxUtil.getRandomString(32);
|
||||
}
|
||||
// 64位随机字符串
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_64.equals(tokenStyle)) {
|
||||
return SaFoxUtil.getRandomString(64);
|
||||
}
|
||||
// 128位随机字符串
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_128.equals(tokenStyle)) {
|
||||
return SaFoxUtil.getRandomString(128);
|
||||
}
|
||||
// tik风格 (2_14_16)
|
||||
if(SaTokenConsts.TOKEN_STYLE_TIK.equals(tokenStyle)) {
|
||||
return SaFoxUtil.getRandomString(2) + "_" + SaFoxUtil.getRandomString(14) + "_" + SaFoxUtil.getRandomString(16) + "__";
|
||||
}
|
||||
// 默认,还是uuid
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个Session
|
||||
*/
|
||||
@Override
|
||||
public SaSession createSession(String sessionId) {
|
||||
return new SaSession(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:集合中是否包含指定元素(模糊匹配)
|
||||
*/
|
||||
@Override
|
||||
public boolean hasElement(List<String> list, String element) {
|
||||
|
||||
// 空集合直接返回false
|
||||
if(list == null || list.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 先尝试一下简单匹配,如果可以匹配成功则无需继续模糊匹配
|
||||
if (list.contains(element)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 开始模糊匹配
|
||||
for (String patt : list) {
|
||||
if(SaFoxUtil.vagueMatch(patt, element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 走出for循环说明没有一个元素可以匹配成功
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对一个Method对象进行注解检查(注解鉴权内部实现)
|
||||
*/
|
||||
@Override
|
||||
public void checkMethodAnnotation(Method method) {
|
||||
|
||||
// 先校验 Method 所属 Class 上的注解
|
||||
validateAnnotation(method.getDeclaringClass());
|
||||
|
||||
// 再校验 Method 上的注解
|
||||
validateAnnotation(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定元素校验注解
|
||||
* @param target see note
|
||||
*/
|
||||
public void validateAnnotation(AnnotatedElement target) {
|
||||
|
||||
// 校验 @SaCheckLogin 注解
|
||||
SaCheckLogin checkLogin = (SaCheckLogin) SaStrategy.me.getAnnotation.apply(target, SaCheckLogin.class);
|
||||
if(checkLogin != null) {
|
||||
SaManager.getStpLogic(checkLogin.type()).checkByAnnotation(checkLogin);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckRole 注解
|
||||
SaCheckRole checkRole = (SaCheckRole) SaStrategy.me.getAnnotation.apply(target, SaCheckRole.class);
|
||||
if(checkRole != null) {
|
||||
SaManager.getStpLogic(checkRole.type()).checkByAnnotation(checkRole);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckPermission 注解
|
||||
SaCheckPermission checkPermission = (SaCheckPermission) SaStrategy.me.getAnnotation.apply(target, SaCheckPermission.class);
|
||||
if(checkPermission != null) {
|
||||
SaManager.getStpLogic(checkPermission.type()).checkByAnnotation(checkPermission);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckSafe 注解
|
||||
SaCheckSafe checkSafe = (SaCheckSafe) SaStrategy.me.getAnnotation.apply(target, SaCheckSafe.class);
|
||||
if(checkSafe != null) {
|
||||
SaManager.getStpLogic(checkSafe.type()).checkByAnnotation(checkSafe);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckBasic 注解
|
||||
SaCheckBasic checkBasic = (SaCheckBasic) SaStrategy.me.getAnnotation.apply(target, SaCheckBasic.class);
|
||||
if(checkBasic != null) {
|
||||
SaBasicUtil.check(checkBasic.realm(), checkBasic.account());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package cn.dev33.satoken.config;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Sa-Token 配置类 Model
|
||||
* Sa-Token 配置类 Model
|
||||
* <p>
|
||||
* 你可以通过yml、properties、java代码等形式配置本类参数,具体请查阅官方文档: http://sa-token.dev33.cn/
|
||||
*
|
||||
@@ -32,6 +32,11 @@ public class SaTokenConfig implements Serializable {
|
||||
/** 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) */
|
||||
private Boolean isShare = true;
|
||||
|
||||
/**
|
||||
* 同一账号最大登录数量,-1代表不限 (只有在 isConcurrent=true, isShare=false 时此配置才有效)
|
||||
*/
|
||||
private int maxLoginCount = 12;
|
||||
|
||||
/** 是否尝试从请求体里读取token */
|
||||
private Boolean isReadBody = true;
|
||||
|
||||
@@ -88,11 +93,6 @@ public class SaTokenConfig implements Serializable {
|
||||
*/
|
||||
public SaCookieConfig cookie = new SaCookieConfig();
|
||||
|
||||
/**
|
||||
* SSO单点登录配置对象
|
||||
*/
|
||||
public SaSsoConfig sso = new SaSsoConfig();
|
||||
|
||||
|
||||
/**
|
||||
* @return token名称 (同时也是cookie名称)
|
||||
@@ -176,6 +176,22 @@ public class SaTokenConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 同一账号最大登录数量,-1代表不限 (只有在 isConcurrent=true, isShare=false 时此配置才有效)
|
||||
*/
|
||||
public int getMaxLoginCount() {
|
||||
return maxLoginCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxLoginCount 同一账号最大登录数量,-1代表不限 (只有在 isConcurrent=true, isShare=false 时此配置才有效)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setMaxLoginCount(int maxLoginCount) {
|
||||
this.maxLoginCount = maxLoginCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否尝试从请求体里读取token
|
||||
*/
|
||||
@@ -418,22 +434,6 @@ public class SaTokenConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SSO单点登录配置对象
|
||||
*/
|
||||
public SaSsoConfig getSso() {
|
||||
return sso;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sso SSO单点登录配置对象
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setSso(SaSsoConfig sso) {
|
||||
this.sso = sso;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Cookie 全局配置对象
|
||||
*/
|
||||
@@ -458,6 +458,7 @@ public class SaTokenConfig implements Serializable {
|
||||
+ ", activityTimeout=" + activityTimeout
|
||||
+ ", isConcurrent=" + isConcurrent
|
||||
+ ", isShare=" + isShare
|
||||
+ ", maxLoginCount=" + maxLoginCount
|
||||
+ ", isReadBody=" + isReadBody
|
||||
+ ", isReadHead=" + isReadHead
|
||||
+ ", isReadCookie=" + isReadCookie
|
||||
@@ -473,7 +474,6 @@ public class SaTokenConfig implements Serializable {
|
||||
+ ", basic=" + basic
|
||||
+ ", currDomain=" + currDomain
|
||||
+ ", checkIdToken=" + checkIdToken
|
||||
+ ", sso=" + sso
|
||||
+ ", cookie=" + cookie
|
||||
+ "]";
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public class ApiDisabledException extends SaTokenException {
|
||||
private static final long serialVersionUID = 6806129545290130133L;
|
||||
|
||||
/** 异常提示语 */
|
||||
public static final String BE_MESSAGE = "This API is disabled";
|
||||
public static final String BE_MESSAGE = "this api is disabled";
|
||||
|
||||
/**
|
||||
* 一个异常:代表 API 已被禁用
|
||||
|
||||
@@ -26,7 +26,7 @@ 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";
|
||||
|
||||
@@ -16,13 +16,13 @@ public class NotPermissionException extends SaTokenException {
|
||||
private static final long serialVersionUID = 6806129545290130141L;
|
||||
|
||||
/** 权限码 */
|
||||
private String code;
|
||||
private String permission;
|
||||
|
||||
/**
|
||||
* @return 获得权限码
|
||||
* @return 获得具体缺少的权限码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
public String getPermission() {
|
||||
return permission;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,14 +39,23 @@ public class NotPermissionException extends SaTokenException {
|
||||
return loginType;
|
||||
}
|
||||
|
||||
public NotPermissionException(String code) {
|
||||
this(code, StpUtil.stpLogic.loginType);
|
||||
public NotPermissionException(String permission) {
|
||||
this(permission, StpUtil.stpLogic.loginType);
|
||||
}
|
||||
|
||||
public NotPermissionException(String code, String loginType) {
|
||||
super("无此权限:" + code);
|
||||
this.code = code;
|
||||
public NotPermissionException(String permission, String loginType) {
|
||||
super("无此权限:" + permission);
|
||||
this.permission = permission;
|
||||
this.loginType = loginType;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 警告:自 v1.30+ 版本起,获取异常权限码由 getCode() 更改为 getPermission(),请及时更换! </h1>
|
||||
* @return 获得权限码
|
||||
*/
|
||||
@Deprecated
|
||||
public int getCode() {
|
||||
return super.getCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
/**
|
||||
* 定义所有异常细分状态码
|
||||
*
|
||||
* @author kong
|
||||
* @since: 2022-4-25
|
||||
*/
|
||||
public class SaExceptionCode {
|
||||
|
||||
/** 代表这个异常在抛出时未指定异常细分状态码 */
|
||||
public static final int CODE_UNDEFINED = -1;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
/**
|
||||
* 一个异常:代表 JSON 转换失败
|
||||
*
|
||||
* @author kong
|
||||
*/
|
||||
public class SaJsonConvertException extends SaTokenException {
|
||||
|
||||
/**
|
||||
* 序列化版本号
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290134144L;
|
||||
|
||||
/**
|
||||
* 一个异常:代表 JSON 转换失败
|
||||
* @param cause 异常对象
|
||||
*/
|
||||
public SaJsonConvertException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,22 @@ public class SaTokenException extends RuntimeException {
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130132L;
|
||||
|
||||
/**
|
||||
* 异常细分状态码
|
||||
*/
|
||||
private int code = SaExceptionCode.CODE_UNDEFINED;
|
||||
|
||||
/**
|
||||
* 构建一个异常
|
||||
*
|
||||
* @param code 异常细分状态码
|
||||
*/
|
||||
public SaTokenException(int code) {
|
||||
super();
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建一个异常
|
||||
*
|
||||
@@ -25,6 +41,17 @@ public class SaTokenException extends RuntimeException {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个异常
|
||||
*
|
||||
* @param code 异常细分状态码
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public SaTokenException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个异常
|
||||
*
|
||||
@@ -44,6 +71,24 @@ public class SaTokenException extends RuntimeException {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常细分状态码
|
||||
* @return 异常细分状态码
|
||||
*/
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入异常细分状态码
|
||||
* @param code 异常细分状态码
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenException setCode(int code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果flag==true,则抛出message异常
|
||||
* @param flag 标记
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.dev33.satoken.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JSON 转换器
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaJsonTemplate {
|
||||
|
||||
/**
|
||||
* 将任意对象转换为 json 字符串
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 转换后的 json 字符串
|
||||
*/
|
||||
public String toJsonString(Object obj);
|
||||
|
||||
/**
|
||||
* 解析 json 字符串为map对象
|
||||
* @param jsonStr json字符串
|
||||
* @return map对象
|
||||
*/
|
||||
public Map<String, Object> parseJsonToMap(String jsonStr);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cn.dev33.satoken.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import cn.dev33.satoken.exception.ApiDisabledException;
|
||||
|
||||
/**
|
||||
* JSON 相关操作接口
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaJsonTemplateDefaultImpl implements SaJsonTemplate {
|
||||
|
||||
public static final String ERROR_MESSAGE = "未实现具体的 json 转换器";
|
||||
|
||||
/**
|
||||
* 将任意对象转换为 json 字符串
|
||||
*/
|
||||
@Override
|
||||
public String toJsonString(Object obj) {
|
||||
throw new ApiDisabledException(ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 json 字符串解析为 Map
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> parseJsonToMap(String jsonStr) {
|
||||
throw new ApiDisabledException(ERROR_MESSAGE);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -14,9 +14,10 @@ public interface SaTokenListener {
|
||||
* 每次登录时触发
|
||||
* @param loginType 账号类别
|
||||
* @param loginId 账号id
|
||||
* @param tokenValue 本次登录产生的 token 值
|
||||
* @param loginModel 登录参数
|
||||
*/
|
||||
public void doLogin(String loginType, Object loginId, SaLoginModel loginModel);
|
||||
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel);
|
||||
|
||||
/**
|
||||
* 每次注销时触发
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ public class SaTokenListenerDefaultImpl implements SaTokenListener {
|
||||
* 每次登录时触发
|
||||
*/
|
||||
@Override
|
||||
public void doLogin(String loginType, Object loginId, SaLoginModel loginModel) {
|
||||
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
|
||||
println("账号[" + loginId + "]登录成功");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.dev33.satoken.session;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -110,12 +111,37 @@ public class SaSession implements Serializable {
|
||||
private final List<TokenSign> tokenSignList = new Vector<>();
|
||||
|
||||
/**
|
||||
* 返回token签名列表的拷贝副本
|
||||
* 此Session绑定的token签名列表
|
||||
*
|
||||
* @return token签名列表
|
||||
*/
|
||||
public List<TokenSign> getTokenSignList() {
|
||||
return new Vector<>(tokenSignList);
|
||||
return tokenSignList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回token签名列表的拷贝副本
|
||||
*
|
||||
* @return token签名列表
|
||||
*/
|
||||
public List<TokenSign> tokenSignListCopy() {
|
||||
return new ArrayList<>(tokenSignList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回token签名列表的拷贝副本,根据 device 筛选
|
||||
*
|
||||
* @param device 设备类型,填 null 代表不限设备类型
|
||||
* @return token签名列表
|
||||
*/
|
||||
public List<TokenSign> tokenSignListCopyByDevice(String device) {
|
||||
List<TokenSign> list = new ArrayList<>();
|
||||
for (TokenSign tokenSign : tokenSignListCopy()) {
|
||||
if(device == null || tokenSign.getDevice().equals(device)) {
|
||||
list.add(tokenSign);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,7 +151,7 @@ public class SaSession implements Serializable {
|
||||
* @return 查找到的tokenSign
|
||||
*/
|
||||
public TokenSign getTokenSign(String tokenValue) {
|
||||
for (TokenSign tokenSign : getTokenSignList()) {
|
||||
for (TokenSign tokenSign : tokenSignListCopy()) {
|
||||
if (tokenSign.getValue().equals(tokenValue)) {
|
||||
return tokenSign;
|
||||
}
|
||||
@@ -140,7 +166,7 @@ public class SaSession implements Serializable {
|
||||
*/
|
||||
public void addTokenSign(TokenSign tokenSign) {
|
||||
// 如果已经存在于列表中,则无需再次添加
|
||||
for (TokenSign tokenSign2 : getTokenSignList()) {
|
||||
for (TokenSign tokenSign2 : tokenSignListCopy()) {
|
||||
if (tokenSign2.getValue().equals(tokenSign.getValue())) {
|
||||
return;
|
||||
}
|
||||
@@ -154,7 +180,7 @@ public class SaSession implements Serializable {
|
||||
* 添加一个token签名
|
||||
*
|
||||
* @param tokenValue token值
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
*/
|
||||
public void addTokenSign(String tokenValue, String device) {
|
||||
addTokenSign(new TokenSign(tokenValue, device));
|
||||
|
||||
@@ -23,7 +23,7 @@ public class TokenSign implements Serializable {
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 所在设备标识
|
||||
* 所属设备类型
|
||||
*/
|
||||
private String device;
|
||||
|
||||
@@ -35,7 +35,7 @@ public class TokenSign implements Serializable {
|
||||
* 构建一个
|
||||
*
|
||||
* @param value token值
|
||||
* @param device 所在设备标识
|
||||
* @param device 所属设备类型
|
||||
*/
|
||||
public TokenSign(String value, String device) {
|
||||
this.value = value;
|
||||
@@ -50,7 +50,7 @@ public class TokenSign implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token登录设备
|
||||
* @return 所属设备类型
|
||||
*/
|
||||
public String getDevice() {
|
||||
return device;
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package cn.dev33.satoken.sign;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import cn.dev33.satoken.secure.SaSecureUtil;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
/**
|
||||
* 参数签名算法
|
||||
*
|
||||
* @author kong
|
||||
* @since: 2022-4-27
|
||||
*/
|
||||
public interface SaSignTemplate {
|
||||
|
||||
/**
|
||||
* 将所有参数连接成一个字符串
|
||||
* @param paramsMap 参数列表
|
||||
* @return 字符串
|
||||
*/
|
||||
public default String joinParams(Map<String, Object> paramsMap) {
|
||||
// 保证字段按照字典顺序排列
|
||||
if(paramsMap instanceof TreeMap == false) {
|
||||
paramsMap = new TreeMap<>(paramsMap);
|
||||
}
|
||||
|
||||
// 按照 k1=v1&k2=v2&k3=v3 排列
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String key : paramsMap.keySet()) {
|
||||
Object value = paramsMap.get(key);
|
||||
if(SaFoxUtil.isEmpty(value) == false) {
|
||||
sb.append(key).append("=").append(value).append("&");
|
||||
}
|
||||
}
|
||||
|
||||
// 删除最后一位 &
|
||||
if(sb.length() > 0) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
|
||||
// .
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建签名:md5(paramsStr + keyStr)
|
||||
* @param paramsMap 参数列表
|
||||
* @param key 秘钥
|
||||
* @return 签名
|
||||
*/
|
||||
public default String createSign(Map<String, Object> paramsMap, String key) {
|
||||
String paramsStr = joinParams(paramsMap);
|
||||
String fullStr = paramsStr + "&key=" + key;
|
||||
return SaSecureUtil.md5(fullStr);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package cn.dev33.satoken.sign;
|
||||
|
||||
/**
|
||||
* 参数签名算法 [默认实现类]
|
||||
*
|
||||
* @author kong
|
||||
* @since: 2022-4-27
|
||||
*/
|
||||
public class SaSignTemplateDefaultImpl implements SaSignTemplate {
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import java.util.Map;
|
||||
public class SaLoginConfig {
|
||||
|
||||
/**
|
||||
* @param device 此次登录的客户端设备标识
|
||||
* @param device 此次登录的客户端设备类型
|
||||
* @return SaLoginModel配置对象
|
||||
*/
|
||||
public static SaLoginModel setDevice(String device) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import cn.dev33.satoken.util.SaTokenConsts;
|
||||
public class SaLoginModel {
|
||||
|
||||
/**
|
||||
* 此次登录的客户端设备标识
|
||||
* 此次登录的客户端设备类型
|
||||
*/
|
||||
public String device;
|
||||
|
||||
@@ -42,14 +42,14 @@ public class SaLoginModel {
|
||||
|
||||
|
||||
/**
|
||||
* @return 此次登录的客户端设备标识
|
||||
* @return 此次登录的客户端设备类型
|
||||
*/
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param device 此次登录的客户端设备标识
|
||||
* @param device 此次登录的客户端设备类型
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel setDevice(String device) {
|
||||
|
||||
@@ -35,7 +35,7 @@ public class SaTokenInfo {
|
||||
/** token剩余无操作有效时间 (单位: 秒) */
|
||||
public long tokenActivityTimeout;
|
||||
|
||||
/** 登录设备标识 */
|
||||
/** 登录设备类型 */
|
||||
public String loginDevice;
|
||||
|
||||
/** 自定义数据 */
|
||||
@@ -170,14 +170,14 @@ public class SaTokenInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 登录设备标识
|
||||
* @return 登录设备类型
|
||||
*/
|
||||
public String getLoginDevice() {
|
||||
return loginDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loginDevice 登录设备标识
|
||||
* @param loginDevice 登录设备类型
|
||||
*/
|
||||
public void setLoginDevice(String loginDevice) {
|
||||
this.loginDevice = loginDevice;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
@@ -84,7 +87,7 @@ public class StpLogic {
|
||||
/**
|
||||
* 创建一个TokenValue
|
||||
* @param loginId loginId
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
* @param timeout 过期时间
|
||||
* @param extraData 扩展信息
|
||||
* @return 生成的tokenValue
|
||||
@@ -251,9 +254,9 @@ public class StpLogic {
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话登录,并指定登录设备
|
||||
* 会话登录,并指定登录设备类型
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
*/
|
||||
public void login(Object id, String device) {
|
||||
login(id, new SaLoginModel().setDevice(device));
|
||||
@@ -315,7 +318,12 @@ public class StpLogic {
|
||||
if(config.getIsConcurrent()) {
|
||||
// 如果配置为共享token, 则尝试从Session签名记录里取出token
|
||||
if(getConfigOfIsShare()) {
|
||||
tokenValue = getTokenValueByLoginId(id, loginModel.getDeviceOrDefault());
|
||||
// 为确保 jwt-simple 模式的 token Extra 数据生成不受旧token影响,这里必须确保 is-share 配置项在 ExtraData 为空时才可以生效
|
||||
if(loginModel.getExtraData() == null || loginModel.getExtraData().size() == 0) {
|
||||
tokenValue = getTokenValueByLoginId(id, loginModel.getDeviceOrDefault());
|
||||
}
|
||||
} else {
|
||||
//
|
||||
}
|
||||
} else {
|
||||
// --- 如果不允许并发登录,则将这个账号的历史登录标记为:被顶下线
|
||||
@@ -345,7 +353,12 @@ public class StpLogic {
|
||||
setLastActivityToNow(tokenValue);
|
||||
|
||||
// $$ 通知监听器,账号xxx 登录成功
|
||||
SaManager.getSaTokenListener().doLogin(loginType, id, loginModel);
|
||||
SaManager.getSaTokenListener().doLogin(loginType, id, tokenValue, loginModel);
|
||||
|
||||
// 检查此账号会话数量是否超出最大值
|
||||
if(config.getMaxLoginCount() != -1) {
|
||||
logoutByMaxLoginCount(id, session, null, config.getMaxLoginCount());
|
||||
}
|
||||
|
||||
// 返回Token
|
||||
return tokenValue;
|
||||
@@ -383,20 +396,64 @@ public class StpLogic {
|
||||
public void logout(Object loginId) {
|
||||
logout(loginId, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id 和 设备标识
|
||||
* 会话注销,根据账号id 和 设备类型
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
* @param device 设备类型 (填null代表注销所有设备类型)
|
||||
*/
|
||||
public void logout(Object loginId, String device) {
|
||||
clearTokenCommonMethod(loginId, device, tokenValue -> {
|
||||
SaSession session = getSessionByLoginId(loginId, false);
|
||||
if(session != null) {
|
||||
for (TokenSign tokenSign: session.tokenSignListCopyByDevice(device)) {
|
||||
// 清理: token签名、token最后活跃时间
|
||||
String tokenValue = tokenSign.getValue();
|
||||
session.removeTokenSign(tokenValue);
|
||||
clearLastActivity(tokenValue);
|
||||
// 删除Token-Id映射 & 清除Token-Session
|
||||
deleteTokenToIdMapping(tokenValue);
|
||||
deleteTokenSession(tokenValue);
|
||||
SaManager.getSaTokenListener().doLogout(loginType, loginId, tokenValue);
|
||||
}
|
||||
// 注销 Session
|
||||
session.logoutByTokenSignCountToZero();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id 和 设备类型 和 最大同时在线数量
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param session 此账号的 Session 对象,可填写null,框架将自动获取
|
||||
* @param device 设备类型 (填null代表注销所有设备类型)
|
||||
* @param maxLoginCount 保留最近的几次登录
|
||||
*/
|
||||
public void logoutByMaxLoginCount(Object loginId, SaSession session, String device, int maxLoginCount) {
|
||||
if(session == null) {
|
||||
session = getSessionByLoginId(loginId, false);
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
List<TokenSign> list = session.tokenSignListCopyByDevice(device);
|
||||
// 遍历操作
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
// 只操作前n条
|
||||
if(i >= list.size() - maxLoginCount) {
|
||||
continue;
|
||||
}
|
||||
// 清理: token签名、token最后活跃时间
|
||||
String tokenValue = list.get(i).getValue();
|
||||
session.removeTokenSign(tokenValue);
|
||||
clearLastActivity(tokenValue);
|
||||
// 删除Token-Id映射 & 清除Token-Session
|
||||
deleteTokenToIdMapping(tokenValue);
|
||||
deleteTokenSession(tokenValue);
|
||||
SaManager.getSaTokenListener().doLogout(loginType, loginId, tokenValue);
|
||||
}, true);
|
||||
}
|
||||
// 注销 Session
|
||||
session.logoutByTokenSignCountToZero();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -444,18 +501,27 @@ public class StpLogic {
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人下线,根据账号id 和 设备标识
|
||||
* 踢人下线,根据账号id 和 设备类型
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表踢出所有设备)
|
||||
* @param device 设备类型 (填null代表踢出所有设备类型)
|
||||
*/
|
||||
public void kickout(Object loginId, String device) {
|
||||
clearTokenCommonMethod(loginId, device, tokenValue -> {
|
||||
// 将此 token 标记为已被踢下线
|
||||
updateTokenToIdMapping(tokenValue, NotLoginException.KICK_OUT);
|
||||
SaManager.getSaTokenListener().doKickout(loginType, loginId, tokenValue);
|
||||
}, true);
|
||||
SaSession session = getSessionByLoginId(loginId, false);
|
||||
if(session != null) {
|
||||
for (TokenSign tokenSign: session.tokenSignListCopyByDevice(device)) {
|
||||
// 清理: token签名、token最后活跃时间
|
||||
String tokenValue = tokenSign.getValue();
|
||||
session.removeTokenSign(tokenValue);
|
||||
clearLastActivity(tokenValue);
|
||||
// 将此 token 标记为已被踢下线
|
||||
updateTokenToIdMapping(tokenValue, NotLoginException.KICK_OUT);
|
||||
SaManager.getSaTokenListener().doKickout(loginType, loginId, tokenValue);
|
||||
}
|
||||
// 注销 Session
|
||||
session.logoutByTokenSignCountToZero();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -491,51 +557,25 @@ public class StpLogic {
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶人下线,根据账号id 和 设备标识
|
||||
* 顶人下线,根据账号id 和 设备类型
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表顶替所有设备)
|
||||
* @param device 设备类型 (填null代表顶替所有设备类型)
|
||||
*/
|
||||
public void replaced(Object loginId, String device) {
|
||||
clearTokenCommonMethod(loginId, device, tokenValue -> {
|
||||
// 将此 token 标记为已被顶替
|
||||
updateTokenToIdMapping(tokenValue, NotLoginException.BE_REPLACED);
|
||||
SaManager.getSaTokenListener().doReplaced(loginType, loginId, tokenValue);
|
||||
}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装 注销、踢人、顶人 三个动作的相同代码(无API含义方法)
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @param appendFun 追加操作
|
||||
* @param isLogoutSession 是否注销 User-Session
|
||||
*/
|
||||
protected void clearTokenCommonMethod(Object loginId, String device, Consumer<String> appendFun, boolean isLogoutSession) {
|
||||
// 1. 如果此账号尚未登录,则不执行任何操作
|
||||
SaSession session = getSessionByLoginId(loginId, false);
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
// 2. 循环token签名列表,开始删除相关信息
|
||||
for (TokenSign tokenSign : session.getTokenSignList()) {
|
||||
if(device == null || tokenSign.getDevice().equals(device)) {
|
||||
// -------- 共有操作
|
||||
// s1. 获取token
|
||||
if(session != null) {
|
||||
for (TokenSign tokenSign: session.tokenSignListCopyByDevice(device)) {
|
||||
// 清理: token签名、token最后活跃时间
|
||||
String tokenValue = tokenSign.getValue();
|
||||
// s2. 清理掉[token-last-activity]
|
||||
session.removeTokenSign(tokenValue);
|
||||
clearLastActivity(tokenValue);
|
||||
// s3. 从token签名列表移除
|
||||
session.removeTokenSign(tokenValue);
|
||||
// -------- 追加操作
|
||||
appendFun.accept(tokenValue);
|
||||
// 将此 token 标记为已被顶替
|
||||
updateTokenToIdMapping(tokenValue, NotLoginException.BE_REPLACED);
|
||||
SaManager.getSaTokenListener().doReplaced(loginType, loginId, tokenValue);
|
||||
}
|
||||
}
|
||||
// 3. 尝试注销session
|
||||
if(isLogoutSession) {
|
||||
session.logoutByTokenSignCountToZero();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 会话查询
|
||||
@@ -957,7 +997,7 @@ public class StpLogic {
|
||||
|
||||
/**
|
||||
* 续签当前token:(将 [最后操作时间] 更新为当前时间戳)
|
||||
* <h1>请注意: 即时token已经 [临时过期] 也可续签成功,
|
||||
* <h1>请注意: 即使token已经 [临时过期] 也可续签成功,
|
||||
* 如果此场景下需要提示续签失败,可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
|
||||
*/
|
||||
public void updateLastActivityToNow() {
|
||||
@@ -1347,11 +1387,11 @@ public class StpLogic {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id指定设备端的tokenValue
|
||||
* 获取指定账号id指定设备类型端的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识,填null代表不限设备
|
||||
* @param device 设备类型,填null代表不限设备类型
|
||||
* @return token值
|
||||
*/
|
||||
public String getTokenValueByLoginId(Object loginId, String device) {
|
||||
@@ -1369,9 +1409,9 @@ public class StpLogic {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id指定设备端的tokenValue 集合
|
||||
* 获取指定账号id指定设备类型端的tokenValue 集合
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识,填null代表不限设备
|
||||
* @param device 设备类型,填null代表不限设备类型
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public List<String> getTokenValueListByLoginId(Object loginId, String device) {
|
||||
@@ -1381,7 +1421,7 @@ public class StpLogic {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 遍历解析
|
||||
List<TokenSign> tokenSignList = session.getTokenSignList();
|
||||
List<TokenSign> tokenSignList = session.tokenSignListCopy();
|
||||
List<String> tokenValueList = new ArrayList<>();
|
||||
for (TokenSign tokenSign : tokenSignList) {
|
||||
if(device == null || tokenSign.getDevice().equals(device)) {
|
||||
@@ -1392,8 +1432,8 @@ public class StpLogic {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前会话的登录设备
|
||||
* @return 当前令牌的登录设备
|
||||
* 返回当前会话的登录设备类型
|
||||
* @return 当前令牌的登录设备类型
|
||||
*/
|
||||
public String getLoginDevice() {
|
||||
// 如果没有token,直接返回 null
|
||||
@@ -1411,7 +1451,7 @@ public class StpLogic {
|
||||
return null;
|
||||
}
|
||||
// 遍历解析
|
||||
List<TokenSign> tokenSignList = session.getTokenSignList();
|
||||
List<TokenSign> tokenSignList = session.tokenSignListCopy();
|
||||
for (TokenSign tokenSign : tokenSignList) {
|
||||
if(tokenSign.getValue().equals(tokenValue)) {
|
||||
return tokenSign.getDevice();
|
||||
@@ -1799,10 +1839,10 @@ public class StpLogic {
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout(id) ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 会话注销,根据账号id and 设备标识 (踢人下线)
|
||||
* 会话注销,根据账号id and 设备类型 (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
* @param device 设备类型 (填null代表注销所有设备类型)
|
||||
*/
|
||||
@Deprecated
|
||||
public void logoutByLoginId(Object loginId, String device) {
|
||||
@@ -1812,7 +1852,7 @@ public class StpLogic {
|
||||
/**
|
||||
* 创建一个TokenValue
|
||||
* @param loginId loginId
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
* @param timeout 过期时间
|
||||
* @return 生成的tokenValue
|
||||
*/
|
||||
|
||||
@@ -106,9 +106,9 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话登录,并指定登录设备
|
||||
* 会话登录,并指定登录设备类型
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
*/
|
||||
public static void login(Object id, String device) {
|
||||
stpLogic.login(id, device);
|
||||
@@ -169,10 +169,10 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id 和 设备标识
|
||||
* 会话注销,根据账号id 和 设备类型
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
* @param device 设备类型 (填null代表注销所有设备类型)
|
||||
*/
|
||||
public static void logout(Object loginId, String device) {
|
||||
stpLogic.logout(loginId, device);
|
||||
@@ -198,11 +198,11 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人下线,根据账号id 和 设备标识
|
||||
* 踢人下线,根据账号id 和 设备类型
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表踢出所有设备)
|
||||
* @param device 设备类型 (填null代表踢出所有设备类型)
|
||||
*/
|
||||
public static void kickout(Object loginId, String device) {
|
||||
stpLogic.kickout(loginId, device);
|
||||
@@ -219,11 +219,11 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶人下线,根据账号id 和 设备标识
|
||||
* 顶人下线,根据账号id 和 设备类型
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表顶替所有设备)
|
||||
* @param device 设备类型 (填null代表顶替所有设备类型)
|
||||
*/
|
||||
public static void replaced(Object loginId, String device) {
|
||||
stpLogic.replaced(loginId, device);
|
||||
@@ -395,7 +395,7 @@ public class StpUtil {
|
||||
|
||||
/**
|
||||
* 续签当前token:(将 [最后操作时间] 更新为当前时间戳)
|
||||
* <h1>请注意: 即时token已经 [临时过期] 也可续签成功,
|
||||
* <h1>请注意: 即使token已经 [临时过期] 也可续签成功,
|
||||
* 如果此场景下需要提示续签失败,可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
|
||||
*/
|
||||
public static void updateLastActivityToNow() {
|
||||
@@ -630,11 +630,11 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id指定设备端的tokenValue
|
||||
* 获取指定账号id指定设备类型端的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
* @return token值
|
||||
*/
|
||||
public static String getTokenValueByLoginId(Object loginId, String device) {
|
||||
@@ -651,9 +651,9 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id指定设备端的tokenValue 集合
|
||||
* 获取指定账号id指定设备类型端的tokenValue 集合
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
|
||||
@@ -661,8 +661,8 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前会话的登录设备
|
||||
* @return 当前令牌的登录设备
|
||||
* 返回当前会话的登录设备类型
|
||||
* @return 当前令牌的登录设备类型
|
||||
*/
|
||||
public static String getLoginDevice() {
|
||||
return stpLogic.getLoginDevice();
|
||||
@@ -847,9 +847,9 @@ public class StpUtil {
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* 在当前会话上登录id, 并指定登录设备类型
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setLoginId(Object loginId, String device) {
|
||||
@@ -859,7 +859,7 @@ public class StpUtil {
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* 在当前会话上登录id, 并指定登录设备类型
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
*/
|
||||
@@ -895,10 +895,10 @@ public class StpUtil {
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 会话注销,根据账号id and 设备标识 (踢人下线)
|
||||
* 会话注销,根据账号id and 设备类型 (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
* @param device 设备类型 (填null代表注销所有设备类型)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void logoutByLoginId(Object loginId, String device) {
|
||||
|
||||
@@ -4,12 +4,21 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
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.basic.SaBasicUtil;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
/**
|
||||
* Sa-Token 策略对象
|
||||
@@ -27,7 +36,6 @@ import cn.dev33.satoken.session.SaSession;
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public final class SaStrategy {
|
||||
|
||||
private SaStrategy() {
|
||||
@@ -47,7 +55,34 @@ public final class SaStrategy {
|
||||
* <p> 参数 [账号id, 账号类型]
|
||||
*/
|
||||
public BiFunction<Object, String, String> createToken = (loginId, loginType) -> {
|
||||
return SaManager.getSaTokenAction().createToken(loginId, loginType);
|
||||
// 根据配置的tokenStyle生成不同风格的token
|
||||
String tokenStyle = SaManager.getConfig().getTokenStyle();
|
||||
// uuid
|
||||
if(SaTokenConsts.TOKEN_STYLE_UUID.equals(tokenStyle)) {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
// 简单uuid (不带下划线)
|
||||
if(SaTokenConsts.TOKEN_STYLE_SIMPLE_UUID.equals(tokenStyle)) {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
// 32位随机字符串
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_32.equals(tokenStyle)) {
|
||||
return SaFoxUtil.getRandomString(32);
|
||||
}
|
||||
// 64位随机字符串
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_64.equals(tokenStyle)) {
|
||||
return SaFoxUtil.getRandomString(64);
|
||||
}
|
||||
// 128位随机字符串
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_128.equals(tokenStyle)) {
|
||||
return SaFoxUtil.getRandomString(128);
|
||||
}
|
||||
// tik风格 (2_14_16)
|
||||
if(SaTokenConsts.TOKEN_STYLE_TIK.equals(tokenStyle)) {
|
||||
return SaFoxUtil.getRandomString(2) + "_" + SaFoxUtil.getRandomString(14) + "_" + SaFoxUtil.getRandomString(16) + "__";
|
||||
}
|
||||
// 默认,还是uuid
|
||||
return UUID.randomUUID().toString();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -55,7 +90,7 @@ public final class SaStrategy {
|
||||
* <p> 参数 [SessionId]
|
||||
*/
|
||||
public Function<String, SaSession> createSession = (sessionId) -> {
|
||||
return SaManager.getSaTokenAction().createSession(sessionId);
|
||||
return new SaSession(sessionId);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -63,7 +98,26 @@ public final class SaStrategy {
|
||||
* <p> 参数 [集合, 元素]
|
||||
*/
|
||||
public BiFunction<List<String>, String, Boolean> hasElement = (list, element) -> {
|
||||
return SaManager.getSaTokenAction().hasElement(list, element);
|
||||
|
||||
// 空集合直接返回false
|
||||
if(list == null || list.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 先尝试一下简单匹配,如果可以匹配成功则无需继续模糊匹配
|
||||
if (list.contains(element)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 开始模糊匹配
|
||||
for (String patt : list) {
|
||||
if(SaFoxUtil.vagueMatch(patt, element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 走出for循环说明没有一个元素可以匹配成功
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -83,9 +137,37 @@ public final class SaStrategy {
|
||||
* 对一个 [元素] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数 [element元素]
|
||||
*/
|
||||
public Consumer<AnnotatedElement> checkElementAnnotation = (element) -> {
|
||||
// 为了兼容旧版本
|
||||
SaManager.getSaTokenAction().validateAnnotation(element);
|
||||
public Consumer<AnnotatedElement> checkElementAnnotation = (target) -> {
|
||||
// 校验 @SaCheckLogin 注解
|
||||
SaCheckLogin checkLogin = (SaCheckLogin) SaStrategy.me.getAnnotation.apply(target, SaCheckLogin.class);
|
||||
if(checkLogin != null) {
|
||||
SaManager.getStpLogic(checkLogin.type()).checkByAnnotation(checkLogin);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckRole 注解
|
||||
SaCheckRole checkRole = (SaCheckRole) SaStrategy.me.getAnnotation.apply(target, SaCheckRole.class);
|
||||
if(checkRole != null) {
|
||||
SaManager.getStpLogic(checkRole.type()).checkByAnnotation(checkRole);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckPermission 注解
|
||||
SaCheckPermission checkPermission = (SaCheckPermission) SaStrategy.me.getAnnotation.apply(target, SaCheckPermission.class);
|
||||
if(checkPermission != null) {
|
||||
SaManager.getStpLogic(checkPermission.type()).checkByAnnotation(checkPermission);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckSafe 注解
|
||||
SaCheckSafe checkSafe = (SaCheckSafe) SaStrategy.me.getAnnotation.apply(target, SaCheckSafe.class);
|
||||
if(checkSafe != null) {
|
||||
SaManager.getStpLogic(checkSafe.type()).checkByAnnotation(checkSafe);
|
||||
}
|
||||
|
||||
// 校验 @SaCheckBasic 注解
|
||||
SaCheckBasic checkBasic = (SaCheckBasic) SaStrategy.me.getAnnotation.apply(target, SaCheckBasic.class);
|
||||
if(checkBasic != null) {
|
||||
SaBasicUtil.check(checkBasic.realm(), checkBasic.account());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,12 +26,16 @@ public class SaFoxUtil {
|
||||
* 打印 Sa-Token 版本字符画
|
||||
*/
|
||||
public static void printSaToken() {
|
||||
String str = "____ ____ ___ ____ _ _ ____ _ _ \r\n" + "[__ |__| __ | | | |_/ |___ |\\ | \r\n"
|
||||
String str = ""
|
||||
+ "____ ____ ___ ____ _ _ ____ _ _ \r\n"
|
||||
+ "[__ |__| __ | | | |_/ |___ |\\ | \r\n"
|
||||
+ "___] | | | |__| | \\_ |___ | \\| "
|
||||
// + SaTokenConsts.VERSION_NO
|
||||
// + "sa-token:"
|
||||
+ "\r\n" + "DevDoc:" + SaTokenConsts.DEV_DOC_URL // + "\r\n";
|
||||
// + "\r\n" + "DevDoc:" + SaTokenConsts.DEV_DOC_URL // + "\r\n";
|
||||
+ "\r\n" + SaTokenConsts.DEV_DOC_URL // + "\r\n";
|
||||
+ " (" + SaTokenConsts.VERSION_NO + ")"
|
||||
+ "\r\n" + "GitHub:" + SaTokenConsts.GITHUB_URL // + "\r\n";
|
||||
// + "\r\n" + "GitHub:" + SaTokenConsts.GITHUB_URL // + "\r\n";
|
||||
;
|
||||
System.out.println(str);
|
||||
}
|
||||
|
||||
@@ -21,11 +21,33 @@ public class SaResult extends LinkedHashMap<String, Object> implements Serializa
|
||||
public static final int CODE_SUCCESS = 200;
|
||||
public static final int CODE_ERROR = 500;
|
||||
|
||||
/**
|
||||
* 构建
|
||||
*/
|
||||
public SaResult() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建
|
||||
* @param code 状态码
|
||||
* @param msg 信息
|
||||
* @param data 数据
|
||||
*/
|
||||
public SaResult(int code, String msg, Object data) {
|
||||
this.setCode(code);
|
||||
this.setMsg(msg);
|
||||
this.setData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 Map 快速构建
|
||||
* @param map /
|
||||
*/
|
||||
public SaResult(Map<String, Object> map) {
|
||||
for (String key: map.keySet()) {
|
||||
this.set(key, map.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取code
|
||||
@@ -138,9 +160,16 @@ public class SaResult extends LinkedHashMap<String, Object> implements Serializa
|
||||
public String toString() {
|
||||
return "{"
|
||||
+ "\"code\": " + this.getCode()
|
||||
+ ", \"msg\": \"" + this.getMsg() + "\""
|
||||
+ ", \"data\": \"" + this.getData() + "\""
|
||||
+ ", \"msg\": " + transValue(this.getMsg())
|
||||
+ ", \"data\": " + transValue(this.getData())
|
||||
+ "}";
|
||||
}
|
||||
|
||||
private String transValue(Object value) {
|
||||
if(value instanceof String) {
|
||||
return "\"" + value + "\"";
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,10 +13,15 @@ public class SaTokenConsts {
|
||||
/**
|
||||
* Sa-Token 当前版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.29.0";
|
||||
public static final String VERSION_NO = "v1.30.0";
|
||||
|
||||
/**
|
||||
* Sa-Token 开源地址
|
||||
* Sa-Token 开源地址 Gitee
|
||||
*/
|
||||
public static final String GITEE_URL = "https://gitee.com/dromara/sa-token";
|
||||
|
||||
/**
|
||||
* Sa-Token 开源地址 GitHub
|
||||
*/
|
||||
public static final String GITHUB_URL = "https://github.com/dromara/sa-token";
|
||||
|
||||
@@ -43,7 +48,7 @@ public class SaTokenConsts {
|
||||
public static final String TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY = "TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY_";
|
||||
|
||||
/**
|
||||
* 常量key标记: 在登录时,默认使用的设备名称
|
||||
* 常量key标记: 在登录时,默认使用的设备类型
|
||||
*/
|
||||
public static final String DEFAULT_LOGIN_DEVICE = "default-device";
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<version>2.5.12</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<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>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<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>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<version>2.5.12</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,7 @@ 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.jwt.StpLogicJwtForSimple;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
*/
|
||||
@Bean
|
||||
public StpLogic getStpLogicJwt() {
|
||||
return new StpLogicJwtForStyle();
|
||||
return new StpLogicJwtForSimple();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public class GlobalException {
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||
} else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getPermission());
|
||||
} else { // 普通异常, 输出:500 + 异常信息
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
@@ -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.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.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.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -37,5 +37,4 @@ spring:
|
||||
min-idle: 0
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<version>2.5.12</version>
|
||||
<!-- <version>2.6.0</version> -->
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-web</artifactId>
|
||||
<version>1.6.1</version>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
|
||||
@@ -31,7 +31,7 @@ public class GlobalException implements EventListener<Throwable> {
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||
} else if (e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getPermission());
|
||||
} else if (e instanceof DisableLoginException) { // 如果是被封禁异常
|
||||
DisableLoginException ee = (DisableLoginException) e;
|
||||
aj = AjaxJson.getNotJur("账号被封禁:" + ee.getDisableTime() + "秒后解封");
|
||||
|
||||
@@ -192,7 +192,7 @@ public class TestController {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试登录接口, 按照设备登录, 浏览器访问: http://localhost:8081/test/login2
|
||||
// 测试登录接口, 按照设备类型登录, 浏览器访问: http://localhost:8081/test/login2
|
||||
@Mapping("login2")
|
||||
public AjaxJson login2(@Param(defaultValue="10001") String id, @Param(defaultValue="PC") String device) {
|
||||
StpUtil.login(id, device);
|
||||
@@ -226,7 +226,7 @@ public class TestController {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试指定设备登录 浏览器访问: http://localhost:8081/test/loginByDevice
|
||||
// 测试指定设备类型登录 浏览器访问: http://localhost:8081/test/loginByDevice
|
||||
@Mapping("loginByDevice")
|
||||
public AjaxJson loginByDevice() {
|
||||
System.out.println("--------------");
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
||||
@@ -0,0 +1,72 @@
|
||||
<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>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-springboot-redis</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.12</version>
|
||||
<!-- <version>1.5.9.RELEASE</version> -->
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot依赖 -->
|
||||
<dependency>
|
||||
<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/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用jdk默认序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</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>
|
||||
|
||||
<!-- 提供Redis连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
/**
|
||||
* Sa-Token整合SpringBoot 示例,整合redis
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaTokenDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenDemoApplication.class, args);
|
||||
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.pj.current;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalException {
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@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.getPermission());
|
||||
}
|
||||
else if(e instanceof DisableLoginException) { // 如果是被封禁异常
|
||||
DisableLoginException ee = (DisableLoginException) e;
|
||||
aj = AjaxJson.getNotJur("账号被封禁:" + ee.getDisableTime() + "秒后解封");
|
||||
}
|
||||
else { // 普通异常, 输出:500 + 异常信息
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回给前端
|
||||
return aj;
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
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 {
|
||||
|
||||
@RequestMapping("/error")
|
||||
public Object error(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
response.setStatus(200);
|
||||
return SaResult.get(404, "not found", null);
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
* [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 全局过滤器]
|
||||
*/
|
||||
@Bean
|
||||
public SaServletFilter getSaServletFilter() {
|
||||
return new SaServletFilter()
|
||||
|
||||
// 指定 [拦截路由] 与 [放行路由]
|
||||
.addInclude("/**")// .addExclude("/favicon.ico")
|
||||
|
||||
// 认证函数: 每次请求执行
|
||||
.setAuth(obj -> {
|
||||
// System.out.println("---------- sa全局认证 " + SaHolder.getRequest().getRequestPath());
|
||||
|
||||
})
|
||||
|
||||
// 异常处理函数:每次认证函数发生异常时执行此函数
|
||||
.setError(e -> {
|
||||
System.out.println("---------- sa全局异常 ");
|
||||
return AjaxJson.getError(e.getMessage());
|
||||
})
|
||||
|
||||
// 前置函数:在每次认证函数之前执行
|
||||
.setBeforeAuth(r -> {
|
||||
// ---------- 设置一些安全响应头 ----------
|
||||
SaHolder.getResponse()
|
||||
// 服务器名称
|
||||
.setServer("sa-server")
|
||||
// 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
|
||||
.setHeader("X-Frame-Options", "SAMEORIGIN")
|
||||
// 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面
|
||||
.setHeader("X-XSS-Protection", "1; mode=block")
|
||||
// 禁用浏览器内容嗅探
|
||||
.setHeader("X-Content-Type-Options", "nosniff")
|
||||
;
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写 Sa-Token 框架内部算法策略
|
||||
*/
|
||||
@Autowired
|
||||
public void rewriteSaStrategy() {
|
||||
// 重写Sa-Token的注解处理器,增加注解合并功能
|
||||
SaStrategy.me.getAnnotation = (element, annotationClass) -> {
|
||||
return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
/**
|
||||
* 自定义权限验证接口扩展
|
||||
*/
|
||||
@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的权限码集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("101");
|
||||
list.add("user-add");
|
||||
list.add("user-delete");
|
||||
list.add("user-update");
|
||||
list.add("user-get");
|
||||
list.add("article-get");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的角色标识集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("admin");
|
||||
list.add("super-admin");
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.pj.satoken.at;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
|
||||
/**
|
||||
* 登录认证(User版):只有登录之后才能进入该方法
|
||||
* <p> 可标注在函数、类上(效果等同于标注在此类的所有方法上)
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SaCheckLogin(type = StpUserUtil.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface SaUserCheckLogin {
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.pj.satoken.at;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
|
||||
/**
|
||||
* 权限认证(User版):必须具有指定权限才能进入该方法
|
||||
* <p> 可标注在函数、类上(效果等同于标注在此类的所有方法上)
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SaCheckPermission(type = StpUserUtil.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface SaUserCheckPermission {
|
||||
|
||||
/**
|
||||
* 需要校验的权限码
|
||||
* @return 需要校验的权限码
|
||||
*/
|
||||
@AliasFor(annotation = SaCheckPermission.class)
|
||||
String [] value() default {};
|
||||
|
||||
/**
|
||||
* 验证模式:AND | OR,默认AND
|
||||
* @return 验证模式
|
||||
*/
|
||||
@AliasFor(annotation = SaCheckPermission.class)
|
||||
SaMode mode() default SaMode.AND;
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.pj.satoken.at;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
|
||||
/**
|
||||
* 角色认证(User版):必须具有指定角色标识才能进入该方法
|
||||
* <p> 可标注在函数、类上(效果等同于标注在此类的所有方法上)
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SaCheckRole(type = StpUserUtil.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface SaUserCheckRole {
|
||||
|
||||
/**
|
||||
* 需要校验的角色标识
|
||||
* @return 需要校验的角色标识
|
||||
*/
|
||||
@AliasFor(annotation = SaCheckRole.class)
|
||||
String [] value() default {};
|
||||
|
||||
/**
|
||||
* 验证模式:AND | OR,默认AND
|
||||
* @return 验证模式
|
||||
*/
|
||||
@AliasFor(annotation = SaCheckRole.class)
|
||||
SaMode mode() default SaMode.AND;
|
||||
|
||||
}
|
||||
+912
@@ -0,0 +1,912 @@
|
||||
package com.pj.satoken.at;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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版)
|
||||
* @author kong
|
||||
*/
|
||||
public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* 账号类型标识
|
||||
*/
|
||||
public static final String TYPE = "user";
|
||||
|
||||
/**
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
public static StpLogic stpLogic = new StpLogic(TYPE);
|
||||
|
||||
/**
|
||||
* 获取当前 StpLogic 的账号类型
|
||||
* @return See Note
|
||||
*/
|
||||
public static String getLoginType(){
|
||||
return stpLogic.getLoginType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置 StpLogic 对象
|
||||
* @param stpLogic /
|
||||
*/
|
||||
public static void setStpLogic(StpLogic stpLogic) {
|
||||
StpUtil.stpLogic = stpLogic;
|
||||
// 防止自定义 stpLogic 被覆盖
|
||||
SaManager.putStpLogic(stpLogic);
|
||||
}
|
||||
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
/**
|
||||
* 返回token名称
|
||||
* @return 此StpLogic的token名称
|
||||
*/
|
||||
public static String getTokenName() {
|
||||
return stpLogic.getTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话写入当前TokenValue
|
||||
* @param tokenValue token值
|
||||
*/
|
||||
public static void setTokenValue(String tokenValue){
|
||||
stpLogic.setTokenValue(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话写入当前TokenValue
|
||||
* @param tokenValue token值
|
||||
* @param cookieTimeout Cookie存活时间(秒)
|
||||
*/
|
||||
public static void setTokenValue(String tokenValue, int cookieTimeout){
|
||||
stpLogic.setTokenValue(tokenValue, cookieTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前TokenValue
|
||||
* @return 当前tokenValue
|
||||
*/
|
||||
public static String getTokenValue() {
|
||||
return stpLogic.getTokenValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前TokenValue (不裁剪前缀)
|
||||
* @return /
|
||||
*/
|
||||
public static String getTokenValueNotCut(){
|
||||
return stpLogic.getTokenValueNotCut();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的Token信息
|
||||
* @return token信息
|
||||
*/
|
||||
public static SaTokenInfo getTokenInfo() {
|
||||
return stpLogic.getTokenInfo();
|
||||
}
|
||||
|
||||
|
||||
// =================== 登录相关操作 ===================
|
||||
|
||||
// --- 登录
|
||||
|
||||
/**
|
||||
* 会话登录
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
*/
|
||||
public static void login(Object id) {
|
||||
stpLogic.login(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话登录,并指定登录设备类型
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
* @param device 设备类型
|
||||
*/
|
||||
public static void login(Object id, String device) {
|
||||
stpLogic.login(id, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话登录,并指定是否 [记住我]
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
*/
|
||||
public static void login(Object id, boolean isLastingCookie) {
|
||||
stpLogic.login(id, isLastingCookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话登录,并指定所有登录参数Model
|
||||
* @param id 登录id,建议的类型:(long | int | String)
|
||||
* @param loginModel 此次登录的参数Model
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号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) {
|
||||
stpLogic.logoutByTokenValue(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人下线,根据账号id
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人下线,根据指定 Token
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param tokenValue 指定token
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
* 当前会话是否已经登录
|
||||
* @return 是否已登录
|
||||
*/
|
||||
public static boolean isLogin() {
|
||||
return stpLogic.isLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验当前会话是否已经登录,如未登录,则抛出异常
|
||||
*/
|
||||
public static void checkLogin() {
|
||||
stpLogic.checkLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 如果未登录,则抛出异常
|
||||
* @return 账号id
|
||||
*/
|
||||
public static Object getLoginId() {
|
||||
return stpLogic.getLoginId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 如果未登录,则返回默认值
|
||||
* @param <T> 返回类型
|
||||
* @param defaultValue 默认值
|
||||
* @return 登录id
|
||||
*/
|
||||
public static <T> T getLoginId(T defaultValue) {
|
||||
return stpLogic.getLoginId(defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 如果未登录,则返回null
|
||||
* @return 账号id
|
||||
*/
|
||||
public static Object getLoginIdDefaultNull() {
|
||||
return stpLogic.getLoginIdDefaultNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 并转换为String类型
|
||||
* @return 账号id
|
||||
*/
|
||||
public static String getLoginIdAsString() {
|
||||
return stpLogic.getLoginIdAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 并转换为int类型
|
||||
* @return 账号id
|
||||
*/
|
||||
public static int getLoginIdAsInt() {
|
||||
return stpLogic.getLoginIdAsInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话账号id, 并转换为long类型
|
||||
* @return 账号id
|
||||
*/
|
||||
public static long getLoginIdAsLong() {
|
||||
return stpLogic.getLoginIdAsLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定Token对应的账号id,如果未登录,则返回 null
|
||||
* @param tokenValue token
|
||||
* @return 账号id
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// =================== User-Session 相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回
|
||||
* @param loginId 账号id
|
||||
* @param isCreate 是否新建
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getSessionByLoginId(Object loginId, boolean isCreate) {
|
||||
return stpLogic.getSessionByLoginId(loginId, isCreate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的Session, 如果Session尚未创建,则返回null
|
||||
* @param sessionId SessionId
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getSessionBySessionId(String sessionId) {
|
||||
return stpLogic.getSessionBySessionId(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id的Session,如果Session尚未创建,则新建并返回
|
||||
* @param loginId 账号id
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getSessionByLoginId(Object loginId) {
|
||||
return stpLogic.getSessionByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的Session, 如果Session尚未创建,isCreate=是否新建并返回
|
||||
* @param isCreate 是否新建
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getSession(boolean isCreate) {
|
||||
return stpLogic.getSession(isCreate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的Session,如果Session尚未创建,则新建并返回
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getSession() {
|
||||
return stpLogic.getSession();
|
||||
}
|
||||
|
||||
|
||||
// =================== Token-Session 相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定Token-Session,如果Session尚未创建,则新建并返回
|
||||
* @param tokenValue Token值
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getTokenSessionByToken(String tokenValue) {
|
||||
return stpLogic.getTokenSessionByToken(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前Token-Session,如果Session尚未创建,则新建并返回
|
||||
* @return Session对象
|
||||
*/
|
||||
public static SaSession getTokenSession() {
|
||||
return stpLogic.getTokenSession();
|
||||
}
|
||||
|
||||
|
||||
// =================== [临时有效期] 验证相关 ===================
|
||||
|
||||
/**
|
||||
* 检查当前token 是否已经[临时过期],如果已经过期则抛出异常
|
||||
*/
|
||||
public static void checkActivityTimeout() {
|
||||
stpLogic.checkActivityTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 续签当前token:(将 [最后操作时间] 更新为当前时间戳)
|
||||
* <h1>请注意: 即使token已经 [临时过期] 也可续签成功,
|
||||
* 如果此场景下需要提示续签失败,可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
|
||||
*/
|
||||
public static void updateLastActivityToNow() {
|
||||
stpLogic.updateLastActivityToNow();
|
||||
}
|
||||
|
||||
|
||||
// =================== 过期时间相关 ===================
|
||||
|
||||
/**
|
||||
* 获取当前登录者的 token 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getTokenTimeout() {
|
||||
return stpLogic.getTokenTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getSessionTimeout() {
|
||||
return stpLogic.getSessionTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 Token-Session 剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public static long getTokenSessionTimeout() {
|
||||
return stpLogic.getTokenSessionTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否拥有指定角色, 返回true或false
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
*/
|
||||
public static boolean hasRole(String role) {
|
||||
return stpLogic.hasRole(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:指定账号是否含有指定角色标识, 返回true或false
|
||||
* @param loginId 账号id
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
*/
|
||||
public static boolean hasRole(Object loginId, String role) {
|
||||
return stpLogic.hasRole(loginId, role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
|
||||
* @param roleArray 角色标识数组
|
||||
* @return true或false
|
||||
*/
|
||||
public static boolean hasRoleAnd(String... roleArray){
|
||||
return stpLogic.hasRoleAnd(roleArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
|
||||
* @param roleArray 角色标识数组
|
||||
* @return true或false
|
||||
*/
|
||||
public static boolean hasRoleOr(String... roleArray){
|
||||
return stpLogic.hasRoleOr(roleArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
|
||||
* @param role 角色标识
|
||||
*/
|
||||
public static void checkRole(String role) {
|
||||
stpLogic.checkRole(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
|
||||
* @param roleArray 角色标识数组
|
||||
*/
|
||||
public static void checkRoleAnd(String... roleArray){
|
||||
stpLogic.checkRoleAnd(roleArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否含有指定权限, 返回true或false
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(String permission) {
|
||||
return stpLogic.hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:指定账号id是否含有指定权限, 返回true或false
|
||||
* @param loginId 账号id
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(Object loginId, String permission) {
|
||||
return stpLogic.hasPermission(loginId, permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否含有指定权限, [指定多个,必须全部具有]
|
||||
* @param permissionArray 权限码数组
|
||||
* @return true 或 false
|
||||
*/
|
||||
public static boolean hasPermissionAnd(String... permissionArray){
|
||||
return stpLogic.hasPermissionAnd(permissionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
* @param permissionArray 权限码数组
|
||||
* @return true 或 false
|
||||
*/
|
||||
public static boolean hasPermissionOr(String... permissionArray){
|
||||
return stpLogic.hasPermissionOr(permissionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
* @param permission 权限码
|
||||
*/
|
||||
public static void checkPermission(String permission) {
|
||||
stpLogic.checkPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
|
||||
* @param permissionArray 权限码数组
|
||||
*/
|
||||
public static void checkPermissionAnd(String... permissionArray) {
|
||||
stpLogic.checkPermissionAnd(permissionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
* @param permissionArray 权限码数组
|
||||
*/
|
||||
public static void checkPermissionOr(String... permissionArray) {
|
||||
stpLogic.checkPermissionOr(permissionArray);
|
||||
}
|
||||
|
||||
|
||||
// =================== id 反查token 相关操作 ===================
|
||||
|
||||
/**
|
||||
* 获取指定账号id的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @return token值
|
||||
*/
|
||||
public static String getTokenValueByLoginId(Object loginId) {
|
||||
return stpLogic.getTokenValueByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id指定设备类型端的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @param device 设备类型
|
||||
* @return token值
|
||||
*/
|
||||
public static String getTokenValueByLoginId(Object loginId, String device) {
|
||||
return stpLogic.getTokenValueByLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id的tokenValue集合
|
||||
* @param loginId 账号id
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public static List<String> getTokenValueListByLoginId(Object loginId) {
|
||||
return stpLogic.getTokenValueListByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id指定设备类型端的tokenValue 集合
|
||||
* @param loginId 账号id
|
||||
* @param device 设备类型
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
|
||||
return stpLogic.getTokenValueListByLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前会话的登录设备类型
|
||||
* @return 当前令牌的登录设备类型
|
||||
*/
|
||||
public static String getLoginDevice() {
|
||||
return stpLogic.getLoginDevice();
|
||||
}
|
||||
|
||||
|
||||
// =================== 会话管理 ===================
|
||||
|
||||
/**
|
||||
* 根据条件查询Token
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return token集合
|
||||
*/
|
||||
public static List<String> searchTokenValue(String keyword, int start, int size) {
|
||||
return stpLogic.searchTokenValue(keyword, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询SessionId
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public static List<String> searchSessionId(String keyword, int start, int size) {
|
||||
return stpLogic.searchSessionId(keyword, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询Token专属Session的Id
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public static List<String> searchTokenSessionId(String keyword, int start, int size) {
|
||||
return stpLogic.searchTokenSessionId(keyword, start, size);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 账号封禁 -------------------
|
||||
|
||||
/**
|
||||
* 封禁指定账号
|
||||
* <p> 此方法不会直接将此账号id踢下线,而是在对方再次登录时抛出`DisableLoginException`异常
|
||||
* @param loginId 指定账号id
|
||||
* @param disableTime 封禁时间, 单位: 秒 (-1=永久封禁)
|
||||
*/
|
||||
public static void disable(Object loginId, long disableTime) {
|
||||
stpLogic.disable(loginId, disableTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定账号是否已被封禁 (true=已被封禁, false=未被封禁)
|
||||
* @param loginId 账号id
|
||||
* @return see note
|
||||
*/
|
||||
public static boolean isDisable(Object loginId) {
|
||||
return stpLogic.isDisable(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号剩余封禁时间,单位:秒(-1=永久封禁,-2=未被封禁)
|
||||
* @param loginId 账号id
|
||||
* @return see note
|
||||
*/
|
||||
public static long getDisableTime(Object loginId) {
|
||||
return stpLogic.getDisableTime(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解封指定账号
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void untieDisable(Object loginId) {
|
||||
stpLogic.untieDisable(loginId);
|
||||
}
|
||||
|
||||
|
||||
// =================== 身份切换 ===================
|
||||
|
||||
/**
|
||||
* 临时切换身份为指定账号id
|
||||
* @param loginId 指定loginId
|
||||
*/
|
||||
public static void switchTo(Object loginId) {
|
||||
stpLogic.switchTo(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束临时切换身份
|
||||
*/
|
||||
public static void endSwitch() {
|
||||
stpLogic.endSwitch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前是否正处于[身份临时切换]中
|
||||
* @return 是否正处于[身份临时切换]中
|
||||
*/
|
||||
public static boolean isSwitch() {
|
||||
return stpLogic.isSwitch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在一个代码段里方法内,临时切换身份为指定账号id
|
||||
* @param loginId 指定账号id
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
public static void switchTo(Object loginId, SaFunction function) {
|
||||
stpLogic.switchTo(loginId, function);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 二级认证 -------------------
|
||||
|
||||
/**
|
||||
* 在当前会话 开启二级认证
|
||||
* @param safeTime 维持时间 (单位: 秒)
|
||||
*/
|
||||
public static void openSafe(long safeTime) {
|
||||
stpLogic.openSafe(safeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话 是否处于二级认证时间内
|
||||
* @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
|
||||
*/
|
||||
public static boolean isSafe() {
|
||||
return stpLogic.isSafe();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前会话是否已通过二级认证,如未通过则抛出异常
|
||||
*/
|
||||
public static void checkSafe() {
|
||||
stpLogic.checkSafe();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
|
||||
* @return 剩余有效时间
|
||||
*/
|
||||
public static long getSafeTime() {
|
||||
return stpLogic.getSafeTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话 结束二级认证
|
||||
*/
|
||||
public static void closeSafe() {
|
||||
stpLogic.closeSafe();
|
||||
}
|
||||
|
||||
|
||||
// =================== 历史API,兼容旧版本 ===================
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 获取当前StpLogin的loginKey
|
||||
* @return 当前StpLogin的loginKey
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getLoginKey(){
|
||||
return stpLogic.getLoginType();
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setLoginId(Object loginId) {
|
||||
stpLogic.login(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备类型
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param device 设备类型
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setLoginId(Object loginId, String device) {
|
||||
stpLogic.login(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备类型
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setLoginId(Object loginId, boolean isLastingCookie) {
|
||||
stpLogic.login(loginId, isLastingCookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定所有登录参数Model
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param loginModel 此次登录的参数Model
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setLoginId(Object loginId, SaLoginModel loginModel) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
+80
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
import com.pj.util.Ttime;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 压力测试
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/s-test/")
|
||||
public class StressTestController {
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/s-test/login
|
||||
// 测试前,请先将 is-read-cookie 配置为 false
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login() {
|
||||
// StpUtil.getTokenSession().logout();
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
|
||||
int count = 10; // 循环多少轮
|
||||
int loginCount = 10000; // 每轮循环多少次
|
||||
|
||||
// 循环10次 取平均时间
|
||||
List<Double> list = new ArrayList<>();
|
||||
for (int i = 1; i <= count; i++) {
|
||||
System.out.println("\n---------------------第" + i + "轮---------------------");
|
||||
Ttime t = new Ttime().start();
|
||||
// 每次登录的次数
|
||||
for (int j = 1; j <= loginCount; j++) {
|
||||
StpUtil.login("1000" + j, "PC-" + j);
|
||||
if(j % 1000 == 0) {
|
||||
System.out.println("已登录:" + j);
|
||||
}
|
||||
}
|
||||
t.end();
|
||||
list.add((t.returnMs() + 0.0) / 1000);
|
||||
System.out.println("第" + i + "轮" + "用时:" + t.toString());
|
||||
}
|
||||
// System.out.println(((SaTokenDaoDefaultImpl)SaTokenManager.getSaTokenDao()).dataMap.size());
|
||||
|
||||
System.out.println("\n---------------------测试结果---------------------");
|
||||
System.out.println(list.size() + "次测试: " + list);
|
||||
double ss = 0;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
ss += list.get(i);
|
||||
}
|
||||
System.out.println("平均用时: " + ss / list.size());
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pj.util.AjaxJson;
|
||||
import com.pj.util.Ttime;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.session.SaSessionCustomUtil;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 测试专用Controller
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/test/")
|
||||
public class TestController {
|
||||
|
||||
// 测试登录接口, 浏览器访问: http://localhost:8081/test/login
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login(@RequestParam(defaultValue="10001") String id) {
|
||||
System.out.println("======================= 进入方法,测试登录接口 ========================= ");
|
||||
System.out.println("当前会话的token:" + StpUtil.getTokenValue());
|
||||
System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("当前登录账号:" + StpUtil.getLoginIdDefaultNull());
|
||||
|
||||
StpUtil.login(id); // 在当前会话登录此账号
|
||||
System.out.println("登录成功");
|
||||
System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("当前登录账号:" + StpUtil.getLoginId());
|
||||
// System.out.println("当前登录账号并转为int:" + StpUtil.getLoginIdAsInt());
|
||||
System.out.println("当前登录设备:" + StpUtil.getLoginDevice());
|
||||
// System.out.println("当前token信息:" + StpUtil.getTokenInfo());
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试退出登录 , 浏览器访问: http://localhost:8081/test/logout
|
||||
@RequestMapping("logout")
|
||||
public AjaxJson logout() {
|
||||
StpUtil.logout();
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试角色接口, 浏览器访问: http://localhost:8081/test/testRole
|
||||
@RequestMapping("testRole")
|
||||
public AjaxJson testRole() {
|
||||
System.out.println("======================= 进入方法,测试角色接口 ========================= ");
|
||||
|
||||
System.out.println("是否具有角色标识 user " + StpUtil.hasRole("user"));
|
||||
System.out.println("是否具有角色标识 admin " + StpUtil.hasRole("admin"));
|
||||
|
||||
System.out.println("没有admin权限就抛出异常");
|
||||
StpUtil.checkRole("admin");
|
||||
|
||||
System.out.println("在【admin、user】中只要拥有一个就不会抛出异常");
|
||||
StpUtil.checkRoleOr("admin", "user");
|
||||
|
||||
System.out.println("在【admin、user】中必须全部拥有才不会抛出异常");
|
||||
StpUtil.checkRoleAnd("admin", "user");
|
||||
|
||||
System.out.println("角色测试通过");
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试权限接口, 浏览器访问: http://localhost:8081/test/testJur
|
||||
@RequestMapping("testJur")
|
||||
public AjaxJson testJur() {
|
||||
System.out.println("======================= 进入方法,测试权限接口 ========================= ");
|
||||
|
||||
System.out.println("是否具有权限101" + StpUtil.hasPermission("101"));
|
||||
System.out.println("是否具有权限user-add" + StpUtil.hasPermission("user-add"));
|
||||
System.out.println("是否具有权限article-get" + StpUtil.hasPermission("article-get"));
|
||||
|
||||
System.out.println("没有user-add权限就抛出异常");
|
||||
StpUtil.checkPermission("user-add");
|
||||
|
||||
System.out.println("在【101、102】中只要拥有一个就不会抛出异常");
|
||||
StpUtil.checkPermissionOr("101", "102");
|
||||
|
||||
System.out.println("在【101、102】中必须全部拥有才不会抛出异常");
|
||||
StpUtil.checkPermissionAnd("101", "102");
|
||||
|
||||
System.out.println("权限测试通过");
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试会话session接口, 浏览器访问: http://localhost:8081/test/session
|
||||
@RequestMapping("session")
|
||||
public AjaxJson session() throws JsonProcessingException {
|
||||
System.out.println("======================= 进入方法,测试会话session接口 ========================= ");
|
||||
System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("当前登录账号session的id" + StpUtil.getSession().getId());
|
||||
System.out.println("当前登录账号session的id" + StpUtil.getSession().getId());
|
||||
System.out.println("测试取值name:" + StpUtil.getSession().get("name"));
|
||||
StpUtil.getSession().set("name", new Date()); // 写入一个值
|
||||
System.out.println("测试取值name:" + StpUtil.getSession().get("name"));
|
||||
System.out.println( new ObjectMapper().writeValueAsString(StpUtil.getSession()));
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试自定义session接口, 浏览器访问: http://localhost:8081/test/session2
|
||||
@RequestMapping("session2")
|
||||
public AjaxJson session2() {
|
||||
System.out.println("======================= 进入方法,测试自定义session接口 ========================= ");
|
||||
// 自定义session就是无需登录也可以使用 的session :比如拿用户的手机号当做 key, 来获取 session
|
||||
System.out.println("自定义 session的id为:" + SaSessionCustomUtil.getSessionById("1895544896").getId());
|
||||
System.out.println("测试取值name:" + SaSessionCustomUtil.getSessionById("1895544896").get("name"));
|
||||
SaSessionCustomUtil.getSessionById("1895544896").set("name", "张三"); // 写入值
|
||||
System.out.println("测试取值name:" + SaSessionCustomUtil.getSessionById("1895544896").get("name"));
|
||||
System.out.println("测试取值name:" + SaSessionCustomUtil.getSessionById("1895544896").get("name"));
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// ----------
|
||||
// 测试token专属session, 浏览器访问: http://localhost:8081/test/getTokenSession
|
||||
@RequestMapping("getTokenSession")
|
||||
public AjaxJson getTokenSession() {
|
||||
System.out.println("======================= 进入方法,测试会话session接口 ========================= ");
|
||||
System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("当前token专属session: " + StpUtil.getTokenSession().getId());
|
||||
|
||||
System.out.println("测试取值name:" + StpUtil.getTokenSession().get("name"));
|
||||
StpUtil.getTokenSession().set("name", "张三"); // 写入一个值
|
||||
System.out.println("测试取值name:" + StpUtil.getTokenSession().get("name"));
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 打印当前token信息, 浏览器访问: http://localhost:8081/test/tokenInfo
|
||||
@RequestMapping("tokenInfo")
|
||||
public AjaxJson tokenInfo() {
|
||||
System.out.println("======================= 进入方法,打印当前token信息 ========================= ");
|
||||
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
|
||||
System.out.println(tokenInfo);
|
||||
return AjaxJson.getSuccessData(tokenInfo);
|
||||
}
|
||||
|
||||
// 测试注解式鉴权, 浏览器访问: http://localhost:8081/test/atCheck
|
||||
@SaCheckLogin // 注解式鉴权:当前会话必须登录才能通过
|
||||
@SaCheckRole("super-admin") // 注解式鉴权:当前会话必须具有指定角色标识才能通过
|
||||
@SaCheckPermission("user-add") // 注解式鉴权:当前会话必须具有指定权限才能通过
|
||||
@RequestMapping("atCheck")
|
||||
public AjaxJson atCheck() {
|
||||
System.out.println("======================= 进入方法,测试注解鉴权接口 ========================= ");
|
||||
System.out.println("只有通过注解鉴权,才能进入此方法");
|
||||
// StpUtil.checkActivityTimeout();
|
||||
// StpUtil.updateLastActivityToNow();
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试注解式鉴权, 浏览器访问: http://localhost:8081/test/atJurOr
|
||||
@RequestMapping("atJurOr")
|
||||
@SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR) // 注解式鉴权:只要具有其中一个权限即可通过校验
|
||||
public AjaxJson atJurOr() {
|
||||
return AjaxJson.getSuccessData("用户信息");
|
||||
}
|
||||
|
||||
// [活动时间] 续签: http://localhost:8081/test/rene
|
||||
@RequestMapping("rene")
|
||||
public AjaxJson rene() {
|
||||
StpUtil.checkActivityTimeout();
|
||||
StpUtil.updateLastActivityToNow();
|
||||
return AjaxJson.getSuccess("续签成功");
|
||||
}
|
||||
|
||||
// 测试踢人下线 浏览器访问: http://localhost:8081/test/kickOut
|
||||
@RequestMapping("kickOut")
|
||||
public AjaxJson kickOut() {
|
||||
// 先登录上
|
||||
StpUtil.login(10001);
|
||||
// 踢下线
|
||||
StpUtil.kickout(10001);
|
||||
// 再尝试获取
|
||||
StpUtil.getLoginId();
|
||||
// 返回
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试登录接口, 按照设备登录, 浏览器访问: http://localhost:8081/test/login2
|
||||
@RequestMapping("login2")
|
||||
public AjaxJson login2(@RequestParam(defaultValue="10001") String id, @RequestParam(defaultValue="PC") String device) {
|
||||
StpUtil.login(id, device);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试身份临时切换: http://localhost:8081/test/switchTo
|
||||
@RequestMapping("switchTo")
|
||||
public AjaxJson switchTo() {
|
||||
System.out.println("当前会话身份:" + StpUtil.getLoginIdDefaultNull());
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
StpUtil.switchTo(10044, () -> {
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
System.out.println("当前会话身份已被切换为:" + StpUtil.getLoginId());
|
||||
});
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试会话治理 浏览器访问: http://localhost:8081/test/search
|
||||
@RequestMapping("search")
|
||||
public AjaxJson search() {
|
||||
System.out.println("--------------");
|
||||
Ttime t = new Ttime().start();
|
||||
List<String> tokenValue = StpUtil.searchTokenValue("8feb8265f773", 0, 10);
|
||||
for (String v : tokenValue) {
|
||||
// SaSession session = StpUtil.getSessionBySessionId(sid);
|
||||
System.out.println(v);
|
||||
}
|
||||
System.out.println("用时:" + t.end().toString());
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试指定设备登录 浏览器访问: http://localhost:8081/test/loginByDevice
|
||||
@RequestMapping("loginByDevice")
|
||||
public AjaxJson loginByDevice() {
|
||||
System.out.println("--------------");
|
||||
StpUtil.login(10001, "PC");
|
||||
return AjaxJson.getSuccessData("登录成功");
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
System.out.println("------------进来了");
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test2
|
||||
@RequestMapping("test2")
|
||||
public AjaxJson test2() {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package com.pj.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* ajax请求返回Json格式数据的封装
|
||||
*/
|
||||
public class AjaxJson implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L; // 序列化版本号
|
||||
|
||||
public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||
public static final int CODE_ERROR = 500; // 错误状态码
|
||||
public static final int CODE_WARNING = 501; // 警告状态码
|
||||
public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||
|
||||
public int code; // 状态码
|
||||
public String msg; // 描述信息
|
||||
public Object data; // 携带对象
|
||||
public Long dataCount; // 数据总数,用于分页
|
||||
|
||||
/**
|
||||
* 返回code
|
||||
* @return
|
||||
*/
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给msg赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
public String getMsg() {
|
||||
return this.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给data赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setData(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将data还原为指定类型并返回
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(Class<T> cs) {
|
||||
return (T) data;
|
||||
}
|
||||
|
||||
// ============================ 构建 ==================================
|
||||
|
||||
public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.dataCount = dataCount;
|
||||
}
|
||||
|
||||
// 返回成功
|
||||
public static AjaxJson getSuccess() {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg, Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessData(Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessArray(Object... data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
|
||||
// 返回失败
|
||||
public static AjaxJson getError() {
|
||||
return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||
}
|
||||
public static AjaxJson getError(String msg) {
|
||||
return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回警告
|
||||
public static AjaxJson getWarning() {
|
||||
return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||
}
|
||||
public static AjaxJson getWarning(String msg) {
|
||||
return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回未登录
|
||||
public static AjaxJson getNotLogin() {
|
||||
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||
}
|
||||
|
||||
// 返回没有权限的
|
||||
public static AjaxJson getNotJur(String msg) {
|
||||
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回一个自定义状态码的
|
||||
public static AjaxJson get(int code, String msg){
|
||||
return new AjaxJson(code, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回分页和数据的
|
||||
public static AjaxJson getPageData(Long dataCount, Object data){
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||
}
|
||||
|
||||
// 返回,根据受影响行数的(大于0=ok,小于0=error)
|
||||
public static AjaxJson getByLine(int line){
|
||||
if(line > 0){
|
||||
return getSuccess("ok", line);
|
||||
}
|
||||
return getError("error").setData(line);
|
||||
}
|
||||
|
||||
// 返回,根据布尔值来确定最终结果的 (true=ok,false=error)
|
||||
public static AjaxJson getByBoolean(boolean b){
|
||||
return b ? getSuccess("ok") : getError("error");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public String toString() {
|
||||
String data_string = null;
|
||||
if(data == null){
|
||||
|
||||
} else if(data instanceof List){
|
||||
data_string = "List(length=" + ((List)data).size() + ")";
|
||||
} else {
|
||||
data_string = data.toString();
|
||||
}
|
||||
return "{"
|
||||
+ "\"code\": " + this.getCode()
|
||||
+ ", \"msg\": \"" + this.getMsg() + "\""
|
||||
+ ", \"data\": " + data_string
|
||||
+ ", \"dataCount\": " + dataCount
|
||||
+ "}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.pj.util;
|
||||
|
||||
|
||||
/**
|
||||
* 用于测试用时
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class Ttime {
|
||||
|
||||
private long start=0; //开始时间
|
||||
private long end=0; //结束时间
|
||||
|
||||
public static Ttime t = new Ttime(); //static快捷使用
|
||||
|
||||
/**
|
||||
* 开始计时
|
||||
* @return
|
||||
*/
|
||||
public Ttime start() {
|
||||
start=System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 结束计时
|
||||
*/
|
||||
public Ttime end() {
|
||||
end=System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回所用毫秒数
|
||||
*/
|
||||
public long returnMs() {
|
||||
return end-start;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出结果
|
||||
*/
|
||||
public void outTime() {
|
||||
System.out.println(this.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束并格式化输出结果
|
||||
*/
|
||||
public void endOutTime() {
|
||||
this.end().outTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (returnMs() + 0.0) / 1000 + "s"; // 格式化为:0.01s
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken
|
||||
# token有效期,单位s 默认30天, -1代表永不过期
|
||||
timeout: 2592000
|
||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
activity-timeout: -1
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: true
|
||||
# token风格
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: false
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<version>2.5.12</version>
|
||||
<!-- <version>1.5.9.RELEASE</version> -->
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -39,26 +39,6 @@
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用jdk默认序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis</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> -->
|
||||
|
||||
<!-- 提供Redis连接池 -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency> -->
|
||||
|
||||
<!-- Sa-Token整合SpringAOP实现注解鉴权 -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
@@ -73,13 +53,6 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool工具类,用来生成雪花算法唯一id -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.5.4</version>
|
||||
</dependency> -->
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ public class GlobalException {
|
||||
}
|
||||
else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getPermission());
|
||||
}
|
||||
else if(e instanceof DisableLoginException) { // 如果是被封禁异常
|
||||
DisableLoginException ee = (DisableLoginException) e;
|
||||
|
||||
-5
@@ -18,11 +18,6 @@ import cn.dev33.satoken.util.SaResult;
|
||||
@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);
|
||||
|
||||
+47
-23
@@ -11,7 +11,7 @@ import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* Sa-Token 权限认证工具类 (User版)
|
||||
* Sa-Token 权限认证工具类 (user版)
|
||||
* @author kong
|
||||
*/
|
||||
public class StpUserUtil {
|
||||
@@ -110,9 +110,9 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话登录,并指定登录设备
|
||||
* 会话登录,并指定登录设备类型
|
||||
* @param id 账号id,建议的类型:(long | int | String)
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
*/
|
||||
public static void login(Object id, String device) {
|
||||
stpLogic.login(id, device);
|
||||
@@ -173,10 +173,10 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据账号id 和 设备标识
|
||||
* 会话注销,根据账号id 和 设备类型
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
* @param device 设备类型 (填null代表注销所有设备类型)
|
||||
*/
|
||||
public static void logout(Object loginId, String device) {
|
||||
stpLogic.logout(loginId, device);
|
||||
@@ -202,11 +202,11 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人下线,根据账号id 和 设备标识
|
||||
* 踢人下线,根据账号id 和 设备类型
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表踢出所有设备)
|
||||
* @param device 设备类型 (填null代表踢出所有设备类型)
|
||||
*/
|
||||
public static void kickout(Object loginId, String device) {
|
||||
stpLogic.kickout(loginId, device);
|
||||
@@ -223,11 +223,11 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶人下线,根据账号id 和 设备标识
|
||||
* 顶人下线,根据账号id 和 设备类型
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表顶替所有设备)
|
||||
* @param device 设备类型 (填null代表顶替所有设备类型)
|
||||
*/
|
||||
public static void replaced(Object loginId, String device) {
|
||||
stpLogic.replaced(loginId, device);
|
||||
@@ -309,7 +309,16 @@ public class StpUserUtil {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// =================== User-Session 相关 ===================
|
||||
|
||||
@@ -390,7 +399,7 @@ public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* 续签当前token:(将 [最后操作时间] 更新为当前时间戳)
|
||||
* <h1>请注意: 即时token已经 [临时过期] 也可续签成功,
|
||||
* <h1>请注意: 即使token已经 [临时过期] 也可续签成功,
|
||||
* 如果此场景下需要提示续签失败,可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
|
||||
*/
|
||||
public static void updateLastActivityToNow() {
|
||||
@@ -431,8 +440,23 @@ public class StpUserUtil {
|
||||
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);
|
||||
}
|
||||
|
||||
// =================== 角色验证操作 ===================
|
||||
|
||||
@@ -610,11 +634,11 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id指定设备端的tokenValue
|
||||
* 获取指定账号id指定设备类型端的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
* @return token值
|
||||
*/
|
||||
public static String getTokenValueByLoginId(Object loginId, String device) {
|
||||
@@ -631,9 +655,9 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号id指定设备端的tokenValue 集合
|
||||
* 获取指定账号id指定设备类型端的tokenValue 集合
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
|
||||
@@ -641,8 +665,8 @@ public class StpUserUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前会话的登录设备
|
||||
* @return 当前令牌的登录设备
|
||||
* 返回当前会话的登录设备类型
|
||||
* @return 当前令牌的登录设备类型
|
||||
*/
|
||||
public static String getLoginDevice() {
|
||||
return stpLogic.getLoginDevice();
|
||||
@@ -827,9 +851,9 @@ public class StpUserUtil {
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* 在当前会话上登录id, 并指定登录设备类型
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param device 设备标识
|
||||
* @param device 设备类型
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setLoginId(Object loginId, String device) {
|
||||
@@ -839,7 +863,7 @@ public class StpUserUtil {
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* 在当前会话上登录id, 并指定登录设备类型
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
*/
|
||||
@@ -875,10 +899,10 @@ public class StpUserUtil {
|
||||
/**
|
||||
* <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
|
||||
*
|
||||
* 会话注销,根据账号id and 设备标识 (踢人下线)
|
||||
* 会话注销,根据账号id and 设备类型 (踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识 (填null代表所有注销设备)
|
||||
* @param device 设备类型 (填null代表注销所有设备类型)
|
||||
*/
|
||||
@Deprecated
|
||||
public static void logoutByLoginId(Object loginId, String device) {
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
if(res.code == 200) {
|
||||
localStorage.setItem('satoken', res.data);
|
||||
location.href = decodeURIComponent(back);
|
||||
} else {
|
||||
alert(res.msg);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<version>2.5.12</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -34,7 +34,14 @@
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token整合redis (使用jackson序列化方式) -->
|
||||
<!-- Sa-Token 插件:整合SSO -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-sso</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 插件:整合redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
|
||||
+14
-7
@@ -7,7 +7,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import com.ejlchina.okhttps.OkHttps;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoHandle;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
@@ -34,15 +34,15 @@ public class SsoServerController {
|
||||
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaTokenConfig cfg) {
|
||||
private void configSso(SaSsoConfig sso) {
|
||||
|
||||
// 配置:未登录时返回的View
|
||||
cfg.sso.setNotLoginView(() -> {
|
||||
sso.setNotLoginView(() -> {
|
||||
return new ModelAndView("sa-login.html");
|
||||
});
|
||||
|
||||
// 配置:登录处理函数
|
||||
cfg.sso.setDoLoginHandle((name, pwd) -> {
|
||||
sso.setDoLoginHandle((name, pwd) -> {
|
||||
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
||||
if("sa".equals(name) && "123456".equals(pwd)) {
|
||||
StpUtil.login(10001);
|
||||
@@ -52,9 +52,16 @@ public class SsoServerController {
|
||||
});
|
||||
|
||||
// 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉)
|
||||
cfg.sso.setSendHttp(url -> {
|
||||
return OkHttps.sync(url).get().getBody().toString();
|
||||
sso.setSendHttp(url -> {
|
||||
try {
|
||||
// 发起 http 请求
|
||||
System.out.println("发起请求:" + url);
|
||||
return OkHttps.sync(url).get().getBody().toString();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<version>2.5.12</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -34,6 +34,13 @@
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 插件:整合SSO -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-sso</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
@@ -18,8 +18,8 @@ public class SsoClientController {
|
||||
// SSO-Client端:首页
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
String authUrl = SaManager.getConfig().getSso().getAuthUrl();
|
||||
String solUrl = SaManager.getConfig().getSso().getSloUrl();
|
||||
String authUrl = SaSsoManager.getConfig().getAuthUrl();
|
||||
String solUrl = SaSsoManager.getConfig().getSloUrl();
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
|
||||
"<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " +
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<version>2.5.12</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -34,6 +34,13 @@
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 插件:整合SSO -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-sso</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
||||
@@ -0,0 +1,35 @@
|
||||
<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>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-sso3-client-nosdk</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.12</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Http 请求工具 -->
|
||||
<dependency>
|
||||
<groupId>com.dtflys.forest</groupId>
|
||||
<artifactId>forest-spring-boot-starter</artifactId>
|
||||
<version>1.5.19</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SaSsoClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoClientApplication.class, args);
|
||||
System.out.println("\nSa-Token SSO模式三 Client端 (无SDK版本) 启动成功");
|
||||
}
|
||||
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.sso.util.AjaxJson;
|
||||
import com.pj.sso.util.MyHttpSessionHolder;
|
||||
|
||||
/**
|
||||
* SSO Client端 Controller
|
||||
* @author kong
|
||||
*/
|
||||
@RestController
|
||||
public class SsoClientController {
|
||||
|
||||
// SSO-Client端:首页
|
||||
@RequestMapping("/")
|
||||
public String index(HttpSession session) {
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
|
||||
"<p>当前会话登录账号:" + session.getAttribute("userId") + "</p>" +
|
||||
"<p><a href=\"javascript:location.href='/sso/login?back=' + encodeURIComponent(location.href);\">登录</a>" +
|
||||
" <a href='/sso/logout?back=' + + encodeURIComponent(location.href);>注销</a>" +
|
||||
" <a href='/sso/myinfo' target=\"_blank\">获取资料</a></p>";
|
||||
return str;
|
||||
}
|
||||
|
||||
// SSO-Client端:单点登录地址
|
||||
@RequestMapping("/sso/login")
|
||||
public Object ssoLogin(String ticket, @RequestParam(defaultValue = "/") String back,
|
||||
HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
|
||||
|
||||
// 如果已经登录,则直接返回
|
||||
if(session.getAttribute("userId") != null) {
|
||||
response.sendRedirect(back);
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* 此时有两种情况:
|
||||
* 情况1:ticket无值,说明此请求是Client端访问,需要重定向至SSO认证中心
|
||||
* 情况2:ticket有值,说明此请求从SSO认证中心重定向而来,需要根据ticket进行登录
|
||||
*/
|
||||
if(ticket == null) {
|
||||
String currUrl = request.getRequestURL().toString();
|
||||
String clientLoginUrl = currUrl + "?back=" + SsoRequestUtil.encodeUrl(back);
|
||||
String serverAuthUrl = SsoRequestUtil.authUrl + "?redirect=" + clientLoginUrl;
|
||||
response.sendRedirect(serverAuthUrl);
|
||||
return null;
|
||||
} else {
|
||||
// 获取当前 client 端的单点注销回调地址
|
||||
String ssoLogoutCall = "";
|
||||
if(SsoRequestUtil.isSlo) {
|
||||
ssoLogoutCall = request.getRequestURL().toString().replace("/sso/login", "/sso/logoutCall");
|
||||
}
|
||||
|
||||
// 校验 ticket
|
||||
String checkUrl = SsoRequestUtil.checkTicketUrl + "?ticket=" + ticket + "&ssoLogoutCall=" + ssoLogoutCall;
|
||||
AjaxJson result = SsoRequestUtil.request(checkUrl);
|
||||
|
||||
// 200 代表校验成功
|
||||
if(result.getCode() == 200 && SsoRequestUtil.isEmpty(result.getData()) == false) {
|
||||
// 登录上
|
||||
Object loginId = result.getData();
|
||||
session.setAttribute("userId", loginId);
|
||||
// 返回 back 地址
|
||||
response.sendRedirect(back);
|
||||
return null;
|
||||
|
||||
} else {
|
||||
// 将 sso-server 回应的消息作为异常抛出
|
||||
throw new RuntimeException(result.getMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SSO-Client端:单点注销地址
|
||||
@RequestMapping("/sso/logout")
|
||||
public Object ssoLogout(@RequestParam(defaultValue = "/") String back,
|
||||
HttpServletResponse response, HttpSession session) throws IOException {
|
||||
|
||||
// 如果未登录,则无需注销
|
||||
if(session.getAttribute("userId") == null) {
|
||||
response.sendRedirect(back);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 调用 sso-server 认证中心单点注销API
|
||||
Object loginId = session.getAttribute("userId"); // 账号id
|
||||
String timestamp = String.valueOf(System.currentTimeMillis()); // 时间戳
|
||||
String nonce = SsoRequestUtil.getRandomString(20); // 随机字符串
|
||||
String sign = SsoRequestUtil.getSign(loginId, timestamp, nonce, SsoRequestUtil.secretkey); // 参数签名
|
||||
|
||||
String url = SsoRequestUtil.sloUrl +
|
||||
"?loginId=" + loginId +
|
||||
"×tamp=" + timestamp +
|
||||
"&nonce=" + nonce +
|
||||
"&sign=" + sign;
|
||||
AjaxJson result = SsoRequestUtil.request(url);
|
||||
|
||||
// 校验响应状态码,200 代表成功
|
||||
if(result.getCode() == 200) {
|
||||
|
||||
// 极端场景下,sso-server 中心的单点注销可能并不会通知到此 client 端,所以这里需要再补一刀
|
||||
session.removeAttribute("userId");
|
||||
// 返回 back 地址
|
||||
response.sendRedirect(back);
|
||||
return null;
|
||||
|
||||
} else {
|
||||
// 将 sso-server 回应的消息作为异常抛出
|
||||
throw new RuntimeException(result.getMsg());
|
||||
}
|
||||
}
|
||||
|
||||
// SSO-Client端:单点注销回调地址
|
||||
@RequestMapping("/sso/logoutCall")
|
||||
public Object ssoLogoutCall(String loginId, String timestamp, String nonce, String sign) {
|
||||
|
||||
// 校验签名
|
||||
String calcSign = SsoRequestUtil.getSign(loginId, timestamp, nonce, SsoRequestUtil.secretkey);
|
||||
if(calcSign.equals(sign) == false) {
|
||||
return AjaxJson.getError("无效签名,拒绝应答");
|
||||
}
|
||||
|
||||
// 注销这个账号id
|
||||
for (HttpSession session: MyHttpSessionHolder.sessionList) {
|
||||
Object userId = session.getAttribute("userId");
|
||||
if(Objects.equals(String.valueOf(userId), loginId)) {
|
||||
session.removeAttribute("userId");
|
||||
}
|
||||
}
|
||||
|
||||
return AjaxJson.getSuccess("账号id=" + loginId + " 注销成功");
|
||||
}
|
||||
|
||||
// 查询我的账号信息 (调用此接口的前提是 sso-server 端开放了 /sso/userinfo 路由)
|
||||
@RequestMapping("/sso/myinfo")
|
||||
public Object myinfo(HttpSession session) {
|
||||
// 如果尚未登录
|
||||
if(session.getAttribute("userId") == null) {
|
||||
return "尚未登录,无法获取";
|
||||
}
|
||||
|
||||
// 组织 url 参数
|
||||
Object loginId = session.getAttribute("userId"); // 账号id
|
||||
String timestamp = String.valueOf(System.currentTimeMillis()); // 时间戳
|
||||
String nonce = SsoRequestUtil.getRandomString(20); // 随机字符串
|
||||
String sign = SsoRequestUtil.getSign(loginId, timestamp, nonce, SsoRequestUtil.secretkey); // 参数签名
|
||||
|
||||
String url = SsoRequestUtil.userinfoUrl +
|
||||
"?loginId=" + loginId +
|
||||
"×tamp=" + timestamp +
|
||||
"&nonce=" + nonce +
|
||||
"&sign=" + sign;
|
||||
AjaxJson result = SsoRequestUtil.request(url);
|
||||
|
||||
// 返回给前端
|
||||
return result;
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import com.dtflys.forest.Forest;
|
||||
import com.pj.sso.util.AjaxJson;
|
||||
|
||||
/**
|
||||
* 封装一些 sso 共用方法
|
||||
*
|
||||
* @author kong
|
||||
* @since: 2022-4-30
|
||||
*/
|
||||
public class SsoRequestUtil {
|
||||
|
||||
/**
|
||||
* SSO-Server端 统一认证地址
|
||||
*/
|
||||
public static String authUrl = "http://sa-sso-server.com:9000/sso/auth";
|
||||
|
||||
/**
|
||||
* 使用 Http 请求校验ticket
|
||||
*/
|
||||
// public static boolean isHttp = true;
|
||||
|
||||
/**
|
||||
* SSO-Server端 ticket校验地址
|
||||
*/
|
||||
public static String checkTicketUrl = "http://sa-sso-server.com:9000/sso/checkTicket";
|
||||
|
||||
/**
|
||||
* 打开单点注销功能
|
||||
*/
|
||||
public static boolean isSlo = true;
|
||||
|
||||
/**
|
||||
* 单点注销地址
|
||||
*/
|
||||
public static String sloUrl = "http://sa-sso-server.com:9000/sso/logout";
|
||||
|
||||
/**
|
||||
* 接口调用秘钥
|
||||
*/
|
||||
public static String secretkey = "kQwIOrYvnXmSDkwEiFngrKidMcdrgKor";
|
||||
|
||||
/**
|
||||
* SSO-Server端 查询userinfo地址
|
||||
*/
|
||||
public static String userinfoUrl = "http://sa-sso-server.com:9000/sso/userinfo";
|
||||
|
||||
|
||||
// -------------------------- 工具方法
|
||||
|
||||
/**
|
||||
* 发出请求,并返回 SaResult 结果
|
||||
* @param url 请求地址
|
||||
* @return 返回的结果
|
||||
*/
|
||||
public static AjaxJson request(String url) {
|
||||
Map<String, Object> map = Forest.post(url).executeAsMap();
|
||||
return new AjaxJson(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据参数计算签名
|
||||
* @param loginId 账号id
|
||||
* @param timestamp 当前时间戳,13位
|
||||
* @param nonce 随机字符串
|
||||
* @param secretkey 账号id
|
||||
* @return 签名
|
||||
*/
|
||||
public static String getSign(Object loginId, String timestamp, String nonce, String secretkey) {
|
||||
return md5("loginId=" + loginId + "&nonce=" + nonce + "×tamp=" + timestamp + "&key=" + secretkey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定元素是否为null或者空字符串
|
||||
* @param str 指定元素
|
||||
* @return 是否为null或者空字符串
|
||||
*/
|
||||
public static boolean isEmpty(Object str) {
|
||||
return str == null || "".equals(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* md5加密
|
||||
* @param str 指定字符串
|
||||
* @return 加密后的字符串
|
||||
*/
|
||||
public static String md5(String str) {
|
||||
str = (str == null ? "" : str);
|
||||
char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
try {
|
||||
byte[] btInput = str.getBytes();
|
||||
MessageDigest mdInst = MessageDigest.getInstance("MD5");
|
||||
mdInst.update(btInput);
|
||||
byte[] md = mdInst.digest();
|
||||
int j = md.length;
|
||||
char[] strA = new char[j * 2];
|
||||
int k = 0;
|
||||
for (byte byte0 : md) {
|
||||
strA[k++] = hexDigits[byte0 >>> 4 & 0xf];
|
||||
strA[k++] = hexDigits[byte0 & 0xf];
|
||||
}
|
||||
return new String(strA);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成指定长度的随机字符串
|
||||
*
|
||||
* @param length 字符串的长度
|
||||
* @return 一个随机字符串
|
||||
*/
|
||||
public static String getRandomString(int length) {
|
||||
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
Random random = new Random();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int number = random.nextInt(62);
|
||||
sb.append(str.charAt(number));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* URL编码
|
||||
* @param url see note
|
||||
* @return see note
|
||||
*/
|
||||
public static String encodeUrl(String url) {
|
||||
try {
|
||||
return URLEncoder.encode(url, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
package com.pj.sso.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* ajax请求返回Json格式数据的封装 <br>
|
||||
* 所有预留字段:<br>
|
||||
* code=状态码 <br>
|
||||
* msg=描述信息 <br>
|
||||
* data=携带对象 <br>
|
||||
* pageNo=当前页 <br>
|
||||
* pageSize=页大小 <br>
|
||||
* startIndex=起始索引 <br>
|
||||
* dataCount=数据总数 <br>
|
||||
* pageCount=分页总数 <br>
|
||||
* <p> 返回范例:</p>
|
||||
* <pre>
|
||||
{
|
||||
"code": 200, // 成功时=200, 失败时=500 msg=失败原因
|
||||
"msg": "ok",
|
||||
"data": {}
|
||||
}
|
||||
</pre>
|
||||
*/
|
||||
public class AjaxJson extends LinkedHashMap<String, Object> implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L; // 序列化版本号
|
||||
|
||||
public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||
public static final int CODE_ERROR = 500; // 错误状态码
|
||||
public static final int CODE_WARNING = 501; // 警告状态码
|
||||
public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||
|
||||
|
||||
|
||||
// ============================ 写值取值 ==================================
|
||||
|
||||
/** 给code赋值,连缀风格 */
|
||||
public AjaxJson setCode(int code) {
|
||||
this.put("code", code);
|
||||
return this;
|
||||
}
|
||||
/** 返回code */
|
||||
public Integer getCode() {
|
||||
return (Integer)this.get("code");
|
||||
}
|
||||
|
||||
/** 给msg赋值,连缀风格 */
|
||||
public AjaxJson setMsg(String msg) {
|
||||
this.put("msg", msg);
|
||||
return this;
|
||||
}
|
||||
/** 获取msg */
|
||||
public String getMsg() {
|
||||
return (String)this.get("msg");
|
||||
}
|
||||
|
||||
/** 给data赋值,连缀风格 */
|
||||
public AjaxJson setData(Object data) {
|
||||
this.put("data", data);
|
||||
return this;
|
||||
}
|
||||
/** 获取data */
|
||||
public Object getData() {
|
||||
return this.get("data");
|
||||
}
|
||||
/** 将data还原为指定类型并返回 */
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(Class<T> cs) {
|
||||
return (T) this.getData();
|
||||
}
|
||||
|
||||
/** 给dataCount(数据总数)赋值,连缀风格 */
|
||||
public AjaxJson setDataCount(Long dataCount) {
|
||||
this.put("dataCount", dataCount);
|
||||
// 如果提供了数据总数,则尝试计算page信息
|
||||
if(dataCount != null && dataCount >= 0) {
|
||||
// 如果:已有page信息
|
||||
if(get("pageNo") != null) {
|
||||
this.initPageInfo();
|
||||
}
|
||||
// // 或者:是JavaWeb环境
|
||||
// else if(SoMap.isJavaWeb()) {
|
||||
// SoMap so = SoMap.getRequestSoMap();
|
||||
// this.setPageNoAndSize(so.getKeyPageNo(), so.getKeyPageSize());
|
||||
// this.initPageInfo();
|
||||
// }
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 获取dataCount(数据总数) */
|
||||
public Long getDataCount() {
|
||||
return (Long)this.get("dataCount");
|
||||
}
|
||||
|
||||
/** 设置pageNo 和 pageSize,并计算出startIndex于pageCount */
|
||||
public AjaxJson setPageNoAndSize(long pageNo, long pageSize) {
|
||||
this.put("pageNo", pageNo);
|
||||
this.put("pageSize", pageSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 根据 pageSize dataCount,计算startIndex 与 pageCount */
|
||||
public AjaxJson initPageInfo() {
|
||||
long pageNo = (long)this.get("pageNo");
|
||||
long pageSize = (long)this.get("pageSize");
|
||||
long dataCount = (long)this.get("dataCount");
|
||||
this.set("startIndex", (pageNo - 1) * pageSize);
|
||||
long pc = dataCount / pageSize;
|
||||
this.set("pageCount", (dataCount % pageSize == 0 ? pc : pc + 1));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/** 写入一个值 自定义key, 连缀风格 */
|
||||
public AjaxJson set(String key, Object data) {
|
||||
this.put(key, data);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 写入一个Map, 连缀风格 */
|
||||
public AjaxJson setMap(Map<String, ?> map) {
|
||||
for (String key : map.keySet()) {
|
||||
this.put(key, map.get(key));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ============================ 构建 ==================================
|
||||
|
||||
public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||
this.setCode(code);
|
||||
this.setMsg(msg);
|
||||
this.setData(data);
|
||||
if(dataCount != null) {
|
||||
this.setDataCount(dataCount);
|
||||
}
|
||||
}
|
||||
|
||||
public AjaxJson(Map<String, Object> map) {
|
||||
for (String key: map.keySet()) {
|
||||
this.set(key, map.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
/** 返回成功 */
|
||||
public static AjaxJson getSuccess() {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg, Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessData(Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
|
||||
|
||||
/** 返回失败 */
|
||||
public static AjaxJson getError() {
|
||||
return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||
}
|
||||
public static AjaxJson getError(String msg) {
|
||||
return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回警告 */
|
||||
public static AjaxJson getWarning() {
|
||||
return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||
}
|
||||
public static AjaxJson getWarning(String msg) {
|
||||
return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回未登录 */
|
||||
public static AjaxJson getNotLogin() {
|
||||
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||
}
|
||||
|
||||
/** 返回没有权限的 */
|
||||
public static AjaxJson getNotJur(String msg) {
|
||||
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回一个自定义状态码的 */
|
||||
public static AjaxJson get(int code, String msg){
|
||||
return new AjaxJson(code, msg, null, null);
|
||||
}
|
||||
|
||||
/** 返回分页和数据的 */
|
||||
public static AjaxJson getPageData(Long dataCount, Object data){
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||
}
|
||||
|
||||
/** 返回, 根据受影响行数的(大于0=ok,小于0=error) */
|
||||
public static AjaxJson getByLine(int line){
|
||||
if(line > 0){
|
||||
return getSuccess("ok", line);
|
||||
}
|
||||
return getError("error").setData(line);
|
||||
}
|
||||
|
||||
/** 返回,根据布尔值来确定最终结果的 (true=ok,false=error) */
|
||||
public static AjaxJson getByBoolean(boolean b){
|
||||
return b ? getSuccess("ok") : getError("error");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // 历史版本遗留代码
|
||||
// public int code; // 状态码
|
||||
// public String msg; // 描述信息
|
||||
// public Object data; // 携带对象
|
||||
// public Long dataCount; // 数据总数,用于分页
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.pj.sso.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpSessionEvent;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 记录所有已创建的 HttpSession 对象
|
||||
*
|
||||
* <b> 此种方式有性能问题,仅做demo示例,真实项目中请更换为其它方案记录用户会话数据 </b>
|
||||
*
|
||||
* @author kong
|
||||
* @since: 2022-4-30
|
||||
*/
|
||||
@Component
|
||||
public class MyHttpSessionHolder implements HttpSessionListener {
|
||||
|
||||
public static List<HttpSession> sessionList = new ArrayList<>();
|
||||
|
||||
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
|
||||
sessionList.add(httpSessionEvent.getSession());
|
||||
}
|
||||
|
||||
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
|
||||
HttpSession session = httpSessionEvent.getSession();
|
||||
sessionList.remove(session);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 9001
|
||||
|
||||
forest:
|
||||
# 打开/关闭Forest请求日志(默认为 true)
|
||||
log-request: true
|
||||
|
||||
spring:
|
||||
# Redis连接
|
||||
redis:
|
||||
# Redis数据库索引
|
||||
database: 2
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<version>2.5.12</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -34,6 +34,13 @@
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 插件:整合SSO -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-sso</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
|
||||
+4
-3
@@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.ejlchina.okhttps.OkHttps;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoHandle;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
@@ -43,9 +43,10 @@ public class SsoClientController {
|
||||
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaTokenConfig cfg) {
|
||||
private void configSso(SaSsoConfig sso) {
|
||||
// 配置Http请求处理器
|
||||
cfg.sso.setSendHttp(url -> {
|
||||
sso.setSendHttp(url -> {
|
||||
System.out.println("发起请求:" + url);
|
||||
return OkHttps.sync(url).get().getBody().toString();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ sa-token:
|
||||
userinfo-url: http://sa-sso-server.com:9000/sso/userinfo
|
||||
|
||||
spring:
|
||||
# 配置Sa-Token单独使用的Redis连接 (此处与SSO-Server端连接不同的Redis)
|
||||
# 配置 Redis 连接 (此处与SSO-Server端连接不同的Redis)
|
||||
redis:
|
||||
# Redis数据库索引
|
||||
database: 2
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<version>2.5.12</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<version>2.5.12</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.29.0</sa-token-version>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -19,6 +19,7 @@ public class DefineRoutes {
|
||||
* 函数式编程,初始化路由表
|
||||
* @return 路由表
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> getRoutes() {
|
||||
return RouterFunctions.route(RequestPredicates.GET("/fun"), req -> {
|
||||
|
||||
@@ -37,7 +37,7 @@ public class GlobalException {
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||
} else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getPermission());
|
||||
} else if(e instanceof DisableLoginException) { // 如果是被封禁异常
|
||||
DisableLoginException ee = (DisableLoginException) e;
|
||||
aj = AjaxJson.getNotJur("账号被封禁:" + ee.getDisableTime() + "秒后解封");
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
||||
@@ -0,0 +1,69 @@
|
||||
<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>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-websocket-spring</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.12</version>
|
||||
<!-- <version>1.5.9.RELEASE</version> -->
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot依赖 -->
|
||||
<dependency>
|
||||
<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>
|
||||
|
||||
<!-- WebScoket 依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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> -->
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
/**
|
||||
* Sa-Token 整合 WebSocket 鉴权示例
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaTokenWebSocketSpringApplication {
|
||||
|
||||
/*
|
||||
* 1、访问登录接口,拿到会话Token:
|
||||
* http://localhost:8081/acc/doLogin?name=zhang&pwd=123456
|
||||
*
|
||||
* 2、找一个WebSocket在线测试页面进行连接,
|
||||
* 例如:
|
||||
* https://www.bejson.com/httputil/websocket/
|
||||
* 然后连接地址:
|
||||
* ws://localhost:8081/ws-connect?satoken=2e6db38f-1e78-40bc-aa8f-e8f1f77fbef5
|
||||
*/
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenWebSocketSpringApplication.class, args);
|
||||
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -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("登录成功").set("token", StpUtil.getTokenValue());
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.pj.ws;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
|
||||
/**
|
||||
* 处理 WebSocket 连接
|
||||
*
|
||||
* @author kong
|
||||
* @since: 2022-2-11
|
||||
*/
|
||||
public class MyWebSocketHandler extends TextWebSocketHandler {
|
||||
|
||||
/**
|
||||
* 固定前缀
|
||||
*/
|
||||
private static final String USER_ID = "user_id_";
|
||||
|
||||
/**
|
||||
* 存放Session集合,方便推送消息
|
||||
*/
|
||||
private static ConcurrentHashMap<String, WebSocketSession> webSocketSessionMaps = new ConcurrentHashMap<>();
|
||||
|
||||
// 监听:连接开启
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
|
||||
// put到集合,方便后续操作
|
||||
String userId = session.getAttributes().get("userId").toString();
|
||||
webSocketSessionMaps.put(USER_ID + userId, session);
|
||||
|
||||
|
||||
// 给个提示
|
||||
String tips = "Web-Socket 连接成功,sid=" + session.getId() + ",userId=" + userId;
|
||||
System.out.println(tips);
|
||||
sendMessage(session, tips);
|
||||
}
|
||||
|
||||
// 监听:连接关闭
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
|
||||
// 从集合移除
|
||||
String userId = session.getAttributes().get("userId").toString();
|
||||
webSocketSessionMaps.remove(USER_ID + userId);
|
||||
|
||||
// 给个提示
|
||||
String tips = "Web-Socket 连接关闭,sid=" + session.getId() + ",userId=" + userId;
|
||||
System.out.println(tips);
|
||||
}
|
||||
|
||||
// 收到消息
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
|
||||
System.out.println("sid为:" + session.getId() + ",发来:" + message);
|
||||
}
|
||||
|
||||
// -----------
|
||||
|
||||
// 向指定客户端推送消息
|
||||
public static void sendMessage(WebSocketSession session, String message) {
|
||||
try {
|
||||
System.out.println("向sid为:" + session.getId() + ",发送:" + message);
|
||||
session.sendMessage(new TextMessage(message));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 向指定用户推送消息
|
||||
public static void sendMessage(long userId, String message) {
|
||||
WebSocketSession session = webSocketSessionMaps.get(USER_ID + userId);
|
||||
if(session != null) {
|
||||
sendMessage(session, message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.pj.ws;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
|
||||
|
||||
/**
|
||||
* WebSocket 相关配置
|
||||
*
|
||||
* @author kong
|
||||
* @since: 2022-2-11
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSocket
|
||||
public class WebSocketConfig implements WebSocketConfigurer {
|
||||
|
||||
// 注册 WebSocket 处理器
|
||||
@Override
|
||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
|
||||
webSocketHandlerRegistry
|
||||
// WebSocket 连接处理器
|
||||
.addHandler(new MyWebSocketHandler(), "/ws-connect")
|
||||
// WebSocket 拦截器
|
||||
.addInterceptors(new WebSocketInterceptor())
|
||||
// 允许跨域
|
||||
.setAllowedOrigins("*");
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.pj.ws;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.server.HandshakeInterceptor;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* WebSocket 握手的前置拦截器
|
||||
*
|
||||
* @author kong
|
||||
* @since: 2022-2-11
|
||||
*/
|
||||
public class WebSocketInterceptor implements HandshakeInterceptor {
|
||||
|
||||
// 握手之前触发 (return true 才会握手成功 )
|
||||
@Override
|
||||
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,
|
||||
Map<String, Object> attr) {
|
||||
|
||||
System.out.println("---- 握手之前触发 " + StpUtil.getTokenValue());
|
||||
|
||||
// 未登录情况下拒绝握手
|
||||
if(StpUtil.isLogin() == false) {
|
||||
System.out.println("---- 未授权客户端,连接失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 标记 userId,握手成功
|
||||
attr.put("userId", StpUtil.getLoginIdAsLong());
|
||||
return true;
|
||||
}
|
||||
|
||||
// 握手之后触发
|
||||
@Override
|
||||
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
|
||||
Exception exception) {
|
||||
System.out.println("---- 握手之后触发 ");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken
|
||||
# token有效期,单位s 默认30天, -1代表永不过期
|
||||
timeout: 2592000
|
||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
activity-timeout: -1
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: true
|
||||
# token风格
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: false
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
||||
@@ -0,0 +1,69 @@
|
||||
<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>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-websocket</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.12</version>
|
||||
<!-- <version>1.5.9.RELEASE</version> -->
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.30.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot依赖 -->
|
||||
<dependency>
|
||||
<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>
|
||||
|
||||
<!-- WebScoket 依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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> -->
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
/**
|
||||
* Sa-Token 整合 WebSocket 鉴权示例
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaTokenWebSocketApplication {
|
||||
|
||||
/*
|
||||
* 1、访问登录接口,拿到会话Token:
|
||||
* http://localhost:8081/acc/doLogin?name=zhang&pwd=123456
|
||||
*
|
||||
* 2、找一个WebSocket在线测试页面进行连接,
|
||||
* 例如:
|
||||
* https://www.bejson.com/httputil/websocket/
|
||||
* 然后连接地址:
|
||||
* ws://localhost:8081/ws-connect/2e6db38f-1e78-40bc-aa8f-e8f1f77fbef5
|
||||
*/
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenWebSocketApplication.class, args);
|
||||
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user