Compare commits
257 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f1104b6a43 | |||
| 87b524201a | |||
| 34a3f68ad8 | |||
| 078119e12c | |||
| 5e97c5d7fa | |||
| b7859b67d3 | |||
| 2fcf71af41 | |||
| c8f9aafe37 | |||
| 7d985602c5 | |||
| dca7830f1b | |||
| a4ae818376 | |||
| 740ad2e132 | |||
| 78b3ba8bfb | |||
| 40fc0810dd | |||
| be2258ee1d | |||
| b1413a3436 | |||
| 9c467793f0 | |||
| 985eaac79a | |||
| 98f94c8b97 | |||
| b66919276d | |||
| 38bfb45b26 | |||
| 094f8eb3ae | |||
| 7de6a1d618 | |||
| 59d8b56b18 | |||
| 5ba3d07ea7 | |||
| 15b0da6351 | |||
| 8c6cd9a668 | |||
| f5f8a3edf6 | |||
| 860642af57 | |||
| 15eefbed33 | |||
| 9b44e05c28 | |||
| 89b1a2a353 | |||
| bd5907ce52 | |||
| 2e79d26817 | |||
| 37f1642f7e | |||
| 884f3a57da | |||
| f4cf338d12 | |||
| cbd1df6895 | |||
| c48ed3e468 | |||
| 70d920b43e | |||
| 9fa5f750f9 | |||
| 14bdde6735 | |||
| f857c8f246 | |||
| e09c019545 | |||
| 7f556c2110 | |||
| 9f68a2d4bd | |||
| c441f4246d | |||
| 70ef691291 | |||
| 3bdc65839e | |||
| 1d4639ec0b | |||
| 64c06a3daf | |||
| a6cf265e4d | |||
| 921e693e7e | |||
| a8117a8021 | |||
| c574e7f8d7 | |||
| 36afaf74d3 | |||
| 55b9c87d6a | |||
| 50ffda7bef | |||
| a122a8b083 | |||
| 69ce298a8a | |||
| f92e64b9cf | |||
| d3138cefc8 | |||
| 71f2522f08 | |||
| a5e9bd6714 | |||
| 27bb436a90 | |||
| d75c91f0ab | |||
| 8ee9f215a6 | |||
| 1df216879c | |||
| 91998af186 | |||
| f33df38f64 | |||
| aaf4bb8931 | |||
| 5808710bbf | |||
| 62787c3257 | |||
| 6dd39505f9 | |||
| d86f72b469 | |||
| 7b7920123a | |||
| 8872982455 | |||
| 80f07ae01e | |||
| 540730d9a4 | |||
| 03c8abb269 | |||
| f9ad71db66 | |||
| 9fe0e1f97b | |||
| 9f6717f8c0 | |||
| af2d04ca1f | |||
| 64e6b58a05 | |||
| 275b3cec6f | |||
| e92ff9fb7d | |||
| cffd462cd8 | |||
| 9ca2cad5e6 | |||
| a076c8f8fc | |||
| dbddd05a6e | |||
| 18760010fc | |||
| 9151f9a5c8 | |||
| cdaf9fa65a | |||
| c316746f0d | |||
| 0695ee6cdf | |||
| 3716b874e3 | |||
| 255a15033e | |||
| ad70bb0300 | |||
| 8c35d838ff | |||
| 1eae6861d1 | |||
| bbcc016fc7 | |||
| c62f9482df | |||
| d2c0ac3f52 | |||
| 51b76f07f4 | |||
| 0b32a1f552 | |||
| f7cf015324 | |||
| 4965149c23 | |||
| 7268279736 | |||
| 20304aa717 | |||
| 0d46d232b4 | |||
| 64aa2edf9f | |||
| cb72d4601c | |||
| 4617ac8226 | |||
| 7e0f592a0f | |||
| 1df97edaf8 | |||
| 494da1029d | |||
| 2b8c3cefb9 | |||
| 0d50eb9f85 | |||
| 8ce9a70dbf | |||
| f1919769a4 | |||
| 85e5e2b558 | |||
| 8e4532e1f6 | |||
| c986cf3034 | |||
| f96f186140 | |||
| 4695c42d83 | |||
| 95a133040b | |||
| 30b47573c4 | |||
| b38091217a | |||
| deed69f80d | |||
| a11ad64d41 | |||
| af0b22854b | |||
| 0f582395cf | |||
| 64a0f4a6cd | |||
| 51f35403d5 | |||
| be7d08639c | |||
| bbadbc76d2 | |||
| 7580b21810 | |||
| 9e7619e689 | |||
| a608f29ed1 | |||
| 48307dcd4a | |||
| d4eb41dd6a | |||
| 59d3b985ec | |||
| 6e3f6103ca | |||
| ce9dd4f5e8 | |||
| 8aa1852870 | |||
| e329928177 | |||
| 8c58c489d7 | |||
| 2d90aac488 | |||
| b8f31da9a2 | |||
| 285f02578a | |||
| 487e15b1f1 | |||
| 643d82cde6 | |||
| 5dd02b3528 | |||
| b211de69ca | |||
| 25a40b3797 | |||
| b18b8a07b5 | |||
| 10f0571ef2 | |||
| b3544ed655 | |||
| a5a4da10b6 | |||
| 1678ee7475 | |||
| f3742aeca7 | |||
| 0989bf8bb7 | |||
| 2052a11e9e | |||
| 47e879ad5e | |||
| 40331c2e3f | |||
| 26ee628b33 | |||
| 03e38c549a | |||
| bf02d7a1e5 | |||
| a3ed0ec1fd | |||
| 8b0976f8c6 | |||
| a068441829 | |||
| 0d652e42b7 | |||
| cb0fd8cdcf | |||
| 43151743b8 | |||
| c3303e4212 | |||
| 575f8f38bc | |||
| 56d197b90c | |||
| 4b38feb60b | |||
| bbc2ccb860 | |||
| 99fd9ebf65 | |||
| ab8667a0c4 | |||
| 94ce6d0d73 | |||
| b3373214be | |||
| 87292cb7d5 | |||
| 3b9f6b71e9 | |||
| 251ac4cf0f | |||
| 5f9193095e | |||
| d1d25f4d49 | |||
| 37bcee92ed | |||
| 028e4e56f0 | |||
| d65469ba3e | |||
| bcb82395d5 | |||
| 88d1f47227 | |||
| 866c0de19d | |||
| ba640295c5 | |||
| 1ce7b945d8 | |||
| 4bfad95bad | |||
| c7f3b6d493 | |||
| c3bedaef99 | |||
| 983c16aa58 | |||
| ac4ac37175 | |||
| 713758c304 | |||
| 857e260a0a | |||
| f1503d93a0 | |||
| 1648422617 | |||
| 600cc98e20 | |||
| 2d3a262e2f | |||
| 6a677ca779 | |||
| dc5bfb6d84 | |||
| fbff086ed9 | |||
| 1028ac0fe6 | |||
| 4abb72bb65 | |||
| 79cb976439 | |||
| f26424537f | |||
| 2328f9d654 | |||
| 22826dac86 | |||
| bbce343a01 | |||
| 8ed9166abf | |||
| a1ec710efd | |||
| ae9ba2d1fd | |||
| 97a5fb2f40 | |||
| 128ab7614e | |||
| 7f0a3aa1c6 | |||
| 69d01e3e6e | |||
| 82ee90f712 | |||
| 9733c8777a | |||
| 4ff6a87ef5 | |||
| 7ffe6cb0e6 | |||
| dbba90d846 | |||
| 064ef4f12c | |||
| a8688cc07f | |||
| 4678e34203 | |||
| 81c0200981 | |||
| 2c6e656834 | |||
| 5ec35cce28 | |||
| 453d83cea4 | |||
| 0091fbad09 | |||
| 13760ccf4d | |||
| d9e2d98390 | |||
| 47140cea07 | |||
| 43308bf593 | |||
| d3792ad286 | |||
| 930c28df6d | |||
| a59ee9408b | |||
| 20f6bd7b3d | |||
| 67abf576f0 | |||
| 72252ab6b4 | |||
| 60e2afe76f | |||
| 62bd2febd0 | |||
| 434049c4a3 | |||
| 6277a1841b | |||
| 97ad4a783b | |||
| a503828e23 | |||
| 52abba6a98 | |||
| ebe6b0917b | |||
| 4b1370e603 |
@@ -6,7 +6,9 @@ bin/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
*.iml
|
||||
|
||||
.factorypath
|
||||
/.factorypath
|
||||
|
||||
.idea/
|
||||
@@ -1,92 +1,172 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150" style="margin-bottom: 10px;">
|
||||
<img alt="logo" src="https://gitee.com/dromara/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.7.0</h1>
|
||||
<h4 align="center">一个JavaWeb轻量级权限认证框架,功能全面,上手简单</h4>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.17.0</h1>
|
||||
<h4 align="center">这可能是史上功能最全的 Java 权限认证框架!</h4>
|
||||
<h4 align="center">
|
||||
<a href="https://gitee.com/sz6/sa-token/stargazers"><img src="https://gitee.com/sz6/sa-token/badge/star.svg"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.7.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token/stargazers"><img src="https://img.shields.io/github/stars/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/watchers"><img src="https://img.shields.io/github/watchers/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/network/members"><img src="https://img.shields.io/github/forks/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/issues"><img src="https://img.shields.io/github/issues/click33/sa-token.svg"></a>
|
||||
<a href="https://github.com/click33/sa-token/blob/master/LICENSE"><img src="https://img.shields.io/github/license/click33/sa-token.svg"></a>
|
||||
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg"></a>
|
||||
<a href="https://gitee.com/dromara/sa-token/members"><img src="https://gitee.com/dromara/sa-token/badge/fork.svg"></a>
|
||||
<a href="https://github.com/dromara/sa-token/stargazers"><img src="https://img.shields.io/github/stars/dromara/sa-token?style=flat-square&logo=GitHub"></a>
|
||||
<a href="https://github.com/dromara/sa-token/network/members"><img src="https://img.shields.io/github/forks/dromara/sa-token?style=flat-square&logo=GitHub"></a>
|
||||
<a href="https://github.com/dromara/sa-token/watchers"><img src="https://img.shields.io/github/watchers/dromara/sa-token?style=flat-square&logo=GitHub"></a>
|
||||
<a href="https://github.com/dromara/sa-token/issues"><img src="https://img.shields.io/github/issues/dromara/sa-token.svg?style=flat-square&logo=GitHub"></a>
|
||||
<a href="https://github.com/dromara/sa-token/blob/master/LICENSE"><img src="https://img.shields.io/github/license/dromara/sa-token.svg?style=flat-square"></a>
|
||||
</h4>
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 😘 在线资料
|
||||
- ##### [官网首页:http://sa-token.dev33.cn/](http://sa-token.dev33.cn/)
|
||||
- ##### [在线文档:http://sa-token.dev33.cn/doc/index.html](http://sa-token.dev33.cn/doc/index.html)
|
||||
- ##### [需求提交:我们深知一个优秀的项目需要海纳百川,点我在线提交需求](http://sa-app.dev33.cn/wall.html?name=sa-token)
|
||||
- ##### [开源不易,求鼓励,点个star吧](https://github.com/click33/sa-token)
|
||||
|
||||
## 在线资料
|
||||
|
||||
- [官网首页:http://sa-token.dev33.cn/](http://sa-token.dev33.cn/)
|
||||
|
||||
- [在线文档:http://sa-token.dev33.cn/doc/index.html](http://sa-token.dev33.cn/doc/index.html)
|
||||
|
||||
- [需求提交:我们深知一个优秀的项目需要海纳百川,点我在线提交需求](http://sa-app.dev33.cn/wall.html?name=sa-token)
|
||||
|
||||
- [开源不易,求鼓励,点个star吧 !](###)
|
||||
|
||||
|
||||
## Sa-Token是什么?
|
||||
Sa-Token是一个轻量级Java权限认证框架,主要解决:登录认证、权限认证、Session会话、单点登录、OAuth2.0 等一系列权限相关问题
|
||||
|
||||
框架针对踢人下线、自动续签、前后台分离、分布式会话……等常见业务进行N多适配,通过sa-token,你可以以一种极简的方式实现系统的权限认证部分
|
||||
|
||||
与其它权限认证框架相比,`sa-token` 具有以下优势:
|
||||
1. **简单** :可零配置启动框架,真正的开箱即用,低成本上手
|
||||
2. **强大** :目前已集成几十项权限相关特性,涵盖了大部分业务场景的解决方案
|
||||
3. **易用** :如丝般顺滑的API调用,大量高级特性统统只需一行代码即可实现
|
||||
4. **高扩展** :几乎所有组件都提供了扩展接口,90%以上的逻辑都可以按需重写
|
||||
|
||||
有了sa-token,你所有的权限认证问题,都不再是问题!
|
||||
|
||||
## Sa-Token 能做什么?
|
||||
|
||||

|
||||
|
||||
- **登录验证** —— 轻松登录鉴权,并提供五种细分场景值
|
||||
- **权限验证** —— 适配RBAC权限模型,不同角色不同授权
|
||||
- **Session会话** —— 专业的数据缓存中心
|
||||
- **踢人下线** —— 将违规用户立刻清退下线
|
||||
- **账号封禁** —— 封禁指定账号,使其无法登陆,还可指定解封时间
|
||||
- **持久层扩展** —— 可集成Redis、Memcached等专业缓存中间件,重启数据不丢失
|
||||
- **分布式会话** —— 提供jwt集成和共享数据中心两种分布式会话方案
|
||||
- **微服务网关鉴权** —— 适配Gateway、Soul、Zuul等常见网关组件的请求拦截认证
|
||||
- **单点登录** —— 一处登录,处处通行
|
||||
- **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- **临时身份切换** —— 将会话身份临时切换为其它账号
|
||||
- **无Cookie模式** —— APP、小程序等前后台分离场景
|
||||
- **同端互斥登录** —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录
|
||||
- **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- **路由拦截式鉴权** —— 根据路由拦截鉴权,可适配restful模式
|
||||
- **自动续签** —— 提供两种token过期策略,灵活搭配使用,还可自动续签
|
||||
- **会话治理** —— 提供方便灵活的会话查询接口
|
||||
- **记住我模式** —— 适配[记住我]模式,重启浏览器免验证
|
||||
- **密码加密** —— 提供密码加密模块,可快速MD5、SHA1、SHA256、AES、RSA加密
|
||||
- **全局侦听器** —— 在用户登陆、注销、被踢下线等关键性操作时进行一些AOP操作
|
||||
- **开箱即用** —— 提供SpringMVC、WebFlux等常见web框架starter集成包,真正的开箱即用
|
||||
- **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
|
||||
|
||||
|
||||
## 代码示例
|
||||
|
||||
sa-token的API调用非常简单,有多简单呢?以登录验证为例,你只需要:
|
||||
|
||||
## ⭐ sa-token是什么?
|
||||
- **sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,有多简单呢?以登录验证为例,你只需要:**
|
||||
``` java
|
||||
// 在登录时写入当前会话的账号id
|
||||
// 在登录时写入当前会话的账号id
|
||||
StpUtil.setLoginId(10001);
|
||||
```
|
||||
|
||||
|
||||
- **然后在任意需要验证登录权限的地方:**
|
||||
``` java
|
||||
// 检测是否登录 --- 如果当前会话未登录,下面这句代码会抛出 `NotLoginException`异常
|
||||
// 然后在任意需要校验登录处调用以下API
|
||||
// 如果当前会话未登录,这句代码会抛出 `NotLoginException`异常
|
||||
StpUtil.checkLogin();
|
||||
```
|
||||
至此,我们已经借助sa-token框架完成登录授权!
|
||||
|
||||
此时的你小脑袋可能飘满了问号,就这么简单?自定义Realm呢?全局过滤器呢?我不用写各种配置文件吗?
|
||||
|
||||
事实上在此我可以负责的告诉你,在sa-token中,登录授权就是如此的简单,不需要什么全局过滤器,不需要各种乱七八糟的配置!只需要这一行简单的API调用,即可完成会话的登录授权!
|
||||
|
||||
当你受够Shiro、Security等框架的三拜九叩之后,你就会明白,相对于这些传统老牌框架,sa-token的API设计是多么的清爽!
|
||||
|
||||
权限认证示例 (只有具有`user:add`权限的会话才可以进入请求)
|
||||
``` java
|
||||
@SaCheckPermission("user:add")
|
||||
@RequestMapping("/user/insert")
|
||||
public String insert(SysUser user) {
|
||||
// ...
|
||||
return "用户增加";
|
||||
}
|
||||
```
|
||||
|
||||
将某个账号踢下线 (待到对方再次访问系统时会抛出`NotLoginException`异常)
|
||||
``` java
|
||||
// 使账号id为10001的会话注销登录
|
||||
StpUtil.logoutByLoginId(10001);
|
||||
```
|
||||
|
||||
除了以上的示例,sa-token还可以一行代码完成以下功能:
|
||||
``` java
|
||||
StpUtil.setLoginId(10001); // 标记当前会话登录的账号id
|
||||
StpUtil.getLoginId(); // 获取当前会话登录的账号id
|
||||
StpUtil.isLogin(); // 获取当前会话是否已经登录, 返回true或false
|
||||
StpUtil.logout(); // 当前会话注销登录
|
||||
StpUtil.logoutByLoginId(10001); // 让账号为10001的会话注销登录(踢人下线)
|
||||
StpUtil.hasRole("super-admin"); // 查询当前账号是否含有指定角色标识, 返回true或false
|
||||
StpUtil.hasPermission("user:add"); // 查询当前账号是否含有指定权限, 返回true或false
|
||||
StpUtil.getSession(); // 获取当前账号id的Session
|
||||
StpUtil.getSessionByLoginId(10001); // 获取账号id为10001的Session
|
||||
StpUtil.getTokenValueByLoginId(10001); // 获取账号id为10001的token令牌值
|
||||
StpUtil.setLoginId(10001, "PC"); // 指定设备标识登录
|
||||
StpUtil.logoutByLoginId(10001, "PC"); // 指定设备标识进行强制注销 (不同端不受影响)
|
||||
StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号
|
||||
```
|
||||
sa-token API 众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档
|
||||
|
||||
|
||||
- **没有复杂的封装!不要任何的配置!先写入,后鉴权!只需这两行简单的调用,即可轻松完成系统登录鉴权!**
|
||||
## Star 趋势
|
||||
[](https://giteye.net/chart/77YQZ6UK)
|
||||
|
||||
[](https://starchart.cc/dromara/sa-token)
|
||||
|
||||
|
||||
## 🔥 框架设计思想
|
||||
与其它权限认证框架相比,`sa-token`尽力保证两点:
|
||||
- 上手简单:能自动化的配置全部自动化,不让你费脑子
|
||||
- 功能强大:能涵盖的功能全部涵盖,不让你用个框架还要自己给框架打各种补丁
|
||||
## 参与贡献
|
||||
众人拾柴火焰高,万丈高楼众人起!
|
||||
sa-token秉承着开放的思想,欢迎大家为框架添砖加瓦:
|
||||
|
||||
1. 核心代码:该部分需要开发者了解整个框架的架构,遵循已有代码规范进行bug修复或提交新功能
|
||||
2. 文档部分:需要以清晰明了的语句书写文档,力求简单易读,授人以鱼同时更授人以渔
|
||||
3. 社区建设:如果框架帮助到了您,希望您可以加入qq群参与交流,对不熟悉框架的新人进行排难解惑
|
||||
4. 框架推广:一个优秀的开源项目不能仅靠闭门造车,它还需要一定的推广方案让更多的人一起参与到项目中
|
||||
5. 其它部分:您可以参考项目issues与需求墙进行贡献
|
||||
|
||||
作者寄语:参与贡献不光只有提交代码,点一个star、提一个issues都是对开源项目的促进,
|
||||
如果sa-token帮助到了你,欢迎你把框架推荐给朋友、同事使用,为sa-token的推广做一份贡献
|
||||
|
||||
|
||||
## 💦️️ 涵盖功能
|
||||
- ⚡ **登录验证** —— 轻松登录鉴权,并提供五种细分场景值
|
||||
- ⚡ **权限验证** —— 拦截违规调用,不同角色不同授权
|
||||
- ⚡ **自定义session会话** —— 专业的数据缓存中心
|
||||
- ⚡ **踢人下线** —— 将违规用户立刻清退下线
|
||||
- ⚡ **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- ⚡ **持久层扩展** —— 可集成redis、MongoDB等专业缓存中间件
|
||||
- ⚡ **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- ⚡ **无cookie模式** —— APP、小程序等前后台分离场景
|
||||
- ⚡ **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- ⚡ **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- ⚡ **组件自动注入** —— 零配置与Spring等框架集成
|
||||
- ⚡ **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
|
||||
## 使用sa-token的开源项目
|
||||
[**[ sa-plus]** 一个基于springboot架构的快速开发框架,内置代码生成器](https://gitee.com/click33/sa-plus)
|
||||
|
||||
[**[ jthink]** 一个基于springboot+sa-token+thymeleaf的博客系统](https://gitee.com/wtsoftware/jthink)
|
||||
|
||||
|
||||
## 🔨 贡献代码
|
||||
sa-token欢迎大家贡献代码,为框架添砖加瓦
|
||||
1. 在github上fork一份到自己的仓库
|
||||
2. clone自己的仓库到本地电脑
|
||||
3. 在本地电脑修改、commit、push
|
||||
4. 提交pr(点击:New Pull Request)
|
||||
5. 等待合并
|
||||
如果您的项目使用了sa-token,欢迎提交pr
|
||||
|
||||
|
||||
## 🌱 建议贡献的地方
|
||||
- 修复源码现有bug,或增加新的实用功能
|
||||
- 完善在线文档,或者修复现有错误之处
|
||||
- 更多demo示例:比如SSM版搭建步骤
|
||||
- 您可以参考项目issues与需求墙进行贡献
|
||||
- 如果更新实用功能,可在文档友情链接处留下自己的推广链接
|
||||
## 友情链接
|
||||
[**[ okhttps ]** 一个轻量级http通信框架,API设计无比优雅,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
|
||||
[**[ 小诺快速开发平台 ]** 基于SpringBoot2 + AntDesignVue全新快速开发平台,同时拥有三个版本](https://xiaonuo.vip/index#pricing)
|
||||
|
||||
|
||||
## 🚀 友情链接
|
||||
[**[ okhttps ]** 一个轻量级http通信框架,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
## 交流群
|
||||
QQ交流群:1002350610 [点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM)
|
||||
|
||||
微信交流群:
|
||||
|
||||
## 😎 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) ,欢迎你的加入
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
(扫码添加微信,备注:sa-token,邀您加入群聊)
|
||||
|
||||
<br>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
call mvn clean
|
||||
|
||||
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-oauth2\sa-token-demo-oauth2-client
|
||||
call mvn clean
|
||||
cd ../..
|
||||
|
||||
cd sa-token-demo-oauth2\sa-token-demo-oauth2-server
|
||||
call mvn clean
|
||||
cd ../..
|
||||
|
||||
echo;
|
||||
echo;
|
||||
echo ----------- clean end -----------
|
||||
echo;
|
||||
pause
|
||||
@@ -8,18 +8,24 @@
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.7.0</version>
|
||||
<version>1.17.0</version>
|
||||
|
||||
<!-- 项目介绍 -->
|
||||
<name>sa-token</name>
|
||||
<description>A Java Web lightweight authority authentication framework, comprehensive function, easy to use</description>
|
||||
<url>https://github.com/click33/sa-token</url>
|
||||
<url>https://github.com/dromara/sa-token</url>
|
||||
|
||||
|
||||
<!-- 所有模块 -->
|
||||
<modules>
|
||||
<module>sp-token-core</module>
|
||||
<module>sa-token-core</module>
|
||||
<module>sa-token-servlet</module>
|
||||
<module>sa-token-spring-boot-starter</module>
|
||||
<module>sa-token-reactor-spring-boot-starter</module>
|
||||
<module>sa-token-dao-redis</module>
|
||||
<module>sa-token-dao-redis-jackson</module>
|
||||
<module>sa-token-spring-aop</module>
|
||||
<!-- <module>sa-token-oauth2</module> -->
|
||||
</modules>
|
||||
|
||||
<!-- 开源协议 apache 2.0 -->
|
||||
@@ -34,6 +40,7 @@
|
||||
|
||||
<!-- 一些属性 -->
|
||||
<properties>
|
||||
<sa-token-version>1.17.0</sa-token-version>
|
||||
<jdk.version>1.8</jdk.version>
|
||||
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
|
||||
@@ -42,9 +49,9 @@
|
||||
<!-- 仓库信息 -->
|
||||
<scm>
|
||||
<tag>master</tag>
|
||||
<url>https://github.com/click33/sa-token.git</url>
|
||||
<connection>scm:git:https://github.com/click33/sa-token.git</connection>
|
||||
<developerConnection>scm:git:https://github.com/click33/sa-token.git</developerConnection>
|
||||
<url>https://github.com/dromara/sa-token.git</url>
|
||||
<connection>scm:git:https://github.com/dromara/sa-token.git</connection>
|
||||
<developerConnection>scm:git:https://github.com/dromara/sa-token.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<!-- 作者信息 -->
|
||||
@@ -61,6 +68,7 @@
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>7</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 仓库依赖 -->
|
||||
@@ -82,6 +90,34 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<versionRange>[1.0.0,)</versionRange>
|
||||
<goals>
|
||||
<goal>enforce</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore />
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
||||
.iml
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.7.0</version>
|
||||
<version>1.17.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -16,11 +16,7 @@
|
||||
<description>A Java Web lightweight authority authentication framework, comprehensive function, easy to use</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</dependency>
|
||||
<!-- Zero dependence! -->
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
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;
|
||||
import cn.dev33.satoken.context.SaTokenContextDefaultImpl;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl;
|
||||
import cn.dev33.satoken.listener.SaTokenListener;
|
||||
import cn.dev33.satoken.listener.SaTokenListenerDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* 管理sa-token所有接口对象
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenManager {
|
||||
|
||||
/**
|
||||
* 配置文件 Bean
|
||||
*/
|
||||
private static SaTokenConfig config;
|
||||
public static void setConfig(SaTokenConfig config) {
|
||||
SaTokenManager.config = config;
|
||||
if(config.getIsV()) {
|
||||
SaTokenInsideUtil.printSaToken();
|
||||
}
|
||||
}
|
||||
public static SaTokenConfig getConfig() {
|
||||
if (config == null) {
|
||||
synchronized (SaTokenManager.class) {
|
||||
if (config == null) {
|
||||
setConfig(SaTokenConfigFactory.createConfig());
|
||||
}
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 持久化 Bean
|
||||
*/
|
||||
private static SaTokenDao saTokenDao;
|
||||
public static void setSaTokenDao(SaTokenDao saTokenDao) {
|
||||
if(SaTokenManager.saTokenDao != null && (SaTokenManager.saTokenDao instanceof SaTokenDaoDefaultImpl)) {
|
||||
((SaTokenDaoDefaultImpl)SaTokenManager.saTokenDao).endRefreshThread();
|
||||
}
|
||||
SaTokenManager.saTokenDao = saTokenDao;
|
||||
}
|
||||
public static SaTokenDao getSaTokenDao() {
|
||||
if (saTokenDao == null) {
|
||||
synchronized (SaTokenManager.class) {
|
||||
if (saTokenDao == null) {
|
||||
setSaTokenDao(new SaTokenDaoDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return saTokenDao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限认证 Bean
|
||||
*/
|
||||
private static StpInterface stpInterface;
|
||||
public static void setStpInterface(StpInterface stpInterface) {
|
||||
SaTokenManager.stpInterface = stpInterface;
|
||||
}
|
||||
public static StpInterface getStpInterface() {
|
||||
if (stpInterface == null) {
|
||||
synchronized (SaTokenManager.class) {
|
||||
if (stpInterface == null) {
|
||||
setStpInterface(new StpInterfaceDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return stpInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* 框架行为 Bean
|
||||
*/
|
||||
private static SaTokenAction saTokenAction;
|
||||
public static void setSaTokenAction(SaTokenAction saTokenAction) {
|
||||
SaTokenManager.saTokenAction = saTokenAction;
|
||||
}
|
||||
public static SaTokenAction getSaTokenAction() {
|
||||
if (saTokenAction == null) {
|
||||
synchronized (SaTokenManager.class) {
|
||||
if (saTokenAction == null) {
|
||||
setSaTokenAction(new SaTokenActionDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return saTokenAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* 容器操作 Bean
|
||||
*/
|
||||
private static SaTokenContext saTokenContext;
|
||||
public static void setSaTokenContext(SaTokenContext saTokenContext) {
|
||||
SaTokenManager.saTokenContext = saTokenContext;
|
||||
}
|
||||
public static SaTokenContext getSaTokenContext() {
|
||||
if (saTokenContext == null) {
|
||||
synchronized (SaTokenManager.class) {
|
||||
if (saTokenContext == null) {
|
||||
setSaTokenContext(new SaTokenContextDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return saTokenContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 侦听器 Bean
|
||||
*/
|
||||
private static SaTokenListener saTokenListener;
|
||||
public static void setSaTokenListener(SaTokenListener saTokenListener) {
|
||||
SaTokenManager.saTokenListener = saTokenListener;
|
||||
}
|
||||
public static SaTokenListener getSaTokenListener() {
|
||||
if (saTokenListener == null) {
|
||||
synchronized (SaTokenManager.class) {
|
||||
if (saTokenListener == null) {
|
||||
setSaTokenListener(new SaTokenListenerDefaultImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return saTokenListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* StpLogic集合, 记录框架所有成功初始化的StpLogic
|
||||
*/
|
||||
public static Map<String, StpLogic> stpLogicMap = new HashMap<String, StpLogic>();
|
||||
|
||||
/**
|
||||
* 向集合中 put 一个 StpLogic
|
||||
* @param stpLogic StpLogic
|
||||
*/
|
||||
public static void putStpLogic(StpLogic stpLogic) {
|
||||
stpLogicMap.put(stpLogic.getLoginKey(), stpLogic);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 LoginKey 获取对应的StpLogic,如果不存在则返回null
|
||||
* @param loginKey 对应的LoginKey
|
||||
* @return 对应的StpLogic
|
||||
*/
|
||||
public static StpLogic getStpLogic(String loginKey) {
|
||||
return stpLogicMap.get(loginKey);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.dev33.satoken.action;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* sa-token逻辑代理接口
|
||||
* <p>此接口将会代理框架内部的一些关键性逻辑,方便开发者进行按需重写</p>
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaTokenAction {
|
||||
|
||||
/**
|
||||
* 根据一定的算法生成一个token
|
||||
* @param loginId 账号id
|
||||
* @param loginKey 账号体系key
|
||||
* @return 一个token
|
||||
*/
|
||||
public String createToken(Object loginId, String loginKey);
|
||||
|
||||
/**
|
||||
* 根据 SessionId 创建一个 Session
|
||||
* @param sessionId Session的Id
|
||||
* @return 创建后的Session
|
||||
*/
|
||||
public SaSession createSession(String sessionId);
|
||||
|
||||
}
|
||||
+21
-16
@@ -3,6 +3,8 @@ package cn.dev33.satoken.action;
|
||||
import java.util.UUID;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
@@ -14,44 +16,47 @@ public class SaTokenActionDefaultImpl implements SaTokenAction {
|
||||
|
||||
|
||||
/**
|
||||
* 生成一个token
|
||||
* 根据一定的算法生成一个token
|
||||
*/
|
||||
@Override
|
||||
public String createToken(Object loginId, String loginKey) {
|
||||
// 生成各种花式token
|
||||
// 根据配置的tokenStyle生成不同风格的token
|
||||
String tokenStyle = SaTokenManager.getConfig().getTokenStyle();
|
||||
// uuid
|
||||
if(tokenStyle.equals("uuid")) {
|
||||
// uuid
|
||||
if(SaTokenConsts.TOKEN_STYLE_UUID.equals(tokenStyle)) {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
// 简单uuid (不带下划线)
|
||||
else if(tokenStyle.equals("simple-uuid")) {
|
||||
if(SaTokenConsts.TOKEN_STYLE_SIMPLE_UUID.equals(tokenStyle)) {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
// 32位随机字符串
|
||||
else if(tokenStyle.equals("random-32")) {
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_32.equals(tokenStyle)) {
|
||||
return SaTokenInsideUtil.getRandomString(32);
|
||||
}
|
||||
// 64位随机字符串
|
||||
else if(tokenStyle.equals("random-64")) {
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_64.equals(tokenStyle)) {
|
||||
return SaTokenInsideUtil.getRandomString(64);
|
||||
}
|
||||
// 128位随机字符串
|
||||
else if(tokenStyle.equals("random-128")) {
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_128.equals(tokenStyle)) {
|
||||
return SaTokenInsideUtil.getRandomString(128);
|
||||
}
|
||||
// tik风格
|
||||
else if(tokenStyle.equals("tik")) {
|
||||
// tik风格 (2_14_16)
|
||||
if(SaTokenConsts.TOKEN_STYLE_TIK.equals(tokenStyle)) {
|
||||
return SaTokenInsideUtil.getRandomString(2) + "_" + SaTokenInsideUtil.getRandomString(14) + "_" + SaTokenInsideUtil.getRandomString(16) + "__";
|
||||
}
|
||||
// 默认
|
||||
else {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
// 默认,还是uuid
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据 SessionId 创建一个 Session
|
||||
*/
|
||||
@Override
|
||||
public SaSession createSession(String sessionId) {
|
||||
return new SaSession(sessionId);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -6,7 +6,8 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 标注一个路由方法,当前会话必须已登录才能通过
|
||||
* 登录校验:标注在一个方法上,当前会话必须已经登录才能进入该方法
|
||||
* <p> 可标注在类上,其效果等同于标注在此类的所有方法上
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.dev33.satoken.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 权限校验:标注在一个方法上,当前会话必须具有指定权限才能进入该方法
|
||||
* <p> 可标注在类上,其效果等同于标注在此类的所有方法上
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD,ElementType.TYPE})
|
||||
public @interface SaCheckPermission {
|
||||
|
||||
/**
|
||||
* 需要校验的权限码
|
||||
* @return 需要校验的权限码
|
||||
*/
|
||||
String [] value() default {};
|
||||
|
||||
/**
|
||||
* 验证模式:AND | OR,默认AND
|
||||
* @return 验证模式
|
||||
*/
|
||||
SaMode mode() default SaMode.AND;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.dev33.satoken.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 角色校验:标注在一个方法上,当前会话必须具有指定角色标识才能进入该方法
|
||||
* <p> 可标注在类上,其效果等同于标注在此类的所有方法上
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD,ElementType.TYPE})
|
||||
public @interface SaCheckRole {
|
||||
|
||||
/**
|
||||
* 需要校验的角色标识
|
||||
* @return 需要校验的角色标识
|
||||
*/
|
||||
String [] value() default {};
|
||||
|
||||
/**
|
||||
* 验证模式:AND | OR,默认AND
|
||||
* @return 验证模式
|
||||
*/
|
||||
SaMode mode() default SaMode.AND;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cn.dev33.satoken.annotation;
|
||||
|
||||
/**
|
||||
* 注解鉴权的验证模式
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public enum SaMode {
|
||||
|
||||
/**
|
||||
* 必须具有所有的选项
|
||||
*/
|
||||
AND,
|
||||
|
||||
/**
|
||||
* 只需具有其中一个选项
|
||||
*/
|
||||
OR
|
||||
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
package cn.dev33.satoken.config;
|
||||
|
||||
/**
|
||||
* sa-token 配置类 Model
|
||||
* <p>
|
||||
* 你可以通过yml、properties、java代码等形式配置本类参数,具体请查阅官方文档: http://sa-token.dev33.cn/
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenConfig {
|
||||
|
||||
/** token名称 (同时也是cookie名称) */
|
||||
private String tokenName = "satoken";
|
||||
|
||||
/** token的长久有效期(单位:秒) 默认30天, -1代表永久 */
|
||||
private long timeout = 30 * 24 * 60 * 60;
|
||||
|
||||
/**
|
||||
* token临时有效期 [指定时间内无操作就视为token过期] (单位: 秒), 默认-1 代表不限制
|
||||
* (例如可以设置为1800代表30分钟内无操作就过期)
|
||||
*/
|
||||
private long activityTimeout = -1;
|
||||
|
||||
/** 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) */
|
||||
private Boolean allowConcurrentLogin = true;
|
||||
|
||||
/** 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) */
|
||||
private Boolean isShare = true;
|
||||
|
||||
/** 是否尝试从请求体里读取token */
|
||||
private Boolean isReadBody = true;
|
||||
|
||||
/** 是否尝试从header里读取token */
|
||||
private Boolean isReadHead = true;
|
||||
|
||||
/** 是否尝试从cookie里读取token */
|
||||
private Boolean isReadCookie = true;
|
||||
|
||||
/** token风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik) */
|
||||
private String tokenStyle = "uuid";
|
||||
|
||||
/** 默认dao层实现类中,每次清理过期数据间隔的时间 (单位: 秒) ,默认值30秒,设置为-1代表不启动定时清理 */
|
||||
private int dataRefreshPeriod = 30;
|
||||
|
||||
/** 获取[token专属session]时是否必须登录 (如果配置为true,会在每次获取[token-session]时校验是否登录) */
|
||||
private Boolean tokenSessionCheckLogin = true;
|
||||
|
||||
/** 是否打开自动续签 (如果此值为true, 框架会在每次直接或间接调用getLoginId()时进行一次过期检查与续签操作) */
|
||||
private Boolean autoRenew = true;
|
||||
|
||||
/** 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景 */
|
||||
private String cookieDomain;
|
||||
|
||||
/** token前缀, 格式样例(satoken: Bearer xxxx-xxxx-xxxx-xxxx) */
|
||||
private String tokenPrefix;
|
||||
|
||||
/** 是否在初始化配置时打印版本字符画 */
|
||||
private Boolean isV = true;
|
||||
|
||||
/** 是否打印操作日志 */
|
||||
private Boolean isLog = false;
|
||||
|
||||
|
||||
/**
|
||||
* @return token名称 (同时也是cookie名称)
|
||||
*/
|
||||
public String getTokenName() {
|
||||
return tokenName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenName token名称 (同时也是cookie名称)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setTokenName(String tokenName) {
|
||||
this.tokenName = tokenName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token的长久有效期(单位:秒) 默认30天, -1代表永久
|
||||
*/
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeout token的长久有效期(单位:秒) 默认30天, -1代表永久
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token临时有效期 [指定时间内无操作就视为token过期] (单位: 秒), 默认-1 代表不限制
|
||||
* (例如可以设置为1800代表30分钟内无操作就过期)
|
||||
*/
|
||||
public long getActivityTimeout() {
|
||||
return activityTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activityTimeout token临时有效期 [指定时间内无操作就视为token过期] (单位: 秒), 默认-1 代表不限制
|
||||
* (例如可以设置为1800代表30分钟内无操作就过期)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setActivityTimeout(long activityTimeout) {
|
||||
this.activityTimeout = activityTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
*/
|
||||
public Boolean getAllowConcurrentLogin() {
|
||||
return allowConcurrentLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allowConcurrentLogin 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setAllowConcurrentLogin(Boolean allowConcurrentLogin) {
|
||||
this.allowConcurrentLogin = allowConcurrentLogin;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
*/
|
||||
public Boolean getIsShare() {
|
||||
return isShare;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isShare 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setIsShare(Boolean isShare) {
|
||||
this.isShare = isShare;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否尝试从请求体里读取token
|
||||
*/
|
||||
public Boolean getIsReadBody() {
|
||||
return isReadBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isReadBody 是否尝试从请求体里读取token
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setIsReadBody(Boolean isReadBody) {
|
||||
this.isReadBody = isReadBody;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否尝试从header里读取token
|
||||
*/
|
||||
public Boolean getIsReadHead() {
|
||||
return isReadHead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isReadHead 是否尝试从header里读取token
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setIsReadHead(Boolean isReadHead) {
|
||||
this.isReadHead = isReadHead;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否尝试从cookie里读取token
|
||||
*/
|
||||
public Boolean getIsReadCookie() {
|
||||
return isReadCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isReadCookie 是否尝试从cookie里读取token
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setIsReadCookie(Boolean isReadCookie) {
|
||||
this.isReadCookie = isReadCookie;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||
*/
|
||||
public String getTokenStyle() {
|
||||
return tokenStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenStyle token风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setTokenStyle(String tokenStyle) {
|
||||
this.tokenStyle = tokenStyle;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 默认dao层实现类中,每次清理过期数据间隔的时间 (单位: 秒) ,默认值30秒,设置为-1代表不启动定时清理
|
||||
*/
|
||||
public int getDataRefreshPeriod() {
|
||||
return dataRefreshPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dataRefreshPeriod 默认dao层实现类中,每次清理过期数据间隔的时间 (单位: 秒)
|
||||
* ,默认值30秒,设置为-1代表不启动定时清理
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setDataRefreshPeriod(int dataRefreshPeriod) {
|
||||
this.dataRefreshPeriod = dataRefreshPeriod;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取[token专属session]时是否必须登录 (如果配置为true,会在每次获取[token-session]时校验是否登录)
|
||||
*/
|
||||
public Boolean getTokenSessionCheckLogin() {
|
||||
return tokenSessionCheckLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenSessionCheckLogin 获取[token专属session]时是否必须登录
|
||||
* (如果配置为true,会在每次获取[token-session]时校验是否登录)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setTokenSessionCheckLogin(Boolean tokenSessionCheckLogin) {
|
||||
this.tokenSessionCheckLogin = tokenSessionCheckLogin;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否打开了自动续签 (如果此值为true, 框架会在每次直接或间接调用getLoginId()时进行一次过期检查与续签操作)
|
||||
*/
|
||||
public Boolean getAutoRenew() {
|
||||
return autoRenew;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param autoRenew 是否打开自动续签 (如果此值为true, 框架会在每次直接或间接调用getLoginId()时进行一次过期检查与续签操作)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setAutoRenew(Boolean autoRenew) {
|
||||
this.autoRenew = autoRenew;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景
|
||||
*/
|
||||
public String getCookieDomain() {
|
||||
return cookieDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cookieDomain 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setCookieDomain(String cookieDomain) {
|
||||
this.cookieDomain = cookieDomain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token前缀, 格式样例(satoken: Bearer xxxx-xxxx-xxxx-xxxx)
|
||||
*/
|
||||
public String getTokenPrefix() {
|
||||
return tokenPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenPrefix token前缀, 格式样例(satoken: Bearer xxxx-xxxx-xxxx-xxxx)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setTokenPrefix(String tokenPrefix) {
|
||||
this.tokenPrefix = tokenPrefix;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否在初始化配置时打印版本字符画
|
||||
*/
|
||||
public Boolean getIsV() {
|
||||
return isV;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isV 是否在初始化配置时打印版本字符画
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaTokenConfig setIsV(Boolean isV) {
|
||||
this.isV = isV;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否打印操作日志
|
||||
*/
|
||||
public Boolean getIsLog() {
|
||||
return isLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isLog 是否打印操作日志
|
||||
*/
|
||||
public SaTokenConfig setIsLog(Boolean isLog) {
|
||||
this.isLog = isLog;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaTokenConfig [tokenName=" + tokenName + ", timeout=" + timeout + ", activityTimeout=" + activityTimeout
|
||||
+ ", allowConcurrentLogin=" + allowConcurrentLogin + ", isShare=" + isShare + ", isReadBody="
|
||||
+ isReadBody + ", isReadHead=" + isReadHead + ", isReadCookie=" + isReadCookie + ", tokenStyle="
|
||||
+ tokenStyle + ", dataRefreshPeriod=" + dataRefreshPeriod + ", tokenSessionCheckLogin="
|
||||
+ tokenSessionCheckLogin + ", autoRenew=" + autoRenew + ", cookieDomain=" + cookieDomain
|
||||
+ ", tokenPrefix=" + tokenPrefix + ", isV=" + isV + ", isLog=" + isLog + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package cn.dev33.satoken.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* sa-token配置文件的构建工厂类
|
||||
* <p>
|
||||
* 只有在非IOC环境下才会用到此类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenConfigFactory {
|
||||
|
||||
/**
|
||||
* 配置文件地址
|
||||
*/
|
||||
public static String configPath = "sa-token.properties";
|
||||
|
||||
/**
|
||||
* 根据configPath路径获取配置信息
|
||||
*
|
||||
* @return 一个SaTokenConfig对象
|
||||
*/
|
||||
public static SaTokenConfig createConfig() {
|
||||
Map<String, String> map = readPropToMap(configPath);
|
||||
if (map == null) {
|
||||
// throw new RuntimeException("找不到配置文件:" + configPath, null);
|
||||
}
|
||||
return (SaTokenConfig) initPropByMap(map, new SaTokenConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具方法: 将指定路径的properties配置文件读取到Map中
|
||||
*
|
||||
* @param propertiesPath 配置文件地址
|
||||
* @return 一个Map
|
||||
*/
|
||||
private static Map<String, String> readPropToMap(String propertiesPath) {
|
||||
Map<String, String> map = new HashMap<String, String>(16);
|
||||
try {
|
||||
InputStream is = SaTokenConfigFactory.class.getClassLoader().getResourceAsStream(propertiesPath);
|
||||
if (is == null) {
|
||||
return null;
|
||||
}
|
||||
Properties prop = new Properties();
|
||||
prop.load(is);
|
||||
for (String key : prop.stringPropertyNames()) {
|
||||
map.put(key, prop.getProperty(key));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("配置文件(" + propertiesPath + ")加载失败", e);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具方法: 将 Map 的值映射到一个 Model 上
|
||||
*
|
||||
* @param map 属性集合
|
||||
* @param obj 对象, 或类型
|
||||
* @return 返回实例化后的对象
|
||||
*/
|
||||
private static Object initPropByMap(Map<String, String> map, Object obj) {
|
||||
|
||||
if (map == null) {
|
||||
map = new HashMap<String, String>(16);
|
||||
}
|
||||
|
||||
// 1、取出类型
|
||||
Class<?> cs = null;
|
||||
if (obj instanceof Class) {
|
||||
// 如果是一个类型,则将obj=null,以便完成静态属性反射赋值
|
||||
cs = (Class<?>) obj;
|
||||
obj = null;
|
||||
} else {
|
||||
// 如果是一个对象,则取出其类型
|
||||
cs = obj.getClass();
|
||||
}
|
||||
|
||||
// 2、遍历类型属性,反射赋值
|
||||
for (Field field : cs.getDeclaredFields()) {
|
||||
String value = map.get(field.getName());
|
||||
if (value == null) {
|
||||
// 如果为空代表没有配置此项
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Object valueConvert = getObjectByClass(value, field.getType());
|
||||
field.setAccessible(true);
|
||||
field.set(obj, valueConvert);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("属性赋值出错:" + field.getName(), e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("属性赋值出错:" + field.getName(), e);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具方法: 将字符串转化为指定数据类型
|
||||
*
|
||||
* @param str 值
|
||||
* @param cs 要转换的类型
|
||||
* @return 转化好的结果
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getObjectByClass(String str, Class<T> cs) {
|
||||
Object value = null;
|
||||
if (str == null) {
|
||||
value = null;
|
||||
} else if (cs.equals(String.class)) {
|
||||
value = str;
|
||||
} else if (cs.equals(int.class) || cs.equals(Integer.class)) {
|
||||
value = new Integer(str);
|
||||
} else if (cs.equals(long.class) || cs.equals(Long.class)) {
|
||||
value = new Long(str);
|
||||
} else if (cs.equals(short.class) || cs.equals(Short.class)) {
|
||||
value = new Short(str);
|
||||
} else if (cs.equals(float.class) || cs.equals(Float.class)) {
|
||||
value = new Float(str);
|
||||
} else if (cs.equals(double.class) || cs.equals(Double.class)) {
|
||||
value = new Double(str);
|
||||
} else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
|
||||
value = new Boolean(str);
|
||||
} else {
|
||||
throw new RuntimeException("未能将值:" + str + ",转换类型为:" + cs, null);
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.dev33.satoken.context;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
|
||||
/**
|
||||
* Sa-Token 上下文处理器
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaTokenContext {
|
||||
|
||||
/**
|
||||
* 获取当前请求的 [Request] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public SaRequest getRequest();
|
||||
|
||||
/**
|
||||
* 获取当前请求的 [Response] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public SaResponse getResponse();
|
||||
|
||||
/**
|
||||
* 获取当前请求的 [存储器] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public SaStorage getStorage();
|
||||
|
||||
/**
|
||||
* 校验指定路由匹配符是否可以匹配成功指定路径
|
||||
*
|
||||
* @param pattern 路由匹配符
|
||||
* @param path 需要匹配的路径
|
||||
* @return see note
|
||||
*/
|
||||
public boolean matchPath(String pattern, String path);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package cn.dev33.satoken.context;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
/**
|
||||
* Sa-Token 上下文处理器 [默认实现类]
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenContextDefaultImpl implements SaTokenContext {
|
||||
|
||||
/**
|
||||
* 默认的错误提示语
|
||||
*/
|
||||
public static final String ERROR_MESSAGE = "未初始化任何有效上下文处理器";
|
||||
|
||||
/**
|
||||
* 获取当前请求的 [Request] 对象
|
||||
*/
|
||||
@Override
|
||||
public SaRequest getRequest() {
|
||||
throw new SaTokenException(ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的 [Response] 对象
|
||||
*/
|
||||
@Override
|
||||
public SaResponse getResponse() {
|
||||
throw new SaTokenException(ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的 [存储器] 对象
|
||||
*/
|
||||
@Override
|
||||
public SaStorage getStorage() {
|
||||
throw new SaTokenException(ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验指定路由匹配符是否可以匹配成功指定路径
|
||||
*/
|
||||
@Override
|
||||
public boolean matchPath(String pattern, String path) {
|
||||
throw new SaTokenException(ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package cn.dev33.satoken.context;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
|
||||
/**
|
||||
* 上下文环境 [ThreadLocal版本]
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenContextForThreadLocal implements SaTokenContext {
|
||||
|
||||
@Override
|
||||
public SaRequest getRequest() {
|
||||
return SaTokenContextForThreadLocalStorage.getRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaResponse getResponse() {
|
||||
return SaTokenContextForThreadLocalStorage.getResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaStorage getStorage() {
|
||||
return SaTokenContextForThreadLocalStorage.getStorage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchPath(String pattern, String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
package cn.dev33.satoken.context;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
/**
|
||||
* 基于ThreadLocal的上下文对象存储器
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenContextForThreadLocalStorage {
|
||||
|
||||
/**
|
||||
* 基于 ThreadLocal 的上下文
|
||||
*/
|
||||
static ThreadLocal<Box> boxThreadLocal = new InheritableThreadLocal<Box>();
|
||||
|
||||
/**
|
||||
* 初始化 [容器]
|
||||
* @param request {@link SaRequest}
|
||||
* @param response {@link SaResponse}
|
||||
* @param storage {@link SaStorage}
|
||||
*/
|
||||
public static void setBox(SaRequest request, SaResponse response, SaStorage storage) {
|
||||
Box bok = new Box(request, response, storage);
|
||||
boxThreadLocal.set(bok);
|
||||
};
|
||||
|
||||
/**
|
||||
* 清除 [容器]
|
||||
*/
|
||||
public static void clearBox() {
|
||||
boxThreadLocal.remove();
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取 [容器]
|
||||
* @return see note
|
||||
*/
|
||||
public static Box getBox() {
|
||||
return boxThreadLocal.get();
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取 [容器], 如果为空则抛出异常
|
||||
* @return see note
|
||||
*/
|
||||
public static Box getBoxNotNull() {
|
||||
Box box = boxThreadLocal.get();
|
||||
if(box == null) {
|
||||
throw new SaTokenException("未成功初始化上下文");
|
||||
}
|
||||
return box;
|
||||
};
|
||||
|
||||
/**
|
||||
* 在 [上下文容器] 获取 [Request] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public static SaRequest getRequest() {
|
||||
return getBoxNotNull().getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [上下文容器] 获取 [Response] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public static SaResponse getResponse() {
|
||||
return getBoxNotNull().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [上下文容器] 获取 [存储器] 对象
|
||||
*
|
||||
* @return see note
|
||||
*/
|
||||
public static SaStorage getStorage() {
|
||||
return getBoxNotNull().getStorage();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 临时内部类,存储三个对象
|
||||
* @author kong
|
||||
*/
|
||||
/**
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public static class Box {
|
||||
|
||||
public SaRequest request;
|
||||
|
||||
public SaResponse response;
|
||||
|
||||
public SaStorage storage;
|
||||
|
||||
public Box(SaRequest request, SaResponse response, SaStorage storage){
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
public SaRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public void setRequest(SaRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public SaResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(SaResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public SaStorage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public void setStorage(SaStorage storage) {
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Box [request=" + request + ", response=" + response + ", storage=" + storage + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package cn.dev33.satoken.context.model;
|
||||
|
||||
/**
|
||||
* Cookie 包装类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaCookie {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package cn.dev33.satoken.context.model;
|
||||
|
||||
/**
|
||||
* Request包装类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaRequest {
|
||||
|
||||
/**
|
||||
* 获取底层源对象
|
||||
* @return see note
|
||||
*/
|
||||
public Object getSource();
|
||||
|
||||
/**
|
||||
* 在 [请求体] 里获取一个值
|
||||
* @param name 键
|
||||
* @return 值
|
||||
*/
|
||||
public String getParameter(String name);
|
||||
|
||||
/**
|
||||
* 在 [请求头] 里获取一个值
|
||||
* @param name 键
|
||||
* @return 值
|
||||
*/
|
||||
public String getHeader(String name);
|
||||
|
||||
/**
|
||||
* 在 [Cookie作用域] 里获取一个值
|
||||
* @param name 键
|
||||
* @return 值
|
||||
*/
|
||||
public String getCookieValue(String name);
|
||||
|
||||
/**
|
||||
* 返回当前请求的URL
|
||||
* @return see note
|
||||
*/
|
||||
public String getRequestURI();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.dev33.satoken.context.model;
|
||||
|
||||
/**
|
||||
* Response 包装类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaResponse {
|
||||
|
||||
/**
|
||||
* 获取底层源对象
|
||||
* @return see note
|
||||
*/
|
||||
public Object getSource();
|
||||
|
||||
/**
|
||||
* 删除指定Cookie
|
||||
* @param name Cookie名称
|
||||
*/
|
||||
public void deleteCookie(String name);
|
||||
|
||||
/**
|
||||
* 写入指定Cookie
|
||||
* @param name Cookie名称
|
||||
* @param value Cookie值
|
||||
* @param path Cookie路径
|
||||
* @param domain Cookie的作用域
|
||||
* @param timeout 过期时间 (秒)
|
||||
*/
|
||||
public void addCookie(String name, String value, String path, String domain, int timeout);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.dev33.satoken.context.model;
|
||||
|
||||
/**
|
||||
* [存储器] 包装类
|
||||
* <p> 在 Request作用域里: 存值、取值
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaStorage {
|
||||
|
||||
/**
|
||||
* 获取底层源对象
|
||||
* @return see note
|
||||
*/
|
||||
public Object getSource();
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里写入一个值
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
*/
|
||||
public void set(String key, Object value);
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里获取一个值
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public Object get(String key);
|
||||
|
||||
/**
|
||||
* 在 [Request作用域] 里删除一个值
|
||||
* @param key 键
|
||||
*/
|
||||
public void delete(String key);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 因为不能确定最终运行的容器属于标准Servlet模型还是非Servlet模型,特封装此包下的包装类进行对接
|
||||
*/
|
||||
package cn.dev33.satoken.context.model;
|
||||
@@ -0,0 +1,178 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* sa-token持久层的接口
|
||||
* @author kong
|
||||
*/
|
||||
public interface SaTokenDao {
|
||||
|
||||
/** 常量,表示一个key永不过期 (在一个key被标注为永远不过期时返回此值) */
|
||||
public static final long NEVER_EXPIRE = -1;
|
||||
|
||||
/** 常量,表示系统中不存在这个缓存 (在对不存在的key获取剩余存活时间时返回此值) */
|
||||
public static final long NOT_VALUE_EXPIRE = -2;
|
||||
|
||||
|
||||
// --------------------- token相关 ---------------------
|
||||
|
||||
/**
|
||||
* 根据key获取value,如果没有,则返回空
|
||||
* @param key 键名称
|
||||
* @return value
|
||||
*/
|
||||
public String get(String key);
|
||||
|
||||
/**
|
||||
* 写入指定key-value键值对,并设定过期时间 (单位: 秒)
|
||||
* @param key 键名称
|
||||
* @param value 值
|
||||
* @param timeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void set(String key, String value, long timeout);
|
||||
|
||||
/**
|
||||
* 修改指定key-value键值对 (过期时间不变)
|
||||
* @param key 键名称
|
||||
* @param value 值
|
||||
*/
|
||||
public void update(String key, String value);
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
* @param key 键名称
|
||||
*/
|
||||
public void delete(String key);
|
||||
|
||||
/**
|
||||
* 获取指定key的剩余存活时间 (单位: 秒)
|
||||
* @param key 指定key
|
||||
* @return 这个key的剩余存活时间
|
||||
*/
|
||||
public long getTimeout(String key);
|
||||
|
||||
/**
|
||||
* 修改指定key的剩余存活时间 (单位: 秒)
|
||||
* @param key 指定key
|
||||
* @param timeout 过期时间
|
||||
*/
|
||||
public void updateTimeout(String key, long timeout);
|
||||
|
||||
|
||||
// --------------------- Object相关 ---------------------
|
||||
|
||||
/**
|
||||
* 根据key获取Object,如果没有,则返回空
|
||||
* @param key 键名称
|
||||
* @return object
|
||||
*/
|
||||
public Object getObject(String key);
|
||||
|
||||
/**
|
||||
* 写入指定键值对,并设定过期时间 (单位: 秒)
|
||||
* @param key 键名称
|
||||
* @param object 值
|
||||
* @param timeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void setObject(String key, Object object, long timeout);
|
||||
|
||||
/**
|
||||
* 修改指定键值对 (过期时间不变)
|
||||
* @param key 键名称
|
||||
* @param object 值
|
||||
*/
|
||||
public void updateObject(String key, Object object);
|
||||
|
||||
/**
|
||||
* 删除一个指定的Object
|
||||
* @param key 键名称
|
||||
*/
|
||||
public void deleteObject(String key);
|
||||
|
||||
/**
|
||||
* 获取指定key的剩余存活时间 (单位: 秒)
|
||||
* @param key 指定key
|
||||
* @return 这个key的剩余存活时间
|
||||
*/
|
||||
public long getObjectTimeout(String key);
|
||||
|
||||
/**
|
||||
* 修改指定key的剩余存活时间 (单位: 秒)
|
||||
* @param key 指定key
|
||||
* @param timeout 过期时间
|
||||
*/
|
||||
public void updateObjectTimeout(String key, long timeout);
|
||||
|
||||
|
||||
// --------------------- Session相关 ---------------------
|
||||
|
||||
/**
|
||||
* 根据指定key的Session,如果没有,则返回空
|
||||
* @param sessionId 键名称
|
||||
* @return SaSession
|
||||
*/
|
||||
public default SaSession getSession(String sessionId) {
|
||||
return (SaSession)getObject(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定Session持久化
|
||||
* @param session 要保存的session对象
|
||||
* @param timeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public default void setSession(SaSession session, long timeout) {
|
||||
setObject(session.getId(), session, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定session
|
||||
* @param session 要更新的session对象
|
||||
*/
|
||||
public default void updateSession(SaSession session) {
|
||||
updateObject(session.getId(), session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的session
|
||||
* @param sessionId sessionId
|
||||
*/
|
||||
public default void deleteSession(String sessionId) {
|
||||
deleteObject(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定SaSession的剩余存活时间 (单位: 秒)
|
||||
* @param sessionId 指定SaSession
|
||||
* @return 这个SaSession的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
public default long getSessionTimeout(String sessionId) {
|
||||
return getObjectTimeout(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定SaSession的剩余存活时间 (单位: 秒)
|
||||
* @param sessionId sessionId
|
||||
* @param timeout 过期时间
|
||||
*/
|
||||
public default void updateSessionTimeout(String sessionId, long timeout) {
|
||||
updateObjectTimeout(sessionId, timeout);
|
||||
}
|
||||
|
||||
|
||||
// --------------------- 会话管理 ---------------------
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
* @param prefix 前缀
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return 查询到的数据集合
|
||||
*/
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token持久层默认的实现类 , 基于内存Map
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
|
||||
|
||||
/**
|
||||
* 数据集合
|
||||
*/
|
||||
public Map<String, Object> dataMap = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* 过期时间集合 (单位: 毫秒) , 记录所有key的到期时间 [注意不是剩余存活时间]
|
||||
*/
|
||||
public Map<String, Long> expireMap = new ConcurrentHashMap<String, Long>();
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public SaTokenDaoDefaultImpl() {
|
||||
initRefreshThread();
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ String 读写操作
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
clearKeyByTimeout(key);
|
||||
return (String)dataMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, String value, long timeout) {
|
||||
dataMap.put(key, value);
|
||||
expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String value) {
|
||||
if(getKeyTimeout(key) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
dataMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeout(String key) {
|
||||
return getKeyTimeout(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
expireMap.put(key, System.currentTimeMillis() + timeout * 1000);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ Object 读写操作
|
||||
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
clearKeyByTimeout(key);
|
||||
return dataMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
dataMap.put(key, object);
|
||||
expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObject(String key, Object object) {
|
||||
if(getKeyTimeout(key) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
// 无动作
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(String key) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getObjectTimeout(String key) {
|
||||
return getKeyTimeout(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
expireMap.put(key, System.currentTimeMillis() + timeout * 1000);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ Session 读写操作
|
||||
// 使用接口默认实现
|
||||
|
||||
|
||||
// ------------------------ 过期时间相关操作
|
||||
|
||||
/**
|
||||
* 如果指定key已经过期,则立即清除它
|
||||
* @param key 指定key
|
||||
*/
|
||||
void clearKeyByTimeout(String key) {
|
||||
Long expirationTime = expireMap.get(key);
|
||||
// 清除条件:如果不为空 && 不是[永不过期] && 已经超过过期时间
|
||||
if(expirationTime != null && expirationTime != SaTokenDao.NEVER_EXPIRE && expirationTime < System.currentTimeMillis()) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的剩余存活时间 (单位:秒)
|
||||
*/
|
||||
long getKeyTimeout(String key) {
|
||||
// 先检查是否已经过期
|
||||
clearKeyByTimeout(key);
|
||||
// 获取过期时间
|
||||
Long expire = expireMap.get(key);
|
||||
// 如果根本没有这个值
|
||||
if(expire == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
// 如果被标注为永不过期
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
return SaTokenDao.NEVER_EXPIRE;
|
||||
}
|
||||
// ---- 计算剩余时间并返回
|
||||
long timeout = (expire - System.currentTimeMillis()) / 1000;
|
||||
// 小于零时,视为不存在
|
||||
if(timeout < 0) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
|
||||
// --------------------- 定时清理过期数据
|
||||
|
||||
/**
|
||||
* 执行数据清理的线程
|
||||
*/
|
||||
public Thread refreshThread;
|
||||
|
||||
/**
|
||||
* 是否继续执行数据清理的线程标记
|
||||
*/
|
||||
public boolean refreshFlag;
|
||||
|
||||
|
||||
/**
|
||||
* 清理所有已经过期的key
|
||||
*/
|
||||
public void refreshDataMap() {
|
||||
Iterator<String> keys = expireMap.keySet().iterator();
|
||||
while (keys.hasNext()) {
|
||||
clearKeyByTimeout(keys.next());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化定时任务
|
||||
*/
|
||||
public void initRefreshThread() {
|
||||
|
||||
// 如果配置了<=0的值,则不启动定时清理
|
||||
if(SaTokenManager.getConfig().getDataRefreshPeriod() <= 0) {
|
||||
return;
|
||||
}
|
||||
// 启动定时刷新
|
||||
this.refreshFlag = true;
|
||||
this.refreshThread = new Thread(() -> {
|
||||
for (;;) {
|
||||
try {
|
||||
try {
|
||||
// 如果已经被标记为结束
|
||||
if(refreshFlag == false) {
|
||||
return;
|
||||
}
|
||||
// 执行清理
|
||||
refreshDataMap();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 休眠N秒
|
||||
int dataRefreshPeriod = SaTokenManager.getConfig().getDataRefreshPeriod();
|
||||
if(dataRefreshPeriod <= 0) {
|
||||
dataRefreshPeriod = 1;
|
||||
}
|
||||
Thread.sleep(dataRefreshPeriod * 1000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
refreshThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束定时任务
|
||||
*/
|
||||
public void endRefreshThread() {
|
||||
this.refreshFlag = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// --------------------- 会话管理
|
||||
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size) {
|
||||
return SaTokenInsideUtil.searchList(expireMap.keySet(), prefix, keyword, start, size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
/**
|
||||
* 一个异常:代表账号已被封禁
|
||||
*
|
||||
* @author kong
|
||||
*/
|
||||
public class DisableLoginException extends SaTokenException {
|
||||
|
||||
/**
|
||||
* 序列化版本号
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130143L;
|
||||
|
||||
/** 异常标记值 */
|
||||
public static final String BE_VALUE = "disable";
|
||||
|
||||
/** 异常提示语 */
|
||||
public static final String BE_MESSAGE = "此账号已被封禁";
|
||||
|
||||
/**
|
||||
* LoginKey
|
||||
*/
|
||||
private String loginKey;
|
||||
|
||||
/**
|
||||
* 被封禁的账号id
|
||||
*/
|
||||
private Object loginId;
|
||||
|
||||
/**
|
||||
* 封禁剩余时间,单位:秒
|
||||
*/
|
||||
private long disableTime;
|
||||
|
||||
/**
|
||||
* 获得LoginKey
|
||||
*
|
||||
* @return See above
|
||||
*/
|
||||
public String getLoginKey() {
|
||||
return loginKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取: 被封禁的账号id
|
||||
*
|
||||
* @return See above
|
||||
*/
|
||||
public Object getLoginId() {
|
||||
return loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取: 封禁剩余时间,单位:秒
|
||||
* @return See above
|
||||
*/
|
||||
public long getDisableTime() {
|
||||
return disableTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造方法创建一个
|
||||
*
|
||||
* @param loginKey loginKey
|
||||
* @param loginId 被封禁的账号id
|
||||
* @param disableTime 封禁剩余时间,单位:秒
|
||||
*/
|
||||
public DisableLoginException(String loginKey, Object loginId, long disableTime) {
|
||||
super(BE_MESSAGE);
|
||||
this.loginId = loginId;
|
||||
this.loginKey = loginKey;
|
||||
this.disableTime = disableTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+16
-26
@@ -4,14 +4,13 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 没有登陆抛出的异常
|
||||
* @author kong
|
||||
*
|
||||
* 一个异常:代表用户没有登录
|
||||
* @author kong
|
||||
*/
|
||||
public class NotLoginException extends RuntimeException {
|
||||
public class NotLoginException extends SaTokenException {
|
||||
|
||||
/**
|
||||
*
|
||||
* 序列化版本号
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130142L;
|
||||
|
||||
@@ -57,9 +56,10 @@ public class NotLoginException extends RuntimeException {
|
||||
* 异常类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 获取异常类型
|
||||
* @return
|
||||
* @return 异常类型
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
@@ -70,59 +70,49 @@ public class NotLoginException extends RuntimeException {
|
||||
* loginKey
|
||||
*/
|
||||
private String loginKey;
|
||||
|
||||
/**
|
||||
* 获得loginKey
|
||||
* @return login_key
|
||||
* @return loginKey
|
||||
*/
|
||||
public String getLoginKey() {
|
||||
return loginKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// /**
|
||||
// * 创建一个
|
||||
// */
|
||||
// public NotLoginException() {
|
||||
// this(StpUtil.stpLogic.loginKey);
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* 创建一个
|
||||
* 构造方法创建一个
|
||||
* @param message 异常消息
|
||||
* @param loginKey loginKey
|
||||
* @param type 类型
|
||||
*/
|
||||
public NotLoginException(String message, String loginKey, String type) {
|
||||
// 这里到底要不要拼接上login_key呢?纠结
|
||||
super(message);
|
||||
this.loginKey = loginKey;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法构建一个NotLoginException
|
||||
* @param loginKey loginKey
|
||||
* 静态方法构建一个NotLoginException
|
||||
* @param loginKey loginKey
|
||||
* @param type 场景类型
|
||||
* @return 构建完毕的异常对象
|
||||
*/
|
||||
public static NotLoginException newInstance(String loginKey, String type) {
|
||||
String message = null;
|
||||
if(type.equals(NOT_TOKEN)) {
|
||||
if(NOT_TOKEN.equals(type)) {
|
||||
message = NOT_TOKEN_MESSAGE;
|
||||
}
|
||||
else if(type.equals(INVALID_TOKEN)) {
|
||||
else if(INVALID_TOKEN.equals(type)) {
|
||||
message = INVALID_TOKEN_MESSAGE;
|
||||
}
|
||||
else if(type.equals(TOKEN_TIMEOUT)) {
|
||||
else if(TOKEN_TIMEOUT.equals(type)) {
|
||||
message = TOKEN_TIMEOUT_MESSAGE;
|
||||
}
|
||||
else if(type.equals(BE_REPLACED)) {
|
||||
else if(BE_REPLACED.equals(type)) {
|
||||
message = BE_REPLACED_MESSAGE;
|
||||
}
|
||||
else if(type.equals(KICK_OUT)) {
|
||||
else if(KICK_OUT.equals(type)) {
|
||||
message = KICK_OUT_MESSAGE;
|
||||
}
|
||||
else {
|
||||
@@ -0,0 +1,52 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 没有指定权限码,抛出的异常
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class NotPermissionException extends SaTokenException {
|
||||
|
||||
/**
|
||||
* 序列化版本号
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130142L;
|
||||
|
||||
/** 权限码 */
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* @return 获得权限码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* loginKey
|
||||
*/
|
||||
private String loginKey;
|
||||
|
||||
/**
|
||||
* 获得loginKey
|
||||
*
|
||||
* @return loginKey
|
||||
*/
|
||||
public String getLoginKey() {
|
||||
return loginKey;
|
||||
}
|
||||
|
||||
public NotPermissionException(String code) {
|
||||
this(code, StpUtil.stpLogic.loginKey);
|
||||
}
|
||||
|
||||
public NotPermissionException(String code, String loginKey) {
|
||||
super("无此权限:" + code);
|
||||
this.code = code;
|
||||
this.loginKey = loginKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 没有指定角色标识,抛出的异常
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class NotRoleException extends SaTokenException {
|
||||
|
||||
/**
|
||||
* 序列化版本号
|
||||
*/
|
||||
private static final long serialVersionUID = 8243974276159004739L;
|
||||
|
||||
/** 角色标识 */
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* @return 获得角色标识
|
||||
*/
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* loginKey
|
||||
*/
|
||||
private String loginKey;
|
||||
|
||||
/**
|
||||
* 获得loginKey
|
||||
*
|
||||
* @return loginKey
|
||||
*/
|
||||
public String getLoginKey() {
|
||||
return loginKey;
|
||||
}
|
||||
|
||||
public NotRoleException(String role) {
|
||||
this(role, StpUtil.stpLogic.loginKey);
|
||||
}
|
||||
|
||||
public NotRoleException(String role, String loginKey) {
|
||||
// 这里到底要不要拼接上loginKey呢?纠结
|
||||
super("无此角色:" + role);
|
||||
this.role = role;
|
||||
this.loginKey = loginKey;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
/**
|
||||
* sa-token框架内部逻辑发生错误抛出的异常
|
||||
* (自定义此异常可方便开发者在做全局异常处理时分辨异常类型)
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* 序列化版本号
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130132L;
|
||||
|
||||
/**
|
||||
* 构建一个异常
|
||||
*
|
||||
* @param message 异常描述信息
|
||||
*/
|
||||
public SaTokenException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个异常
|
||||
*
|
||||
* @param cause 异常对象
|
||||
*/
|
||||
public SaTokenException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个异常
|
||||
*
|
||||
* @param message 异常信息
|
||||
* @param cause 异常对象
|
||||
*/
|
||||
public SaTokenException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.dev33.satoken.filter;
|
||||
|
||||
/**
|
||||
* sa-token全局过滤器-认证策略
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaFilterAuthStrategy {
|
||||
|
||||
/**
|
||||
* 执行方法
|
||||
* @param r 无含义参数,留作扩展
|
||||
*/
|
||||
public void run(Object r);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.dev33.satoken.filter;
|
||||
|
||||
/**
|
||||
* sa-token全局过滤器-异常处理策略
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaFilterErrorStrategy {
|
||||
|
||||
/**
|
||||
* 执行方法
|
||||
* @param e 异常对象
|
||||
* @return 输出对象(请提前序列化)
|
||||
*/
|
||||
public Object run(Throwable e);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package cn.dev33.satoken.fun;
|
||||
|
||||
/**
|
||||
* 根据boolean变量,决定是否执行一个函数
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class IsRunFunction {
|
||||
|
||||
/**
|
||||
* 变量
|
||||
*/
|
||||
public final Boolean isRun;
|
||||
|
||||
/**
|
||||
* 设定一个变量,如果为true,则执行exe函数
|
||||
*
|
||||
* @param isRun 变量
|
||||
*/
|
||||
public IsRunFunction(boolean isRun) {
|
||||
this.isRun = isRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 isRun == true 时执行此函数
|
||||
* @param function 函数
|
||||
* @return 对象自身
|
||||
*/
|
||||
public IsRunFunction exe(SaFunction function) {
|
||||
if (isRun) {
|
||||
function.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 isRun == false 时执行此函数
|
||||
* @param function 函数
|
||||
* @return 对象自身
|
||||
*/
|
||||
public IsRunFunction noExe(SaFunction function) {
|
||||
if (!isRun) {
|
||||
function.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.dev33.satoken.fun;
|
||||
|
||||
/**
|
||||
* 设定一个函数,方便在Lambda表达式下的函数式编程
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaFunction {
|
||||
|
||||
/**
|
||||
* 执行的方法
|
||||
*/
|
||||
public void run();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package cn.dev33.satoken.listener;
|
||||
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
|
||||
/**
|
||||
* Sa-Token的侦听器
|
||||
* <p> 你可以通过实现此接口在用户登陆、退出等关键性操作时进行一些AOP操作
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaTokenListener {
|
||||
|
||||
/**
|
||||
* 每次登录时触发
|
||||
* @param loginKey 账号类别
|
||||
* @param loginId 账号id
|
||||
* @param loginModel 登录参数
|
||||
*/
|
||||
public void doLogin(String loginKey, Object loginId, SaLoginModel loginModel);
|
||||
|
||||
/**
|
||||
* 每次注销时触发
|
||||
* @param loginKey 账号类别
|
||||
* @param loginId 账号id
|
||||
* @param tokenValue token值
|
||||
*/
|
||||
public void doLogout(String loginKey, Object loginId, String tokenValue);
|
||||
|
||||
/**
|
||||
* 每次被踢下线时触发
|
||||
* @param loginKey 账号类别
|
||||
* @param loginId 账号id
|
||||
* @param tokenValue token值
|
||||
* @param device 设备标识
|
||||
*/
|
||||
public void doLogoutByLoginId(String loginKey, Object loginId, String tokenValue, String device);
|
||||
|
||||
/**
|
||||
* 每次被顶下线时触发
|
||||
* @param loginKey 账号类别
|
||||
* @param loginId 账号id
|
||||
* @param tokenValue token值
|
||||
* @param device 设备标识
|
||||
*/
|
||||
public void doReplaced(String loginKey, Object loginId, String tokenValue, String device);
|
||||
|
||||
/**
|
||||
* 每次被封禁时触发
|
||||
* @param loginKey 账号类别
|
||||
* @param loginId 账号id
|
||||
* @param disableTime 封禁时长,单位: 秒
|
||||
*/
|
||||
public void doDisable(String loginKey, Object loginId, long disableTime);
|
||||
|
||||
/**
|
||||
* 每次被解封时触发
|
||||
* @param loginKey 账号类别
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public void doUntieDisable(String loginKey, Object loginId);
|
||||
|
||||
/**
|
||||
* 每次创建Session时触发
|
||||
* @param id SessionId
|
||||
*/
|
||||
public void doCreateSession(String id);
|
||||
|
||||
/**
|
||||
* 每次注销Session时触发
|
||||
* @param id SessionId
|
||||
*/
|
||||
public void doLogoutSession(String id);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package cn.dev33.satoken.listener;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* Sa-Token 侦听器的默认实现:log打印
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenListenerDefaultImpl implements SaTokenListener {
|
||||
|
||||
/**
|
||||
* 每次登录时触发
|
||||
*/
|
||||
@Override
|
||||
public void doLogin(String loginKey, Object loginId, SaLoginModel loginModel) {
|
||||
println("账号[" + loginId + "]登录成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次注销时触发
|
||||
*/
|
||||
@Override
|
||||
public void doLogout(String loginKey, Object loginId, String tokenValue) {
|
||||
println("账号[" + loginId + "]注销成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次被踢下线时触发
|
||||
*/
|
||||
@Override
|
||||
public void doLogoutByLoginId(String loginKey, Object loginId, String tokenValue, String device) {
|
||||
println("账号[" + loginId + "]被踢下线 (终端: " + device + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次被顶下线时触发
|
||||
*/
|
||||
@Override
|
||||
public void doReplaced(String loginKey, Object loginId, String tokenValue, String device) {
|
||||
println("账号[" + loginId + "]被顶下线 (终端: " + device + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次被封禁时触发
|
||||
*/
|
||||
@Override
|
||||
public void doDisable(String loginKey, Object loginId, long disableTime) {
|
||||
Date date = new Date(System.currentTimeMillis() + disableTime * 1000);
|
||||
println("账号[" + loginId + "]被封禁 (解封时间: " + SaTokenInsideUtil.formatDate(date) + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次被解封时触发
|
||||
*/
|
||||
@Override
|
||||
public void doUntieDisable(String loginKey, Object loginId) {
|
||||
println("账号[" + loginId + "]被解除封禁");
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次创建Session时触发
|
||||
*/
|
||||
@Override
|
||||
public void doCreateSession(String id) {
|
||||
println("Session[" + id + "]创建成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次注销Session时触发
|
||||
*/
|
||||
@Override
|
||||
public void doLogoutSession(String id) {
|
||||
println("Session[" + id + "]注销成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志输出的前缀
|
||||
*/
|
||||
public static final String LOG_PREFIX = "SaLog -->: ";
|
||||
|
||||
/**
|
||||
* 打印指定字符串
|
||||
* @param str 字符串
|
||||
*/
|
||||
public void println(String str) {
|
||||
if(SaTokenManager.getConfig().getIsLog()) {
|
||||
System.out.println(LOG_PREFIX + str);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.dev33.satoken.router;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
|
||||
/**
|
||||
* 执行验证方法的辅助类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaRouteFunction {
|
||||
|
||||
/**
|
||||
* 执行验证的方法
|
||||
*
|
||||
* @param request Request包装对象
|
||||
* @param response Response包装对象
|
||||
* @param handler 处理对象
|
||||
*/
|
||||
public void run(SaRequest request, SaResponse response, Object handler);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package cn.dev33.satoken.router;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.fun.IsRunFunction;
|
||||
import cn.dev33.satoken.fun.SaFunction;
|
||||
|
||||
/**
|
||||
* 对路由匹配符相关操作的封装工具类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaRouterUtil {
|
||||
|
||||
// -------------------- 路由匹配相关 --------------------
|
||||
|
||||
/**
|
||||
* 校验指定路由匹配符是否可以匹配成功指定路径
|
||||
* @param pattern 路由匹配符
|
||||
* @param path 需要匹配的路径
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatch(String pattern, String path) {
|
||||
return SaTokenManager.getSaTokenContext().matchPath(pattern, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验指定路由匹配符是否可以匹配成功指定路径
|
||||
* @param patterns 路由匹配符
|
||||
* @param path 需要匹配的路径集合
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatch(List<String> patterns, String path) {
|
||||
for (String pattern : patterns) {
|
||||
if(isMatch(pattern, path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验指定路由匹配符是否可以匹配成功当前URI
|
||||
* @param pattern 路由匹配符
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatchCurrURI(String pattern) {
|
||||
return isMatch(pattern, SaTokenManager.getSaTokenContext().getRequest().getRequestURI());
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验指定路由匹配符是否可以匹配成功当前URI
|
||||
* @param patterns 路由匹配符
|
||||
* @return 是否匹配成功
|
||||
*/
|
||||
public static boolean isMatchCurrURI(List<String> patterns) {
|
||||
return isMatch(patterns, SaTokenManager.getSaTokenContext().getRequest().getRequestURI());
|
||||
}
|
||||
|
||||
|
||||
// -------------------- 执行相关 --------------------
|
||||
|
||||
/**
|
||||
* 使用路由匹配符与当前URI执行匹配,如果匹配成功则执行验证函数
|
||||
* @param pattern 路由匹配符
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
public static void match(String pattern, SaFunction function) {
|
||||
if(isMatchCurrURI(pattern)) {
|
||||
function.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用路由匹配符与当前URI执行匹配 (并指定排除匹配符),如果匹配成功则执行验证函数
|
||||
* @param pattern 路由匹配符
|
||||
* @param excludePattern 要排除的路由匹配符
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
public static void match(String pattern, String excludePattern, SaFunction function) {
|
||||
if(isMatchCurrURI(pattern)) {
|
||||
if(isMatchCurrURI(excludePattern) == false) {
|
||||
function.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用路由匹配符集合与当前URI执行匹配,如果匹配成功则执行验证函数
|
||||
* @param patterns 路由匹配符集合
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
public static void match(List<String> patterns, SaFunction function) {
|
||||
if(isMatchCurrURI(patterns)) {
|
||||
function.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用路由匹配符集合与当前URI执行匹配 (并指定排除匹配符),如果匹配成功则执行验证函数
|
||||
* @param patterns 路由匹配符集合
|
||||
* @param excludePatterns 要排除的路由匹配符集合
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
public static void match(List<String> patterns, List<String> excludePatterns, SaFunction function) {
|
||||
if(isMatchCurrURI(patterns)) {
|
||||
if(isMatchCurrURI(excludePatterns) == false) {
|
||||
function.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用路由匹配符集合与当前URI执行匹配,如果匹配成功则执行验证函数
|
||||
* @param patterns 路由匹配符集合
|
||||
* @return 匹配结果包装对象
|
||||
*/
|
||||
public static IsRunFunction match(String... patterns) {
|
||||
boolean matchResult = isMatch(Arrays.asList(patterns), SaTokenManager.getSaTokenContext().getRequest().getRequestURI());
|
||||
return new IsRunFunction(matchResult);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package cn.dev33.satoken.secure;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* Base64工具类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaBase64Util {
|
||||
|
||||
private static Base64.Encoder encoder = Base64.getEncoder();
|
||||
private static Base64.Decoder decoder = Base64.getDecoder();
|
||||
|
||||
/**
|
||||
* Base64编码,byte[] 转 String
|
||||
* @param bytes byte[]
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String encodeBytesToString(byte[] bytes){
|
||||
return encoder.encodeToString(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64解码,String 转 byte[]
|
||||
* @param text 字符串
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] decodeStringToBytes(String text){
|
||||
return decoder.decode(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64编码,String 转 String
|
||||
* @param text 字符串
|
||||
* @return Base64格式字符串
|
||||
*/
|
||||
public static String encode(String text){
|
||||
try {
|
||||
return encoder.encodeToString(text.getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64解码,String 转 String
|
||||
* @param base64Text Base64格式字符串
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String decode(String base64Text){
|
||||
try {
|
||||
return new String(decoder.decode(base64Text), "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
package cn.dev33.satoken.secure;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
/**
|
||||
* sa-token 常见加密算法工具类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaSecureUtil {
|
||||
|
||||
/**
|
||||
* Base64编码
|
||||
*/
|
||||
private static Base64.Encoder encoder = Base64.getEncoder();
|
||||
|
||||
/**
|
||||
* Base64解码
|
||||
*/
|
||||
private static Base64.Decoder decoder = Base64.getDecoder();
|
||||
|
||||
// ----------------------- 摘要加密 -----------------------
|
||||
|
||||
/**
|
||||
* md5加密
|
||||
* @param str 指定字符串
|
||||
* @return 加密后的字符串
|
||||
*/
|
||||
public static String md5(String str) {
|
||||
str = (str == null ? "" : str);
|
||||
char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
try {
|
||||
byte[] btInput = str.getBytes();
|
||||
MessageDigest mdInst = MessageDigest.getInstance("MD5");
|
||||
mdInst.update(btInput);
|
||||
byte[] md = mdInst.digest();
|
||||
int j = md.length;
|
||||
char[] strA = new char[j * 2];
|
||||
int k = 0;
|
||||
for (byte byte0 : md) {
|
||||
strA[k++] = hexDigits[byte0 >>> 4 & 0xf];
|
||||
strA[k++] = hexDigits[byte0 & 0xf];
|
||||
}
|
||||
return new String(strA);
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sha1加密
|
||||
*
|
||||
* @param str 指定字符串
|
||||
* @return 加密后的字符串
|
||||
*/
|
||||
public static String sha1(String str) {
|
||||
try {
|
||||
str = (str == null ? "" : str);
|
||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||
byte[] b = str.getBytes();
|
||||
md.update(b);
|
||||
byte[] b2 = md.digest();
|
||||
int len = b2.length;
|
||||
String strA = "0123456789abcdef";
|
||||
char[] ch = strA.toCharArray();
|
||||
char[] chs = new char[len * 2];
|
||||
for (int i = 0, k = 0; i < len; i++) {
|
||||
byte b3 = b2[i];
|
||||
chs[k++] = ch[b3 >>> 4 & 0xf];
|
||||
chs[k++] = ch[b3 & 0xf];
|
||||
}
|
||||
return new String(chs);
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sha256加密
|
||||
*
|
||||
* @param str 指定字符串
|
||||
* @return 加密后的字符串
|
||||
*/
|
||||
public static String sha256(String str) {
|
||||
try {
|
||||
str = (str == null ? "" : str);
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
|
||||
messageDigest.update(str.getBytes("UTF-8"));
|
||||
|
||||
byte[] bytes = messageDigest.digest();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String temp;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
temp = Integer.toHexString(bytes[i] & 0xFF);
|
||||
if (temp.length() == 1) {
|
||||
builder.append("0");
|
||||
}
|
||||
builder.append(temp);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* md5加盐加密: md5(md5(str) + md5(salt))
|
||||
* @param str 字符串
|
||||
* @param salt 盐
|
||||
* @return 加密后的字符串
|
||||
*/
|
||||
public static String md5BySalt(String str, String salt) {
|
||||
return md5(md5(str) + md5(salt));
|
||||
}
|
||||
|
||||
|
||||
// ----------------------- 对称加密 AES -----------------------
|
||||
|
||||
/**
|
||||
* 默认密码算法
|
||||
*/
|
||||
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
|
||||
|
||||
/**
|
||||
* AES加密
|
||||
*
|
||||
* @param key 加密的密钥
|
||||
* @param text 需要加密的字符串
|
||||
* @return 返回Base64转码后的加密数据
|
||||
*/
|
||||
public static String aesEncrypt(String key, String text) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
|
||||
byte[] byteContent = text.getBytes("utf-8");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));
|
||||
byte[] result = cipher.doFinal(byteContent);
|
||||
return encoder.encodeToString(result);
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AES解密
|
||||
* @param key 加密的密钥
|
||||
* @param text 已加密的密文
|
||||
* @return 返回解密后的数据
|
||||
*/
|
||||
public static String aesDecrypt(String key, String text) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
|
||||
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));
|
||||
byte[] result = cipher.doFinal(decoder.decode(text));
|
||||
return new String(result, "utf-8");
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成加密秘钥
|
||||
* @param password 秘钥
|
||||
* @return SecretKeySpec
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
private static SecretKeySpec getSecretKey(final String password) throws NoSuchAlgorithmException {
|
||||
KeyGenerator kg = KeyGenerator.getInstance("AES");
|
||||
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
|
||||
random.setSeed(password.getBytes());
|
||||
kg.init(128, random);
|
||||
SecretKey secretKey = kg.generateKey();
|
||||
return new SecretKeySpec(secretKey.getEncoded(), "AES");
|
||||
}
|
||||
|
||||
|
||||
// ----------------------- 非对称加密 RSA -----------------------
|
||||
|
||||
private static final String ALGORITHM = "RSA";
|
||||
|
||||
private static final int KEY_SIZE = 1024;
|
||||
|
||||
|
||||
// ---------- 5个常用方法
|
||||
|
||||
/**
|
||||
* 生成密钥对
|
||||
* @return Map对象 (private=私钥, public=公钥)
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
public static HashMap<String, String> rsaGenerateKeyPair() throws Exception {
|
||||
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
|
||||
KeyPair keyPair;
|
||||
|
||||
try {
|
||||
keyPairGenerator.initialize(KEY_SIZE,
|
||||
new SecureRandom(UUID.randomUUID().toString().replaceAll("-", "").getBytes()));
|
||||
keyPair = keyPairGenerator.generateKeyPair();
|
||||
} catch (InvalidParameterException e) {
|
||||
throw e;
|
||||
} catch (NullPointerException e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||
|
||||
HashMap<String, String> map = new HashMap<String, String>(16);
|
||||
map.put("private", encoder.encodeToString(rsaPrivateKey.getEncoded()));
|
||||
map.put("public", encoder.encodeToString(rsaPublicKey.getEncoded()));
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA公钥加密
|
||||
* @param publicKeyString 公钥
|
||||
* @param content 内容
|
||||
* @return 加密后内容
|
||||
*/
|
||||
public static String rsaEncryptByPublic(String publicKeyString, String content) {
|
||||
try {
|
||||
// 获得公钥对象
|
||||
PublicKey publicKey = getPublicKeyFromString(publicKeyString);
|
||||
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
// 该密钥能够加密的最大字节长度
|
||||
int splitLength = ((RSAPublicKey) publicKey).getModulus().bitLength() / 8 - 11;
|
||||
byte[][] arrays = splitBytes(content.getBytes(), splitLength);
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
for (byte[] array : arrays) {
|
||||
stringBuffer.append(bytesToHexString(cipher.doFinal(array)));
|
||||
}
|
||||
return stringBuffer.toString();
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA私钥加密
|
||||
* @param privateKeyString 私钥
|
||||
* @param content 内容
|
||||
* @return 加密后内容
|
||||
*/
|
||||
public static String rsaEncryptByPrivate(String privateKeyString, String content) {
|
||||
try {
|
||||
PrivateKey privateKey = getPrivateKeyFromString(privateKeyString);
|
||||
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
|
||||
// 该密钥能够加密的最大字节长度
|
||||
int splitLength = ((RSAPrivateKey) privateKey).getModulus().bitLength() / 8 - 11;
|
||||
byte[][] arrays = splitBytes(content.getBytes(), splitLength);
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
for (byte[] array : arrays) {
|
||||
stringBuffer.append(bytesToHexString(cipher.doFinal(array)));
|
||||
}
|
||||
return stringBuffer.toString();
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA公钥解密
|
||||
* @param publicKeyString 公钥
|
||||
* @param content 已加密内容
|
||||
* @return 解密后内容
|
||||
*/
|
||||
public static String rsaDecryptByPublic(String publicKeyString, String content) {
|
||||
|
||||
try {
|
||||
PublicKey publicKey = getPublicKeyFromString(publicKeyString);
|
||||
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
||||
// 该密钥能够加密的最大字节长度
|
||||
int splitLength = ((RSAPublicKey) publicKey).getModulus().bitLength() / 8;
|
||||
byte[] contentBytes = hexStringToBytes(content);
|
||||
byte[][] arrays = splitBytes(contentBytes, splitLength);
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
for (byte[] array : arrays) {
|
||||
stringBuffer.append(new String(cipher.doFinal(array)));
|
||||
}
|
||||
return stringBuffer.toString();
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA私钥解密
|
||||
* @param privateKeyString 公钥
|
||||
* @param content 已加密内容
|
||||
* @return 解密后内容
|
||||
*/
|
||||
public static String rsaDecryptByPrivate(String privateKeyString, String content) {
|
||||
try {
|
||||
PrivateKey privateKey = getPrivateKeyFromString(privateKeyString);
|
||||
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
// 该密钥能够加密的最大字节长度
|
||||
int splitLength = ((RSAPrivateKey) privateKey).getModulus().bitLength() / 8;
|
||||
byte[] contentBytes = hexStringToBytes(content);
|
||||
byte[][] arrays = splitBytes(contentBytes, splitLength);
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
for (byte[] array : arrays) {
|
||||
stringBuffer.append(new String(cipher.doFinal(array)));
|
||||
}
|
||||
return stringBuffer.toString();
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------- 获取*钥
|
||||
|
||||
/** 根据公钥字符串获取 公钥对象 */
|
||||
private static PublicKey getPublicKeyFromString(String key)
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
|
||||
// 过滤掉\r\n
|
||||
key = key.replace("\r\n", "");
|
||||
|
||||
// 取得公钥
|
||||
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(decoder.decode(key));
|
||||
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
|
||||
|
||||
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
|
||||
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
/** 根据私钥字符串获取 私钥对象 */
|
||||
private static PrivateKey getPrivateKeyFromString(String key)
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
|
||||
// 过滤掉\r\n
|
||||
key = key.replace("\r\n", "");
|
||||
|
||||
// 取得私钥
|
||||
PKCS8EncodedKeySpec x509KeySpec = new PKCS8EncodedKeySpec(decoder.decode(key));
|
||||
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
|
||||
|
||||
PrivateKey privateKey = keyFactory.generatePrivate(x509KeySpec);
|
||||
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
|
||||
// ---------- 一些辅助方法
|
||||
|
||||
/** 根据限定的每组字节长度,将字节数组分组 */
|
||||
private static byte[][] splitBytes(byte[] bytes, int splitLength) {
|
||||
|
||||
// bytes与splitLength的余数
|
||||
int remainder = bytes.length % splitLength;
|
||||
// 数据拆分后的组数,余数不为0时加1
|
||||
int quotient = remainder != 0 ? bytes.length / splitLength + 1 : bytes.length / splitLength;
|
||||
byte[][] arrays = new byte[quotient][];
|
||||
byte[] array = null;
|
||||
for (int i = 0; i < quotient; i++) {
|
||||
// 如果是最后一组(quotient-1),同时余数不等于0,就将最后一组设置为remainder的长度
|
||||
if (i == quotient - 1 && remainder != 0) {
|
||||
array = new byte[remainder];
|
||||
System.arraycopy(bytes, i * splitLength, array, 0, remainder);
|
||||
} else {
|
||||
array = new byte[splitLength];
|
||||
System.arraycopy(bytes, i * splitLength, array, 0, splitLength);
|
||||
}
|
||||
arrays[i] = array;
|
||||
}
|
||||
return arrays;
|
||||
}
|
||||
|
||||
/** 将字节数组转换成16进制字符串 */
|
||||
private static String bytesToHexString(byte[] bytes) {
|
||||
|
||||
StringBuffer sb = new StringBuffer(bytes.length);
|
||||
String temp = null;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
temp = Integer.toHexString(0xFF & bytes[i]);
|
||||
if (temp.length() < 2) {
|
||||
sb.append(0);
|
||||
}
|
||||
sb.append(temp);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** 将16进制字符串转换成字节数组 */
|
||||
private static byte[] hexStringToBytes(String hex) {
|
||||
|
||||
int len = (hex.length() / 2);
|
||||
hex = hex.toUpperCase();
|
||||
byte[] result = new byte[len];
|
||||
char[] chars = hex.toCharArray();
|
||||
for (int i = 0; i < len; i++) {
|
||||
int pos = i * 2;
|
||||
result[i] = (byte) (toByte(chars[pos]) << 4 | toByte(chars[pos + 1]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 将char转换为byte */
|
||||
private static byte toByte(char c) {
|
||||
|
||||
return (byte) "0123456789ABCDEF".indexOf(c);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,524 @@
|
||||
package cn.dev33.satoken.session;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
/**
|
||||
* Session Model
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaSession implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 此Session的id */
|
||||
private String id;
|
||||
|
||||
/** 此Session的创建时间 */
|
||||
private long createTime;
|
||||
|
||||
/** 此Session的所有挂载数据 */
|
||||
private final Map<String, Object> dataMap = new ConcurrentHashMap<>();
|
||||
|
||||
// ----------------------- 构建相关
|
||||
|
||||
/**
|
||||
* 构建一个Session对象
|
||||
*/
|
||||
public SaSession() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个Session对象
|
||||
* @param id Session的id
|
||||
*/
|
||||
public SaSession(String id) {
|
||||
this.id = id;
|
||||
this.createTime = System.currentTimeMillis();
|
||||
// $$ 通知监听器
|
||||
SaTokenManager.getSaTokenListener().doCreateSession(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此Session的id
|
||||
* @return 此会话的id
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入此Session的id
|
||||
* @param id SessionId
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSession setId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前会话创建时间
|
||||
* @return 时间戳
|
||||
*/
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入此Session的创建时间
|
||||
* @param createTime 时间戳
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSession setCreateTime(long createTime) {
|
||||
this.createTime = createTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------- TokenSign相关
|
||||
|
||||
/**
|
||||
* 此Session绑定的token签名列表
|
||||
*/
|
||||
private final List<TokenSign> tokenSignList = new Vector<>();
|
||||
|
||||
/**
|
||||
* 返回token签名列表的拷贝副本
|
||||
*
|
||||
* @return token签名列表
|
||||
*/
|
||||
public List<TokenSign> getTokenSignList() {
|
||||
return new Vector<>(tokenSignList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找一个token签名
|
||||
*
|
||||
* @param tokenValue token值
|
||||
* @return 查找到的tokenSign
|
||||
*/
|
||||
public TokenSign getTokenSign(String tokenValue) {
|
||||
for (TokenSign tokenSign : getTokenSignList()) {
|
||||
if (tokenSign.getValue().equals(tokenValue)) {
|
||||
return tokenSign;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个token签名
|
||||
*
|
||||
* @param tokenSign token签名
|
||||
*/
|
||||
public void addTokenSign(TokenSign tokenSign) {
|
||||
// 如果已经存在于列表中,则无需再次添加
|
||||
for (TokenSign tokenSign2 : getTokenSignList()) {
|
||||
if (tokenSign2.getValue().equals(tokenSign.getValue())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 添加并更新
|
||||
tokenSignList.add(tokenSign);
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除一个token签名
|
||||
*
|
||||
* @param tokenValue token名称
|
||||
*/
|
||||
public void removeTokenSign(String tokenValue) {
|
||||
TokenSign tokenSign = getTokenSign(tokenValue);
|
||||
if (tokenSignList.remove(tokenSign)) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------- 存取值
|
||||
|
||||
/**
|
||||
* 写入一个值
|
||||
*
|
||||
* @param key 名称
|
||||
* @param value 值
|
||||
*/
|
||||
public void setAttribute(String key, Object value) {
|
||||
dataMap.put(key, value);
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取出一个值
|
||||
*
|
||||
* @param key 名称
|
||||
* @return 值
|
||||
*/
|
||||
public Object getAttribute(String key) {
|
||||
return dataMap.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取值,并指定取不到值时的默认值
|
||||
*
|
||||
* @param key 名称
|
||||
* @param defaultValue 取不到值的时候返回的默认值
|
||||
* @return value
|
||||
*/
|
||||
public Object getAttribute(String key, Object defaultValue) {
|
||||
Object value = getAttribute(key);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除一个值
|
||||
*
|
||||
* @param key 要移除的值的名字
|
||||
*/
|
||||
public void removeAttribute(String key) {
|
||||
dataMap.remove(key);
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有值
|
||||
*/
|
||||
public void clearAttribute() {
|
||||
dataMap.clear();
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否含有指定key
|
||||
*
|
||||
* @param key 是否含有指定值
|
||||
* @return 是否含有
|
||||
*/
|
||||
public boolean containsAttribute(String key) {
|
||||
return dataMap.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前session会话所有key
|
||||
*
|
||||
* @return 所有值的key列表
|
||||
*/
|
||||
public Set<String> attributeKeys() {
|
||||
return dataMap.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据挂载集合(如果更新map里的值,请调用session.update()方法避免产生脏数据 )
|
||||
*
|
||||
* @return 返回底层储存值的map对象
|
||||
*/
|
||||
public Map<String, Object> getDataMap() {
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入数据集合 (不改变底层对象,只将此dataMap所有数据进行替换)
|
||||
* @param dataMap 数据集合
|
||||
*/
|
||||
public void refreshDataMap(Map<String, Object> dataMap) {
|
||||
this.dataMap.clear();
|
||||
this.dataMap.putAll(dataMap);
|
||||
this.update();
|
||||
}
|
||||
|
||||
|
||||
// ----------------------- 一些操作
|
||||
|
||||
/**
|
||||
* 更新Session(从持久库更新刷新一下)
|
||||
*/
|
||||
public void update() {
|
||||
SaTokenManager.getSaTokenDao().updateSession(this);
|
||||
}
|
||||
|
||||
/** 注销Session (从持久库删除) */
|
||||
public void logout() {
|
||||
SaTokenManager.getSaTokenDao().deleteSession(this.id);
|
||||
// $$ 通知监听器
|
||||
SaTokenManager.getSaTokenListener().doLogoutSession(id);
|
||||
}
|
||||
|
||||
/** 当Session上的tokenSign数量为零时,注销会话 */
|
||||
public void logoutByTokenSignCountToZero() {
|
||||
if (tokenSignList.size() == 0) {
|
||||
logout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此Session的剩余存活时间 (单位: 秒)
|
||||
* @return 此Session的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
public long getTimeout() {
|
||||
return SaTokenManager.getSaTokenDao().getSessionTimeout(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改此Session的剩余存活时间
|
||||
* @param timeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void updateTimeout(long timeout) {
|
||||
SaTokenManager.getSaTokenDao().updateSessionTimeout(this.id, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改此Session的最小剩余存活时间 (只有在Session的过期时间低于指定的minTimeout时才会进行修改)
|
||||
* @param minTimeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void updateMinTimeout(long minTimeout) {
|
||||
if(getTimeout() < minTimeout) {
|
||||
SaTokenManager.getSaTokenDao().updateSessionTimeout(this.id, minTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改此Session的最大剩余存活时间 (只有在Session的过期时间高于指定的maxTimeout时才会进行修改)
|
||||
* @param maxTimeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void updateMaxTimeout(long maxTimeout) {
|
||||
if(getTimeout() > maxTimeout) {
|
||||
SaTokenManager.getSaTokenDao().updateSessionTimeout(this.id, maxTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------- 存取值 (类型转换)
|
||||
|
||||
// ---- 取值
|
||||
/**
|
||||
* 取值
|
||||
* @param key key
|
||||
* @return 值
|
||||
*/
|
||||
public Object get(String key) {
|
||||
return dataMap.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 取值 (指定默认值)
|
||||
* @param <T> 默认值的类型
|
||||
* @param key key
|
||||
* @param defaultValue 取不到值时返回的默认值
|
||||
* @return 值
|
||||
*/
|
||||
public <T> T get(String key, T defaultValue) {
|
||||
return getValueByDefaultValue(get(key), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取值 (转String类型)
|
||||
* @param key key
|
||||
* @return 值
|
||||
*/
|
||||
public String getString(String key) {
|
||||
Object value = get(key);
|
||||
if(value == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取值 (转int类型)
|
||||
* @param key key
|
||||
* @return 值
|
||||
*/
|
||||
public int getInt(String key) {
|
||||
return getValueByDefaultValue(get(key), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取值 (转long类型)
|
||||
* @param key key
|
||||
* @return 值
|
||||
*/
|
||||
public long getLong(String key) {
|
||||
return getValueByDefaultValue(get(key), 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取值 (转double类型)
|
||||
* @param key key
|
||||
* @return 值
|
||||
*/
|
||||
public double getDouble(String key) {
|
||||
return getValueByDefaultValue(get(key), 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取值 (转float类型)
|
||||
* @param key key
|
||||
* @return 值
|
||||
*/
|
||||
public float getFloat(String key) {
|
||||
return getValueByDefaultValue(get(key), 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取值 (指定转换类型)
|
||||
* @param <T> 泛型
|
||||
* @param key key
|
||||
* @param cs 指定转换类型
|
||||
* @return 值
|
||||
*/
|
||||
public <T> T getModel(String key, Class<T> cs) {
|
||||
return getValueByClass(get(key), cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取值 (指定转换类型, 并指定值为Null时返回的默认值)
|
||||
* @param <T> 泛型
|
||||
* @param key key
|
||||
* @param cs 指定转换类型
|
||||
* @param defaultValue 值为Null时返回的默认值
|
||||
* @return 值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getModel(String key, Class<T> cs, Object defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return (T)defaultValue;
|
||||
}
|
||||
return getValueByClass(value, cs);
|
||||
}
|
||||
|
||||
// ---- 其他
|
||||
/**
|
||||
* 写值
|
||||
* @param key 名称
|
||||
* @param value 值
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSession set(String key, Object value) {
|
||||
dataMap.put(key, value);
|
||||
update();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写值(只有在此key原本无值的时候才会写入)
|
||||
* @param key 名称
|
||||
* @param value 值
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSession setDefaultValue(String key, Object value) {
|
||||
if(has(key) == false) {
|
||||
dataMap.put(key, value);
|
||||
update();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否含有某个key
|
||||
* @param key has
|
||||
* @return 是否含有
|
||||
*/
|
||||
public boolean has(String key) {
|
||||
return !valueIsNull(get(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删值
|
||||
* @param key 要删除的key
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSession delete(String key) {
|
||||
dataMap.remove(key);
|
||||
update();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// --------- 工具方法
|
||||
|
||||
/**
|
||||
* 判断一个值是否为null
|
||||
* @param value 指定值
|
||||
* @return 此value是否为null
|
||||
*/
|
||||
public boolean valueIsNull(Object value) {
|
||||
return value == null || value.equals("");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定值转化为指定类型
|
||||
* @param <T> 泛型
|
||||
* @param obj 值
|
||||
* @param cs 类型
|
||||
* @return 转换后的值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T getValueByClass(Object obj, Class<T> cs) {
|
||||
// 如果 obj 本来就是 cs 类型
|
||||
if(obj != null && obj.getClass().equals(cs)) {
|
||||
return (T)obj;
|
||||
}
|
||||
// 开始转换
|
||||
String obj2 = String.valueOf(obj);
|
||||
Object obj3 = null;
|
||||
if (cs.equals(String.class)) {
|
||||
obj3 = obj2;
|
||||
} else if (cs.equals(int.class) || cs.equals(Integer.class)) {
|
||||
obj3 = Integer.valueOf(obj2);
|
||||
} else if (cs.equals(long.class) || cs.equals(Long.class)) {
|
||||
obj3 = Long.valueOf(obj2);
|
||||
} else if (cs.equals(short.class) || cs.equals(Short.class)) {
|
||||
obj3 = Short.valueOf(obj2);
|
||||
} else if (cs.equals(byte.class) || cs.equals(Byte.class)) {
|
||||
obj3 = Byte.valueOf(obj2);
|
||||
} else if (cs.equals(float.class) || cs.equals(Float.class)) {
|
||||
obj3 = Float.valueOf(obj2);
|
||||
} else if (cs.equals(double.class) || cs.equals(Double.class)) {
|
||||
obj3 = Double.valueOf(obj2);
|
||||
} else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
|
||||
obj3 = Boolean.valueOf(obj2);
|
||||
} else {
|
||||
obj3 = (T)obj;
|
||||
}
|
||||
return (T)obj3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据默认值来获取值
|
||||
* @param <T> 泛型
|
||||
* @param value 值
|
||||
* @param defaultValue 默认值
|
||||
* @return 转换后的值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T getValueByDefaultValue(Object value, T defaultValue) {
|
||||
|
||||
// 如果 obj 为 null,则直接返回默认值
|
||||
if(valueIsNull(value)) {
|
||||
return (T)defaultValue;
|
||||
}
|
||||
|
||||
// 开始转换
|
||||
Class<T> cs = (Class<T>) defaultValue.getClass();
|
||||
return getValueByClass(value, cs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package cn.dev33.satoken.session;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
/**
|
||||
* 自定义Session工具类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaSessionCustomUtil {
|
||||
|
||||
/**
|
||||
* 添加上指定前缀,防止恶意伪造session
|
||||
*/
|
||||
public static String sessionKey = "custom";
|
||||
|
||||
/**
|
||||
* 组织一下自定义Session的id
|
||||
*
|
||||
* @param sessionId 会话id
|
||||
* @return sessionId
|
||||
*/
|
||||
public static String splicingSessionKey(String sessionId) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + sessionKey + ":session:" + sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证指定key的Session是否存在
|
||||
*
|
||||
* @param sessionId session的id
|
||||
* @return 是否存在
|
||||
*/
|
||||
public static boolean isExists(String sessionId) {
|
||||
return SaTokenManager.getSaTokenDao().getSession(splicingSessionKey(sessionId)) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的Session
|
||||
*
|
||||
* @param sessionId key
|
||||
* @param isCreate 如果此Session尚未在DB创建,是否新建并返回
|
||||
* @return SaSession
|
||||
*/
|
||||
public static SaSession getSessionById(String sessionId, boolean isCreate) {
|
||||
SaSession session = SaTokenManager.getSaTokenDao().getSession(splicingSessionKey(sessionId));
|
||||
if (session == null && isCreate) {
|
||||
session = SaTokenManager.getSaTokenAction().createSession(sessionId);
|
||||
SaTokenManager.getSaTokenDao().setSession(session, SaTokenManager.getConfig().getTimeout());
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的Session, 如果此Session尚未在DB创建,则新建并返回
|
||||
*
|
||||
* @param sessionId key
|
||||
* @return session对象
|
||||
*/
|
||||
public static SaSession getSessionById(String sessionId) {
|
||||
return getSessionById(sessionId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定key的session
|
||||
*
|
||||
* @param sessionId 指定key
|
||||
*/
|
||||
public static void deleteSessionById(String sessionId) {
|
||||
SaTokenManager.getSaTokenDao().deleteSession(splicingSessionKey(sessionId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package cn.dev33.satoken.session;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* token签名 Model
|
||||
*
|
||||
* 挂在到SaSession上的token签名
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class TokenSign implements Serializable {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1406115065849845073L;
|
||||
|
||||
/**
|
||||
* token值
|
||||
*/
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 所在设备标识
|
||||
*/
|
||||
private String device;
|
||||
|
||||
/** 构建一个 */
|
||||
public TokenSign() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个
|
||||
*
|
||||
* @param value token值
|
||||
* @param device 所在设备标识
|
||||
*/
|
||||
public TokenSign(String value, String device) {
|
||||
this.value = value;
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token value
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token登录设备
|
||||
*/
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TokenSign [value=" + value + ", device=" + device + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
/**
|
||||
* 调用 `StpUtil.setLogin()` 时的 [配置参数 Model ]
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaLoginModel {
|
||||
|
||||
|
||||
/**
|
||||
* 此次登录的客户端设备标识
|
||||
*/
|
||||
public String device;
|
||||
|
||||
/**
|
||||
* 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在)
|
||||
*/
|
||||
public Boolean isLastingCookie;
|
||||
|
||||
/**
|
||||
* 指定此次登录token的有效期, 单位:秒 (如未指定,自动取全局配置的timeout值)
|
||||
*/
|
||||
public Long timeout;
|
||||
|
||||
|
||||
/**
|
||||
* @return device
|
||||
*/
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param device 要设置的 device
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel setDevice(String device) {
|
||||
this.device = device;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isLastingCookie
|
||||
*/
|
||||
public Boolean getIsLastingCookie() {
|
||||
return isLastingCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isLastingCookie 要设置的 isLastingCookie
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel setIsLastingCookie(Boolean isLastingCookie) {
|
||||
this.isLastingCookie = isLastingCookie;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return timeout
|
||||
*/
|
||||
public Long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeout 要设置的 timeout
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return cookie时长
|
||||
*/
|
||||
public int getCookieTimeout() {
|
||||
if(isLastingCookie == false) {
|
||||
return -1;
|
||||
}
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
return (int)(long)timeout;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建对象,初始化默认值
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel build() {
|
||||
return build(SaTokenManager.getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建对象,初始化默认值
|
||||
* @param config 配置对象
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaLoginModel build(SaTokenConfig config) {
|
||||
if(device == null) {
|
||||
device = SaTokenConsts.DEFAULT_LOGIN_DEVICE;
|
||||
}
|
||||
if(isLastingCookie == null) {
|
||||
isLastingCookie = true;
|
||||
}
|
||||
if(timeout == null) {
|
||||
timeout = config.getTimeout();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 静态方法获取一个 SaLoginModel 对象
|
||||
* @return SaLoginModel 对象
|
||||
*/
|
||||
public static SaLoginModel create() {
|
||||
return new SaLoginModel();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* toString
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaLoginModel [device=" + device + ", isLastingCookie=" + isLastingCookie + ", timeout=" + timeout + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
/**
|
||||
* token信息Model: 用来描述一个token的常用参数
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenInfo {
|
||||
|
||||
/** token名称 */
|
||||
public String tokenName;
|
||||
|
||||
/** token值 */
|
||||
public String tokenValue;
|
||||
|
||||
/** 此token是否已经登录 */
|
||||
public Boolean isLogin;
|
||||
|
||||
/** 此token对应的LoginId,未登录时为null */
|
||||
public Object loginId;
|
||||
|
||||
/** LoginKey账号体系标识 */
|
||||
public String loginKey;
|
||||
|
||||
/** token剩余有效期 (单位: 秒) */
|
||||
public long tokenTimeout;
|
||||
|
||||
/** User-Session剩余有效时间 (单位: 秒) */
|
||||
public long sessionTimeout;
|
||||
|
||||
/** Token-Session剩余有效时间 (单位: 秒) */
|
||||
public long tokenSessionTimeout;
|
||||
|
||||
/** token剩余无操作有效时间 (单位: 秒) */
|
||||
public long tokenActivityTimeout;
|
||||
|
||||
/** 登录设备标识 */
|
||||
public String loginDevice;
|
||||
|
||||
/**
|
||||
* @return token名称
|
||||
*/
|
||||
public String getTokenName() {
|
||||
return tokenName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenName token名称
|
||||
*/
|
||||
public void setTokenName(String tokenName) {
|
||||
this.tokenName = tokenName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token值
|
||||
*/
|
||||
public String getTokenValue() {
|
||||
return tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenValue token值
|
||||
*/
|
||||
public void setTokenValue(String tokenValue) {
|
||||
this.tokenValue = tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 此token是否已经登录
|
||||
*/
|
||||
public Boolean getIsLogin() {
|
||||
return isLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isLogin 此token是否已经登录
|
||||
*/
|
||||
public void setIsLogin(Boolean isLogin) {
|
||||
this.isLogin = isLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 此token对应的LoginId,未登录时为null
|
||||
*/
|
||||
public Object getLoginId() {
|
||||
return loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loginId 此token对应的LoginId,未登录时为null
|
||||
*/
|
||||
public void setLoginId(Object loginId) {
|
||||
this.loginId = loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LoginKey账号体系标识
|
||||
*/
|
||||
public String getLoginKey() {
|
||||
return loginKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loginKey LoginKey账号体系标识
|
||||
*/
|
||||
public void setLoginKey(String loginKey) {
|
||||
this.loginKey = loginKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token剩余有效期 (单位: 秒)
|
||||
*/
|
||||
public long getTokenTimeout() {
|
||||
return tokenTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenTimeout token剩余有效期 (单位: 秒)
|
||||
*/
|
||||
public void setTokenTimeout(long tokenTimeout) {
|
||||
this.tokenTimeout = tokenTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User-Session剩余有效时间 (单位: 秒)
|
||||
*/
|
||||
public long getSessionTimeout() {
|
||||
return sessionTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sessionTimeout User-Session剩余有效时间 (单位: 秒)
|
||||
*/
|
||||
public void setSessionTimeout(long sessionTimeout) {
|
||||
this.sessionTimeout = sessionTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Token-Session剩余有效时间 (单位: 秒)
|
||||
*/
|
||||
public long getTokenSessionTimeout() {
|
||||
return tokenSessionTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenSessionTimeout Token-Session剩余有效时间 (单位: 秒)
|
||||
*/
|
||||
public void setTokenSessionTimeout(long tokenSessionTimeout) {
|
||||
this.tokenSessionTimeout = tokenSessionTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token剩余无操作有效时间 (单位: 秒)
|
||||
*/
|
||||
public long getTokenActivityTimeout() {
|
||||
return tokenActivityTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenActivityTimeout token剩余无操作有效时间 (单位: 秒)
|
||||
*/
|
||||
public void setTokenActivityTimeout(long tokenActivityTimeout) {
|
||||
this.tokenActivityTimeout = tokenActivityTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 登录设备标识
|
||||
*/
|
||||
public String getLoginDevice() {
|
||||
return loginDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loginDevice 登录设备标识
|
||||
*/
|
||||
public void setLoginDevice(String loginDevice) {
|
||||
this.loginDevice = loginDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
* toString
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaTokenInfo [tokenName=" + tokenName + ", tokenValue=" + tokenValue + ", isLogin=" + isLogin
|
||||
+ ", loginId=" + loginId + ", loginKey=" + loginKey + ", tokenTimeout=" + tokenTimeout
|
||||
+ ", sessionTimeout=" + sessionTimeout + ", tokenSessionTimeout=" + tokenSessionTimeout
|
||||
+ ", tokenActivityTimeout=" + tokenActivityTimeout + ", loginDevice=" + loginDevice + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 权限认证接口,实现此接口即可集成权限认证功能
|
||||
*
|
||||
* @author kong
|
||||
*/
|
||||
public interface StpInterface {
|
||||
|
||||
/**
|
||||
* 返回指定 LoginId 所拥有的权限码集合
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param loginKey 账号体系标识
|
||||
* @return 该账号id具有的权限码集合
|
||||
*/
|
||||
public List<String> getPermissionList(Object loginId, String loginKey);
|
||||
|
||||
/**
|
||||
* 返回指定loginId所拥有的角色标识集合
|
||||
*
|
||||
* @param loginId 账号id
|
||||
* @param loginKey 账号体系标识
|
||||
* @return 该账号id具有的角色标识集合
|
||||
*/
|
||||
public List<String> getRoleList(Object loginId, String loginKey);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 对StpInterface接口默认的实现类
|
||||
* <p>
|
||||
* 如果开发者没有实现StpInterface接口,则使用此默认实现
|
||||
*
|
||||
* @author kong
|
||||
*/
|
||||
public class StpInterfaceDefaultImpl implements StpInterface {
|
||||
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginKey) {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginKey) {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,588 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.fun.SaFunction;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* 一个默认的实现
|
||||
* @author kong
|
||||
*/
|
||||
public class StpUtil {
|
||||
|
||||
/**
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
public static StpLogic stpLogic = new StpLogic("login");
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前StpLogin的loginKey
|
||||
* @return 当前StpLogin的loginKey
|
||||
*/
|
||||
public static String getLoginKey(){
|
||||
return stpLogic.getLoginKey();
|
||||
}
|
||||
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
/**
|
||||
* 返回token名称
|
||||
* @return 此StpLogic的token名称
|
||||
*/
|
||||
public static String getTokenName() {
|
||||
return stpLogic.getTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话写入当前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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的token信息
|
||||
* @return token信息
|
||||
*/
|
||||
public static SaTokenInfo getTokenInfo() {
|
||||
return stpLogic.getTokenInfo();
|
||||
}
|
||||
|
||||
|
||||
// =================== 登录相关操作 ===================
|
||||
|
||||
/**
|
||||
* 在当前会话上登录id
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
*/
|
||||
public static void setLoginId(Object loginId) {
|
||||
stpLogic.setLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param device 设备标识
|
||||
*/
|
||||
public static void setLoginId(Object loginId, String device) {
|
||||
stpLogic.setLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话上登录id, 并指定登录设备
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param isLastingCookie 是否为持久Cookie
|
||||
*/
|
||||
public static void setLoginId(Object loginId, boolean isLastingCookie) {
|
||||
stpLogic.setLoginId(loginId, isLastingCookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前会话上登录id, 并指定所有登录参数Model
|
||||
* @param loginId 登录id,建议的类型:(long | int | String)
|
||||
* @param loginModel 此次登录的参数Model
|
||||
*/
|
||||
public static void setLoginId(Object loginId, SaLoginModel loginModel) {
|
||||
stpLogic.setLoginId(loginId, loginModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话注销登录
|
||||
*/
|
||||
public static void logout() {
|
||||
stpLogic.logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定token的会话注销登录
|
||||
* @param tokenValue 指定token
|
||||
*/
|
||||
public static void logoutByTokenValue(String tokenValue) {
|
||||
stpLogic.logoutByTokenValue(tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定loginId的会话注销登录(踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void logoutByLoginId(Object loginId) {
|
||||
stpLogic.logoutByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定loginId指定设备的会话注销登录(踢人下线)
|
||||
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
*/
|
||||
public static void logoutByLoginId(Object loginId, String device) {
|
||||
stpLogic.logoutByLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封禁指定账号
|
||||
* <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);
|
||||
}
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
* 获取当前会话是否已经登录
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
// =================== session相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定loginId的session, 如果session尚未创建,isCreate=是否新建并返回
|
||||
* @param loginId 账号id
|
||||
* @param isCreate 是否新建
|
||||
* @return SaSession
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录者的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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== 角色验证操作 ===================
|
||||
|
||||
/**
|
||||
* 指定账号id是否含有角色标识, 返回true或false
|
||||
* @param loginId 账号id
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
*/
|
||||
public static boolean hasRole(Object loginId, String role) {
|
||||
return stpLogic.hasRole(loginId, role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定角色标识, 返回true或false
|
||||
* @param role 角色标识
|
||||
* @return 是否含有指定角色标识
|
||||
*/
|
||||
public static boolean hasRole(String role) {
|
||||
return stpLogic.hasRole(role);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: 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);
|
||||
}
|
||||
|
||||
|
||||
// =================== 权限验证操作 ===================
|
||||
|
||||
/**
|
||||
* 指定账号id是否含有指定权限, 返回true或false
|
||||
* @param loginId 账号id
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(Object loginId, String permission) {
|
||||
return stpLogic.hasPermission(loginId, permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限, 返回true或false
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(String permission) {
|
||||
return stpLogic.hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: 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 相关操作 ===================
|
||||
|
||||
/**
|
||||
* 获取指定loginId的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @return token值
|
||||
*/
|
||||
public static String getTokenValueByLoginId(Object loginId) {
|
||||
return stpLogic.getTokenValueByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId指定设备端的tokenValue
|
||||
* <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
|
||||
* 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @return token值
|
||||
*/
|
||||
public static String getTokenValueByLoginId(Object loginId, String device) {
|
||||
return stpLogic.getTokenValueByLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的tokenValue集合
|
||||
* @param loginId 账号id
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public static List<String> getTokenValueListByLoginId(Object loginId) {
|
||||
return stpLogic.getTokenValueListByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId指定设备端的tokenValue集合
|
||||
* @param loginId 账号id
|
||||
* @param device 设备标识
|
||||
* @return 此loginId的所有相关token
|
||||
*/
|
||||
public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
|
||||
return stpLogic.getTokenValueListByLoginId(loginId, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前token的登录设备
|
||||
* @return 当前令牌的登录设备
|
||||
*/
|
||||
public static String getLoginDevice() {
|
||||
return stpLogic.getLoginDevice();
|
||||
}
|
||||
|
||||
|
||||
// =================== 会话管理 ===================
|
||||
|
||||
/**
|
||||
* 根据条件查询token
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return token集合
|
||||
*/
|
||||
public static List<String> searchTokenValue(String keyword, int start, int size) {
|
||||
return stpLogic.searchTokenValue(keyword, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询SessionId
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public static List<String> searchSessionId(String keyword, int start, int size) {
|
||||
return stpLogic.searchSessionId(keyword, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询token专属Session的Id
|
||||
* @param keyword 关键字
|
||||
* @param start 开始处索引 (-1代表查询所有)
|
||||
* @param size 获取数量
|
||||
* @return sessionId集合
|
||||
*/
|
||||
public static List<String> searchTokenSessionId(String keyword, int start, int size) {
|
||||
return stpLogic.searchTokenSessionId(keyword, start, size);
|
||||
}
|
||||
|
||||
|
||||
// =================== 身份切换 ===================
|
||||
|
||||
/**
|
||||
* 临时切换身份为指定loginId
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在一个代码段里方法内,临时切换身份为指定loginId
|
||||
* @param loginId 指定loginId
|
||||
* @param function 要执行的方法
|
||||
*/
|
||||
public static void switchTo(Object loginId, SaFunction function) {
|
||||
stpLogic.switchTo(loginId, function);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
/**
|
||||
* sa-token常量类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenConsts {
|
||||
|
||||
|
||||
// =================== sa-token版本信息 ===================
|
||||
|
||||
/**
|
||||
* sa-token 当前版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.17.0";
|
||||
|
||||
/**
|
||||
* sa-token 开源地址
|
||||
*/
|
||||
public static final String GITHUB_URL = "https://github.com/dromara/sa-token";
|
||||
|
||||
/**
|
||||
* sa-token 开发文档地址
|
||||
*/
|
||||
public static final String DEV_DOC_URL = "http://sa-token.dev33.cn";
|
||||
|
||||
// =================== 常量key标记 ===================
|
||||
|
||||
/**
|
||||
* 常量key标记: 如果token为本次请求新创建的,则以此字符串为key存储在当前request中
|
||||
*/
|
||||
public static final String JUST_CREATED_SAVE_KEY = "JUST_CREATED_SAVE_KEY_";
|
||||
|
||||
/**
|
||||
* 常量key标记: 如果本次请求已经验证过[无操作过期], 则以此值存储在当前request中
|
||||
*/
|
||||
public static final String TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY = "TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY_";
|
||||
|
||||
/**
|
||||
* 常量key标记: 在登录时,默认使用的设备名称
|
||||
*/
|
||||
public static final String DEFAULT_LOGIN_DEVICE = "default-device";
|
||||
|
||||
/**
|
||||
* 常量key标记: 在进行临时身份切换时使用的key
|
||||
*/
|
||||
public static final String SWITCH_TO_SAVE_KEY = "SWITCH_TO_SAVE_KEY_";
|
||||
|
||||
|
||||
// =================== token-style 相关 ===================
|
||||
|
||||
/**
|
||||
* token风格: uuid
|
||||
*/
|
||||
public static final String TOKEN_STYLE_UUID = "uuid";
|
||||
|
||||
/**
|
||||
* token风格: 简单uuid (不带下划线)
|
||||
*/
|
||||
public static final String TOKEN_STYLE_SIMPLE_UUID = "simple-uuid";
|
||||
|
||||
/**
|
||||
* token风格: 32位随机字符串
|
||||
*/
|
||||
public static final String TOKEN_STYLE_RANDOM_32 = "random-32";
|
||||
|
||||
/**
|
||||
* token风格: 64位随机字符串
|
||||
*/
|
||||
public static final String TOKEN_STYLE_RANDOM_64 = "random-64";
|
||||
|
||||
/**
|
||||
* token风格: 128位随机字符串
|
||||
*/
|
||||
public static final String TOKEN_STYLE_RANDOM_128 = "random-128";
|
||||
|
||||
/**
|
||||
* token风格: tik风格 (2_14_16)
|
||||
*/
|
||||
public static final String TOKEN_STYLE_TIK = "tik";
|
||||
|
||||
|
||||
// =================== 其它 ===================
|
||||
|
||||
/**
|
||||
* 连接token前缀和token值的字符
|
||||
*/
|
||||
public static final String TOKEN_CONNECTOR_CHAT = " ";
|
||||
|
||||
/**
|
||||
* 切面、拦截器、过滤器等各种组件的注册优先级顺序
|
||||
*/
|
||||
public static final int ASSEMBLY_ORDER = -100;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* sa-token 内部代码工具类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenInsideUtil {
|
||||
|
||||
/**
|
||||
* 打印 sa-token 版本字符画
|
||||
*/
|
||||
public static void printSaToken() {
|
||||
String str = "____ ____ ___ ____ _ _ ____ _ _ \r\n" + "[__ |__| __ | | | |_/ |___ |\\ | \r\n"
|
||||
+ "___] | | | |__| | \\_ |___ | \\| "
|
||||
// + "sa-token:"
|
||||
+ "\r\n" + "DevDoc:" + SaTokenConsts.DEV_DOC_URL // + "\r\n";
|
||||
+ " (" + SaTokenConsts.VERSION_NO + ")"
|
||||
+ "\r\n" + "GitHub:" + SaTokenConsts.GITHUB_URL // + "\r\n";
|
||||
;
|
||||
System.out.println(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成指定长度的随机字符串
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定字符串是否为null或者空字符串
|
||||
* @param str 指定字符串
|
||||
* @return 是否为null或者空字符串
|
||||
*/
|
||||
public static boolean isEmpty(String str) {
|
||||
return str == null || "".equals(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以当前时间戳和随机int数字拼接一个随机字符串
|
||||
*
|
||||
* @return 随机字符串
|
||||
*/
|
||||
public static String getMarking28() {
|
||||
return System.currentTimeMillis() + "" + new Random().nextInt(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将日期格式化
|
||||
* @param date 日期
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatDate(Date date){
|
||||
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从集合里查询数据
|
||||
*
|
||||
* @param dataList 数据集合
|
||||
* @param prefix 前缀
|
||||
* @param keyword 关键字
|
||||
* @param start 起始位置 (-1代表查询所有)
|
||||
* @param size 获取条数
|
||||
* @return 符合条件的新数据集合
|
||||
*/
|
||||
public static List<String> searchList(Collection<String> dataList, String prefix, String keyword, int start,
|
||||
int size) {
|
||||
if (prefix == null) {
|
||||
prefix = "";
|
||||
}
|
||||
if (keyword == null) {
|
||||
keyword = "";
|
||||
}
|
||||
// 挑选出所有符合条件的
|
||||
List<String> list = new ArrayList<String>();
|
||||
Iterator<String> keys = dataList.iterator();
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
if (key.startsWith(prefix) && key.indexOf(keyword) > -1) {
|
||||
list.add(key);
|
||||
}
|
||||
}
|
||||
// 取指定段数据
|
||||
return searchList(list, start, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从集合里查询数据
|
||||
*
|
||||
* @param list 数据集合
|
||||
* @param start 起始位置 (-1代表查询所有)
|
||||
* @param size 获取条数
|
||||
* @return 符合条件的新数据集合
|
||||
*/
|
||||
public static List<String> searchList(List<String> list, int start, int size) {
|
||||
// 取指定段数据
|
||||
if (start < 0) {
|
||||
return list;
|
||||
}
|
||||
int end = start + size;
|
||||
List<String> list2 = new ArrayList<String>();
|
||||
for (int i = start; i < end; i++) {
|
||||
if (i >= list.size()) {
|
||||
return list2;
|
||||
}
|
||||
list2.add(list.get(i));
|
||||
}
|
||||
return list2;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
||||
.iml
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.17.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sa-token-dao-redis-jackson</name>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<description>sa-token integrate redis (to jackson)</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- sa-token-spring-boot-starter -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
<!-- RedisTemplate 相关操作API -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</dependency>
|
||||
<!-- jackson-databind -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.11.2</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token持久层的实现类, 基于redis (使用 jackson 序列化方式)
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
|
||||
/**
|
||||
* ObjectMapper对象 (以public作用域暴露出此对象,方便开发者二次更改配置)
|
||||
*/
|
||||
public ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* String专用
|
||||
*/
|
||||
@Autowired
|
||||
public StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* Object专用
|
||||
*/
|
||||
public RedisTemplate<String, Object> objectRedisTemplate;
|
||||
@Autowired
|
||||
public void setObjectRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
// 指定相应的序列化方案
|
||||
StringRedisSerializer keySerializer = new StringRedisSerializer();
|
||||
GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
|
||||
// 通过反射获取Mapper对象, 配置[忽略未知字段], 增强兼容性
|
||||
try {
|
||||
Field field = GenericJackson2JsonRedisSerializer.class.getDeclaredField("mapper");
|
||||
field.setAccessible(true);
|
||||
ObjectMapper objectMapper = (ObjectMapper) field.get(valueSerializer);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
this.objectMapper = objectMapper;
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
// 构建RedisTemplate
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(keySerializer);
|
||||
template.setHashKeySerializer(keySerializer);
|
||||
template.setValueSerializer(valueSerializer);
|
||||
template.setHashValueSerializer(valueSerializer);
|
||||
template.afterPropertiesSet();
|
||||
if(this.objectRedisTemplate == null) {
|
||||
this.objectRedisTemplate = template;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据key获取value,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return stringRedisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入指定key-value键值对,并设定过期时间(单位:秒)
|
||||
*/
|
||||
@Override
|
||||
public void set(String key, String value, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
stringRedisTemplate.opsForValue().set(key, value);
|
||||
} else {
|
||||
stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定key-value键值对 (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void update(String key, String value) {
|
||||
long expire = getTimeout(key);
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.set(key, value, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
*/
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
stringRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取value,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public long getTimeout(String key) {
|
||||
return stringRedisTemplate.getExpire(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定key的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getTimeout(key);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.set(key, this.get(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据key获取Object,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
return objectRedisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入指定键值对,并设定过期时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
objectRedisTemplate.opsForValue().set(key, object);
|
||||
} else {
|
||||
objectRedisTemplate.opsForValue().set(key, object, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定键值对 (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void updateObject(String key, Object object) {
|
||||
long expire = getObjectTimeout(key);
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.setObject(key, object, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的object
|
||||
*/
|
||||
@Override
|
||||
public void deleteObject(String key) {
|
||||
objectRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public long getObjectTimeout(String key) {
|
||||
return objectRedisTemplate.getExpire(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定key的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getObjectTimeout(key);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.setObject(key, this.getObject(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
objectRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
*/
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size) {
|
||||
Set<String> keys = stringRedisTemplate.keys(prefix + "*" + keyword + "*");
|
||||
List<String> list = new ArrayList<String>(keys);
|
||||
return SaTokenInsideUtil.searchList(list, start, size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.dao.SaTokenDaoRedisJackson
|
||||
@@ -0,0 +1,13 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
||||
.iml
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.17.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sa-token-dao-redis</name>
|
||||
<artifactId>sa-token-dao-redis</artifactId>
|
||||
<description>sa-token integrate redis</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- sa-token-spring-boot-starter -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
<!-- RedisTemplate 相关操作API -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,208 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token持久层的实现类, 基于redis
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class SaTokenDaoRedis implements SaTokenDao {
|
||||
|
||||
/**
|
||||
* String专用
|
||||
*/
|
||||
@Autowired
|
||||
public StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* Objecy专用
|
||||
*/
|
||||
public RedisTemplate<String, Object> objectRedisTemplate;
|
||||
@Autowired
|
||||
public void setObjectRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
// 指定相应的序列化方案
|
||||
StringRedisSerializer keySerializer = new StringRedisSerializer();
|
||||
JdkSerializationRedisSerializer valueSerializer = new JdkSerializationRedisSerializer();
|
||||
// 构建RedisTemplate
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(keySerializer);
|
||||
template.setHashKeySerializer(keySerializer);
|
||||
template.setValueSerializer(valueSerializer);
|
||||
template.setHashValueSerializer(valueSerializer);
|
||||
template.afterPropertiesSet();
|
||||
if(this.objectRedisTemplate == null) {
|
||||
this.objectRedisTemplate = template;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据key获取value,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return stringRedisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入指定key-value键值对,并设定过期时间(单位:秒)
|
||||
*/
|
||||
@Override
|
||||
public void set(String key, String value, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
stringRedisTemplate.opsForValue().set(key, value);
|
||||
} else {
|
||||
stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定key-value键值对 (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void update(String key, String value) {
|
||||
long expire = getTimeout(key);
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.set(key, value, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
*/
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
stringRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取value,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public long getTimeout(String key) {
|
||||
return stringRedisTemplate.getExpire(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定key的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getTimeout(key);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.set(key, this.get(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据key获取Object,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
return objectRedisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入指定键值对,并设定过期时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
objectRedisTemplate.opsForValue().set(key, object);
|
||||
} else {
|
||||
objectRedisTemplate.opsForValue().set(key, object, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定键值对 (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void updateObject(String key, Object object) {
|
||||
long expire = getObjectTimeout(key);
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.setObject(key, object, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的object
|
||||
*/
|
||||
@Override
|
||||
public void deleteObject(String key) {
|
||||
objectRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public long getObjectTimeout(String key) {
|
||||
return objectRedisTemplate.getExpire(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定key的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getObjectTimeout(key);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.setObject(key, this.getObject(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
objectRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
*/
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size) {
|
||||
Set<String> keys = stringRedisTemplate.keys(prefix + "*" + keyword + "*");
|
||||
List<String> list = new ArrayList<String>(keys);
|
||||
return SaTokenInsideUtil.searchList(list, start, size);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.dao.SaTokenDaoRedis
|
||||
@@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
|
||||
.factorypath
|
||||
@@ -0,0 +1,86 @@
|
||||
<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-jwt</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.17.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>
|
||||
|
||||
<!-- jwt -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.1</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>
|
||||
<artifactId>sa-token-spring-aop</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SaTokenJwtDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenJwtDemoApplication.class, args);
|
||||
System.out.println("\n启动成功:sa-token配置如下:" + SaTokenManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.pj.satoken.jwt;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
|
||||
@Component
|
||||
public class SaTokenJwtUtil {
|
||||
|
||||
/**
|
||||
* 秘钥 (随便手打几个字母就好了)
|
||||
*/
|
||||
public static final String BASE64_SECURITY = "79e7c69681b8270162386e6daa53d1dd";
|
||||
|
||||
/**
|
||||
* token有效期 (单位: 秒)
|
||||
*/
|
||||
public static final long TIMEOUT = 60 * 60 * 2;
|
||||
|
||||
|
||||
public static final String LOGIN_ID_KEY = "loginId";
|
||||
|
||||
|
||||
/**
|
||||
* 根据userId生成token
|
||||
* @param loginId 账号id
|
||||
* @param base64Security 秘钥
|
||||
* @return jwt-token
|
||||
*/
|
||||
public static String createToken(Object loginId) {
|
||||
// 判断,不可使用默认秘钥
|
||||
// if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1dd")) {
|
||||
// throw new SaTokenException("请更换秘钥");
|
||||
// }
|
||||
// 在这里你可以使用官方提供的claim方法构建载荷,也可以使用setPayload自定义载荷,但是两者不可一起使用
|
||||
JwtBuilder builder = Jwts.builder()
|
||||
.setHeaderParam("type", "JWT")
|
||||
.claim(LOGIN_ID_KEY, loginId)
|
||||
.setIssuedAt(new Date()) // 签发日期
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 1000 * TIMEOUT)) // 有效截止日期
|
||||
.signWith(SignatureAlgorithm.HS256, BASE64_SECURITY.getBytes()); // 加密算法
|
||||
//生成JWT
|
||||
return builder.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个jwt里面解析出Claims
|
||||
* @param tokenValue token值
|
||||
* @param base64Security 秘钥
|
||||
* @return Claims对象
|
||||
*/
|
||||
public static Claims getClaims(String tokenValue) {
|
||||
// System.out.println(tokenValue);
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(BASE64_SECURITY.getBytes())
|
||||
.parseClaimsJws(tokenValue).getBody();
|
||||
return claims;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一个jwt里面解析loginId
|
||||
* @param tokenValue token值
|
||||
* @param base64Security 秘钥
|
||||
* @return loginId
|
||||
*/
|
||||
public static String getLoginId(String tokenValue) {
|
||||
try {
|
||||
Object loginId = getClaims(tokenValue).get(LOGIN_ID_KEY);
|
||||
if(loginId == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(loginId);
|
||||
} catch (ExpiredJwtException e) {
|
||||
// throw NotLoginException.newInstance(StpUtil.stpLogic.loginKey, NotLoginException.TOKEN_TIMEOUT);
|
||||
return NotLoginException.TOKEN_TIMEOUT;
|
||||
} catch (MalformedJwtException e) {
|
||||
throw NotLoginException.newInstance(StpUtil.stpLogic.loginKey, NotLoginException.INVALID_TOKEN);
|
||||
} catch (Exception e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static {
|
||||
|
||||
// 判断秘钥
|
||||
if(BASE64_SECURITY.equals("79e7c69681b8270162386e6daa53d1dd")) {
|
||||
String warn = "-------------------------------------\n";
|
||||
warn += "请更换JWT秘钥,不要使用示例默认秘钥\n";
|
||||
warn += "-------------------------------------";
|
||||
System.err.println(warn);
|
||||
}
|
||||
|
||||
// 修改默认实现
|
||||
StpUtil.stpLogic = new StpLogic("login") {
|
||||
|
||||
// 重写 (随机生成一个tokenValue)
|
||||
@Override
|
||||
public String createTokenValue(Object loginId) {
|
||||
return SaTokenJwtUtil.createToken(loginId);
|
||||
}
|
||||
|
||||
// 重写 (在当前会话上登录id )
|
||||
@Override
|
||||
public void setLoginId(Object loginId, SaLoginModel loginModel) {
|
||||
// ------ 1、获取相应对象
|
||||
SaStorage storage = SaTokenManager.getSaTokenContext().getStorage();
|
||||
SaTokenConfig config = getConfig();
|
||||
// ------ 2、生成一个token
|
||||
String tokenValue = createTokenValue(loginId);
|
||||
storage.set(splicingKeyJustCreatedSave(), tokenValue); // 将token保存到本次request里
|
||||
if(config.getIsReadCookie() == true){ // cookie注入
|
||||
SaTokenManager.getSaTokenContext().getResponse().addCookie(getTokenName(), tokenValue, "/", config.getCookieDomain(), (int)config.getTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
// 重写 (获取指定token对应的登录id)
|
||||
@Override
|
||||
public String getLoginIdNotHandle(String tokenValue) {
|
||||
try {
|
||||
return SaTokenJwtUtil.getLoginId(tokenValue);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 重写 (当前会话注销登录)
|
||||
@Override
|
||||
public void logout() {
|
||||
// 如果连token都没有,那么无需执行任何操作
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return;
|
||||
}
|
||||
// 如果打开了cookie模式,把cookie清除掉
|
||||
if(getConfig().getIsReadCookie() == true){
|
||||
SaTokenManager.getSaTokenContext().getResponse().deleteCookie(getTokenName());
|
||||
}
|
||||
}
|
||||
|
||||
// 重写 (获取指定key的session)
|
||||
@Override
|
||||
public SaSession getSessionBySessionId(String sessionId, boolean isCreate) {
|
||||
throw new SaTokenException("jwt has not session");
|
||||
}
|
||||
|
||||
// 重写 (获取当前登录者的token剩余有效时间 (单位: 秒))
|
||||
@Override
|
||||
public long getTokenTimeout() {
|
||||
// 如果没有token
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
// 开始取值
|
||||
Claims claims = null;
|
||||
try {
|
||||
claims = SaTokenJwtUtil.getClaims(tokenValue);
|
||||
} catch (Exception e) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
if(claims == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
Date expiration = claims.getExpiration();
|
||||
if(expiration == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
return (expiration.getTime() - System.currentTimeMillis()) / 1000;
|
||||
}
|
||||
|
||||
// 重写 (返回当前token的登录设备)
|
||||
@Override
|
||||
public String getLoginDevice() {
|
||||
return SaTokenConsts.DEFAULT_LOGIN_DEVICE;
|
||||
}
|
||||
|
||||
// 重写 (获取当前会话的token信息)
|
||||
@Override
|
||||
public SaTokenInfo getTokenInfo() {
|
||||
SaTokenInfo info = new SaTokenInfo();
|
||||
info.tokenName = getTokenName();
|
||||
info.tokenValue = getTokenValue();
|
||||
info.isLogin = isLogin();
|
||||
info.loginId = getLoginIdDefaultNull();
|
||||
info.loginKey = getLoginKey();
|
||||
info.tokenTimeout = getTokenTimeout();
|
||||
// info.sessionTimeout = getSessionTimeout();
|
||||
// info.tokenSessionTimeout = getTokenSessionTimeout();
|
||||
// info.tokenActivityTimeout = getTokenActivityTimeout();
|
||||
info.loginDevice = getLoginDevice();
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@RestControllerAdvice // 可指定包前缀,比如:(basePackages = "com.pj.admin")
|
||||
public class GlobalException {
|
||||
|
||||
// 在每个控制器之前触发的操作
|
||||
@ModelAttribute
|
||||
public void get(HttpServletRequest request) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
e.printStackTrace();
|
||||
|
||||
// 不同异常返回不同状态码
|
||||
AjaxJson aj = null;
|
||||
if (e instanceof NotLoginException) { // 如果是未登录异常
|
||||
NotLoginException ee = (NotLoginException) e;
|
||||
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
|
||||
} else if(e instanceof NotRoleException) { // 如果是角色异常
|
||||
NotRoleException ee = (NotRoleException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||
} else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
} else { // 普通异常, 输出:500 + 异常信息
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回给前端
|
||||
return aj;
|
||||
|
||||
// 输出到客户端
|
||||
// response.setContentType("application/json; charset=utf-8"); // http说明,我要返回JSON对象
|
||||
// response.getWriter().print(new ObjectMapper().writeValueAsString(aj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 全局异常拦截(拦截项目中的NotLoginException异常)
|
||||
// @ExceptionHandler(NotLoginException.class)
|
||||
// public AjaxJson handlerNotLoginException(NotLoginException nle, HttpServletRequest request, HttpServletResponse response)
|
||||
// throws Exception {
|
||||
//
|
||||
// // 打印堆栈,以供调试
|
||||
// nle.printStackTrace();
|
||||
//
|
||||
// // 判断场景值,定制化异常信息
|
||||
// String message = "";
|
||||
// if(nle.getType().equals(NotLoginException.NOT_TOKEN)) {
|
||||
// message = "未提供token";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
|
||||
// message = "token无效";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
|
||||
// message = "token已过期";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.BE_REPLACED)) {
|
||||
// message = "token已被顶下线";
|
||||
// }
|
||||
// else if(nle.getType().equals(NotLoginException.KICK_OUT)) {
|
||||
// message = "token已被踢下线";
|
||||
// }
|
||||
// else {
|
||||
// message = "当前会话未登录";
|
||||
// }
|
||||
//
|
||||
// // 返回给前端
|
||||
// return AjaxJson.getError(message);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
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 cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 测试专用Controller
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/test/")
|
||||
public class TestJwtController {
|
||||
|
||||
|
||||
|
||||
// 测试登录接口, 浏览器访问: 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.setLoginId(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();
|
||||
}
|
||||
|
||||
// 打印当前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);
|
||||
}
|
||||
|
||||
|
||||
// 测试会话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().getAttribute("name"));
|
||||
StpUtil.getSession().setAttribute("name", new Date()); // 写入一个值
|
||||
System.out.println("测试取值name:" + StpUtil.getSession().getAttribute("name"));
|
||||
System.out.println( new ObjectMapper().writeValueAsString(StpUtil.getSession()));
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
StpUtil.getTokenSession().logout();
|
||||
StpUtil.logoutByLoginId(10001);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.pj.test;
|
||||
package com.pj.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
@@ -0,0 +1,45 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
spring:
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken
|
||||
# token有效期,单位s 默认30天, -1代表永不过期
|
||||
timeout: 2592000
|
||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
activity-timeout: -1
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
allow-concurrent-login: true
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: true
|
||||
# token风格
|
||||
token-style: uuid
|
||||
|
||||
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 10000ms
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
target/
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
|
||||
/.idea/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
/.apt_generated/
|
||||
/.apt_generated_tests/
|
||||
@@ -0,0 +1,56 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.pj</groupId>
|
||||
<artifactId>sa-token-demo-oauth2-client</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<!-- 指定一些属性 -->
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<sa-token-version>1.15.0.RELEASE</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- springboot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</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>
|
||||
|
||||
<!-- OkHttps网络请求库: http://okhttps.ejlchina.com/ -->
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps</artifactId>
|
||||
<version>2.4.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 启动
|
||||
* @author kong
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaOAuth2ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaOAuth2ClientApplication.class, args);
|
||||
System.out.println("\n客户端启动成功,访问: http://localhost:8002/login.html");
|
||||
}
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package com.pj.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.ejlchina.okhttps.OkHttps;
|
||||
import com.pj.utils.AjaxJson;
|
||||
import com.pj.utils.SoMap;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 登录注册Controller
|
||||
* @author kong
|
||||
*/
|
||||
@RestController
|
||||
public class ClientAccController {
|
||||
|
||||
|
||||
// 返回当前登录者的账号id, 如果未登录, 返回null
|
||||
@RequestMapping("/getLoginInfo")
|
||||
public AjaxJson getLoginInfo() {
|
||||
Object loginId = StpUtil.getLoginIdDefaultNull();
|
||||
return AjaxJson.getSuccessData(loginId);
|
||||
}
|
||||
|
||||
// 注销登录
|
||||
@RequestMapping("/logout")
|
||||
public AjaxJson logout() {
|
||||
StpUtil.logout();
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 根据code码进行登录
|
||||
@RequestMapping("/doCodeLogin")
|
||||
public AjaxJson doCodeLogin(String code) {
|
||||
System.out.println("------------------ 成功进入请求 ------------------");
|
||||
|
||||
// 请求服务提供方接口地址,获取 access_token 以及其他信息
|
||||
// 携带三个关键参数: code、client_id、client_secret
|
||||
String str = OkHttps.sync("http://localhost:8001/oauth2/getAccessToken")
|
||||
.addBodyPara("code", code)
|
||||
.addBodyPara("client_id", "123123123")
|
||||
.addBodyPara("client_secret", "aaaa-bbbb-cccc-dddd-eeee")
|
||||
.post()
|
||||
.getBody()
|
||||
.toString();
|
||||
SoMap so = SoMap.getSoMap().setJsonString(str);
|
||||
System.out.println("返回结果: " + so);
|
||||
|
||||
// code不等于200 代表请求失败
|
||||
if(so.getInt("code") != 200) {
|
||||
return AjaxJson.getError(so.getString("msg"));
|
||||
}
|
||||
|
||||
// 根据openid获取其对应的userId
|
||||
String openid = so.getString("openid");
|
||||
long userId = getUserIdByOpenid(openid);
|
||||
|
||||
// 登录并返回账号信息
|
||||
StpUtil.setLoginId(userId);
|
||||
return AjaxJson.getSuccessData(userId).set("openid", openid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------------ 模拟方法 ------------------
|
||||
|
||||
// 模拟方法:根据openid获取userId
|
||||
private long getUserIdByOpenid(String openid) {
|
||||
// 此方法仅做模拟,实际开发要根据具体业务逻辑来获取userId
|
||||
return 10001;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.pj.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.pj.utils.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
/**
|
||||
* 全局异常拦截
|
||||
* <p> @ControllerAdvice 可指定包前缀,例如:(basePackages = "com.pj.controller.admin")
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class ExceptionHandle {
|
||||
|
||||
|
||||
/** 全局异常拦截 */
|
||||
@ResponseBody
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e) {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
e.printStackTrace();
|
||||
|
||||
// 记录日志信息
|
||||
AjaxJson aj = null;
|
||||
|
||||
// ------------- 判断异常类型,提供个性化提示信息
|
||||
|
||||
// 如果是未登录异常
|
||||
if(e instanceof NotLoginException){
|
||||
aj = AjaxJson.getNotLogin();
|
||||
}
|
||||
// 如果是角色异常
|
||||
else if(e instanceof NotRoleException) {
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getCode());
|
||||
}
|
||||
// 如果是权限异常
|
||||
else if(e instanceof NotPermissionException) {
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
}
|
||||
// 普通异常输出:500 + 异常信息
|
||||
else {
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回到前台
|
||||
return aj;
|
||||
}
|
||||
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
package com.pj.utils;
|
||||
|
||||
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 String getData() {
|
||||
return (String)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 String getDataCount() {
|
||||
return (String)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);
|
||||
this.setDataCount(dataCount == null ? -1 : 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 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; // 数据总数,用于分页
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+723
@@ -0,0 +1,723 @@
|
||||
package com.pj.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Map< String, Object> 是最常用的一种Map类型,但是它写着麻烦
|
||||
* <p>所以特封装此类,继承Map,进行一些扩展,可以让Map更灵活使用
|
||||
* <p>最新:2020-12-10 新增部分构造方法
|
||||
* @author kong
|
||||
*/
|
||||
public class SoMap extends LinkedHashMap<String, Object> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SoMap() {
|
||||
}
|
||||
|
||||
|
||||
/** 以下元素会在isNull函数中被判定为Null, */
|
||||
public static final Object[] NULL_ELEMENT_ARRAY = {null, ""};
|
||||
public static final List<Object> NULL_ELEMENT_LIST;
|
||||
|
||||
|
||||
static {
|
||||
NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
|
||||
}
|
||||
|
||||
// ============================= 读值 =============================
|
||||
|
||||
/** 获取一个值 */
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
if("this".equals(key)) {
|
||||
return this;
|
||||
}
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
/** 如果为空,则返回默认值 */
|
||||
public Object get(Object key, Object defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/** 转为String并返回 */
|
||||
public String getString(String key) {
|
||||
Object value = get(key);
|
||||
if(value == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/** 如果为空,则返回默认值 */
|
||||
public String getString(String key, String defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/** 转为int并返回 */
|
||||
public int getInt(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.valueOf(String.valueOf(value));
|
||||
}
|
||||
/** 转为int并返回,同时指定默认值 */
|
||||
public int getInt(String key, int defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Integer.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为long并返回 */
|
||||
public long getLong(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Long.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为double并返回 */
|
||||
public double getDouble(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0.0;
|
||||
}
|
||||
return Double.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为boolean并返回 */
|
||||
public boolean getBoolean(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据自定义格式 */
|
||||
public Date getDateByFormat(String key, String format) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(getString(key));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据格式: yyyy-MM-dd */
|
||||
public Date getDate(String key) {
|
||||
return getDateByFormat(key, "yyyy-MM-dd");
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据格式: yyyy-MM-dd HH:mm:ss */
|
||||
public Date getDateTime(String key) {
|
||||
return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
/** 获取集合(必须原先就是个集合,否则会创建个新集合并返回) */
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> getList(String key) {
|
||||
Object value = get(key);
|
||||
List<Object> list = null;
|
||||
if(value == null || value.equals("")) {
|
||||
list = new ArrayList<Object>();
|
||||
}
|
||||
else if(value instanceof List) {
|
||||
list = (List<Object>)value;
|
||||
} else {
|
||||
list = new ArrayList<Object>();
|
||||
list.add(value);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/** 获取集合 (指定泛型类型) */
|
||||
public <T> List<T> getList(String key, Class<T> cs) {
|
||||
List<Object> list = getList(key);
|
||||
List<T> list2 = new ArrayList<T>();
|
||||
for (Object obj : list) {
|
||||
T objC = getValueByClass(obj, cs);
|
||||
list2.add(objC);
|
||||
}
|
||||
return list2;
|
||||
}
|
||||
|
||||
/** 获取集合(逗号分隔式),(指定类型) */
|
||||
public <T> List<T> getListByComma(String key, Class<T> cs) {
|
||||
String listStr = getString(key);
|
||||
if(listStr == null || listStr.equals("")) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 开始转化
|
||||
String [] arr = listStr.split(",");
|
||||
List<T> list = new ArrayList<T>();
|
||||
for (String str : arr) {
|
||||
if(cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class) {
|
||||
str = str.trim();
|
||||
}
|
||||
T objC = getValueByClass(str, cs);
|
||||
list.add(objC);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/** 根据指定类型从map中取值,返回实体对象 */
|
||||
public <T> T getModel(Class<T> cs) {
|
||||
try {
|
||||
return getModelByObject(cs.newInstance());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 从map中取值,塞到一个对象中 */
|
||||
public <T> T getModelByObject(T obj) {
|
||||
// 获取类型
|
||||
Class<?> cs = obj.getClass();
|
||||
// 循环复制
|
||||
for (Field field : cs.getDeclaredFields()) {
|
||||
try {
|
||||
// 获取对象
|
||||
Object value = this.get(field.getName());
|
||||
if(value == null) {
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
Object valueConvert = getValueByClass(value, field.getType());
|
||||
field.set(obj, valueConvert);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new RuntimeException("属性取值出错:" + field.getName(), e);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 将指定值转化为指定类型并返回
|
||||
* @param obj
|
||||
* @param cs
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getValueByClass(Object obj, Class<T> cs) {
|
||||
String obj2 = String.valueOf(obj);
|
||||
Object obj3 = null;
|
||||
if (cs.equals(String.class)) {
|
||||
obj3 = obj2;
|
||||
} else if (cs.equals(int.class) || cs.equals(Integer.class)) {
|
||||
obj3 = Integer.valueOf(obj2);
|
||||
} else if (cs.equals(long.class) || cs.equals(Long.class)) {
|
||||
obj3 = Long.valueOf(obj2);
|
||||
} else if (cs.equals(short.class) || cs.equals(Short.class)) {
|
||||
obj3 = Short.valueOf(obj2);
|
||||
} else if (cs.equals(byte.class) || cs.equals(Byte.class)) {
|
||||
obj3 = Byte.valueOf(obj2);
|
||||
} else if (cs.equals(float.class) || cs.equals(Float.class)) {
|
||||
obj3 = Float.valueOf(obj2);
|
||||
} else if (cs.equals(double.class) || cs.equals(Double.class)) {
|
||||
obj3 = Double.valueOf(obj2);
|
||||
} else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
|
||||
obj3 = Boolean.valueOf(obj2);
|
||||
} else {
|
||||
obj3 = (T)obj;
|
||||
}
|
||||
return (T)obj3;
|
||||
}
|
||||
|
||||
|
||||
// ============================= 写值 =============================
|
||||
|
||||
/**
|
||||
* 给指定key添加一个默认值(只有在这个key原来无值的情况先才会set进去)
|
||||
*/
|
||||
public void setDefaultValue(String key, Object defaultValue) {
|
||||
if(isNull(key)) {
|
||||
set(key, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/** set一个值,连缀风格 */
|
||||
public SoMap set(String key, Object value) {
|
||||
// 防止敏感key
|
||||
if(key.toLowerCase().equals("this")) {
|
||||
return this;
|
||||
}
|
||||
put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将一个Map塞进SoMap */
|
||||
public SoMap setMap(Map<String, ?> map) {
|
||||
if(map != null) {
|
||||
for (String key : map.keySet()) {
|
||||
this.set(key, map.get(key));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将一个对象解析塞进SoMap */
|
||||
public SoMap setModel(Object model) {
|
||||
if(model == null) {
|
||||
return this;
|
||||
}
|
||||
Field[] fields = model.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
try{
|
||||
field.setAccessible(true);
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if(!isStatic) {
|
||||
this.set(field.getName(), field.get(model));
|
||||
}
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将json字符串解析后塞进SoMap */
|
||||
public SoMap setJsonString(String jsonString) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = new ObjectMapper().readValue(jsonString, Map.class);
|
||||
return this.setMap(map);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================= 删值 =============================
|
||||
|
||||
/** delete一个值,连缀风格 */
|
||||
public SoMap delete(String key) {
|
||||
remove(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 清理所有value为null的字段 */
|
||||
public SoMap clearNull() {
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(this.isNull(key)) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理指定key */
|
||||
public SoMap clearIn(String ...keys) {
|
||||
List<String> keys2 = Arrays.asList(keys);
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(keys2.contains(key) == true) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理掉不在列表中的key */
|
||||
public SoMap clearNotIn(String ...keys) {
|
||||
List<String> keys2 = Arrays.asList(keys);
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(keys2.contains(key) == false) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理掉所有key */
|
||||
public SoMap clearAll() {
|
||||
clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ============================= 快速构建 =============================
|
||||
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap() {
|
||||
return new SoMap();
|
||||
}
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap(String key, Object value) {
|
||||
return new SoMap().set(key, value);
|
||||
}
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap(Map<String, ?> map) {
|
||||
return new SoMap().setMap(map);
|
||||
}
|
||||
|
||||
/** 将一个对象集合解析成为SoMap */
|
||||
public static SoMap getSoMapByModel(Object model) {
|
||||
return SoMap.getSoMap().setModel(model);
|
||||
}
|
||||
|
||||
/** 将一个对象集合解析成为SoMap集合 */
|
||||
public static List<SoMap> getSoMapByList(List<?> list) {
|
||||
List<SoMap> listMap = new ArrayList<SoMap>();
|
||||
for (Object model : list) {
|
||||
listMap.add(getSoMapByModel(model));
|
||||
}
|
||||
return listMap;
|
||||
}
|
||||
|
||||
/** 克隆指定key,返回一个新的SoMap */
|
||||
public SoMap cloneKeys(String... keys) {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : keys) {
|
||||
so.set(key, this.get(key));
|
||||
}
|
||||
return so;
|
||||
}
|
||||
/** 克隆所有key,返回一个新的SoMap */
|
||||
public SoMap cloneSoMap() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key, this.get(key));
|
||||
}
|
||||
return so;
|
||||
}
|
||||
|
||||
/** 将所有key转为大写 */
|
||||
public SoMap toUpperCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key.toUpperCase(), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key转为小写 */
|
||||
public SoMap toLowerCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key.toLowerCase(), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中下划线转为中划线模式 (kebab-case风格) */
|
||||
public SoMap toKebabCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordEachKebabCase(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中下划线转为小驼峰模式 */
|
||||
public SoMap toHumpCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordEachBigFs(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中小驼峰转为下划线模式 */
|
||||
public SoMap humpToLineCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordHumpToLine(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================= 辅助方法 =============================
|
||||
|
||||
|
||||
/** 指定key是否为null,判定标准为 NULL_ELEMENT_ARRAY 中的元素 */
|
||||
public boolean isNull(String key) {
|
||||
return valueIsNull(get(key));
|
||||
}
|
||||
|
||||
/** 指定key列表中是否包含value为null的元素,只要有一个为null,就会返回true */
|
||||
public boolean isContainNull(String ...keys) {
|
||||
for (String key : keys) {
|
||||
if(this.isNull(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 与isNull()相反 */
|
||||
public boolean isNotNull(String key) {
|
||||
return !isNull(key);
|
||||
}
|
||||
/** 指定key的value是否为null,作用同isNotNull() */
|
||||
public boolean has(String key) {
|
||||
return !isNull(key);
|
||||
}
|
||||
|
||||
/** 指定value在此SoMap的判断标准中是否为null */
|
||||
public boolean valueIsNull(Object value) {
|
||||
return NULL_ELEMENT_LIST.contains(value);
|
||||
}
|
||||
|
||||
/** 验证指定key不为空,为空则抛出异常 */
|
||||
public SoMap checkNull(String ...keys) {
|
||||
for (String key : keys) {
|
||||
if(this.isNull(key)) {
|
||||
throw new RuntimeException("参数" + key + "不能为空");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
static Pattern patternNumber = Pattern.compile("[0-9]*");
|
||||
/** 指定key是否为数字 */
|
||||
public boolean isNumber(String key) {
|
||||
String value = getString(key);
|
||||
if(value == null) {
|
||||
return false;
|
||||
}
|
||||
return patternNumber.matcher(value).matches();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 转为JSON字符串
|
||||
*/
|
||||
public String toJsonString() {
|
||||
try {
|
||||
// SoMap so = SoMap.getSoMap(this);
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 转为JSON字符串, 带格式的
|
||||
// */
|
||||
// public String toJsonFormatString() {
|
||||
// try {
|
||||
// return JSON.toJSONString(this, true);
|
||||
// } catch (Exception e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
// ============================= web辅助 =============================
|
||||
|
||||
|
||||
/**
|
||||
* 返回当前request请求的的所有参数
|
||||
* @return
|
||||
*/
|
||||
public static SoMap getRequestSoMap() {
|
||||
// 大善人SpringMVC提供的封装
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if(servletRequestAttributes == null) {
|
||||
throw new RuntimeException("当前线程非JavaWeb环境");
|
||||
}
|
||||
// 当前request
|
||||
HttpServletRequest request = servletRequestAttributes.getRequest();
|
||||
if (request.getAttribute("currentSoMap") == null || request.getAttribute("currentSoMap") instanceof SoMap == false ) {
|
||||
initRequestSoMap(request);
|
||||
}
|
||||
return (SoMap)request.getAttribute("currentSoMap");
|
||||
}
|
||||
|
||||
/** 初始化当前request的 SoMap */
|
||||
private static void initRequestSoMap(HttpServletRequest request) {
|
||||
SoMap soMap = new SoMap();
|
||||
Map<String, String[]> parameterMap = request.getParameterMap(); // 获取所有参数
|
||||
for (String key : parameterMap.keySet()) {
|
||||
try {
|
||||
String[] values = parameterMap.get(key); // 获得values
|
||||
if(values.length == 1) {
|
||||
soMap.set(key, values[0]);
|
||||
} else {
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (String v : values) {
|
||||
list.add(v);
|
||||
}
|
||||
soMap.set(key, list);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
request.setAttribute("currentSoMap", soMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证返回当前线程是否为JavaWeb环境
|
||||
* @return
|
||||
*/
|
||||
public static boolean isJavaWeb() {
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装
|
||||
if(servletRequestAttributes == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================= 常见key (以下key经常用,所以封装以下,方便写代码) =============================
|
||||
|
||||
/** get 当前页 */
|
||||
public int getKeyPageNo() {
|
||||
int pageNo = getInt("pageNo", 1);
|
||||
if(pageNo <= 0) {
|
||||
pageNo = 1;
|
||||
}
|
||||
return pageNo;
|
||||
}
|
||||
/** get 页大小 */
|
||||
public int getKeyPageSize() {
|
||||
int pageSize = getInt("pageSize", 10);
|
||||
if(pageSize <= 0 || pageSize > 1000) {
|
||||
pageSize = 10;
|
||||
}
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
/** get 排序方式 */
|
||||
public int getKeySortType() {
|
||||
return getInt("sortType");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================= 工具方法 =============================
|
||||
|
||||
|
||||
/**
|
||||
* 将一个一维集合转换为树形集合
|
||||
* @param list 集合
|
||||
* @param idKey id标识key
|
||||
* @param parentIdKey 父id标识key
|
||||
* @param childListKey 子节点标识key
|
||||
* @return 转换后的tree集合
|
||||
*/
|
||||
public static List<SoMap> listToTree(List<SoMap> list, String idKey, String parentIdKey, String childListKey) {
|
||||
// 声明新的集合,存储tree形数据
|
||||
List<SoMap> newTreeList = new ArrayList<SoMap>();
|
||||
// 声明hash-Map,方便查找数据
|
||||
SoMap hash = new SoMap();
|
||||
// 将数组转为Object的形式,key为数组中的id
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
SoMap json = (SoMap) list.get(i);
|
||||
hash.put(json.getString(idKey), json);
|
||||
}
|
||||
// 遍历结果集
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
// 单条记录
|
||||
SoMap aVal = (SoMap) list.get(j);
|
||||
// 在hash中取出key为单条记录中pid的值
|
||||
SoMap hashVp = (SoMap) hash.get(aVal.get(parentIdKey, "").toString());
|
||||
// 如果记录的pid存在,则说明它有父节点,将她添加到孩子节点的集合中
|
||||
if (hashVp != null) {
|
||||
// 检查是否有child属性,有则添加,没有则新建
|
||||
if (hashVp.get(childListKey) != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SoMap> ch = (List<SoMap>) hashVp.get(childListKey);
|
||||
ch.add(aVal);
|
||||
hashVp.put(childListKey, ch);
|
||||
} else {
|
||||
List<SoMap> ch = new ArrayList<SoMap>();
|
||||
ch.add(aVal);
|
||||
hashVp.put(childListKey, ch);
|
||||
}
|
||||
} else {
|
||||
newTreeList.add(aVal);
|
||||
}
|
||||
}
|
||||
return newTreeList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 指定字符串的字符串下划线转大写模式 */
|
||||
private static String wordEachBig(String str){
|
||||
String newStr = "";
|
||||
for (String s : str.split("_")) {
|
||||
newStr += wordFirstBig(s);
|
||||
}
|
||||
return newStr;
|
||||
}
|
||||
/** 返回下划线转小驼峰形式 */
|
||||
private static String wordEachBigFs(String str){
|
||||
return wordFirstSmall(wordEachBig(str));
|
||||
}
|
||||
|
||||
/** 将指定单词首字母大写 */
|
||||
private static String wordFirstBig(String str) {
|
||||
return str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
|
||||
}
|
||||
|
||||
/** 将指定单词首字母小写 */
|
||||
private static String wordFirstSmall(String str) {
|
||||
return str.substring(0, 1).toLowerCase() + str.substring(1, str.length());
|
||||
}
|
||||
|
||||
/** 下划线转中划线 */
|
||||
private static String wordEachKebabCase(String str) {
|
||||
return str.replaceAll("_", "-");
|
||||
}
|
||||
|
||||
/** 驼峰转下划线 */
|
||||
private static String wordHumpToLine(String str) {
|
||||
return str.replaceAll("[A-Z]", "_$0").toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
server:
|
||||
port: 8002
|
||||
|
||||
spring:
|
||||
# 静态文件路径映射
|
||||
resources:
|
||||
static-locations: classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/
|
||||
# static-locations: file:E:\work\project-yun\sa-token\sa-token-demo-oauth2\sa-token-demo-oauth2-client\src\main\resources\static\
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken-client
|
||||
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>客户端-登录页</title>
|
||||
<style type="text/css">
|
||||
*{margin: 0px; padding: 0px;}
|
||||
.login-box{width: 500px; margin: 50px auto;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-box">
|
||||
<h2>客户端-登录页</h2> <br>
|
||||
<div>
|
||||
当前是否登录: <span class="login-info"></span>
|
||||
<button onclick="logout()">注销登录</button>
|
||||
</div>
|
||||
<br/>
|
||||
点此按钮开始使用OAuth2.0开放平台快捷登录:
|
||||
<button onclick="requestOAuth2()">快捷登录</button>
|
||||
</div>
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// 获取登录信息
|
||||
var currUserId = null;
|
||||
function getLoginInfo() {
|
||||
$.ajax({
|
||||
url: '/getLoginInfo',
|
||||
data: {},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.data == null) {
|
||||
$(".login-info").html('<b style="color: red;">未登录</b>');
|
||||
// 如果当前未登录,并且此时url中有code参数, 则尝试使用code码进行登录
|
||||
var code = getParam('code', null);
|
||||
if(code != null) {
|
||||
doCodeLogin();
|
||||
}
|
||||
} else {
|
||||
$(".login-info").html('<b style="color: green;">已登录, userId=' + res.data + '</b>');
|
||||
currUserId = res.data;
|
||||
}
|
||||
},
|
||||
error: function(e, ee, eee) {
|
||||
console.log('error');
|
||||
alert(eee);
|
||||
}
|
||||
});
|
||||
}
|
||||
getLoginInfo();
|
||||
|
||||
// 注销登录
|
||||
function logout() {
|
||||
$.ajax({
|
||||
url: '/logout',
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
alert('注销成功');
|
||||
location.href = "./login.html";
|
||||
},
|
||||
error: function(e, ee, eee) {
|
||||
alert(eee);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 请求OAuth2授权
|
||||
function requestOAuth2() {
|
||||
// 如果当前已经登录,则必须先退出
|
||||
if(currUserId != null) {
|
||||
alert('当前已经登录, 请先注销');
|
||||
return;
|
||||
}
|
||||
// 拼接地址
|
||||
var url = "http://localhost:8001/oauth2/authorize" +
|
||||
"?client_id=123123123" + // 应用id
|
||||
"&scope=userinfo" + // 授权范围
|
||||
// "&redirect_uri=" + encodeURIComponent(location.href) // 重定向地址
|
||||
"&redirect_uri=" + encodeURIComponent("http://localhost:8002/login.html") // 重定向地址
|
||||
"&response_type=code" + // 返回格式
|
||||
"&state=123456789"; // 授权范围
|
||||
console.log(url);
|
||||
location.href = url;
|
||||
}
|
||||
|
||||
// 使用code进行 (从url中获取code码进行登录)
|
||||
function doCodeLogin() {
|
||||
var code = getParam('code');
|
||||
$.ajax({
|
||||
url: '/doCodeLogin',
|
||||
data: {
|
||||
code: code
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.code == 200) {
|
||||
alert('OAuth2登录成功');
|
||||
getLoginInfo(); // 刷新user信息
|
||||
} else {
|
||||
alert("登录失败:" + res.msg);
|
||||
}
|
||||
},
|
||||
error: function(e, ee, eee) {
|
||||
alert(eee);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 从url中查询到指定名称的参数值
|
||||
function getParam(name, defaultValue){
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
for (var i=0;i<vars.length;i++) {
|
||||
var pair = vars[i].split("=");
|
||||
if(pair[0] == name){return pair[1];}
|
||||
}
|
||||
return(defaultValue == undefined ? null : defaultValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
target/
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
|
||||
/.idea/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
/.apt_generated/
|
||||
/.apt_generated_tests/
|
||||
@@ -0,0 +1,68 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.pj</groupId>
|
||||
<artifactId>sa-token-demo-oauth2-server</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<!-- 指定一些属性 -->
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<sa-token-version>1.15.0.RELEASE</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- springboot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</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 实现 oauth2.0 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-oauth2</artifactId>
|
||||
<version>1.15.0-alpha</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>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.pj;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 启动
|
||||
* @author kong
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaOAuth2ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaOAuth2ServerApplication.class, args);
|
||||
System.out.println("\n服务端启动成功");
|
||||
}
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.pj.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.pj.utils.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
|
||||
/**
|
||||
* 全局异常拦截
|
||||
* <p> @ControllerAdvice 可指定包前缀,例如:(basePackages = "com.pj.controller.admin")
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class ExceptionHandle {
|
||||
|
||||
|
||||
/** 全局异常拦截 */
|
||||
@ResponseBody
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e) {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
e.printStackTrace();
|
||||
|
||||
// 记录日志信息
|
||||
AjaxJson aj = null;
|
||||
|
||||
// ------------- 判断异常类型,提供个性化提示信息
|
||||
|
||||
// 如果是未登录异常
|
||||
if(e instanceof NotLoginException){
|
||||
aj = AjaxJson.getNotLogin();
|
||||
}
|
||||
// 如果是角色异常
|
||||
else if(e instanceof NotRoleException) {
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getCode());
|
||||
}
|
||||
// 如果是权限异常
|
||||
else if(e instanceof NotPermissionException) {
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
}
|
||||
// 普通异常输出:500 + 异常信息
|
||||
else {
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回到前台
|
||||
return aj;
|
||||
}
|
||||
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
package com.pj.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.utils.AjaxJson;
|
||||
import com.pj.utils.SoMap;
|
||||
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
|
||||
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
|
||||
import cn.dev33.satoken.oauth2.model.CodeModel;
|
||||
import cn.dev33.satoken.oauth2.model.RequestAuthModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/oauth2/")
|
||||
public class OAuth2Controller {
|
||||
|
||||
|
||||
// 获取授权码
|
||||
@RequestMapping("/authorize")
|
||||
public AjaxJson authorize(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
// 获取参数
|
||||
System.out.println("------------------ 成功进入请求 ------------------");
|
||||
|
||||
// 如果暂未登录,则先跳转到登录页 (转发)
|
||||
if(StpUtil.isLogin() == false) {
|
||||
response.setContentType("text/html");
|
||||
request.getRequestDispatcher("/login.html").forward(request, response);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 构建Model
|
||||
RequestAuthModel authModel = new RequestAuthModel()
|
||||
.setClientId(request.getParameter("client_id")) // 应用id
|
||||
.setScope(request.getParameter("scope")) // 授权类型
|
||||
.setLoginId(StpUtil.getLoginIdAsLong()) // 当前登录账号id
|
||||
.setRedirectUri(URLDecoder.decode(request.getParameter("redirect_uri"), "utf-8")) // 重定向地址
|
||||
.setResponseType(request.getParameter("response_type")) // 返回类型
|
||||
.setState(request.getParameter("state")) // 状态值
|
||||
.checkModel(); // 校验参数完整性
|
||||
|
||||
// 生成授权码Model
|
||||
CodeModel codeModel = SaOAuth2Util.generateCode(authModel);
|
||||
|
||||
// 打印调试
|
||||
System.out.println("应用id=" + authModel.getClientId() + "请求授权,授权类型=" + authModel.getResponseType());
|
||||
System.out.println("重定向地址:" + authModel.getRedirectUri());
|
||||
System.out.println("拼接完成的redirect_uri: " + codeModel.getRedirectUri());
|
||||
System.out.println("如果用户拒绝授权,则重定向至: " + codeModel.getRejectUri());
|
||||
|
||||
// 如果请求的权限用户已经确认,直接开始重定向授权
|
||||
if(codeModel.getIsConfirm() == true) {
|
||||
response.sendRedirect(codeModel.getRedirectUri());
|
||||
} else {
|
||||
// 如果请求的权限用户尚未确认,则进入到确定页
|
||||
request.setAttribute("name", "sdd");
|
||||
response.sendRedirect("/auth.html?code=" + codeModel.getCode());
|
||||
}
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 根据授权码获取应用信息
|
||||
@RequestMapping("/getCodeInfo")
|
||||
public AjaxJson getCodeInfo(String code) {
|
||||
// 获取codeModel
|
||||
CodeModel codeModel = SaOAuth2Util.getCode(code);
|
||||
System.out.println(code);
|
||||
System.out.println(codeModel);
|
||||
// 返回
|
||||
return AjaxJson.getSuccessData(codeModel);
|
||||
}
|
||||
|
||||
// 确认授权一个授权码
|
||||
@RequestMapping("/confirm")
|
||||
public AjaxJson confirm(String code) {
|
||||
// 获取codeModel
|
||||
CodeModel codeModel = SaOAuth2Util.getCode(code);
|
||||
if(codeModel == null) {
|
||||
return AjaxJson.getError("无效code码");
|
||||
}
|
||||
// 此处的判断是为了保证当前账号id 和 创建授权码的账号id一致 才可以进行确认
|
||||
if(codeModel.getLoginId().toString().equals(StpUtil.getLoginIdAsString()) == false) {
|
||||
return AjaxJson.getError("暂无权限");
|
||||
}
|
||||
// 进行确认
|
||||
SaOAuth2Util.confirmCode(code);
|
||||
|
||||
// 返回ok
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 根据授权码等参数,获取 access_token 等信息
|
||||
@RequestMapping("/getAccessToken")
|
||||
public SoMap getAccessToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
// 获取参数
|
||||
System.out.println("------------------ 成功进入请求 ------------------");
|
||||
String code = request.getParameter("code"); // code码
|
||||
String clientId = request.getParameter("client_id"); // 应用id
|
||||
String clientSecret = request.getParameter("client_secret"); // 应用秘钥
|
||||
|
||||
// 校验参数
|
||||
SaOAuth2Util.checkCodeIdSecret(code, clientId, clientSecret);
|
||||
|
||||
// 生成
|
||||
CodeModel codeModel = SaOAuth2Util.getCode(code);
|
||||
AccessTokenModel tokenModel = SaOAuth2Util.generateAccessToken(codeModel);
|
||||
|
||||
// 生成AccessToken之后,将授权码立即销毁
|
||||
SaOAuth2Util.deleteCode(code);
|
||||
|
||||
// 返回
|
||||
return SoMap.getSoMap()
|
||||
.setModel(tokenModel)
|
||||
.set("code", 200)
|
||||
.set("msg", "ok");
|
||||
}
|
||||
|
||||
// 根据access_token返回指定的资源
|
||||
@RequestMapping("/getResources")
|
||||
public SoMap getResources(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
|
||||
// 获取信息
|
||||
String accessToken = request.getParameter("access_token");
|
||||
Object LoginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
|
||||
System.out.println("LoginId=" + LoginId);
|
||||
|
||||
// 根据LoginId获取相应信息...
|
||||
// 此处仅做模拟
|
||||
return new SoMap()
|
||||
.set("nickname", "shengzhang")
|
||||
.set("acatar", "xxx")
|
||||
.set("sex", 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.pj.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.utils.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 服务端登录Controller
|
||||
* @author kong
|
||||
*/
|
||||
@RestController
|
||||
public class ServerAccController {
|
||||
|
||||
// 登录方法
|
||||
@RequestMapping("/doLogin")
|
||||
public AjaxJson test(String username, String password) {
|
||||
System.out.println("------------------ 成功进入请求 ------------------");
|
||||
if("test".equals(username) && "test".equals(password)) {
|
||||
StpUtil.setLoginId(10001);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
return AjaxJson.getError();
|
||||
}
|
||||
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.pj.oauth2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Interface;
|
||||
|
||||
/**
|
||||
* 使用oauth2.0 所必须的一些自定义实现
|
||||
* @author kong
|
||||
*/
|
||||
@Component
|
||||
public class SaOAuth2InterfaceImpl implements SaOAuth2Interface {
|
||||
|
||||
|
||||
/*
|
||||
* ------ 注意: 以下代码均为示例,真实环境需要根据数据库查询相关信息
|
||||
*/
|
||||
|
||||
// 返回此平台所有权限集合
|
||||
@Override
|
||||
public List<String> getAppScopeList() {
|
||||
return Arrays.asList("userinfo");
|
||||
}
|
||||
|
||||
// 返回指定Client签约的所有Scope集合
|
||||
@Override
|
||||
public List<String> getClientScopeList(String clientId) {
|
||||
return Arrays.asList("userinfo");
|
||||
}
|
||||
|
||||
// 获取指定 LoginId 对指定 Client 已经授权过的所有 Scope
|
||||
@Override
|
||||
public List<String> getGrantScopeList(Object loginId, String clientId) {
|
||||
return Arrays.asList();
|
||||
}
|
||||
|
||||
// 返回指定Client允许的回调域名, 多个用逗号隔开, *代表不限制
|
||||
@Override
|
||||
public String getClientDomain(String clientId) {
|
||||
return "*";
|
||||
}
|
||||
|
||||
// 返回指定ClientId的ClientSecret
|
||||
@Override
|
||||
public String getClientSecret(String clientId) {
|
||||
return "aaaa-bbbb-cccc-dddd-eeee";
|
||||
}
|
||||
|
||||
// 根据ClientId和LoginId返回openid
|
||||
@Override
|
||||
public String getOpenid(String clientId, Object loginId) {
|
||||
return "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__";
|
||||
}
|
||||
|
||||
// 根据ClientId和openid返回LoginId
|
||||
@Override
|
||||
public Object getLoginId(String clientId, String openid) {
|
||||
return 10001;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 以上函数为开发时必须重写实现,其余函数可以按需重写
|
||||
*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.pj.oauth2;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
||||
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Interface;
|
||||
|
||||
/**
|
||||
* 利用Spring完成自动装配
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class SaOAuth2SpringAutowired {
|
||||
|
||||
/**
|
||||
* 获取OAuth2配置Bean
|
||||
*
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.sa-token.oauth2")
|
||||
public SaOAuth2Config getSaOAuth2Config() {
|
||||
return new SaOAuth2Config();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入OAuth2配置Bean
|
||||
*
|
||||
* @param saOAuth2Config 配置对象
|
||||
*/
|
||||
@Autowired
|
||||
public void setSaOAuth2Config(SaOAuth2Config saOAuth2Config) {
|
||||
SaOAuth2Manager.setConfig(saOAuth2Config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入OAuth2接口Bean
|
||||
*
|
||||
* @param saOAuth2Interface OAuth2接口Bean
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaOAuth2Interface(SaOAuth2Interface saOAuth2Interface) {
|
||||
SaOAuth2Manager.setInterface(saOAuth2Interface);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
package com.pj.utils;
|
||||
|
||||
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 String getData() {
|
||||
return (String)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 String getDataCount() {
|
||||
return (String)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);
|
||||
this.setDataCount(dataCount == null ? -1 : 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 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; // 数据总数,用于分页
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+723
@@ -0,0 +1,723 @@
|
||||
package com.pj.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Map< String, Object> 是最常用的一种Map类型,但是它写着麻烦
|
||||
* <p>所以特封装此类,继承Map,进行一些扩展,可以让Map更灵活使用
|
||||
* <p>最新:2020-12-10 新增部分构造方法
|
||||
* @author kong
|
||||
*/
|
||||
public class SoMap extends LinkedHashMap<String, Object> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SoMap() {
|
||||
}
|
||||
|
||||
|
||||
/** 以下元素会在isNull函数中被判定为Null, */
|
||||
public static final Object[] NULL_ELEMENT_ARRAY = {null, ""};
|
||||
public static final List<Object> NULL_ELEMENT_LIST;
|
||||
|
||||
|
||||
static {
|
||||
NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
|
||||
}
|
||||
|
||||
// ============================= 读值 =============================
|
||||
|
||||
/** 获取一个值 */
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
if("this".equals(key)) {
|
||||
return this;
|
||||
}
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
/** 如果为空,则返回默认值 */
|
||||
public Object get(Object key, Object defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/** 转为String并返回 */
|
||||
public String getString(String key) {
|
||||
Object value = get(key);
|
||||
if(value == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/** 如果为空,则返回默认值 */
|
||||
public String getString(String key, String defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/** 转为int并返回 */
|
||||
public int getInt(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.valueOf(String.valueOf(value));
|
||||
}
|
||||
/** 转为int并返回,同时指定默认值 */
|
||||
public int getInt(String key, int defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Integer.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为long并返回 */
|
||||
public long getLong(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Long.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为double并返回 */
|
||||
public double getDouble(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0.0;
|
||||
}
|
||||
return Double.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为boolean并返回 */
|
||||
public boolean getBoolean(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据自定义格式 */
|
||||
public Date getDateByFormat(String key, String format) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(getString(key));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据格式: yyyy-MM-dd */
|
||||
public Date getDate(String key) {
|
||||
return getDateByFormat(key, "yyyy-MM-dd");
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据格式: yyyy-MM-dd HH:mm:ss */
|
||||
public Date getDateTime(String key) {
|
||||
return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
/** 获取集合(必须原先就是个集合,否则会创建个新集合并返回) */
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> getList(String key) {
|
||||
Object value = get(key);
|
||||
List<Object> list = null;
|
||||
if(value == null || value.equals("")) {
|
||||
list = new ArrayList<Object>();
|
||||
}
|
||||
else if(value instanceof List) {
|
||||
list = (List<Object>)value;
|
||||
} else {
|
||||
list = new ArrayList<Object>();
|
||||
list.add(value);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/** 获取集合 (指定泛型类型) */
|
||||
public <T> List<T> getList(String key, Class<T> cs) {
|
||||
List<Object> list = getList(key);
|
||||
List<T> list2 = new ArrayList<T>();
|
||||
for (Object obj : list) {
|
||||
T objC = getValueByClass(obj, cs);
|
||||
list2.add(objC);
|
||||
}
|
||||
return list2;
|
||||
}
|
||||
|
||||
/** 获取集合(逗号分隔式),(指定类型) */
|
||||
public <T> List<T> getListByComma(String key, Class<T> cs) {
|
||||
String listStr = getString(key);
|
||||
if(listStr == null || listStr.equals("")) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 开始转化
|
||||
String [] arr = listStr.split(",");
|
||||
List<T> list = new ArrayList<T>();
|
||||
for (String str : arr) {
|
||||
if(cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class) {
|
||||
str = str.trim();
|
||||
}
|
||||
T objC = getValueByClass(str, cs);
|
||||
list.add(objC);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/** 根据指定类型从map中取值,返回实体对象 */
|
||||
public <T> T getModel(Class<T> cs) {
|
||||
try {
|
||||
return getModelByObject(cs.newInstance());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 从map中取值,塞到一个对象中 */
|
||||
public <T> T getModelByObject(T obj) {
|
||||
// 获取类型
|
||||
Class<?> cs = obj.getClass();
|
||||
// 循环复制
|
||||
for (Field field : cs.getDeclaredFields()) {
|
||||
try {
|
||||
// 获取对象
|
||||
Object value = this.get(field.getName());
|
||||
if(value == null) {
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
Object valueConvert = getValueByClass(value, field.getType());
|
||||
field.set(obj, valueConvert);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new RuntimeException("属性取值出错:" + field.getName(), e);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 将指定值转化为指定类型并返回
|
||||
* @param obj
|
||||
* @param cs
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getValueByClass(Object obj, Class<T> cs) {
|
||||
String obj2 = String.valueOf(obj);
|
||||
Object obj3 = null;
|
||||
if (cs.equals(String.class)) {
|
||||
obj3 = obj2;
|
||||
} else if (cs.equals(int.class) || cs.equals(Integer.class)) {
|
||||
obj3 = Integer.valueOf(obj2);
|
||||
} else if (cs.equals(long.class) || cs.equals(Long.class)) {
|
||||
obj3 = Long.valueOf(obj2);
|
||||
} else if (cs.equals(short.class) || cs.equals(Short.class)) {
|
||||
obj3 = Short.valueOf(obj2);
|
||||
} else if (cs.equals(byte.class) || cs.equals(Byte.class)) {
|
||||
obj3 = Byte.valueOf(obj2);
|
||||
} else if (cs.equals(float.class) || cs.equals(Float.class)) {
|
||||
obj3 = Float.valueOf(obj2);
|
||||
} else if (cs.equals(double.class) || cs.equals(Double.class)) {
|
||||
obj3 = Double.valueOf(obj2);
|
||||
} else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
|
||||
obj3 = Boolean.valueOf(obj2);
|
||||
} else {
|
||||
obj3 = (T)obj;
|
||||
}
|
||||
return (T)obj3;
|
||||
}
|
||||
|
||||
|
||||
// ============================= 写值 =============================
|
||||
|
||||
/**
|
||||
* 给指定key添加一个默认值(只有在这个key原来无值的情况先才会set进去)
|
||||
*/
|
||||
public void setDefaultValue(String key, Object defaultValue) {
|
||||
if(isNull(key)) {
|
||||
set(key, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/** set一个值,连缀风格 */
|
||||
public SoMap set(String key, Object value) {
|
||||
// 防止敏感key
|
||||
if(key.toLowerCase().equals("this")) {
|
||||
return this;
|
||||
}
|
||||
put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将一个Map塞进SoMap */
|
||||
public SoMap setMap(Map<String, ?> map) {
|
||||
if(map != null) {
|
||||
for (String key : map.keySet()) {
|
||||
this.set(key, map.get(key));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将一个对象解析塞进SoMap */
|
||||
public SoMap setModel(Object model) {
|
||||
if(model == null) {
|
||||
return this;
|
||||
}
|
||||
Field[] fields = model.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
try{
|
||||
field.setAccessible(true);
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if(!isStatic) {
|
||||
this.set(field.getName(), field.get(model));
|
||||
}
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将json字符串解析后塞进SoMap */
|
||||
public SoMap setJsonString(String jsonString) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = new ObjectMapper().readValue(jsonString, Map.class);
|
||||
return this.setMap(map);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================= 删值 =============================
|
||||
|
||||
/** delete一个值,连缀风格 */
|
||||
public SoMap delete(String key) {
|
||||
remove(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 清理所有value为null的字段 */
|
||||
public SoMap clearNull() {
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(this.isNull(key)) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理指定key */
|
||||
public SoMap clearIn(String ...keys) {
|
||||
List<String> keys2 = Arrays.asList(keys);
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(keys2.contains(key) == true) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理掉不在列表中的key */
|
||||
public SoMap clearNotIn(String ...keys) {
|
||||
List<String> keys2 = Arrays.asList(keys);
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(keys2.contains(key) == false) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理掉所有key */
|
||||
public SoMap clearAll() {
|
||||
clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ============================= 快速构建 =============================
|
||||
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap() {
|
||||
return new SoMap();
|
||||
}
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap(String key, Object value) {
|
||||
return new SoMap().set(key, value);
|
||||
}
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap(Map<String, ?> map) {
|
||||
return new SoMap().setMap(map);
|
||||
}
|
||||
|
||||
/** 将一个对象集合解析成为SoMap */
|
||||
public static SoMap getSoMapByModel(Object model) {
|
||||
return SoMap.getSoMap().setModel(model);
|
||||
}
|
||||
|
||||
/** 将一个对象集合解析成为SoMap集合 */
|
||||
public static List<SoMap> getSoMapByList(List<?> list) {
|
||||
List<SoMap> listMap = new ArrayList<SoMap>();
|
||||
for (Object model : list) {
|
||||
listMap.add(getSoMapByModel(model));
|
||||
}
|
||||
return listMap;
|
||||
}
|
||||
|
||||
/** 克隆指定key,返回一个新的SoMap */
|
||||
public SoMap cloneKeys(String... keys) {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : keys) {
|
||||
so.set(key, this.get(key));
|
||||
}
|
||||
return so;
|
||||
}
|
||||
/** 克隆所有key,返回一个新的SoMap */
|
||||
public SoMap cloneSoMap() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key, this.get(key));
|
||||
}
|
||||
return so;
|
||||
}
|
||||
|
||||
/** 将所有key转为大写 */
|
||||
public SoMap toUpperCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key.toUpperCase(), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key转为小写 */
|
||||
public SoMap toLowerCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key.toLowerCase(), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中下划线转为中划线模式 (kebab-case风格) */
|
||||
public SoMap toKebabCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordEachKebabCase(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中下划线转为小驼峰模式 */
|
||||
public SoMap toHumpCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordEachBigFs(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中小驼峰转为下划线模式 */
|
||||
public SoMap humpToLineCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordHumpToLine(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================= 辅助方法 =============================
|
||||
|
||||
|
||||
/** 指定key是否为null,判定标准为 NULL_ELEMENT_ARRAY 中的元素 */
|
||||
public boolean isNull(String key) {
|
||||
return valueIsNull(get(key));
|
||||
}
|
||||
|
||||
/** 指定key列表中是否包含value为null的元素,只要有一个为null,就会返回true */
|
||||
public boolean isContainNull(String ...keys) {
|
||||
for (String key : keys) {
|
||||
if(this.isNull(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 与isNull()相反 */
|
||||
public boolean isNotNull(String key) {
|
||||
return !isNull(key);
|
||||
}
|
||||
/** 指定key的value是否为null,作用同isNotNull() */
|
||||
public boolean has(String key) {
|
||||
return !isNull(key);
|
||||
}
|
||||
|
||||
/** 指定value在此SoMap的判断标准中是否为null */
|
||||
public boolean valueIsNull(Object value) {
|
||||
return NULL_ELEMENT_LIST.contains(value);
|
||||
}
|
||||
|
||||
/** 验证指定key不为空,为空则抛出异常 */
|
||||
public SoMap checkNull(String ...keys) {
|
||||
for (String key : keys) {
|
||||
if(this.isNull(key)) {
|
||||
throw new RuntimeException("参数" + key + "不能为空");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
static Pattern patternNumber = Pattern.compile("[0-9]*");
|
||||
/** 指定key是否为数字 */
|
||||
public boolean isNumber(String key) {
|
||||
String value = getString(key);
|
||||
if(value == null) {
|
||||
return false;
|
||||
}
|
||||
return patternNumber.matcher(value).matches();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 转为JSON字符串
|
||||
*/
|
||||
public String toJsonString() {
|
||||
try {
|
||||
// SoMap so = SoMap.getSoMap(this);
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 转为JSON字符串, 带格式的
|
||||
// */
|
||||
// public String toJsonFormatString() {
|
||||
// try {
|
||||
// return JSON.toJSONString(this, true);
|
||||
// } catch (Exception e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
// ============================= web辅助 =============================
|
||||
|
||||
|
||||
/**
|
||||
* 返回当前request请求的的所有参数
|
||||
* @return
|
||||
*/
|
||||
public static SoMap getRequestSoMap() {
|
||||
// 大善人SpringMVC提供的封装
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if(servletRequestAttributes == null) {
|
||||
throw new RuntimeException("当前线程非JavaWeb环境");
|
||||
}
|
||||
// 当前request
|
||||
HttpServletRequest request = servletRequestAttributes.getRequest();
|
||||
if (request.getAttribute("currentSoMap") == null || request.getAttribute("currentSoMap") instanceof SoMap == false ) {
|
||||
initRequestSoMap(request);
|
||||
}
|
||||
return (SoMap)request.getAttribute("currentSoMap");
|
||||
}
|
||||
|
||||
/** 初始化当前request的 SoMap */
|
||||
private static void initRequestSoMap(HttpServletRequest request) {
|
||||
SoMap soMap = new SoMap();
|
||||
Map<String, String[]> parameterMap = request.getParameterMap(); // 获取所有参数
|
||||
for (String key : parameterMap.keySet()) {
|
||||
try {
|
||||
String[] values = parameterMap.get(key); // 获得values
|
||||
if(values.length == 1) {
|
||||
soMap.set(key, values[0]);
|
||||
} else {
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (String v : values) {
|
||||
list.add(v);
|
||||
}
|
||||
soMap.set(key, list);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
request.setAttribute("currentSoMap", soMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证返回当前线程是否为JavaWeb环境
|
||||
* @return
|
||||
*/
|
||||
public static boolean isJavaWeb() {
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装
|
||||
if(servletRequestAttributes == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================= 常见key (以下key经常用,所以封装以下,方便写代码) =============================
|
||||
|
||||
/** get 当前页 */
|
||||
public int getKeyPageNo() {
|
||||
int pageNo = getInt("pageNo", 1);
|
||||
if(pageNo <= 0) {
|
||||
pageNo = 1;
|
||||
}
|
||||
return pageNo;
|
||||
}
|
||||
/** get 页大小 */
|
||||
public int getKeyPageSize() {
|
||||
int pageSize = getInt("pageSize", 10);
|
||||
if(pageSize <= 0 || pageSize > 1000) {
|
||||
pageSize = 10;
|
||||
}
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
/** get 排序方式 */
|
||||
public int getKeySortType() {
|
||||
return getInt("sortType");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================= 工具方法 =============================
|
||||
|
||||
|
||||
/**
|
||||
* 将一个一维集合转换为树形集合
|
||||
* @param list 集合
|
||||
* @param idKey id标识key
|
||||
* @param parentIdKey 父id标识key
|
||||
* @param childListKey 子节点标识key
|
||||
* @return 转换后的tree集合
|
||||
*/
|
||||
public static List<SoMap> listToTree(List<SoMap> list, String idKey, String parentIdKey, String childListKey) {
|
||||
// 声明新的集合,存储tree形数据
|
||||
List<SoMap> newTreeList = new ArrayList<SoMap>();
|
||||
// 声明hash-Map,方便查找数据
|
||||
SoMap hash = new SoMap();
|
||||
// 将数组转为Object的形式,key为数组中的id
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
SoMap json = (SoMap) list.get(i);
|
||||
hash.put(json.getString(idKey), json);
|
||||
}
|
||||
// 遍历结果集
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
// 单条记录
|
||||
SoMap aVal = (SoMap) list.get(j);
|
||||
// 在hash中取出key为单条记录中pid的值
|
||||
SoMap hashVp = (SoMap) hash.get(aVal.get(parentIdKey, "").toString());
|
||||
// 如果记录的pid存在,则说明它有父节点,将她添加到孩子节点的集合中
|
||||
if (hashVp != null) {
|
||||
// 检查是否有child属性,有则添加,没有则新建
|
||||
if (hashVp.get(childListKey) != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SoMap> ch = (List<SoMap>) hashVp.get(childListKey);
|
||||
ch.add(aVal);
|
||||
hashVp.put(childListKey, ch);
|
||||
} else {
|
||||
List<SoMap> ch = new ArrayList<SoMap>();
|
||||
ch.add(aVal);
|
||||
hashVp.put(childListKey, ch);
|
||||
}
|
||||
} else {
|
||||
newTreeList.add(aVal);
|
||||
}
|
||||
}
|
||||
return newTreeList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 指定字符串的字符串下划线转大写模式 */
|
||||
private static String wordEachBig(String str){
|
||||
String newStr = "";
|
||||
for (String s : str.split("_")) {
|
||||
newStr += wordFirstBig(s);
|
||||
}
|
||||
return newStr;
|
||||
}
|
||||
/** 返回下划线转小驼峰形式 */
|
||||
private static String wordEachBigFs(String str){
|
||||
return wordFirstSmall(wordEachBig(str));
|
||||
}
|
||||
|
||||
/** 将指定单词首字母大写 */
|
||||
private static String wordFirstBig(String str) {
|
||||
return str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
|
||||
}
|
||||
|
||||
/** 将指定单词首字母小写 */
|
||||
private static String wordFirstSmall(String str) {
|
||||
return str.substring(0, 1).toLowerCase() + str.substring(1, str.length());
|
||||
}
|
||||
|
||||
/** 下划线转中划线 */
|
||||
private static String wordEachKebabCase(String str) {
|
||||
return str.replaceAll("_", "-");
|
||||
}
|
||||
|
||||
/** 驼峰转下划线 */
|
||||
private static String wordHumpToLine(String str) {
|
||||
return str.replaceAll("[A-Z]", "_$0").toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
server:
|
||||
port: 8001
|
||||
|
||||
spring:
|
||||
# 静态文件路径映射
|
||||
resources:
|
||||
static-locations: classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/
|
||||
# static-locations: file:E:\work\project-yun\sa-token\sa-token-demo-oauth2\sa-token-demo-oauth2-server\src\main\resources\static\
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken-server
|
||||
|
||||
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
# password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 1000ms
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>服务提供方-确认授权页</title>
|
||||
<style type="text/css">
|
||||
*{margin: 0px; padding: 0px;}
|
||||
.login-box{width: 300px; margin: 50px auto; padding: 100px; border: 1px #000 solid;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-box">
|
||||
<h2>服务提供方-确认授权页</h2> <br>
|
||||
<div>
|
||||
<div>应用id: <span class="client_id"></span></div>
|
||||
<div>请求授权: <span class="scope"></span></div>
|
||||
<br>
|
||||
<div>是否同意授权: </div>
|
||||
<div>
|
||||
<button onclick="ok()">同意</button>
|
||||
<button onclick="no()">拒绝</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// 获取code详细信息
|
||||
var code = getParam("code", null);
|
||||
var codeObj = null;
|
||||
if(code == null) {
|
||||
throw alert("无效code");
|
||||
}
|
||||
$.ajax({
|
||||
url: '/oauth2/getCodeInfo',
|
||||
data: {code: code},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.data == null) {
|
||||
return alert('无效授权码');
|
||||
}
|
||||
codeObj = res.data;
|
||||
$(".client_id").html(res.data.clientId);
|
||||
$(".scope").html(res.data.scope);
|
||||
},
|
||||
error: function(e, ee, eee) {
|
||||
console.log('error');
|
||||
alert(eee);
|
||||
}
|
||||
});
|
||||
|
||||
// 确认授权
|
||||
function ok() {
|
||||
$.ajax({
|
||||
url: '/oauth2/confirm',
|
||||
data: {code: code},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.code == 200) {
|
||||
// 跳转
|
||||
location.href = codeObj.redirectUri;
|
||||
} else {
|
||||
alert(res.msg);
|
||||
}
|
||||
},
|
||||
error: function(e, ee, eee) {
|
||||
alert(eee);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 拒绝授权时,跳入拒绝授权地址
|
||||
function no() {
|
||||
// alert(codeObj.rejectUri)
|
||||
location.href = codeObj.rejectUri;
|
||||
}
|
||||
|
||||
// 从url中查询到指定名称的参数值
|
||||
function getParam(name, defaultValue){
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
for (var i=0;i<vars.length;i++) {
|
||||
var pair = vars[i].split("=");
|
||||
if(pair[0] == name){return pair[1];}
|
||||
}
|
||||
return(defaultValue == undefined ? null : defaultValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>服务提供方-登录页</title>
|
||||
<style type="text/css">
|
||||
*{margin: 0px; padding: 0px;}
|
||||
.login-box{width: 400px; margin: 50px auto;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-box">
|
||||
<h2>服务提供方-登录页</h2> <br>
|
||||
<div>注:您当前在服务提供方尚未登录,请先登录</div>
|
||||
<div>测试账号: test test</div> <br>
|
||||
账号:<input name="username" /> <br>
|
||||
密码:<input name="password" type="password" /> <br>
|
||||
<button onclick="doLogin()">登录</button>
|
||||
</div>
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// 登录方法
|
||||
function getLoginId() {
|
||||
|
||||
}
|
||||
|
||||
// 登录方法
|
||||
function doLogin() {
|
||||
console.log('-----------');
|
||||
$.ajax({
|
||||
url: '/doLogin',
|
||||
data: {
|
||||
username: $('[name=username]').val(),
|
||||
password: $('[name=password]').val()
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.code == 200) {
|
||||
alert('登录成功');
|
||||
location.reload(true);
|
||||
} else {
|
||||
alert('登录失败');
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
console.log('error');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,52 +0,0 @@
|
||||
<factorypath>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-web/2.0.0.RELEASE/spring-boot-starter-web-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter/2.0.0.RELEASE/spring-boot-starter-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot/2.0.0.RELEASE/spring-boot-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-autoconfigure/2.0.0.RELEASE/spring-boot-autoconfigure-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-logging/2.0.0.RELEASE/spring-boot-starter-logging-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-to-slf4j/2.10.0/log4j-to-slf4j-2.10.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-api/2.10.0/log4j-api-2.10.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-core/5.0.4.RELEASE/spring-core-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jcl/5.0.4.RELEASE/spring-jcl-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/yaml/snakeyaml/1.19/snakeyaml-1.19.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-json/2.0.0.RELEASE/spring-boot-starter-json-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.9.4/jackson-databind-2.9.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.9.0/jackson-annotations-2.9.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.9.4/jackson-core-2.9.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.9.4/jackson-datatype-jdk8-2.9.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.9.4/jackson-datatype-jsr310-2.9.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/module/jackson-module-parameter-names/2.9.4/jackson-module-parameter-names-2.9.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-tomcat/2.0.0.RELEASE/spring-boot-starter-tomcat-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-core/8.5.28/tomcat-embed-core-8.5.28.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-el/8.5.28/tomcat-embed-el-8.5.28.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-websocket/8.5.28/tomcat-embed-websocket-8.5.28.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/hibernate/validator/hibernate-validator/6.0.7.Final/hibernate-validator-6.0.7.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/jboss/logging/jboss-logging/3.3.2.Final/jboss-logging-3.3.2.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/classmate/1.3.4/classmate-1.3.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-web/5.0.4.RELEASE/spring-web-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-beans/5.0.4.RELEASE/spring-beans-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-webmvc/5.0.4.RELEASE/spring-webmvc-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context/5.0.4.RELEASE/spring-context-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-expression/5.0.4.RELEASE/spring-expression-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-aop/2.0.0.RELEASE/spring-boot-starter-aop-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-aop/5.0.4.RELEASE/spring-aop-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/aspectj/aspectjweaver/1.8.13/aspectjweaver-1.8.13.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-redis/1.4.7.RELEASE/spring-boot-starter-redis-1.4.7.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-redis/2.0.5.RELEASE/spring-data-redis-2.0.5.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-keyvalue/2.0.5.RELEASE/spring-data-keyvalue-2.0.5.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-commons/2.0.5.RELEASE/spring-data-commons-2.0.5.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-tx/5.0.4.RELEASE/spring-tx-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-oxm/5.0.4.RELEASE/spring-oxm-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context-support/5.0.4.RELEASE/spring-context-support-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/redis/clients/jedis/2.9.0/jedis-2.9.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-pool2/2.5.0/commons-pool2-2.5.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-configuration-processor/2.0.0.RELEASE/spring-boot-configuration-processor-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="PLUGIN" id="net.harawata.mybatipse" enabled="false" runInBatchMode="false"/>
|
||||
</factorypath>
|
||||
@@ -7,4 +7,6 @@ unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.idea/
|
||||
.idea/
|
||||
|
||||
.factorypath
|
||||
@@ -11,7 +11,13 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义sa-token版本号 -->
|
||||
<properties>
|
||||
<sa-token-version>1.17.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -25,19 +31,39 @@
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.7.0</version>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot整合redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-redis</artifactId>
|
||||
<version>RELEASE</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>
|
||||
<artifactId>sa-token-spring-aop</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
|
||||
@@ -5,12 +5,17 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
/**
|
||||
* sa-token整合SpringBoot 示例
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaTokenDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenDemoApplication.class, args);
|
||||
SpringApplication.run(SaTokenDemoApplication.class, args);
|
||||
System.out.println("\n启动成功:sa-token配置如下:" + SaTokenManager.getConfig());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
|
||||
|
||||
/**
|
||||
* 继承sa-token行为Bean默认实现, 重写部分逻辑
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class MySaTokenAction extends SaTokenActionDefaultImpl {
|
||||
|
||||
// 重写token生成策略
|
||||
// @Override
|
||||
// public String createToken(Object loginId, String loginKey) {
|
||||
// return SaTokenInsideUtil.getRandomString(60);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.interceptor.SaCheckInterceptor;
|
||||
|
||||
/**
|
||||
* sa-token代码方式进行配置
|
||||
*/
|
||||
@Configuration
|
||||
public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
|
||||
// 获取配置Bean (以代码的方式配置sa-token, 此配置会覆盖yml中的配置 )
|
||||
// @Primary
|
||||
// @Bean(name="MySaTokenConfig")
|
||||
public SaTokenConfig getSaTokenConfig() {
|
||||
SaTokenConfig config = new SaTokenConfig();
|
||||
config.setTokenName("satoken"); // token名称 (同时也是cookie名称)
|
||||
config.setTimeout(30 * 24 * 60 * 60); // token有效期,单位s 默认30天
|
||||
config.setIsShare(true); // 在多人登录同一账号时,是否共享会话 (为true时共用一个,为false时新登录挤掉旧登录)
|
||||
config.setIsReadBody(true); // 是否尝试从请求体里读取token
|
||||
config.setIsReadHead(true); // 是否尝试从header里读取token
|
||||
config.setIsReadCookie(true); // 是否尝试从cookie里读取token
|
||||
config.setTokenStyle("uuid"); // token风格
|
||||
config.setIsV(true); // 是否在初始化配置时打印版本字符画
|
||||
return config;
|
||||
}
|
||||
|
||||
// 注册sa-token的拦截器,打开注解式鉴权功能
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new SaCheckInterceptor()).addPathPatterns("/**"); // 全局拦截器
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
|
||||
|
||||
|
||||
/**
|
||||
* [Sa-Token 权限认证] 配置类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 注册sa-token的拦截器,打开注解式鉴权功能
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册注解拦截器
|
||||
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**").excludePathPatterns("");
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 注册 [sa-token全局过滤器]
|
||||
// */
|
||||
// @Bean
|
||||
// public SaServletFilter getSaReactorFilter() {
|
||||
// return new SaServletFilter()
|
||||
// // 指定 [拦截路由]
|
||||
// .addInclude("/**")
|
||||
// // 指定 [放行路由]
|
||||
// .addExclude("/favicon.ico")
|
||||
// // 指定[认证函数]: 每次请求执行
|
||||
// .setAuth(r -> {
|
||||
// System.out.println("---------- sa全局认证");
|
||||
// SaRouterUtil.match("/test/test", () -> StpUtil.checkLogin());
|
||||
// })
|
||||
// // 指定[异常处理函数]:每次[认证函数]发生异常时执行此函数
|
||||
// .setError(e -> {
|
||||
// System.out.println("---------- sa全局异常 ");
|
||||
// return AjaxJson.getError(e.getMessage());
|
||||
// })
|
||||
// ;
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* sa-token持久层的实现类 , 基于redis
|
||||
*/
|
||||
//@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token与redis的集成
|
||||
public class SaTokenDaoRedis implements SaTokenDao {
|
||||
|
||||
|
||||
// string专用
|
||||
@Autowired
|
||||
StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
// SaSession专用
|
||||
RedisTemplate<String, SaSession> redisTemplate;
|
||||
@Autowired
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void setRedisTemplate(RedisTemplate redisTemplate) {
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
|
||||
// 根据key获取value ,如果没有,则返回空
|
||||
@Override
|
||||
public String getValue(String key) {
|
||||
return stringRedisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
// 写入指定key-value键值对,并设定过期时间(单位:秒)
|
||||
@Override
|
||||
public void setValue(String key, String value, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
stringRedisTemplate.opsForValue().set(key, value);
|
||||
} else {
|
||||
stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新指定key-value键值对 (过期时间取原来的值)
|
||||
@Override
|
||||
public void updateValue(String key, String value) {
|
||||
long expire = getTimeout(key);
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { // -2 = 无此键
|
||||
return;
|
||||
}
|
||||
this.setValue(key, value, expire);
|
||||
}
|
||||
|
||||
// 删除一个指定的key
|
||||
@Override
|
||||
public void deleteKey(String key) {
|
||||
stringRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
// 获取指定key的剩余存活时间 (单位: 秒)
|
||||
@Override
|
||||
public long getTimeout(String key) {
|
||||
return stringRedisTemplate.getExpire(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 根据指定key的session,如果没有,则返回空
|
||||
@Override
|
||||
public SaSession getSession(String sessionId) {
|
||||
return redisTemplate.opsForValue().get(sessionId);
|
||||
}
|
||||
|
||||
// 将指定session持久化
|
||||
@Override
|
||||
public void saveSession(SaSession session, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
redisTemplate.opsForValue().set(session.getId(), session);
|
||||
} else {
|
||||
redisTemplate.opsForValue().set(session.getId(), session, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新指定session
|
||||
@Override
|
||||
public void updateSession(SaSession session) {
|
||||
long expire = getSessionTimeout(session.getId());
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { // -2 = 无此键
|
||||
return;
|
||||
}
|
||||
this.saveSession(session, expire);
|
||||
}
|
||||
|
||||
// 删除一个指定的session
|
||||
@Override
|
||||
public void deleteSession(String sessionId) {
|
||||
redisTemplate.delete(sessionId);
|
||||
}
|
||||
|
||||
// 获取指定SaSession的剩余存活时间 (单位: 秒)
|
||||
@Override
|
||||
public long getSessionTimeout(String sessionId) {
|
||||
return redisTemplate.getExpire(sessionId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -8,15 +8,18 @@ import org.springframework.stereotype.Component;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
/**
|
||||
* 自定义权限验证接口扩展
|
||||
* 自定义权限验证接口扩展
|
||||
*/
|
||||
@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
// 返回一个账号所拥有的权限码集合
|
||||
/**
|
||||
* 返回一个账号所拥有的权限码集合
|
||||
*/
|
||||
@Override
|
||||
public List<Object> getPermissionCodeList(Object login_id, String login_key) {
|
||||
List<Object> list = new ArrayList<Object>(); // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
|
||||
public List<String> getPermissionList(Object loginId, String loginKey) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("101");
|
||||
list.add("user-add");
|
||||
list.add("user-delete");
|
||||
@@ -26,4 +29,16 @@ public class StpInterfaceImpl implements StpInterface {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的角色标识集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginKey) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("admin");
|
||||
list.add("super-admin");
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
|
||||
/**
|
||||
* 一个默认的实现
|
||||
* @author kong
|
||||
*/
|
||||
public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
public static StpLogic stpLogic = new StpLogic("user");
|
||||
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
/**
|
||||
* 返回token名称
|
||||
* @return 此StpLogic的token名称
|
||||
*/
|
||||
public static String getTokenName() {
|
||||
return stpLogic.getTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前tokenValue
|
||||
* @return 当前tokenValue
|
||||
*/
|
||||
public static String getTokenValue() {
|
||||
return stpLogic.getTokenValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定id的tokenValue
|
||||
* @param loginId .
|
||||
* @return
|
||||
*/
|
||||
public static String getTokenValueByLoginId(Object loginId) {
|
||||
return stpLogic.getTokenValueByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前StpLogin的loginKey
|
||||
* @return 当前StpLogin的loginKey
|
||||
*/
|
||||
public static String getLoginKey(){
|
||||
return stpLogic.getLoginKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的token信息
|
||||
* @return token信息
|
||||
*/
|
||||
public static SaTokenInfo getTokenInfo() {
|
||||
return stpLogic.getTokenInfo();
|
||||
}
|
||||
|
||||
// =================== 登录相关操作 ===================
|
||||
|
||||
/**
|
||||
* 在当前会话上登录id
|
||||
* @param loginId 登录id ,建议的类型:(long | int | String)
|
||||
*/
|
||||
public static void setLoginId(Object loginId) {
|
||||
stpLogic.setLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话注销登录
|
||||
*/
|
||||
public static void logout() {
|
||||
stpLogic.logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定loginId的会话注销登录(清退下线)
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void logoutByLoginId(Object loginId) {
|
||||
stpLogic.logoutByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定loginId的会话注销登录(踢人下线)
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void kickoutByLoginId(Object loginId) {
|
||||
stpLogic.kickoutByLoginId(loginId);
|
||||
}
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
* 获取当前会话是否已经登录
|
||||
* @return 是否已登录
|
||||
*/
|
||||
public static boolean isLogin() {
|
||||
return stpLogic.isLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验当前会话是否已经登录,如未登录,则抛出异常
|
||||
*/
|
||||
public static void checkLogin() {
|
||||
getLoginId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 如果未登录,则抛出异常
|
||||
* @return .
|
||||
*/
|
||||
public static Object getLoginId() {
|
||||
return stpLogic.getLoginId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 如果未登录,则返回默认值
|
||||
* @param defaultValue .
|
||||
* @return .
|
||||
*/
|
||||
public static <T> T getLoginId(T defaultValue) {
|
||||
return stpLogic.getLoginId(defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 如果未登录,则返回null
|
||||
* @return
|
||||
*/
|
||||
public static Object getLoginIdDefaultNull() {
|
||||
return stpLogic.getLoginIdDefaultNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 并转换为String
|
||||
* @return
|
||||
*/
|
||||
public static String getLoginIdAsString() {
|
||||
return stpLogic.getLoginIdAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 并转换为int
|
||||
* @return
|
||||
*/
|
||||
public static int getLoginIdAsInt() {
|
||||
return stpLogic.getLoginIdAsInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 并转换为long
|
||||
* @return
|
||||
*/
|
||||
public static long getLoginIdAsLong() {
|
||||
return stpLogic.getLoginIdAsLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定token对应的登录id,如果未登录,则返回 null
|
||||
* @return .
|
||||
*/
|
||||
public static Object getLoginIdByToken(String tokenValue) {
|
||||
return stpLogic.getLoginIdByToken(tokenValue);
|
||||
}
|
||||
|
||||
|
||||
// =================== session相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定loginId的session, 如果没有,isCreate=是否新建并返回
|
||||
* @param loginId 登录id
|
||||
* @param isCreate 是否新建
|
||||
* @return SaSession
|
||||
*/
|
||||
public static SaSession getSessionByLoginId(Object loginId, boolean isCreate) {
|
||||
return stpLogic.getSessionByLoginId(loginId, isCreate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的session
|
||||
* @param loginId .
|
||||
* @return .
|
||||
*/
|
||||
public static SaSession getSessionByLoginId(Object loginId) {
|
||||
return stpLogic.getSessionByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的session
|
||||
* @return
|
||||
*/
|
||||
public static SaSession getSession() {
|
||||
return stpLogic.getSession();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== 过期时间相关 ===================
|
||||
|
||||
/**
|
||||
* 获取当前登录者的token剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public long getTimeout() {
|
||||
return stpLogic.getTokenTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的token剩余有效时间 (单位: 秒)
|
||||
* @param loginId 指定loginId
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public long getTimeoutByLoginId(Object loginId) {
|
||||
return stpLogic.getTokenTimeoutByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录者的Session剩余有效时间 (单位: 秒)
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public long getSessionTimeout() {
|
||||
return stpLogic.getSessionTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的Session剩余有效时间 (单位: 秒)
|
||||
* @param loginId 指定loginId
|
||||
* @return token剩余有效时间
|
||||
*/
|
||||
public long getSessionTimeoutByLoginId(Object loginId) {
|
||||
return stpLogic.getSessionTimeoutByLoginId(loginId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== 权限验证操作 ===================
|
||||
|
||||
/**
|
||||
* 指定loginId是否含有指定权限
|
||||
* @param loginId .
|
||||
* @param pcode .
|
||||
* @return .
|
||||
*/
|
||||
public static boolean hasPermission(Object loginId, Object pcode) {
|
||||
return stpLogic.hasPermission(loginId, pcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话是否含有指定权限
|
||||
* @param pcode .
|
||||
* @return .
|
||||
*/
|
||||
public static boolean hasPermission(Object pcode) {
|
||||
return stpLogic.hasPermission(pcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 , 没有就抛出异常
|
||||
* @param pcode .
|
||||
*/
|
||||
public static void checkPermission(Object pcode) {
|
||||
stpLogic.checkPermission(pcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 , 【指定多个,必须全都有】
|
||||
* @param pcodeArray .
|
||||
*/
|
||||
public static void checkPermissionAnd(Object... pcodeArray) {
|
||||
stpLogic.checkPermissionAnd(pcodeArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 , 【指定多个,有一个就可以了】
|
||||
* @param pcodeArray .
|
||||
*/
|
||||
public static void checkPermissionOr(Object... pcodeArray) {
|
||||
stpLogic.checkPermissionOr(pcodeArray);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user