Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 9ddc1eb7d0 | |||
| 34f1e1f2db | |||
| d9836f00ca | |||
| 5c684ac7f1 | |||
| ded6da5554 | |||
| 149b2e54c9 | |||
| aad953c6eb | |||
| 4f22be308e | |||
| 4962742988 | |||
| 1d9ed55438 | |||
| 4db058cdf0 | |||
| d776501c3d | |||
| 235e9afcd3 | |||
| 983af6691e |
+13
@@ -0,0 +1,13 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.factorypath
|
||||
/.factorypath
|
||||
|
||||
.idea/
|
||||
@@ -1,21 +1,201 @@
|
||||
MIT License
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
Copyright (c) 2020 www.uviewui.com
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
1. Definitions.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2011-2019 hubin.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150" style="margin-bottom: 10px;">
|
||||
<img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.5.1</h1>
|
||||
<h4 align="center">一个JavaWeb轻量级权限认证框架,功能全面,上手简单</h4>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.13.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.5.1-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.13.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token/stargazers"><img src="https://img.shields.io/github/stars/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/watchers"><img src="https://img.shields.io/github/watchers/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/network/members"><img src="https://img.shields.io/github/forks/click33/sa-token"></a>
|
||||
@@ -16,76 +16,198 @@
|
||||
---
|
||||
|
||||
|
||||
## 😘 在线资料
|
||||
- ##### ⚡ [官网首页: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、分布式Session等等难题,无一不有着各种优秀框架大行其道
|
||||
|
||||
然而当我们把视线放低,那些最基础的有如:登录认证、权限认证、Session会话等基础问题却一直被两大上古神兽 `Apache Shiro`、`Spring Security` 所把持
|
||||
|
||||
在此并非专门diss此两大框架,诚然两大框架背景强大,历史悠久,其生态也比较齐全。但是它们毕竟已经是十几年前的产物,那是一个还在用 `jsp` 写页面的时代,两大框架的很多功能都是为jsp那一套量身定做。
|
||||
|
||||
在前后台分离已成标配的今天,两大框架的很多设计理念已经比较滞后,已经不能和我们的项目进行无缝适配,很多功能点都需要进行二次封装,甚至找一大堆扩展插件才能集成,已经逐渐不太适合现代化项目的应用
|
||||
|
||||
所以,为什么不能有一个自底向上,从最基础的登录、权限做起,以业务需求为核心,做到开箱即用的轻量级权限认证框架?
|
||||
|
||||
秉承着这个目的,`sa-token` 诞生了!
|
||||
|
||||
|
||||
## Sa-Token 优点?
|
||||
|
||||
sa-token架构设计精简,不引入各种复杂的概念,如丝般顺滑的API调用,大量高级特性统统只需一行代码即可实现
|
||||
- 在sa-token之前,权限认证业务概念纷飞、代码复杂,在sa-token之后,权限认证将会变成:逻辑清晰,架构简单,人人可写
|
||||
- 在sa-token之前,你会在百度上频繁搜索: xx框架如何集成Redis?前后台分离下如何鉴权?踢人下线的原理是什么?在sa-token之后,你将轻松知道这一切的答案
|
||||
- 在sa-token之前,权限认证、分布式会话、单点登录、多账号认证,你需要找不同的框架,在sa-token之后,你只需要这一个框架就足够了
|
||||
|
||||
与其它权限认证框架相比,你将会从以下方面感受到 `sa-token` 的优势:
|
||||
1. **简单** :可零配置启动框架,真正的开箱即用,低成本上手
|
||||
2. **强大** :目前已集成几十项权限相关特性,涵盖了大部分业务场景的解决方案
|
||||
3. **易用** :如丝般顺滑的API调用,大量高级特性统统只需一行代码即可实现
|
||||
4. **高扩展** :几乎所有组件都提供了扩展接口,90%以上的逻辑都可以按需重写
|
||||
|
||||
有了sa-token,你所有的权限认证问题,都不再是问题!
|
||||
|
||||
|
||||
## Sa-Token 能做什么?
|
||||
- **登录验证** —— 轻松登录鉴权,并提供五种细分场景值
|
||||
- **权限验证** —— 适配RBAC权限模型,不同角色不同授权
|
||||
- **Session会话** —— 专业的数据缓存中心
|
||||
- **踢人下线** —— 将违规用户立刻清退下线
|
||||
- **持久层扩展** —— 可集成Redis、Memcached等专业缓存中间件,重启数据不丢失
|
||||
- **分布式会话** —— 提供jwt集成和共享数据中心两种分布式会话方案
|
||||
- **单点登录** —— 一处登录,处处通行
|
||||
- **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- **临时身份切换** —— 将会话身份临时切换为其它账号
|
||||
- **无Cookie模式** —— APP、小程序等前后台分离场景
|
||||
- **同端互斥登录** —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录
|
||||
- **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- **路由拦截式鉴权** —— 设定全局路由拦截,并排除指定路由
|
||||
- **自动续签** —— 提供两种token过期策略,灵活搭配使用,还可自动续签
|
||||
- **会话治理** —— 提供方便灵活的会话查询接口
|
||||
- **组件自动注入** —— 零配置与Spring等框架集成
|
||||
- **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
|
||||
|
||||
|
||||
## 代码示例
|
||||
|
||||
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众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档
|
||||
|
||||
|
||||
- **没有复杂的封装!不要任何的配置!先写入,后鉴权!只需这两行简单的调用,即可轻松完成系统登录鉴权!**
|
||||
## 迭代模式
|
||||
sa-token的功能提案主要来源于社区,这意味着人人都可以参与到sa-token的功能定制,决定框架的未来走向,
|
||||
如果你有好的想法,可以在issues提出或者加入群一起交流,对于社区的提出的功能要求,主要分为以下几类:
|
||||
- 对框架新增特性功能且比较简单,会在第一时间进行开发
|
||||
- 对框架新增特性功能但比较复杂,会延后几个版本制定相应的计划后进行开发
|
||||
- 与框架设计理念不太相符,或超出权限认证范畴,将会视需求人数决定是否开发
|
||||
|
||||
|
||||
## 🔥 框架设计思想
|
||||
与其它权限认证框架相比,`sa-token`尽力保证两点:
|
||||
- 上手简单:能自动化的配置全部自动化,不让你费脑子
|
||||
- 功能强大:能涵盖的功能全部涵盖,不让你用个框架还要自己给框架打各种补丁
|
||||
## 参与贡献
|
||||
众人拾柴火焰高,万丈高楼众人起!
|
||||
sa-token秉承着开放的思想,欢迎大家贡献代码,为框架添砖加瓦,对框架有卓越贡献者将会出现在贡献者名单里
|
||||
|
||||
|
||||
## 💦️️ 涵盖功能
|
||||
- ⚡ **登录验证** —— 轻松登录鉴权,并提供五种细分场景值
|
||||
- ⚡ **权限验证** —— 拦截违规调用,不同角色不同授权
|
||||
- ⚡ **自定义session会话** —— 专业的数据缓存中心
|
||||
- ⚡ **踢人下线** —— 将违规用户立刻清退下线
|
||||
- ⚡ **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- ⚡ **持久层扩展** —— 可集成redis、MongoDB等专业缓存中间件
|
||||
- ⚡ **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- ⚡ **无cookie模式** —— APP、小程序等前后台分离场景
|
||||
- ⚡ **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- ⚡ **组件自动注入** —— 零配置与Spring等框架集成
|
||||
- ⚡ **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
|
||||
|
||||
|
||||
## 🔨 贡献代码
|
||||
sa-token欢迎大家贡献代码,为框架添砖加瓦
|
||||
1. 在github上fork一份到自己的仓库
|
||||
1. 在gitee或者github上fork一份代码到自己的仓库
|
||||
2. clone自己的仓库到本地电脑
|
||||
3. 在本地电脑修改、commit、push
|
||||
4. 提交pr(点击:New Pull Request)
|
||||
5. 等待合并
|
||||
|
||||
|
||||
## 🌱 建议贡献的地方
|
||||
- 修复源码现有bug,或增加新的实用功能
|
||||
- 完善在线文档,或者修复现有错误之处
|
||||
- 更多demo示例:比如SSM版搭建步骤
|
||||
- 您可以参考项目issues与需求墙进行贡献
|
||||
- 如果更新实用功能,可在文档友情链接处留下自己的推广链接
|
||||
作者寄语:参与贡献不光只有提交代码一个选择,点一个star、提一个issues都是对开源项目的促进,
|
||||
如果框架帮助到了你,欢迎你把框架推荐给你的朋友、同事使用,为sa-token的推广做一份贡献
|
||||
|
||||
|
||||
## 🚀 友情链接
|
||||
[**[ okhttps ]** 一个轻量级http通信框架,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
## 建议贡献的地方
|
||||
目前框架的主要有以下部分需要大家一起参与贡献:
|
||||
- 核心代码:该部分需要开发者了解整个框架的架构,遵循已有代码规范进行bug修复或提交新功能
|
||||
- 文档部分:需要以清晰明了的语句书写文档,力求简单易读,授人以鱼同时更授人以渔
|
||||
- 社区建设:如果框架帮助到了您,希望您可以加入qq群参与交流,对不熟悉框架的新人进行排难解惑
|
||||
- 框架推广:一个优秀的开源项目不能仅靠闭门造车,它还需要一定的推广方案让更多的人一起参与到项目中
|
||||
- 其它部分:您可以参考项目issues与需求墙进行贡献
|
||||
|
||||
|
||||
## 😎 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) ,欢迎你的加入
|
||||
## 贡献者名单
|
||||
[省长](https://gitee.com/sz6)、
|
||||
[RockMan](https://gitee.com/njx33)、
|
||||
[click33](https://github.com/click33)、
|
||||
[AppleOfGray](https://gitee.com/appleOfGray)、
|
||||
[Auster](https://github.com/auster9021)、
|
||||
[ZhuBJ0510](https://gitee.com/zhubj0510)、
|
||||
[legg](https://gitee.com/legg321)、
|
||||
[xiaoshitou](https://gitee.com/smallstoneZ)、
|
||||
[zhangjiaxiaozhuo](https://gitee.com/zhangjiaxiaozhuo)、
|
||||
[离你多远](https://gitee.com/liniduoyuan)
|
||||
|
||||
|
||||
## 知乎专栏
|
||||
- [初识sa-token,一行代码搞定登录授权!](https://zhuanlan.zhihu.com/p/344106099)
|
||||
- [一个登录功能也能玩出这么多花样?sa-token带你轻松搞定多地登录、单地登录、同端互斥登录](https://zhuanlan.zhihu.com/p/344511415)
|
||||
- [浅谈踢人下线的设计思路!(附代码实现方案)](https://zhuanlan.zhihu.com/p/345844002)
|
||||
- 文章已在 [csdn](https://blog.csdn.net/shengzhang_/article/details/112593247)、
|
||||
[掘金](https://juejin.cn/post/6917250126650015751)、
|
||||
[开源中国](https://my.oschina.net/u/3503445/blog/4897816)、
|
||||
[博客园](https://www.cnblogs.com/shengzhang/p/14275558.html)、
|
||||
[知乎](https://zhuanlan.zhihu.com/p/344106099)
|
||||
等平台连载中...欢迎投稿
|
||||
|
||||
|
||||
## 使用sa-token的开源项目
|
||||
[**[ sa-plus]** 一个基于springboot架构的快速开发框架,内置代码生成器](https://gitee.com/sz6/sa-plus)
|
||||
|
||||
如果您的项目使用了sa-token,欢迎提交pr
|
||||
|
||||
|
||||
## 友情链接
|
||||
[**[ okhttps ]** 一个轻量级http通信框架,API设计无比优雅,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
|
||||
|
||||
## 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM)
|
||||
|
||||

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

|
||||
|
||||
<br>
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
<?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>
|
||||
|
||||
<!-- 基础信息 -->
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.13.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>
|
||||
|
||||
|
||||
<!-- 所有模块 -->
|
||||
<modules>
|
||||
<module>sa-token-core</module>
|
||||
<module>sa-token-spring-boot-starter</module>
|
||||
<module>sa-token-dao-redis</module>
|
||||
<module>sa-token-dao-redis-jackson</module>
|
||||
<module>sa-token-spring-aop</module>
|
||||
</modules>
|
||||
|
||||
<!-- 开源协议 apache 2.0 -->
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
<comments>A business-friendly OSS license</comments>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<!-- 一些属性 -->
|
||||
<properties>
|
||||
<sa-token-version>1.13.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>
|
||||
</properties>
|
||||
|
||||
<!-- 仓库信息 -->
|
||||
<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>
|
||||
</scm>
|
||||
|
||||
<!-- 作者信息 -->
|
||||
<developers>
|
||||
<developer>
|
||||
<name>shengzhang</name>
|
||||
<email>2393584716@qq.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
|
||||
<!-- 父仓库 -->
|
||||
<parent>
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>7</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 仓库依赖 -->
|
||||
<dependencies>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<!-- 项目构建 -->
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.2</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</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,28 @@
|
||||
<?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.13.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sa-token-core</name>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
<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>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,149 @@
|
||||
package cn.dev33.satoken;
|
||||
|
||||
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.cookie.SaTokenCookie;
|
||||
import cn.dev33.satoken.cookie.SaTokenCookieDefaultImpl;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl;
|
||||
import cn.dev33.satoken.servlet.SaTokenServlet;
|
||||
import cn.dev33.satoken.servlet.SaTokenServletDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* 管理sa-token所有接口对象
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenManager {
|
||||
|
||||
|
||||
/**
|
||||
* 配置文件 Bean
|
||||
*/
|
||||
private static SaTokenConfig config;
|
||||
public static SaTokenConfig getConfig() {
|
||||
if (config == null) {
|
||||
initConfig();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
public static void setConfig(SaTokenConfig config) {
|
||||
SaTokenManager.config = config;
|
||||
if(config.getIsV()) {
|
||||
SaTokenInsideUtil.printSaToken();
|
||||
}
|
||||
}
|
||||
public synchronized static void initConfig() {
|
||||
if (config == null) {
|
||||
setConfig(SaTokenConfigFactory.createConfig());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 持久化 Bean
|
||||
*/
|
||||
public static SaTokenDao saTokenDao;
|
||||
public static SaTokenDao getSaTokenDao() {
|
||||
if (saTokenDao == null) {
|
||||
initSaTokenDao();
|
||||
}
|
||||
return saTokenDao;
|
||||
}
|
||||
public static void setSaTokenDao(SaTokenDao saTokenDao) {
|
||||
if(SaTokenManager.saTokenDao != null && (SaTokenManager.saTokenDao instanceof SaTokenDaoDefaultImpl)) {
|
||||
((SaTokenDaoDefaultImpl)SaTokenManager.saTokenDao).endRefreshTimer();
|
||||
}
|
||||
SaTokenManager.saTokenDao = saTokenDao;
|
||||
}
|
||||
public synchronized static void initSaTokenDao() {
|
||||
if (saTokenDao == null) {
|
||||
setSaTokenDao(new SaTokenDaoDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限认证 Bean
|
||||
*/
|
||||
public static StpInterface stpInterface;
|
||||
public static StpInterface getStpInterface() {
|
||||
if (stpInterface == null) {
|
||||
initStpInterface();
|
||||
}
|
||||
return stpInterface;
|
||||
}
|
||||
public static void setStpInterface(StpInterface stpInterface) {
|
||||
SaTokenManager.stpInterface = stpInterface;
|
||||
}
|
||||
public synchronized static void initStpInterface() {
|
||||
if (stpInterface == null) {
|
||||
setStpInterface(new StpInterfaceDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 框架行为 Bean
|
||||
*/
|
||||
public static SaTokenAction saTokenAction;
|
||||
public static SaTokenAction getSaTokenAction() {
|
||||
if (saTokenAction == null) {
|
||||
initSaTokenAction();
|
||||
}
|
||||
return saTokenAction;
|
||||
}
|
||||
public static void setSaTokenAction(SaTokenAction saTokenAction) {
|
||||
SaTokenManager.saTokenAction = saTokenAction;
|
||||
}
|
||||
public synchronized static void initSaTokenAction() {
|
||||
if (saTokenAction == null) {
|
||||
setSaTokenAction(new SaTokenActionDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie操作 Bean
|
||||
*/
|
||||
public static SaTokenCookie saTokenCookie;
|
||||
public static SaTokenCookie getSaTokenCookie() {
|
||||
if (saTokenCookie == null) {
|
||||
initSaTokenCookie();
|
||||
}
|
||||
return saTokenCookie;
|
||||
}
|
||||
public static void setSaTokenCookie(SaTokenCookie saTokenCookie) {
|
||||
SaTokenManager.saTokenCookie = saTokenCookie;
|
||||
}
|
||||
public synchronized static void initSaTokenCookie() {
|
||||
if (saTokenCookie == null) {
|
||||
setSaTokenCookie(new SaTokenCookieDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Servlet操作 Bean
|
||||
*/
|
||||
public static SaTokenServlet saTokenServlet;
|
||||
public static SaTokenServlet getSaTokenServlet() {
|
||||
if (saTokenServlet == null) {
|
||||
initSaTokenServlet();
|
||||
}
|
||||
return saTokenServlet;
|
||||
}
|
||||
public static void setSaTokenServlet(SaTokenServlet saTokenServlet) {
|
||||
SaTokenManager.saTokenServlet = saTokenServlet;
|
||||
}
|
||||
public synchronized static void initSaTokenServlet() {
|
||||
if (saTokenServlet == null) {
|
||||
setSaTokenServlet(new SaTokenServletDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 对 SaTokenAction 接口的默认实现
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenActionDefaultImpl implements SaTokenAction {
|
||||
|
||||
|
||||
/**
|
||||
* 生成一个token
|
||||
*/
|
||||
@Override
|
||||
public String createToken(Object loginId, String loginKey) {
|
||||
// 根据配置的tokenStyle生成不同风格的token
|
||||
String tokenStyle = SaTokenManager.getConfig().getTokenStyle();
|
||||
// uuid
|
||||
if(SaTokenConsts.TOKEN_STYLE_UUID.equals(tokenStyle)) {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
// 简单uuid (不带下划线)
|
||||
if(SaTokenConsts.TOKEN_STYLE_SIMPLE_UUID.equals(tokenStyle)) {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
// 32位随机字符串
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_32.equals(tokenStyle)) {
|
||||
return SaTokenInsideUtil.getRandomString(32);
|
||||
}
|
||||
// 64位随机字符串
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_64.equals(tokenStyle)) {
|
||||
return SaTokenInsideUtil.getRandomString(64);
|
||||
}
|
||||
// 128位随机字符串
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_128.equals(tokenStyle)) {
|
||||
return SaTokenInsideUtil.getRandomString(128);
|
||||
}
|
||||
// tik风格 (2_14_16)
|
||||
if(SaTokenConsts.TOKEN_STYLE_RANDOM_TIK.equals(tokenStyle)) {
|
||||
return SaTokenInsideUtil.getRandomString(2) + "_" + SaTokenInsideUtil.getRandomString(14) + "_" + SaTokenInsideUtil.getRandomString(16) + "__";
|
||||
}
|
||||
// 默认,还是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,277 @@
|
||||
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;
|
||||
|
||||
/** 是否在初始化配置时打印版本字符画 */
|
||||
private Boolean isV = true;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return token名称 (同时也是cookie名称)
|
||||
*/
|
||||
public String getTokenName() {
|
||||
return tokenName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenName token名称 (同时也是cookie名称)
|
||||
*/
|
||||
public void setTokenName(String tokenName) {
|
||||
this.tokenName = tokenName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token的长久有效期(单位:秒) 默认30天, -1代表永久
|
||||
*/
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeout token的长久有效期(单位:秒) 默认30天, -1代表永久
|
||||
*/
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token临时有效期 [指定时间内无操作就视为token过期] (单位: 秒), 默认-1 代表不限制
|
||||
* (例如可以设置为1800代表30分钟内无操作就过期)
|
||||
*/
|
||||
public long getActivityTimeout() {
|
||||
return activityTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activityTimeout token临时有效期 [指定时间内无操作就视为token过期] (单位: 秒), 默认-1 代表不限制
|
||||
* (例如可以设置为1800代表30分钟内无操作就过期)
|
||||
*/
|
||||
public void setActivityTimeout(long activityTimeout) {
|
||||
this.activityTimeout = activityTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
*/
|
||||
public Boolean getAllowConcurrentLogin() {
|
||||
return allowConcurrentLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allowConcurrentLogin 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
*/
|
||||
public void setAllowConcurrentLogin(Boolean allowConcurrentLogin) {
|
||||
this.allowConcurrentLogin = allowConcurrentLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
*/
|
||||
public Boolean getIsShare() {
|
||||
return isShare;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isShare 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
*/
|
||||
public void setIsShare(Boolean isShare) {
|
||||
this.isShare = isShare;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否尝试从请求体里读取token
|
||||
*/
|
||||
public Boolean getIsReadBody() {
|
||||
return isReadBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isReadBody 是否尝试从请求体里读取token
|
||||
*/
|
||||
public void setIsReadBody(Boolean isReadBody) {
|
||||
this.isReadBody = isReadBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否尝试从header里读取token
|
||||
*/
|
||||
public Boolean getIsReadHead() {
|
||||
return isReadHead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isReadHead 是否尝试从header里读取token
|
||||
*/
|
||||
public void setIsReadHead(Boolean isReadHead) {
|
||||
this.isReadHead = isReadHead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否尝试从cookie里读取token
|
||||
*/
|
||||
public Boolean getIsReadCookie() {
|
||||
return isReadCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isReadCookie 是否尝试从cookie里读取token
|
||||
*/
|
||||
public void setIsReadCookie(Boolean isReadCookie) {
|
||||
this.isReadCookie = isReadCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
*/
|
||||
public void setTokenStyle(String tokenStyle) {
|
||||
this.tokenStyle = tokenStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 默认dao层实现类中,每次清理过期数据间隔的时间 (单位: 秒) ,默认值30秒,设置为-1代表不启动定时清理
|
||||
*/
|
||||
public int getDataRefreshPeriod() {
|
||||
return dataRefreshPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dataRefreshPeriod 默认dao层实现类中,每次清理过期数据间隔的时间 (单位: 秒)
|
||||
* ,默认值30秒,设置为-1代表不启动定时清理
|
||||
*/
|
||||
public void setDataRefreshPeriod(int dataRefreshPeriod) {
|
||||
this.dataRefreshPeriod = dataRefreshPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取[token专属session]时是否必须登录 (如果配置为true,会在每次获取[token-session]时校验是否登录)
|
||||
*/
|
||||
public Boolean getTokenSessionCheckLogin() {
|
||||
return tokenSessionCheckLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenSessionCheckLogin 获取[token专属session]时是否必须登录
|
||||
* (如果配置为true,会在每次获取[token-session]时校验是否登录)
|
||||
*/
|
||||
public void setTokenSessionCheckLogin(Boolean tokenSessionCheckLogin) {
|
||||
this.tokenSessionCheckLogin = tokenSessionCheckLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否打开了自动续签 (如果此值为true, 框架会在每次直接或间接调用getLoginId()时进行一次过期检查与续签操作)
|
||||
*/
|
||||
public Boolean getAutoRenew() {
|
||||
return autoRenew;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param autoRenew 是否打开自动续签 (如果此值为true, 框架会在每次直接或间接调用getLoginId()时进行一次过期检查与续签操作)
|
||||
*/
|
||||
public void setAutoRenew(Boolean autoRenew) {
|
||||
this.autoRenew = autoRenew;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景
|
||||
*/
|
||||
public String getCookieDomain() {
|
||||
return cookieDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cookieDomain 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景
|
||||
*/
|
||||
public void setCookieDomain(String cookieDomain) {
|
||||
this.cookieDomain = cookieDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否在初始化配置时打印版本字符画
|
||||
*/
|
||||
public Boolean getIsV() {
|
||||
return isV;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isV 是否在初始化配置时打印版本字符画
|
||||
*/
|
||||
public void setIsV(Boolean isV) {
|
||||
this.isV = isV;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 + ", isV="
|
||||
+ isV + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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,55 @@
|
||||
package cn.dev33.satoken.cookie;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* sa-token 对cookie的相关操作 接口类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaTokenCookie {
|
||||
|
||||
/**
|
||||
* 在request对象中获取指定Cookie
|
||||
*
|
||||
* @param request request对象
|
||||
* @param cookieName Cookie名称
|
||||
* @return 查找到的Cookie对象
|
||||
*/
|
||||
public Cookie getCookie(HttpServletRequest request, String cookieName);
|
||||
|
||||
/**
|
||||
* 添加Cookie
|
||||
*
|
||||
* @param response response对象
|
||||
* @param name Cookie名称
|
||||
* @param value Cookie值
|
||||
* @param path Cookie路径
|
||||
* @param domain Cookie的作用域
|
||||
* @param timeout 过期时间 (秒)
|
||||
*/
|
||||
public void addCookie(HttpServletResponse response, String name, String value, String path, String domain, int timeout);
|
||||
|
||||
/**
|
||||
* 删除Cookie
|
||||
*
|
||||
* @param request request对象
|
||||
* @param response response对象
|
||||
* @param name Cookie名称
|
||||
*/
|
||||
public void delCookie(HttpServletRequest request, HttpServletResponse response, String name);
|
||||
|
||||
/**
|
||||
* 修改Cookie的value值
|
||||
*
|
||||
* @param request request对象
|
||||
* @param response response对象
|
||||
* @param name Cookie名称
|
||||
* @param value Cookie值
|
||||
*/
|
||||
public void updateCookie(HttpServletRequest request, HttpServletResponse response, String name, String value);
|
||||
|
||||
}
|
||||
+16
-13
@@ -4,41 +4,44 @@ import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import cn.dev33.satoken.util.SaCookieUtil;
|
||||
|
||||
/**
|
||||
* sa-token 对cookie的相关操作 接口实现类
|
||||
* sa-token 对cookie的相关操作 接口实现类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaCookieOperDefaultImpl implements SaCookieOper {
|
||||
public class SaTokenCookieDefaultImpl implements SaTokenCookie {
|
||||
|
||||
/**
|
||||
* 获取指定cookie
|
||||
* 获取指定cookie
|
||||
*/
|
||||
@Override
|
||||
public Cookie getCookie(HttpServletRequest request, String cookieName) {
|
||||
return SaCookieUtil.getCookie(request, cookieName);
|
||||
return SaTokenCookieUtil.getCookie(request, cookieName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加cookie
|
||||
* 添加cookie
|
||||
*/
|
||||
public void addCookie(HttpServletResponse response, String name, String value, String path, int timeout) {
|
||||
SaCookieUtil.addCookie(response, name, value, path, timeout);
|
||||
@Override
|
||||
public void addCookie(HttpServletResponse response, String name, String value, String path, String domain, int timeout) {
|
||||
SaTokenCookieUtil.addCookie(response, name, value, path, domain, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除cookie
|
||||
* 删除cookie
|
||||
*/
|
||||
@Override
|
||||
public void delCookie(HttpServletRequest request, HttpServletResponse response, String name) {
|
||||
SaCookieUtil.delCookie(request, response, name);
|
||||
SaTokenCookieUtil.delCookie(request, response, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改cookie的value值
|
||||
* 修改cookie的value值
|
||||
*/
|
||||
@Override
|
||||
public void updateCookie(HttpServletRequest request, HttpServletResponse response, String name, String value) {
|
||||
SaCookieUtil.updateCookie(request, response, name, value);
|
||||
SaTokenCookieUtil.updateCookie(request, response, name, value);
|
||||
}
|
||||
|
||||
}
|
||||
+32
-27
@@ -1,23 +1,24 @@
|
||||
package cn.dev33.satoken.util;
|
||||
package cn.dev33.satoken.cookie;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* cookie操作工具类
|
||||
* Cookie操作工具类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaCookieUtil {
|
||||
public class SaTokenCookieUtil {
|
||||
|
||||
/**
|
||||
* 获取指定cookie .
|
||||
* 在request对象中获取指定Cookie
|
||||
*
|
||||
* @param request .
|
||||
* @param cookieName .
|
||||
* @return .
|
||||
* @param request request对象
|
||||
* @param cookieName Cookie名称
|
||||
* @return 查找到的Cookie对象
|
||||
*/
|
||||
public static Cookie getCookie(HttpServletRequest request, String cookieName) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
@@ -32,37 +33,41 @@ public class SaCookieUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加cookie
|
||||
* 添加cookie
|
||||
*
|
||||
* @param response .
|
||||
* @param name .
|
||||
* @param value .
|
||||
* @param path .
|
||||
* @param timeout .
|
||||
* @param response response
|
||||
* @param name Cookie名称
|
||||
* @param value Cookie值
|
||||
* @param path Cookie写入路径
|
||||
* @param domain Cookie的作用域
|
||||
* @param timeout Cookie有效期 (秒)
|
||||
*/
|
||||
public static void addCookie(HttpServletResponse response, String name, String value, String path, int timeout) {
|
||||
public static void addCookie(HttpServletResponse response, String name, String value, String path, String domain, int timeout) {
|
||||
Cookie cookie = new Cookie(name, value);
|
||||
if (path == null) {
|
||||
if(SaTokenInsideUtil.isEmpty(path) == false) {
|
||||
path = "/";
|
||||
}
|
||||
if(SaTokenInsideUtil.isEmpty(domain) == false) {
|
||||
cookie.setDomain(domain);
|
||||
}
|
||||
cookie.setPath(path);
|
||||
cookie.setMaxAge(timeout);
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除cookie .
|
||||
* 删除Cookie
|
||||
*
|
||||
* @param request .
|
||||
* @param response .
|
||||
* @param name .
|
||||
* @param request request对象
|
||||
* @param response response对象
|
||||
* @param name Cookie名称
|
||||
*/
|
||||
public static void delCookie(HttpServletRequest request, HttpServletResponse response, String name) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie != null && (name).equals(cookie.getName())) {
|
||||
addCookie(response, name, null, null, 0);
|
||||
addCookie(response, name, null, null, null, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -70,12 +75,12 @@ public class SaCookieUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改cookie的value值
|
||||
* 修改cookie的value值
|
||||
*
|
||||
* @param request .
|
||||
* @param response .
|
||||
* @param name .
|
||||
* @param value .
|
||||
* @param request request对象
|
||||
* @param response response对象
|
||||
* @param name Cookie名称
|
||||
* @param value Cookie值
|
||||
*/
|
||||
public static void updateCookie(HttpServletRequest request, HttpServletResponse response, String name,
|
||||
String value) {
|
||||
@@ -83,7 +88,7 @@ public class SaCookieUtil {
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie != null && (name).equals(cookie.getName())) {
|
||||
addCookie(response, name, value, cookie.getPath(), cookie.getMaxAge());
|
||||
addCookie(response, name, value, cookie.getPath(), cookie.getDomain(), cookie.getMaxAge());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
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 = -1L;
|
||||
|
||||
/** 常量,表示系统中不存在这个缓存 (在对不存在的key获取剩余存活时间时返回此值) */
|
||||
public static final Long NOT_VALUE_EXPIRE = -2L;
|
||||
|
||||
|
||||
// --------------------- token相关 ---------------------
|
||||
|
||||
/**
|
||||
* 根据key获取value,如果没有,则返回空
|
||||
* @param key 键名称
|
||||
* @return value
|
||||
*/
|
||||
public String getValue(String key);
|
||||
|
||||
/**
|
||||
* 写入指定key-value键值对,并设定过期时间 (单位: 秒)
|
||||
* @param key 键名称
|
||||
* @param value 值
|
||||
* @param timeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void setValue(String key, String value, long timeout);
|
||||
|
||||
/**
|
||||
* 修改指定key-value键值对 (过期时间取原来的值)
|
||||
* @param key 键名称
|
||||
* @param value 值
|
||||
*/
|
||||
public void updateValue(String key, String value);
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
* @param key 键名称
|
||||
*/
|
||||
public void deleteKey(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);
|
||||
|
||||
|
||||
// --------------------- Session相关 ---------------------
|
||||
|
||||
/**
|
||||
* 根据指定key的Session,如果没有,则返回空
|
||||
* @param sessionId 键名称
|
||||
* @return SaSession
|
||||
*/
|
||||
public SaSession getSession(String sessionId);
|
||||
|
||||
/**
|
||||
* 将指定Session持久化
|
||||
* @param session 要保存的session对象
|
||||
* @param timeout 过期时间 (单位: 秒)
|
||||
*/
|
||||
public void saveSession(SaSession session, long timeout);
|
||||
|
||||
/**
|
||||
* 更新指定session
|
||||
* @param session 要更新的session对象
|
||||
*/
|
||||
public void updateSession(SaSession session);
|
||||
|
||||
/**
|
||||
* 删除一个指定的session
|
||||
* @param sessionId sessionId
|
||||
*/
|
||||
public void deleteSession(String sessionId);
|
||||
|
||||
/**
|
||||
* 获取指定SaSession的剩余存活时间 (单位: 秒)
|
||||
* @param sessionId 指定SaSession
|
||||
* @return 这个SaSession的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
public long getSessionTimeout(String sessionId);
|
||||
|
||||
/**
|
||||
* 修改指定SaSession的剩余存活时间 (单位: 秒)
|
||||
* @param sessionId sessionId
|
||||
* @param timeout 过期时间
|
||||
*/
|
||||
public void updateSessionTimeout(String sessionId, long 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,224 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaTaskUtil;
|
||||
import cn.dev33.satoken.util.SaTaskUtil.FunctionRunClass;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token持久层默认的实现类 , 基于内存Map
|
||||
* @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() {
|
||||
initRefreshTimer();
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ String 读写操作
|
||||
|
||||
@Override
|
||||
public String getValue(String key) {
|
||||
clearKeyByTimeout(key);
|
||||
return (String)dataMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(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 updateValue(String key, String value) {
|
||||
if(getKeyTimeout(key) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
dataMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteKey(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);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ Session 读写操作
|
||||
|
||||
@Override
|
||||
public SaSession getSession(String sessionId) {
|
||||
clearKeyByTimeout(sessionId);
|
||||
return (SaSession)dataMap.get(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveSession(SaSession session, long timeout) {
|
||||
dataMap.put(session.getId(), session);
|
||||
expireMap.put(session.getId(), (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSession(SaSession session) {
|
||||
if(getKeyTimeout(session.getId()) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
// 无动作
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSession(String sessionId) {
|
||||
dataMap.remove(sessionId);
|
||||
expireMap.remove(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSessionTimeout(String sessionId) {
|
||||
return getKeyTimeout(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSessionTimeout(String sessionId, long timeout) {
|
||||
expireMap.put(sessionId, System.currentTimeMillis() + timeout * 1000);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ 过期时间相关操作
|
||||
|
||||
/**
|
||||
* 如果指定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 Timer refreshTimer;
|
||||
|
||||
/**
|
||||
* 清理所有已经过期的key
|
||||
*/
|
||||
public void refreshDataMap() {
|
||||
Iterator<String> keys = expireMap.keySet().iterator();
|
||||
while (keys.hasNext()) {
|
||||
clearKeyByTimeout(keys.next());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化定时任务
|
||||
*/
|
||||
public void initRefreshTimer() {
|
||||
// 如果已经被初始化过了, 则停止它
|
||||
if(this.refreshTimer != null) {
|
||||
this.endRefreshTimer();
|
||||
}
|
||||
|
||||
// 开始新的定时任务
|
||||
if(SaTokenManager.getConfig().getDataRefreshPeriod() < 0) {
|
||||
return;
|
||||
}
|
||||
int period = SaTokenManager.getConfig().getDataRefreshPeriod() * 1000;
|
||||
this.refreshTimer = SaTaskUtil.setInterval(new FunctionRunClass() {
|
||||
@Override
|
||||
public void run() {
|
||||
refreshDataMap();
|
||||
}
|
||||
}, period, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束定时任务
|
||||
*/
|
||||
public void endRefreshTimer() {
|
||||
this.refreshTimer.cancel();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// --------------------- 会话管理
|
||||
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size) {
|
||||
return SaTokenInsideUtil.searchList(expireMap.keySet(), prefix, keyword, start, size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+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,35 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据变量决定是否执行此函数
|
||||
*
|
||||
* @param function 函数
|
||||
*/
|
||||
public void exe(SaFunction function) {
|
||||
if (isRun) {
|
||||
function.run();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.dev33.satoken.fun;
|
||||
|
||||
/**
|
||||
* 设定一个函数,方便在Lambda表达式下的函数式编程
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaFunction {
|
||||
|
||||
/**
|
||||
* 执行的方法
|
||||
*/
|
||||
public void run();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.dev33.satoken.servlet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Servlet相关操作接口
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaTokenServlet {
|
||||
|
||||
/**
|
||||
* 获取当前请求的 Request 对象
|
||||
*
|
||||
* @return 当前请求的Request对象
|
||||
*/
|
||||
public HttpServletRequest getRequest();
|
||||
|
||||
/**
|
||||
* 获取当前请求的 Response 对象
|
||||
*
|
||||
* @return 当前请求的response对象
|
||||
*/
|
||||
public HttpServletResponse getResponse();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.dev33.satoken.servlet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
/**
|
||||
* sa-token 对SaTokenServlet接口默认实现类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenServletDefaultImpl implements SaTokenServlet {
|
||||
|
||||
/**
|
||||
* 获取当前请求的Request对象
|
||||
*/
|
||||
@Override
|
||||
public HttpServletRequest getRequest() {
|
||||
throw new SaTokenException("请实现SaTokenServlet接口后进行Servlet相关操作");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的Response对象
|
||||
*/
|
||||
@Override
|
||||
public HttpServletResponse getResponse() {
|
||||
throw new SaTokenException("请实现SaTokenServlet接口后进行Servlet相关操作");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
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 Map<String, Object> dataMap = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
// ----------------------- 构建相关
|
||||
|
||||
/**
|
||||
* 构建一个Session对象
|
||||
*/
|
||||
public SaSession() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个Session对象
|
||||
* @param id Session的id
|
||||
*/
|
||||
public SaSession(String id) {
|
||||
this.id = id;
|
||||
this.createTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此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 List<TokenSign> tokenSignList = new Vector<TokenSign>();
|
||||
|
||||
/**
|
||||
* 返回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.keySet().contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前session会话所有key
|
||||
*
|
||||
* @return 所有值的key列表
|
||||
*/
|
||||
public Set<String> attributeKeys() {
|
||||
return dataMap.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据挂载集合(如果更新map里的值,请调用session.update()方法避免产生脏数据 )
|
||||
*
|
||||
* @return 返回底层储存值的map对象
|
||||
*/
|
||||
public Map<String, Object> getDataMap() {
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
// ----------------------- 一些操作
|
||||
|
||||
/**
|
||||
* 将这个Session从持久库更新一下
|
||||
*/
|
||||
public void update() {
|
||||
SaTokenManager.getSaTokenDao().updateSession(this);
|
||||
}
|
||||
|
||||
/** 注销会话 (注销后,此session会话将不再存储服务器上) */
|
||||
public void logout() {
|
||||
SaTokenManager.getSaTokenDao().deleteSession(this.id);
|
||||
}
|
||||
|
||||
/** 当Session上的tokenSign数量为零时,注销会话 */
|
||||
public void logoutByTokenSignCountToZero() {
|
||||
if (tokenSignList.size() == 0) {
|
||||
logout();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 getSessionKey(String sessionId) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + sessionKey + ":session:" + sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证指定key的Session是否存在
|
||||
*
|
||||
* @param sessionId session的id
|
||||
* @return 是否存在
|
||||
*/
|
||||
public boolean isExists(String sessionId) {
|
||||
return SaTokenManager.getSaTokenDao().getSession(getSessionKey(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(getSessionKey(sessionId));
|
||||
if (session == null && isCreate) {
|
||||
session = SaTokenManager.getSaTokenAction().createSession(sessionId);
|
||||
SaTokenManager.getSaTokenDao().saveSession(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(getSessionKey(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,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,526 @@
|
||||
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
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话注销登录
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
* 获取当前会话是否已经登录
|
||||
* @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,60 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* 任务调度Util
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTaskUtil {
|
||||
|
||||
/**
|
||||
* 延时指定毫秒执行一个函数
|
||||
* @param fc 要执行的函数
|
||||
* @param delay 延时的毫秒数
|
||||
* @return timer任务对象
|
||||
*/
|
||||
public static Timer setTimeout(FunctionRunClass fc, int delay) {
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
fc.run();
|
||||
timer.cancel();
|
||||
}
|
||||
}, delay);
|
||||
return timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 延时delay毫秒,每隔period毫秒执行一个函数
|
||||
* @param fc 要执行的函数
|
||||
* @param delay 延时的毫秒数
|
||||
* @param period 每隔多少毫秒执行一次
|
||||
* @return timer任务对象
|
||||
*/
|
||||
public static Timer setInterval(FunctionRunClass fc, int delay, int period) {
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
fc.run();
|
||||
}
|
||||
}, delay, period);
|
||||
return timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装一个内部类,便于操作
|
||||
* @author kong
|
||||
*/
|
||||
public static interface FunctionRunClass{
|
||||
/**
|
||||
* 要执行的方法
|
||||
*/
|
||||
public void run();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
/**
|
||||
* sa-token常量类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenConsts {
|
||||
|
||||
|
||||
// =================== sa-token版本信息 ===================
|
||||
|
||||
/**
|
||||
* sa-token 版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.13.0";
|
||||
|
||||
/**
|
||||
* sa-token 开源地址
|
||||
*/
|
||||
public static final String GITHUB_URL = "https://github.com/click33/sa-token";
|
||||
|
||||
|
||||
// =================== 常量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_RANDOM_TIK = "tik";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* sa-token 内部代码工具类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenInsideUtil {
|
||||
|
||||
/**
|
||||
* 打印 sa-token 版本字符画
|
||||
*/
|
||||
public static void printSaToken() {
|
||||
String str = "____ ____ ___ ____ _ _ ____ _ _ \r\n" + "[__ |__| __ | | | |_/ |___ |\\ | \r\n"
|
||||
+ "___] | | | |__| | \\_ |___ | \\| \r\n" + "sa-token:" + 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 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,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.13.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-spring-boot-starter</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>
|
||||
+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.session.SaSession;
|
||||
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;
|
||||
|
||||
/**
|
||||
* SaSession专用
|
||||
*/
|
||||
public RedisTemplate<String, SaSession> sessionRedisTemplate;
|
||||
@Autowired
|
||||
public void setSessionRedisTemplate(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, SaSession> template = new RedisTemplate<String, SaSession>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(keySerializer);
|
||||
template.setHashKeySerializer(keySerializer);
|
||||
template.setValueSerializer(valueSerializer);
|
||||
template.setHashValueSerializer(valueSerializer);
|
||||
template.afterPropertiesSet();
|
||||
if(this.sessionRedisTemplate == null) {
|
||||
this.sessionRedisTemplate = template;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据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);
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.setValue(key, value, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
*/
|
||||
@Override
|
||||
public void deleteKey(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.setValue(key, this.getValue(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据指定key的Session,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public SaSession getSession(String sessionId) {
|
||||
return sessionRedisTemplate.opsForValue().get(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定Session持久化
|
||||
*/
|
||||
@Override
|
||||
public void saveSession(SaSession session, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
sessionRedisTemplate.opsForValue().set(session.getId(), session);
|
||||
} else {
|
||||
sessionRedisTemplate.opsForValue().set(session.getId(), session, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定session
|
||||
*/
|
||||
@Override
|
||||
public void updateSession(SaSession session) {
|
||||
long expire = getSessionTimeout(session.getId());
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.saveSession(session, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的session
|
||||
*/
|
||||
@Override
|
||||
public void deleteSession(String sessionId) {
|
||||
sessionRedisTemplate.delete(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定SaSession的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public long getSessionTimeout(String sessionId) {
|
||||
return sessionRedisTemplate.getExpire(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定SaSession的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateSessionTimeout(String sessionId, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getSessionTimeout(sessionId);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.saveSession(this.getSession(sessionId), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
sessionRedisTemplate.expire(sessionId, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
*/
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size) {
|
||||
Set<String> keys = stringRedisTemplate.keys(prefix + "*" + keyword + "*");
|
||||
List<String> list = new ArrayList<String>(keys);
|
||||
return SaTokenInsideUtil.searchList(list, start, size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.dao.SaTokenDaoRedisJackson
|
||||
@@ -0,0 +1,12 @@
|
||||
target/
|
||||
|
||||
node_modules/
|
||||
bin/
|
||||
.settings/
|
||||
unpackage/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.factorypath
|
||||
|
||||
.idea/
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-parent</artifactId>
|
||||
<version>1.13.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-spring-boot-starter</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,210 @@
|
||||
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.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token持久层的实现类, 基于redis
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class SaTokenDaoRedis implements SaTokenDao {
|
||||
|
||||
/**
|
||||
* string专用
|
||||
*/
|
||||
@Autowired
|
||||
public StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* SaSession专用
|
||||
*/
|
||||
public RedisTemplate<String, SaSession> sessionRedisTemplate;
|
||||
@Autowired
|
||||
public void setSessionRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
// 指定相应的序列化方案
|
||||
StringRedisSerializer keySerializer = new StringRedisSerializer();
|
||||
JdkSerializationRedisSerializer valueSerializer = new JdkSerializationRedisSerializer();
|
||||
// 构建RedisTemplate
|
||||
RedisTemplate<String, SaSession> template = new RedisTemplate<String, SaSession>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(keySerializer);
|
||||
template.setHashKeySerializer(keySerializer);
|
||||
template.setValueSerializer(valueSerializer);
|
||||
template.setHashValueSerializer(valueSerializer);
|
||||
template.afterPropertiesSet();
|
||||
if(this.sessionRedisTemplate == null) {
|
||||
this.sessionRedisTemplate = template;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据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);
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.setValue(key, value, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
*/
|
||||
@Override
|
||||
public void deleteKey(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.setValue(key, this.getValue(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据指定key的Session,如果没有,则返回空
|
||||
*/
|
||||
@Override
|
||||
public SaSession getSession(String sessionId) {
|
||||
return sessionRedisTemplate.opsForValue().get(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定Session持久化
|
||||
*/
|
||||
@Override
|
||||
public void saveSession(SaSession session, long timeout) {
|
||||
// 判断是否为永不过期
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
sessionRedisTemplate.opsForValue().set(session.getId(), session);
|
||||
} else {
|
||||
sessionRedisTemplate.opsForValue().set(session.getId(), session, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定session
|
||||
*/
|
||||
@Override
|
||||
public void updateSession(SaSession session) {
|
||||
long expire = getSessionTimeout(session.getId());
|
||||
// -2 = 无此键
|
||||
if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.saveSession(session, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定的session
|
||||
*/
|
||||
@Override
|
||||
public void deleteSession(String sessionId) {
|
||||
sessionRedisTemplate.delete(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定SaSession的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public long getSessionTimeout(String sessionId) {
|
||||
return sessionRedisTemplate.getExpire(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改指定SaSession的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateSessionTimeout(String sessionId, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if(timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getSessionTimeout(sessionId);
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.saveSession(this.getSession(sessionId), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
sessionRedisTemplate.expire(sessionId, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
*/
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size) {
|
||||
Set<String> keys = stringRedisTemplate.keys(prefix + "*" + keyword + "*");
|
||||
List<String> list = new ArrayList<String>(keys);
|
||||
return SaTokenInsideUtil.searchList(list, start, size);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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.13.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 javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
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.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("79e7c69681b8270162386e6daa53d1d3")) {
|
||||
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, String device) {
|
||||
// ------ 1、获取相应对象
|
||||
HttpServletRequest request = SaTokenManager.getSaTokenServlet().getRequest();
|
||||
SaTokenConfig config = getConfig();
|
||||
// ------ 2、生成一个token
|
||||
String tokenValue = createTokenValue(loginId);
|
||||
request.setAttribute(getKeyJustCreatedSave(), tokenValue); // 将token保存到本次request里
|
||||
if(config.getIsReadCookie() == true){ // cookie注入
|
||||
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), 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.getSaTokenCookie().delCookie(SaTokenManager.getSaTokenServlet().getRequest(), SaTokenManager.getSaTokenServlet().getResponse(), 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
|
||||
|
||||
|
||||
@@ -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.13.0</sa-token-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -25,26 +31,54 @@
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 开发测试 -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dev</artifactId>
|
||||
<version>1.5.1</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot整合redis -->
|
||||
<!-- 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-starter-redis</artifactId>
|
||||
<version>RELEASE</version>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool工具类,用来生成雪花算法唯一id -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.5.4</version>
|
||||
</dependency> -->
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.pj;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 跨域过滤器
|
||||
* @author kong
|
||||
*/
|
||||
@Component
|
||||
public class CorsFilter implements Filter {
|
||||
|
||||
static final String OPTIONS = "OPTIONS";
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
HttpServletResponse response = (HttpServletResponse) res;
|
||||
// 获得客户端domain
|
||||
String origin = request.getHeader("Origin");
|
||||
if (origin == null) {
|
||||
origin = request.getHeader("Referer");
|
||||
}
|
||||
// 允许指定域访问跨域资源
|
||||
response.setHeader("Access-Control-Allow-Origin", origin);
|
||||
// 允许客户端携带跨域cookie,此时origin值不能为“*”,只能为指定单一域名
|
||||
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
// 允许所有请求方式
|
||||
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
|
||||
// 有效时间
|
||||
response.setHeader("Access-Control-Max-Age", "3600");
|
||||
// 允许的header参数
|
||||
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,satoken");
|
||||
// 允许的header参数
|
||||
// response.setHeader("Access-Control-Allow-Headers", "*");
|
||||
|
||||
// 如果是预检请求,直接返回
|
||||
if (OPTIONS.equals(request.getMethod())) {
|
||||
System.out.println("=======================浏览器发来了OPTIONS预检请求==========");
|
||||
response.getWriter().print("");
|
||||
return;
|
||||
}
|
||||
|
||||
// System.out.println("*********************************过滤器被使用**************************2233");
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,17 +4,13 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.spring.SaTokenSetup;
|
||||
|
||||
@SaTokenSetup // 必须有这个注解,用来标注加载sa-token
|
||||
@SpringBootApplication
|
||||
public class SaTokenDemoApplication {
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenDemoApplication.class, args);
|
||||
System.out.println("启动成功:sa-token配置如下:" + SaTokenManager.getConfig());
|
||||
// StpUtil.getSessionByLoginId(10001)
|
||||
System.out.println("\n启动成功:sa-token配置如下:" + SaTokenManager.getConfig());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckInterceptor;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
|
||||
|
||||
/**
|
||||
* sa-token代码方式进行配置
|
||||
@@ -16,24 +14,24 @@ import cn.dev33.satoken.config.SaTokenConfig;
|
||||
public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
|
||||
// 获取配置Bean (以代码的方式配置sa-token, 此配置会覆盖yml中的配置 )
|
||||
@Primary
|
||||
@Bean(name="MySaTokenConfig")
|
||||
// @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.setIsV(true); // 是否在初始化配置时打印版本字符画
|
||||
config.setActivityTimeout(-1); // token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
config.setAllowConcurrentLogin(true); // 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
config.setIsShare(true); // 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
config.setTokenStyle("uuid"); // token风格
|
||||
return config;
|
||||
}
|
||||
|
||||
// 注册sa-token的拦截器,打开注解式鉴权功能
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new SaCheckInterceptor()).addPathPatterns("/**"); // 全局拦截器
|
||||
// 注册注解拦截器
|
||||
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,103 +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) {
|
||||
stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
// 更新指定key-value键值对 (过期时间取原来的值)
|
||||
@Override
|
||||
public void updateValue(String key, String value) {
|
||||
long expire = redisTemplate.getExpire(key);
|
||||
if(expire == -2) { // -2 = 无此键
|
||||
return;
|
||||
}
|
||||
stringRedisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
// 删除一个指定的key
|
||||
@Override
|
||||
public void delKey(String key) {
|
||||
stringRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
|
||||
// 根据指定key的session,如果没有,则返回空
|
||||
@Override
|
||||
public SaSession getSaSession(String sessionId) {
|
||||
return redisTemplate.opsForValue().get(sessionId);
|
||||
}
|
||||
|
||||
// 将指定session持久化
|
||||
@Override
|
||||
public void saveSaSession(SaSession session, long timeout) {
|
||||
redisTemplate.opsForValue().set(session.getId(), session, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
// 更新指定session
|
||||
@Override
|
||||
public void updateSaSession(SaSession session) {
|
||||
long expire = redisTemplate.getExpire(session.getId());
|
||||
if(expire == -2) { // -2 = 无此键
|
||||
return;
|
||||
}
|
||||
redisTemplate.opsForValue().set(session.getId(), session, expire, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
// 删除一个指定的session
|
||||
@Override
|
||||
public void deleteSaSession(String sessionId) {
|
||||
redisTemplate.delete(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,238 +0,0 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
|
||||
/**
|
||||
* 一个默认的实现
|
||||
* @author kong
|
||||
*/
|
||||
@Service
|
||||
public class StpUserUtil {
|
||||
|
||||
/**
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
public static StpLogic stpLogic = new StpLogic("user") {
|
||||
@Override
|
||||
public String getKeyTokenName() {
|
||||
return SaTokenManager.getConfig().getTokenName() + "-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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的token信息:tokenName与tokenValue
|
||||
* @return 一个Map对象
|
||||
*/
|
||||
public static Map<String, String> 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);
|
||||
}
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
* 获取当前会话是否已经登录
|
||||
* @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();
|
||||
}
|
||||
|
||||
// =================== 权限验证操作 ===================
|
||||
|
||||
/**
|
||||
* 指定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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -9,8 +9,11 @@ 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;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
@@ -40,6 +43,9 @@ public class GlobalException {
|
||||
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());
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 测试: 同域单点登录
|
||||
* @author kong
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/sso/")
|
||||
public class SSOController {
|
||||
|
||||
// 测试:进行登录
|
||||
@RequestMapping("doLogin")
|
||||
public AjaxJson doLogin(@RequestParam(defaultValue = "10001") String id) {
|
||||
System.out.println("---------------- 进行登录 ");
|
||||
StpUtil.setLoginId(id);
|
||||
return AjaxJson.getSuccess("登录成功: " + id);
|
||||
}
|
||||
|
||||
// 测试:是否登录
|
||||
@RequestMapping("isLogin")
|
||||
public AjaxJson isLogin() {
|
||||
System.out.println("---------------- 是否登录 ");
|
||||
boolean isLogin = StpUtil.isLogin();
|
||||
return AjaxJson.getSuccess("是否登录: " + isLogin);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
import com.pj.util.Ttime;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 压力测试
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/s-test/")
|
||||
public class StressTestController {
|
||||
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/s-test/login
|
||||
// 测试前,请先将 is-read-cookie 配置为 false
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login() {
|
||||
// StpUtil.getTokenSession().logout();
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
|
||||
int count = 10; // 循环多少轮
|
||||
int loginCount = 10000; // 每轮循环多少次
|
||||
|
||||
// 循环10次 取平均时间
|
||||
List<Double> list = new ArrayList<>();
|
||||
for (int i = 1; i <= count; i++) {
|
||||
System.out.println("\n---------------------第" + i + "轮---------------------");
|
||||
Ttime t = new Ttime().start();
|
||||
// 每次登录的次数
|
||||
for (int j = 1; j <= loginCount; j++) {
|
||||
StpUtil.setLoginId("1000" + j, "PC-" + j);
|
||||
if(j % 1000 == 0) {
|
||||
System.out.println("已登录:" + j);
|
||||
}
|
||||
}
|
||||
t.end();
|
||||
list.add((t.returnMs() + 0.0) / 1000);
|
||||
System.out.println("第" + i + "轮" + "用时:" + t.toString());
|
||||
}
|
||||
// System.out.println(((SaTokenDaoDefaultImpl)SaTokenManager.getSaTokenDao()).dataMap.size());
|
||||
|
||||
System.out.println("\n---------------------测试结果---------------------");
|
||||
System.out.println(list.size() + "次测试: " + list);
|
||||
double ss = 0;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
ss += list.get(i);
|
||||
}
|
||||
System.out.println("平均用时: " + ss / list.size());
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,12 +1,24 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pj.util.AjaxJson;
|
||||
import com.pj.util.Ttime;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.dev33.satoken.session.SaSessionCustomUtil;
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
@@ -18,6 +30,7 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
@RequestMapping("/test/")
|
||||
public class TestController {
|
||||
|
||||
|
||||
// 测试登录接口, 浏览器访问: http://localhost:8081/test/login
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login(@RequestParam(defaultValue="10001") String id) {
|
||||
@@ -25,31 +38,54 @@ public class TestController {
|
||||
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("当前登录账号:" + StpUtil.getLoginIdAsInt()); // 获取登录id并转为int
|
||||
|
||||
// StpUtil.logout();
|
||||
// System.out.println("注销登录");
|
||||
// System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
// System.out.println("当前登录账号:" + StpUtil.getLoginIdDefaultNull());
|
||||
// StpUtil.setLoginId(id); // 在当前会话登录此账号
|
||||
// System.out.println("根据token找登录id:" + StpUtil.getLoginIdByToken(StpUtil.getTokenValue()));
|
||||
|
||||
System.out.println("当前token信息:" + StpUtil.getTokenInfo()); // 获取登录id并转为int
|
||||
System.out.println("当前登录账号:" + StpUtil.getLoginIdDefaultNull());
|
||||
// System.out.println("当前登录账号并转为int:" + StpUtil.getLoginIdAsInt());
|
||||
System.out.println("当前登录设备:" + StpUtil.getLoginDevice());
|
||||
// System.out.println("当前token信息:" + StpUtil.getTokenInfo());
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试权限接口, 浏览器访问: http://localhost:8081/test/jur
|
||||
@RequestMapping("jur")
|
||||
public AjaxJson jur() {
|
||||
// 测试退出登录 , 浏览器访问: http://localhost:8081/test/logout
|
||||
@RequestMapping("logout")
|
||||
public AjaxJson logout() {
|
||||
StpUtil.logout();
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试角色接口, 浏览器访问: http://localhost:8081/test/testRole
|
||||
@RequestMapping("testRole")
|
||||
public AjaxJson testRole() {
|
||||
System.out.println("======================= 进入方法,测试角色接口 ========================= ");
|
||||
|
||||
System.out.println("是否具有角色标识 user " + StpUtil.hasRole("user"));
|
||||
System.out.println("是否具有角色标识 admin " + StpUtil.hasRole("admin"));
|
||||
|
||||
System.out.println("没有admin权限就抛出异常");
|
||||
StpUtil.checkRole("admin");
|
||||
|
||||
System.out.println("在【admin、user】中只要拥有一个就不会抛出异常");
|
||||
StpUtil.checkRoleOr("admin", "user");
|
||||
|
||||
System.out.println("在【admin、user】中必须全部拥有才不会抛出异常");
|
||||
StpUtil.checkRoleAnd("admin", "user");
|
||||
|
||||
System.out.println("角色测试通过");
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试权限接口, 浏览器访问: http://localhost:8081/test/testJur
|
||||
@RequestMapping("testJur")
|
||||
public AjaxJson testJur() {
|
||||
System.out.println("======================= 进入方法,测试权限接口 ========================= ");
|
||||
|
||||
System.out.println("是否具有权限101" + StpUtil.hasPermission(101));
|
||||
System.out.println("是否具有权限101" + StpUtil.hasPermission("101"));
|
||||
System.out.println("是否具有权限user-add" + StpUtil.hasPermission("user-add"));
|
||||
System.out.println("是否具有权限article-get" + StpUtil.hasPermission("article-get"));
|
||||
|
||||
@@ -69,14 +105,15 @@ public class TestController {
|
||||
|
||||
// 测试会话session接口, 浏览器访问: http://localhost:8081/test/session
|
||||
@RequestMapping("session")
|
||||
public AjaxJson 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", "张三"); // 写入一个值
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -93,67 +130,132 @@ public class TestController {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// ----------
|
||||
// 测试token专属session, 浏览器访问: http://localhost:8081/test/getTokenSession
|
||||
@RequestMapping("getTokenSession")
|
||||
public AjaxJson getTokenSession() {
|
||||
System.out.println("======================= 进入方法,测试会话session接口 ========================= ");
|
||||
System.out.println("当前是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("当前token专属session: " + StpUtil.getTokenSession().getId());
|
||||
|
||||
System.out.println("测试取值name:" + StpUtil.getTokenSession().getAttribute("name"));
|
||||
StpUtil.getTokenSession().setAttribute("name", "张三"); // 写入一个值
|
||||
System.out.println("测试取值name:" + StpUtil.getTokenSession().getAttribute("name"));
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 打印当前token信息, 浏览器访问: http://localhost:8081/test/tokenInfo
|
||||
@RequestMapping("tokenInfo")
|
||||
public AjaxJson tokenInfo() {
|
||||
System.out.println("======================= 进入方法,打印当前token信息 ========================= ");
|
||||
System.out.println(StpUtil.getTokenInfo());
|
||||
return AjaxJson.getSuccess();
|
||||
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
|
||||
System.out.println(tokenInfo);
|
||||
return AjaxJson.getSuccessData(tokenInfo);
|
||||
}
|
||||
|
||||
|
||||
// 测试注解式鉴权, 浏览器访问: http://localhost:8081/test/atCheck
|
||||
@SaCheckLogin // 注解式鉴权:当前会话必须登录才能通过
|
||||
@SaCheckRole("super-admin") // 注解式鉴权:当前会话必须具有指定角色标识才能通过
|
||||
@SaCheckPermission("user-add") // 注解式鉴权:当前会话必须具有指定权限才能通过
|
||||
@RequestMapping("atCheck")
|
||||
public AjaxJson atCheck() {
|
||||
System.out.println("======================= 进入方法,测试注解鉴权接口 ========================= ");
|
||||
System.out.println("只有通过注解鉴权,才能进入此方法");
|
||||
// StpUtil.checkActivityTimeout();
|
||||
// StpUtil.updateLastActivityToNow();
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试注解式鉴权, 浏览器访问: http://localhost:8081/test/getInfo
|
||||
@SaCheckLogin // 注解式鉴权:当前会话必须登录才能通过
|
||||
@RequestMapping("getInfo")
|
||||
public AjaxJson getInfo() {
|
||||
// 测试注解式鉴权, 浏览器访问: http://localhost:8081/test/atJurOr
|
||||
@RequestMapping("atJurOr")
|
||||
@SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR) // 注解式鉴权:只要具有其中一个权限即可通过校验
|
||||
public AjaxJson atJurOr() {
|
||||
return AjaxJson.getSuccessData("用户信息");
|
||||
}
|
||||
|
||||
// [活动时间] 续签: http://localhost:8081/test/rene
|
||||
@RequestMapping("rene")
|
||||
public AjaxJson rene() {
|
||||
StpUtil.checkActivityTimeout();
|
||||
StpUtil.updateLastActivityToNow();
|
||||
return AjaxJson.getSuccess("续签成功");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 测试踢人下线 浏览器访问: http://localhost:8081/test/kickOut
|
||||
@RequestMapping("kickOut")
|
||||
public AjaxJson kickOut() {
|
||||
// 先登录上
|
||||
StpUtil.setLoginId(10001);
|
||||
// 清退下线
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
// 踢下线
|
||||
StpUtil.kickoutByLoginId(10001);
|
||||
StpUtil.logoutByLoginId(10001);
|
||||
// 再尝试获取
|
||||
StpUtil.getLoginId();
|
||||
// 返回
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
StpUtil.setLoginId(10001);
|
||||
// StpUtil.getSession();
|
||||
StpUtil.logout();
|
||||
|
||||
// System.out.println(StpUtil.getSession().getId());
|
||||
// System.out.println(StpUserUtil.getSession().getId());
|
||||
// StpUtil.getSessionByLoginId(10001).setAttribute("name", "123");
|
||||
// System.out.println(StpUtil.getSessionByLoginId(10001).getAttribute("name"));
|
||||
|
||||
// 测试登录接口, 按照设备登录, 浏览器访问: http://localhost:8081/test/login2
|
||||
@RequestMapping("login2")
|
||||
public AjaxJson login2(@RequestParam(defaultValue="10001") String id, @RequestParam(defaultValue="PC") String device) {
|
||||
StpUtil.setLoginId(id, device);
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试身份临时切换: http://localhost:8081/test/switchTo
|
||||
@RequestMapping("switchTo")
|
||||
public AjaxJson switchTo() {
|
||||
System.out.println("当前会话身份:" + StpUtil.getLoginIdDefaultNull());
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
StpUtil.switchTo(10044, () -> {
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
System.out.println("当前会话身份已被切换为:" + StpUtil.getLoginId());
|
||||
});
|
||||
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试会话治理 浏览器访问: http://localhost:8081/test/search
|
||||
@RequestMapping("search")
|
||||
public AjaxJson search() {
|
||||
System.out.println("--------------");
|
||||
Ttime t = new Ttime().start();
|
||||
List<String> tokenValue = StpUtil.searchTokenValue("8feb8265f773", 0, 10);
|
||||
for (String v : tokenValue) {
|
||||
// SaSession session = StpUtil.getSessionBySessionId(sid);
|
||||
System.out.println(v);
|
||||
}
|
||||
System.out.println("用时:" + t.end().toString());
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
TestService TestService;
|
||||
|
||||
// 测试AOP注解鉴权: http://localhost:8081/test/testAOP
|
||||
@RequestMapping("testAOP")
|
||||
public AjaxJson testAOP() {
|
||||
System.out.println("testAOP");
|
||||
TestService.getList();
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
// StpUtil.getTokenSession().logout();
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
StpUtil.getLoginId();
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test2
|
||||
@RequestMapping("test2")
|
||||
public AjaxJson test2() {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.pj.test;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
|
||||
/**
|
||||
* 用来测试AOP注解鉴权
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class TestService {
|
||||
|
||||
@SaCheckLogin
|
||||
public List<String> getList() {
|
||||
System.out.println("getList");
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("user")
|
||||
public class UserController {
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/user/doLogin
|
||||
@RequestMapping("doLogin")
|
||||
public String test(String username, String password) {
|
||||
if("zhang".equals(username) && "123456".equals(password)) {
|
||||
StpUtil.setLoginId(10001);
|
||||
return "登录成功";
|
||||
}
|
||||
return "登录失败";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package com.pj.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* ajax请求返回Json格式数据的封装
|
||||
*/
|
||||
public class AjaxJson implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L; // 序列化版本号
|
||||
|
||||
public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||
public static final int CODE_ERROR = 500; // 错误状态码
|
||||
public static final int CODE_WARNING = 501; // 警告状态码
|
||||
public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||
|
||||
public int code; // 状态码
|
||||
public String msg; // 描述信息
|
||||
public Object data; // 携带对象
|
||||
public Long dataCount; // 数据总数,用于分页
|
||||
|
||||
/**
|
||||
* 返回code
|
||||
* @return
|
||||
*/
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给msg赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
public String getMsg() {
|
||||
return this.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给data赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setData(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将data还原为指定类型并返回
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(Class<T> cs) {
|
||||
return (T) data;
|
||||
}
|
||||
|
||||
// ============================ 构建 ==================================
|
||||
|
||||
public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.dataCount = dataCount;
|
||||
}
|
||||
|
||||
// 返回成功
|
||||
public static AjaxJson getSuccess() {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg, Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessData(Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessArray(Object... data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
|
||||
// 返回失败
|
||||
public static AjaxJson getError() {
|
||||
return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||
}
|
||||
public static AjaxJson getError(String msg) {
|
||||
return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回警告
|
||||
public static AjaxJson getWarning() {
|
||||
return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||
}
|
||||
public static AjaxJson getWarning(String msg) {
|
||||
return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回未登录
|
||||
public static AjaxJson getNotLogin() {
|
||||
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||
}
|
||||
|
||||
// 返回没有权限的
|
||||
public static AjaxJson getNotJur(String msg) {
|
||||
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回一个自定义状态码的
|
||||
public static AjaxJson get(int code, String msg){
|
||||
return new AjaxJson(code, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回分页和数据的
|
||||
public static AjaxJson getPageData(Long dataCount, Object data){
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||
}
|
||||
|
||||
// 返回,根据受影响行数的(大于0=ok,小于0=error)
|
||||
public static AjaxJson getByLine(int line){
|
||||
if(line > 0){
|
||||
return getSuccess("ok", line);
|
||||
}
|
||||
return getError("error").setData(line);
|
||||
}
|
||||
|
||||
// 返回,根据布尔值来确定最终结果的 (true=ok,false=error)
|
||||
public static AjaxJson getByBoolean(boolean b){
|
||||
return b ? getSuccess("ok") : getError("error");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public String toString() {
|
||||
String data_string = null;
|
||||
if(data == null){
|
||||
|
||||
} else if(data instanceof List){
|
||||
data_string = "List(length=" + ((List)data).size() + ")";
|
||||
} else {
|
||||
data_string = data.toString();
|
||||
}
|
||||
return "{"
|
||||
+ "\"code\": " + this.getCode()
|
||||
+ ", \"msg\": \"" + this.getMsg() + "\""
|
||||
+ ", \"data\": " + data_string
|
||||
+ ", \"dataCount\": " + dataCount
|
||||
+ "}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.pj.util;
|
||||
|
||||
|
||||
/**
|
||||
* 用于测试用时
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class Ttime {
|
||||
|
||||
private long start=0; //开始时间
|
||||
private long end=0; //结束时间
|
||||
|
||||
public static Ttime t = new Ttime(); //static快捷使用
|
||||
|
||||
/**
|
||||
* 开始计时
|
||||
* @return
|
||||
*/
|
||||
public Ttime start() {
|
||||
start=System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 结束计时
|
||||
*/
|
||||
public Ttime end() {
|
||||
end=System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回所用毫秒数
|
||||
*/
|
||||
public long returnMs() {
|
||||
return end-start;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出结果
|
||||
*/
|
||||
public void outTime() {
|
||||
System.out.println(this.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束并格式化输出结果
|
||||
*/
|
||||
public void endOutTime() {
|
||||
this.end().outTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (returnMs() + 0.0) / 1000 + "s"; // 格式化为:0.01s
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -7,24 +7,22 @@ spring:
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken
|
||||
# token有效期,单位s 默认30天
|
||||
# token有效期,单位s 默认30天, -1代表永不过期
|
||||
timeout: 2592000
|
||||
# 在多人登录同一账号时,是否共享会话 (为true时共用一个,为false时新登录挤掉旧登录)
|
||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
activity-timeout: -1
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
allow-concurrent-login: true
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: true
|
||||
# 是否尝试从请求体里读取token
|
||||
is-read-body: true
|
||||
# 是否尝试从header里读取token
|
||||
is-read-head: true
|
||||
# 是否尝试从cookie里读取token
|
||||
is-read-cookie: true
|
||||
# 是否在初始化配置时打印版本字符画
|
||||
is-v: true
|
||||
# token风格
|
||||
token-style: uuid
|
||||
|
||||
|
||||
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
@@ -32,7 +30,7 @@ spring:
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 1000ms
|
||||
timeout: 10000ms
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
package cn.dev33.satoken;
|
||||
|
||||
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.cookie.SaCookieOper;
|
||||
import cn.dev33.satoken.cookie.SaCookieOperDefaultImpl;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* 管理sa-token所有对象
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenManager {
|
||||
|
||||
|
||||
/**
|
||||
* 配置文件 Bean
|
||||
*/
|
||||
private static SaTokenConfig config;
|
||||
public static SaTokenConfig getConfig() {
|
||||
if (config == null) {
|
||||
initConfig();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
public static void setConfig(SaTokenConfig config) {
|
||||
SaTokenManager.config = config;
|
||||
if(config.getIsV()) {
|
||||
SaTokenInsideUtil.printSaToken();
|
||||
}
|
||||
}
|
||||
public synchronized static void initConfig() {
|
||||
if (config == null) {
|
||||
setConfig(SaTokenConfigFactory.createConfig());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 持久化 Bean
|
||||
*/
|
||||
public static SaTokenDao dao;
|
||||
public static SaTokenDao getDao() {
|
||||
if (dao == null) {
|
||||
initDao();
|
||||
}
|
||||
return dao;
|
||||
}
|
||||
public static void setDao(SaTokenDao dao) {
|
||||
SaTokenManager.dao = dao;
|
||||
}
|
||||
public synchronized static void initDao() {
|
||||
if (dao == null) {
|
||||
setDao(new SaTokenDaoDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限认证 Bean
|
||||
*/
|
||||
public static StpInterface stp;
|
||||
public static StpInterface getStp() {
|
||||
if (stp == null) {
|
||||
initStp();
|
||||
}
|
||||
return stp;
|
||||
}
|
||||
public static void setStp(StpInterface stp) {
|
||||
SaTokenManager.stp = stp;
|
||||
}
|
||||
public synchronized static void initStp() {
|
||||
if (stp == null) {
|
||||
setStp(new StpInterfaceDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sa-token行为 Bean
|
||||
*/
|
||||
public static SaTokenAction sta;
|
||||
public static SaTokenAction getSta() {
|
||||
if (sta == null) {
|
||||
initSta();
|
||||
}
|
||||
return sta;
|
||||
}
|
||||
public static void setSta(SaTokenAction sta) {
|
||||
SaTokenManager.sta = sta;
|
||||
}
|
||||
public synchronized static void initSta() {
|
||||
if (sta == null) {
|
||||
setSta(new SaTokenActionDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sa-token cookie操作 Bean
|
||||
*/
|
||||
public static SaCookieOper saCookieOper;
|
||||
public static SaCookieOper getSaCookieOper() {
|
||||
if (saCookieOper == null) {
|
||||
initgetSaCookieOper();
|
||||
}
|
||||
return saCookieOper;
|
||||
}
|
||||
public static void setSaCookieOper(SaCookieOper saCookieOper) {
|
||||
SaTokenManager.saCookieOper = saCookieOper;
|
||||
}
|
||||
public synchronized static void initgetSaCookieOper() {
|
||||
if (saCookieOper == null) {
|
||||
setSaCookieOper(new SaCookieOperDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package cn.dev33.satoken.action;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public interface SaTokenAction {
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前请求的Request对象
|
||||
* @return 当前请求的Request对象
|
||||
*/
|
||||
public HttpServletRequest getCurrRequest();
|
||||
|
||||
/**
|
||||
* 获取当前会话的 response
|
||||
* @return 当前请求的response
|
||||
*/
|
||||
public HttpServletResponse getResponse();
|
||||
|
||||
/**
|
||||
* 生成一个token
|
||||
* @param loginId 账号id
|
||||
* @param loginKey 登录标识key
|
||||
* @return 一个token
|
||||
*/
|
||||
public String createToken(Object loginId, String loginKey);
|
||||
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package cn.dev33.satoken.action;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import cn.dev33.satoken.util.SpringMvcUtil;
|
||||
|
||||
/**
|
||||
* 对 SaTokenAction 接口的默认实现
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenActionDefaultImpl implements SaTokenAction {
|
||||
|
||||
/**
|
||||
* 获取当前请求的Request对象
|
||||
*/
|
||||
@Override
|
||||
public HttpServletRequest getCurrRequest() {
|
||||
return SpringMvcUtil.getRequest();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前请求的Response对象
|
||||
*/
|
||||
@Override
|
||||
public HttpServletResponse getResponse() {
|
||||
return SpringMvcUtil.getResponse();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成一个token
|
||||
*/
|
||||
@Override
|
||||
public String createToken(Object loginId, String loginKey) {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package cn.dev33.satoken.annotation;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 注解式鉴权 - 拦截器
|
||||
* @author kong
|
||||
*/
|
||||
public class SaCheckInterceptor implements HandlerInterceptor {
|
||||
|
||||
|
||||
/**
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
public StpLogic stpLogic = null;
|
||||
|
||||
/**
|
||||
* 创建,并指定一个默认的 StpLogic
|
||||
*/
|
||||
public SaCheckInterceptor() {
|
||||
this(StpUtil.stpLogic);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建,并指定一个的 StpLogic
|
||||
* @param stpLogic 指定的StpLogic
|
||||
*/
|
||||
public SaCheckInterceptor(StpLogic stpLogic) {
|
||||
this.stpLogic = stpLogic;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 每次请求之前触发
|
||||
*/
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
|
||||
// 获取处理method
|
||||
if (handler instanceof HandlerMethod == false) {
|
||||
return true;
|
||||
}
|
||||
HandlerMethod method = (HandlerMethod ) handler;
|
||||
|
||||
// 验证登录
|
||||
if(method.hasMethodAnnotation(SaCheckLogin.class) || method.getBeanType().isAnnotationPresent(SaCheckLogin.class)) {
|
||||
stpLogic.checkLogin();
|
||||
}
|
||||
|
||||
// 获取权限注解
|
||||
SaCheckPermission scp = method.getMethodAnnotation(SaCheckPermission.class);
|
||||
if(scp == null) {
|
||||
scp = method.getBeanType().getAnnotation(SaCheckPermission.class);
|
||||
}
|
||||
if(scp == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 开始验证权限
|
||||
Object[] codeArray = concatAbc(scp.value(), scp.valueInt(), scp.valueLong());
|
||||
if(scp.isAnd()) {
|
||||
stpLogic.checkPermissionAnd(codeArray); // 必须全部都有
|
||||
} else {
|
||||
stpLogic.checkPermissionOr(codeArray); // 有一个就行了
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 合并三个数组
|
||||
* @param a .
|
||||
* @param b .
|
||||
* @param c .
|
||||
* @return .
|
||||
*/
|
||||
private Object[] concatAbc(String[] a, int[] b, long[] c) {
|
||||
// 循环赋值
|
||||
Object[] d = new Object[a.length + b.length + c.length];
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
d[i] = a[i];
|
||||
}
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
d[a.length + i] = b[i];
|
||||
}
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
d[a.length + b.length + i] = c[i];
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 标注一个路由方法,当前会话必须具有指定权限才可以通过
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD,ElementType.TYPE})
|
||||
public @interface SaCheckPermission {
|
||||
|
||||
/**
|
||||
* 权限码数组 ,String类型
|
||||
* @return .
|
||||
*/
|
||||
String [] value() default {};
|
||||
|
||||
/**
|
||||
* 权限码数组 ,int类型
|
||||
* @return .
|
||||
*/
|
||||
int [] valueInt() default {};
|
||||
|
||||
/**
|
||||
* 权限码数组 ,long类型
|
||||
* @return .
|
||||
*/
|
||||
long [] valueLong() default {};
|
||||
|
||||
/**
|
||||
* 是否属于and型验证 ,true=必须全部具有,false=只要具有一个就可以通过
|
||||
* @return .
|
||||
*/
|
||||
boolean isAnd() default true;
|
||||
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package cn.dev33.satoken.config;
|
||||
|
||||
/**
|
||||
* sa-token 总配置类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenConfig {
|
||||
|
||||
private String tokenName = "satoken"; // token名称 (同时也是cookie名称)
|
||||
private long timeout = 30 * 24 * 60 * 60; // token有效期,单位s 默认30天
|
||||
private Boolean isShare = true; // 在多人登录同一账号时,是否共享会话 (为true时共用一个,为false时新登录挤掉旧登录)
|
||||
private Boolean isReadBody = true; // 是否尝试从请求体里读取token
|
||||
private Boolean isReadHead = true; // 是否尝试从header里读取token
|
||||
private Boolean isReadCookie = true; // 是否尝试从cookie里读取token
|
||||
|
||||
private Boolean isV = true; // 是否在初始化配置时打印版本字符画
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return tokenName
|
||||
*/
|
||||
public String getTokenName() {
|
||||
return tokenName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenName 要设置的 tokenName
|
||||
*/
|
||||
public void setTokenName(String tokenName) {
|
||||
this.tokenName = tokenName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return timeout
|
||||
*/
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeout 要设置的 timeout
|
||||
*/
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isShare
|
||||
*/
|
||||
public Boolean getIsShare() {
|
||||
return isShare;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isShare 要设置的 isShare
|
||||
*/
|
||||
public void setIsShare(Boolean isShare) {
|
||||
this.isShare = isShare;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isReadCookie
|
||||
*/
|
||||
public Boolean getIsReadCookie() {
|
||||
return isReadCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isReadCookie 要设置的 isReadCookie
|
||||
*/
|
||||
public void setIsReadCookie(Boolean isReadCookie) {
|
||||
this.isReadCookie = isReadCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isReadHead
|
||||
*/
|
||||
public Boolean getIsReadHead() {
|
||||
return isReadHead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isReadHead 要设置的 isReadHead
|
||||
*/
|
||||
public void setIsReadHead(Boolean isReadHead) {
|
||||
this.isReadHead = isReadHead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isReadBody
|
||||
*/
|
||||
public Boolean getIsReadBody() {
|
||||
return isReadBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isReadBody 要设置的 isReadBody
|
||||
*/
|
||||
public void setIsReadBody(Boolean isReadBody) {
|
||||
this.isReadBody = isReadBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isV
|
||||
*/
|
||||
public Boolean getIsV() {
|
||||
return isV;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isV 要设置的 isV
|
||||
*/
|
||||
public void setIsV(Boolean isV) {
|
||||
this.isV = isV;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaTokenConfig [tokenName=" + tokenName + ", timeout=" + timeout + ", isShare=" + isShare
|
||||
+ ", isReadBody=" + isReadBody + ", isReadHead=" + isReadHead + ", isReadCookie=" + isReadCookie
|
||||
+ ", isV=" + isV + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 创建一个配置文件
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenConfigFactory {
|
||||
|
||||
|
||||
/**
|
||||
* 默认配置文件地址
|
||||
*/
|
||||
public static String configPath = "sa-token.properties";
|
||||
|
||||
/**
|
||||
* 根据指定路径获取配置信息
|
||||
* @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>();
|
||||
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>();
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package cn.dev33.satoken.cookie;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* sa-token 对cookie的相关操作 接口类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaCookieOper {
|
||||
|
||||
/**
|
||||
* 获取指定cookie .
|
||||
*
|
||||
* @param request .
|
||||
* @param cookieName .
|
||||
* @return .
|
||||
*/
|
||||
public Cookie getCookie(HttpServletRequest request, String cookieName);
|
||||
|
||||
/**
|
||||
* 添加cookie
|
||||
*
|
||||
* @param response .
|
||||
* @param name .
|
||||
* @param value .
|
||||
* @param path .
|
||||
* @param timeout .
|
||||
*/
|
||||
public void addCookie(HttpServletResponse response, String name, String value, String path, int timeout);
|
||||
|
||||
/**
|
||||
* 删除cookie .
|
||||
*
|
||||
* @param request .
|
||||
* @param response .
|
||||
* @param name .
|
||||
*/
|
||||
public void delCookie(HttpServletRequest request, HttpServletResponse response, String name);
|
||||
|
||||
/**
|
||||
* 修改cookie的value值
|
||||
*
|
||||
* @param request .
|
||||
* @param response .
|
||||
* @param name .
|
||||
* @param value .
|
||||
*/
|
||||
public void updateCookie(HttpServletRequest request, HttpServletResponse response, String name, String value);
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* sa-token持久层的接口
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public interface SaTokenDao {
|
||||
|
||||
|
||||
/**
|
||||
* 根据key获取value ,如果没有,则返回空
|
||||
* @param key 键名称
|
||||
* @return value
|
||||
*/
|
||||
public String getValue(String key);
|
||||
|
||||
/**
|
||||
* 写入指定key-value键值对,并设定过期时间 (单位:秒)
|
||||
* @param key 键名称
|
||||
* @param value 值
|
||||
* @param timeout 过期时间,单位:s
|
||||
*/
|
||||
public void setValue(String key, String value, long timeout);
|
||||
|
||||
/**
|
||||
* 修改指定key-value键值对 (过期时间取原来的值)
|
||||
* @param key 键名称
|
||||
* @param value 值
|
||||
*/
|
||||
public void updateValue(String key, String value);
|
||||
|
||||
/**
|
||||
* 删除一个指定的key
|
||||
* @param key 键名称
|
||||
*/
|
||||
public void delKey(String key);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据指定key的session,如果没有,则返回空
|
||||
* @param sessionId 键名称
|
||||
* @return SaSession
|
||||
*/
|
||||
public SaSession getSaSession(String sessionId);
|
||||
|
||||
/**
|
||||
* 将指定session持久化
|
||||
* @param session 要保存的session对象
|
||||
* @param timeout 过期时间,单位: s
|
||||
*/
|
||||
public void saveSaSession(SaSession session, long timeout);
|
||||
|
||||
/**
|
||||
* 更新指定session
|
||||
* @param session 要更新的session对象
|
||||
*/
|
||||
public void updateSaSession(SaSession session);
|
||||
|
||||
/**
|
||||
* 删除一个指定的session
|
||||
* @param sessionId sessionId
|
||||
*/
|
||||
public void deleteSaSession(String sessionId);
|
||||
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* sa-token持久层默认的实现类 , 基于内存Map
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
|
||||
/**
|
||||
* 所有数据集合
|
||||
*/
|
||||
Map<String, Object> dataMap = new HashMap<String, Object>();
|
||||
|
||||
|
||||
@Override
|
||||
public String getValue(String key) {
|
||||
return (String)dataMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String key, String value, long timeout) {
|
||||
dataMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateValue(String key, String value) {
|
||||
this.setValue(key, value, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delKey(String key) {
|
||||
dataMap.remove(key);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SaSession getSaSession(String sessionId) {
|
||||
return (SaSession)dataMap.get(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveSaSession(SaSession session, long timeout) {
|
||||
dataMap.put(session.getId(), session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSaSession(SaSession session) {
|
||||
// 无动作
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSaSession(String sessionId) {
|
||||
dataMap.remove(sessionId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 没有指定权限码,抛出的异常
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class NotPermissionException extends RuntimeException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130142L;
|
||||
|
||||
/**
|
||||
* 权限码
|
||||
*/
|
||||
private Object code;
|
||||
/**
|
||||
* @return 获得权限码
|
||||
*/
|
||||
public Object getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* login_key
|
||||
*/
|
||||
private String loginKey;
|
||||
/**
|
||||
* 获得login_key
|
||||
* @return login_key
|
||||
*/
|
||||
public String getLoginKey() {
|
||||
return loginKey;
|
||||
}
|
||||
|
||||
|
||||
public NotPermissionException(Object code) {
|
||||
this(code, StpUtil.stpLogic.loginKey);
|
||||
}
|
||||
public NotPermissionException(Object code, String loginKey) {
|
||||
// 这里到底要不要拼接上login_key呢?纠结
|
||||
super("无此权限:" + code);
|
||||
this.code = code;
|
||||
this.loginKey = loginKey;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
package cn.dev33.satoken.session;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
|
||||
/**
|
||||
* session会话
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaSession implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 会话id
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 当前会话创建时间
|
||||
*/
|
||||
private long createTime;
|
||||
/**
|
||||
* 当前会话键值对
|
||||
*/
|
||||
private Map<String, Object> dataMap;
|
||||
|
||||
|
||||
/**
|
||||
* 构建一个 session对象
|
||||
* @param id sessionId
|
||||
*/
|
||||
public SaSession(String id) {
|
||||
this.id = id;
|
||||
this.createTime = System.currentTimeMillis();
|
||||
this.dataMap = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话id
|
||||
* @return id
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前会话创建时间
|
||||
* @return 时间戳
|
||||
*/
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入一个值
|
||||
* @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.keySet().contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前session会话所有key
|
||||
* @return 所有值的key列表
|
||||
*/
|
||||
public Set<String> getAttributeKeys() {
|
||||
return dataMap.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据集合(如果更新map里的值,请调用session.update()方法避免数据过时 )
|
||||
* @return 返回底层储存值的map对象
|
||||
*/
|
||||
public Map<String, Object> getDataMap() {
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将这个session从持久库更新一下
|
||||
*/
|
||||
public void update() {
|
||||
SaTokenManager.getDao().updateSaSession(this);
|
||||
}
|
||||
|
||||
|
||||
// /** 注销会话(注销后,此session会话将不再存储服务器上) */
|
||||
// public void logout() {
|
||||
// SaTokenManager.getDao().delSaSession(this.id);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package cn.dev33.satoken.session;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
|
||||
/**
|
||||
* 自定义sa-session工具类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaSessionCustomUtil {
|
||||
|
||||
/**
|
||||
* 添加上指定前缀,防止恶意伪造session
|
||||
*/
|
||||
public static String session_key = "custom";
|
||||
public static String getSessionKey(String sessionId) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + session_key + ":session:" + sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定key的session是否存在
|
||||
* @param sessionId session的id
|
||||
* @return 是否存在
|
||||
*/
|
||||
public boolean isExists(String sessionId) {
|
||||
return SaTokenManager.getDao().getSaSession(getSessionKey(sessionId)) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的session
|
||||
* @param sessionId key
|
||||
* @param isCreate 如果没有,是否新建并返回
|
||||
* @return SaSession
|
||||
*/
|
||||
public static SaSession getSessionById(String sessionId, boolean isCreate) {
|
||||
SaSession session = SaTokenManager.getDao().getSaSession(getSessionKey(sessionId));
|
||||
if(session == null && isCreate) {
|
||||
session = new SaSession(getSessionKey(sessionId));
|
||||
SaTokenManager.getDao().saveSaSession(session, SaTokenManager.getConfig().getTimeout());
|
||||
}
|
||||
return session;
|
||||
}
|
||||
/**
|
||||
* 获取指定key的session, 如果没有则新建并返回
|
||||
* @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.getDao().deleteSaSession(getSessionKey(sessionId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package cn.dev33.satoken.spring;
|
||||
|
||||
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.SaTokenManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
/**
|
||||
* 与SpringBoot集成, 保证此类被扫描,即可完成sa-token与SpringBoot的集成
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class SpringSaToken {
|
||||
|
||||
|
||||
/**
|
||||
* 获取配置Bean
|
||||
* @return .
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix="spring.sa-token")
|
||||
public SaTokenConfig getSaTokenConfig() {
|
||||
return new SaTokenConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入配置Bean
|
||||
* @param saTokenConfig .
|
||||
*/
|
||||
@Autowired
|
||||
public void setConfig(SaTokenConfig saTokenConfig){
|
||||
SaTokenManager.setConfig(saTokenConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入持久化Bean
|
||||
* @param dao .
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setDao(SaTokenDao dao){
|
||||
SaTokenManager.setDao(dao);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入权限认证Bean
|
||||
* @param stp .
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setStp(StpInterface stp){
|
||||
SaTokenManager.setStp(stp);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 开放权限验证接口,方便重写
|
||||
* @author kong
|
||||
*/
|
||||
public interface StpInterface {
|
||||
|
||||
/**
|
||||
* 返回指定login_id所拥有的权限码集合
|
||||
* @param loginId 账号id
|
||||
* @param loginKey 具体的stp标识
|
||||
* @return .
|
||||
*/
|
||||
public List<Object> getPermissionCodeList(Object loginId, String loginKey);
|
||||
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 权限验证接口 ,默认实现
|
||||
* @author kong
|
||||
*/
|
||||
public class StpInterfaceDefaultImpl implements StpInterface {
|
||||
|
||||
@Override
|
||||
public List<Object> getPermissionCodeList(Object loginId, String loginKey) {
|
||||
return new ArrayList<Object>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,505 +0,0 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
|
||||
/**
|
||||
* sa-token 权限验证,逻辑 实现类
|
||||
* <p>
|
||||
* (stp = sa-token-permission 的缩写 )
|
||||
* @author kong
|
||||
*/
|
||||
public class StpLogic {
|
||||
|
||||
/**
|
||||
* 持久化的key前缀,多账号体系时以此值区分,比如:login、user、admin
|
||||
*/
|
||||
public String loginKey = "";
|
||||
|
||||
/**
|
||||
* 初始化StpLogic, 并制定loginKey
|
||||
* @param loginKey .
|
||||
*/
|
||||
public StpLogic(String loginKey) {
|
||||
this.loginKey = loginKey;
|
||||
}
|
||||
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
/**
|
||||
* 返回token名称
|
||||
* @return 此StpLogic的token名称
|
||||
*/
|
||||
public String getTokenName() {
|
||||
return getKeyTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成一个tokenValue
|
||||
* @param loginId loginId
|
||||
* @return 生成的tokenValue
|
||||
*/
|
||||
public String randomTokenValue(Object loginId) {
|
||||
return SaTokenManager.getSta().createToken(loginId, loginKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前tokenValue
|
||||
* @return 当前tokenValue
|
||||
*/
|
||||
public String getTokenValue(){
|
||||
// 0、获取相应对象
|
||||
HttpServletRequest request = SaTokenManager.getSta().getCurrRequest();
|
||||
SaTokenConfig config = SaTokenManager.getConfig();
|
||||
String keyTokenName = getTokenName();
|
||||
|
||||
// 1、尝试从request里读取
|
||||
if(request.getAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY) != null) {
|
||||
return String.valueOf(request.getAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY));
|
||||
}
|
||||
// 2、尝试从请求体里面读取
|
||||
if(config.getIsReadBody() == true){
|
||||
String tokenValue = request.getParameter(keyTokenName);
|
||||
if(tokenValue != null) {
|
||||
return tokenValue;
|
||||
}
|
||||
}
|
||||
// 3、尝试从header力读取
|
||||
if(config.getIsReadHead() == true){
|
||||
String tokenValue = request.getHeader(keyTokenName);
|
||||
if(tokenValue != null) {
|
||||
return tokenValue;
|
||||
}
|
||||
}
|
||||
// 4、尝试从cookie里读取
|
||||
if(config.getIsReadCookie() == true){
|
||||
Cookie cookie = SaTokenManager.getSaCookieOper().getCookie(request, keyTokenName);
|
||||
if(cookie != null){
|
||||
String tokenValue = cookie.getValue();
|
||||
if(tokenValue != null) {
|
||||
return tokenValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 5、都读取不到,那算了吧还是
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定id的tokenValue
|
||||
* @param loginId .
|
||||
* @return .
|
||||
*/
|
||||
public String getTokenValueByLoginId(Object loginId) {
|
||||
return SaTokenManager.getDao().getValue(getKeyLoginId(loginId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的token信息:tokenName与tokenValue
|
||||
* @return 一个Map对象
|
||||
*/
|
||||
public Map<String, String> getTokenInfo() {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("tokenName", getTokenName());
|
||||
map.put("tokenValue", getTokenValue());
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
// =================== 登录相关操作 ===================
|
||||
|
||||
/**
|
||||
* 在当前会话上登录id
|
||||
* @param loginId 登录id ,建议的类型:(long | int | String)
|
||||
*/
|
||||
public void setLoginId(Object loginId) {
|
||||
|
||||
// 1、获取相应对象
|
||||
HttpServletRequest request = SaTokenManager.getSta().getCurrRequest();
|
||||
SaTokenConfig config = SaTokenManager.getConfig();
|
||||
SaTokenDao dao = SaTokenManager.getDao();
|
||||
|
||||
// 2、获取tokenValue
|
||||
String tokenValue = getTokenValueByLoginId(loginId); // 获取旧tokenValue
|
||||
if(tokenValue == null){ // 为null则创建一个新的
|
||||
tokenValue = randomTokenValue(loginId);
|
||||
} else {
|
||||
// 不为null, 并且配置不共享,则:将原来的标记为[被顶替]
|
||||
if(config.getIsShare() == false){
|
||||
// dao.delKey(getKeyTokenValue(tokenValue));
|
||||
dao.updateValue(getKeyTokenValue(tokenValue), NotLoginException.BE_REPLACED);
|
||||
tokenValue = randomTokenValue(loginId);
|
||||
}
|
||||
}
|
||||
|
||||
// 3、持久化
|
||||
dao.setValue(getKeyTokenValue(tokenValue), String.valueOf(loginId), config.getTimeout()); // token -> uid
|
||||
dao.setValue(getKeyLoginId(loginId), tokenValue, config.getTimeout()); // uid -> token
|
||||
request.setAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY, tokenValue); // 保存到本次request里
|
||||
if(config.getIsReadCookie() == true){
|
||||
SaTokenManager.getSaCookieOper().addCookie(SaTokenManager.getSta().getResponse(), getTokenName(), tokenValue, "/", (int)config.getTimeout()); // cookie注入
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话注销登录
|
||||
*/
|
||||
public void logout() {
|
||||
// 如果连token都没有,那么无需执行任何操作
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return;
|
||||
}
|
||||
// 如果打开了cookie模式,第一步,先把cookie清除掉
|
||||
if(SaTokenManager.getConfig().getIsReadCookie() == true){
|
||||
SaTokenManager.getSaCookieOper().delCookie(SaTokenManager.getSta().getCurrRequest(), SaTokenManager.getSta().getResponse(), getTokenName());
|
||||
}
|
||||
// 尝试从db中获取loginId值
|
||||
String loginId = SaTokenManager.getDao().getValue(getKeyTokenValue(tokenValue));
|
||||
// 如果根本查不到loginId,那么也无需执行任何操作
|
||||
if(loginId == null) {
|
||||
return;
|
||||
}
|
||||
// 如果已过期或被顶替或被挤下线,那么只删除此token即可
|
||||
if(loginId.equals(NotLoginException.TOKEN_TIMEOUT) || loginId.equals(NotLoginException.BE_REPLACED) || loginId.equals(NotLoginException.KICK_OUT)) {
|
||||
return;
|
||||
}
|
||||
// 至此,已经是一个正常的loginId,开始三清
|
||||
logoutByLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定loginId的会话注销登录(清退下线)
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public void logoutByLoginId(Object loginId) {
|
||||
|
||||
// 获取相应tokenValue
|
||||
String tokenValue = getTokenValueByLoginId(loginId);
|
||||
if(tokenValue == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除相关数据
|
||||
SaTokenManager.getDao().delKey(getKeyTokenValue(tokenValue)); // 清除token-id键值对
|
||||
SaTokenManager.getDao().delKey(getKeyLoginId(loginId)); // 清除id-token键值对
|
||||
SaTokenManager.getDao().deleteSaSession(getKeySession(loginId)); // 清除其session
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定loginId的会话注销登录(踢人下线)
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public void kickoutByLoginId(Object loginId) {
|
||||
|
||||
// 获取相应tokenValue
|
||||
String tokenValue = getTokenValueByLoginId(loginId);
|
||||
if(tokenValue == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除相关数据
|
||||
SaTokenManager.getDao().updateValue(getKeyTokenValue(tokenValue), NotLoginException.KICK_OUT); // 标记:已被踢下线
|
||||
SaTokenManager.getDao().delKey(getKeyLoginId(loginId)); // 清除id-token键值对
|
||||
SaTokenManager.getDao().deleteSaSession(getKeySession(loginId)); // 清除其session
|
||||
}
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
* 获取当前会话是否已经登录
|
||||
* @return 是否已登录
|
||||
*/
|
||||
public boolean isLogin() {
|
||||
// 判断条件:不为null,并且不在异常项集合里
|
||||
return getLoginIdDefaultNull() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验当前会话是否已经登录,如未登录,则抛出异常
|
||||
*/
|
||||
public void checkLogin() {
|
||||
getLoginId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 如果未登录,则抛出异常
|
||||
* @return .
|
||||
*/
|
||||
public Object getLoginId() {
|
||||
// 如果获取不到token,则抛出:无token
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
throw NotLoginException.newInstance(loginKey, NotLoginException.NOT_TOKEN);
|
||||
}
|
||||
// 查找此token对应loginId, 则抛出:无效token
|
||||
String loginId = SaTokenManager.getDao().getValue(getKeyTokenValue(tokenValue));
|
||||
if(loginId == null) {
|
||||
throw NotLoginException.newInstance(loginKey, NotLoginException.INVALID_TOKEN);
|
||||
}
|
||||
// 如果是已经过期,则抛出已经过期
|
||||
if(loginId.equals(NotLoginException.TOKEN_TIMEOUT)) {
|
||||
throw NotLoginException.newInstance(loginKey, NotLoginException.TOKEN_TIMEOUT);
|
||||
}
|
||||
// 如果是已经被顶替下去了, 则抛出:已被顶下线
|
||||
if(loginId.equals(NotLoginException.BE_REPLACED)) {
|
||||
throw NotLoginException.newInstance(loginKey, NotLoginException.BE_REPLACED);
|
||||
}
|
||||
// 如果是已经被踢下线了, 则抛出:已被踢下线
|
||||
if(loginId.equals(NotLoginException.KICK_OUT)) {
|
||||
throw NotLoginException.newInstance(loginKey, NotLoginException.KICK_OUT);
|
||||
}
|
||||
// 至此,返回loginId
|
||||
return loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 如果未登录,则返回默认值
|
||||
* @param defaultValue .
|
||||
* @return .
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T>T getLoginId(T defaultValue) {
|
||||
Object loginId = getLoginIdDefaultNull();
|
||||
// 如果loginId为null,则返回默认值
|
||||
if(loginId == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
// 开始尝试类型转换,只尝试三种类型:int、long、String
|
||||
if(defaultValue instanceof Integer) {
|
||||
return (T)Integer.valueOf(loginId.toString());
|
||||
}
|
||||
if(defaultValue instanceof Long) {
|
||||
return (T)Long.valueOf(loginId.toString());
|
||||
}
|
||||
if(defaultValue instanceof String) {
|
||||
return (T)loginId.toString();
|
||||
}
|
||||
return (T)loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 如果未登录,则返回null
|
||||
* @return .
|
||||
*/
|
||||
public Object getLoginIdDefaultNull() {
|
||||
// 如果连token都是空的,则直接返回
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return null;
|
||||
}
|
||||
// loginId为null或者在异常项里面,均视为未登录
|
||||
Object loginId = SaTokenManager.getDao().getValue(getKeyTokenValue(tokenValue));
|
||||
if(loginId == null || NotLoginException.ABNORMAL_LIST.contains(loginId)) {
|
||||
return null;
|
||||
}
|
||||
// 执行到此,证明loginId已经是个正常的账号id了
|
||||
return loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 并转换为String
|
||||
* @return
|
||||
*/
|
||||
public String getLoginIdAsString() {
|
||||
return String.valueOf(getLoginId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 并转换为int
|
||||
* @return .
|
||||
*/
|
||||
public int getLoginIdAsInt() {
|
||||
// Object loginId = getLoginId();
|
||||
// if(loginId instanceof Integer) {
|
||||
// return (Integer)loginId;
|
||||
// }
|
||||
return Integer.valueOf(String.valueOf(getLoginId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 并转换为long
|
||||
* @return .
|
||||
*/
|
||||
public long getLoginIdAsLong() {
|
||||
// Object loginId = getLoginId();
|
||||
// if(loginId instanceof Long) {
|
||||
// return (Long)loginId;
|
||||
// }
|
||||
return Long.valueOf(String.valueOf(getLoginId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定token对应的登录id,如果未登录,则返回 null
|
||||
* @return .
|
||||
*/
|
||||
public Object getLoginIdByToken(String tokenValue) {
|
||||
if(tokenValue != null) {
|
||||
Object loginId = SaTokenManager.getDao().getValue(getKeyTokenValue(tokenValue));
|
||||
if(loginId != null) {
|
||||
return loginId;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// =================== session相关 ===================
|
||||
|
||||
/**
|
||||
* 获取指定key的session, 如果没有,isCreate=是否新建并返回
|
||||
* @param sessionId .
|
||||
* @param isCreate .
|
||||
* @return .
|
||||
*/
|
||||
protected SaSession getSessionBySessionId(String sessionId, boolean isCreate) {
|
||||
SaSession session = SaTokenManager.getDao().getSaSession(sessionId);
|
||||
if(session == null && isCreate) {
|
||||
session = new SaSession(sessionId);
|
||||
SaTokenManager.getDao().saveSaSession(session, SaTokenManager.getConfig().getTimeout());
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的session, 如果没有,isCreate=是否新建并返回
|
||||
* @param loginId 登录id
|
||||
* @param isCreate 是否新建
|
||||
* @return SaSession
|
||||
*/
|
||||
public SaSession getSessionByLoginId(Object loginId, boolean isCreate) {
|
||||
return getSessionBySessionId(getKeySession(loginId), isCreate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定loginId的session
|
||||
* @param loginId .
|
||||
* @return .
|
||||
*/
|
||||
public SaSession getSessionByLoginId(Object loginId) {
|
||||
return getSessionByLoginId(loginId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的session
|
||||
* @return
|
||||
*/
|
||||
public SaSession getSession() {
|
||||
return getSessionByLoginId(getLoginId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== 权限验证操作 ===================
|
||||
|
||||
/**
|
||||
* 指定loginId是否含有指定权限
|
||||
* @param loginId .
|
||||
* @param pcode .
|
||||
* @return .
|
||||
*/
|
||||
public boolean hasPermission(Object loginId, Object pcode) {
|
||||
List<Object> pcodeList = SaTokenManager.getStp().getPermissionCodeList(loginId, loginKey);
|
||||
return !(pcodeList == null || pcodeList.contains(pcode) == false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会话是否含有指定权限
|
||||
* @param pcode .
|
||||
* @return .
|
||||
*/
|
||||
public boolean hasPermission(Object pcode) {
|
||||
return hasPermission(getLoginId(), pcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 , 没有就抛出异常
|
||||
* @param pcode .
|
||||
*/
|
||||
public void checkPermission(Object pcode) {
|
||||
if(hasPermission(pcode) == false) {
|
||||
throw new NotPermissionException(pcode, this.loginKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 , 【指定多个,必须全都有】
|
||||
* @param pcodeArray .
|
||||
*/
|
||||
public void checkPermissionAnd(Object... pcodeArray){
|
||||
Object loginId = getLoginId();
|
||||
List<Object> pcodeList = SaTokenManager.getStp().getPermissionCodeList(loginId, loginKey);
|
||||
for (Object pcode : pcodeArray) {
|
||||
if(pcodeList.contains(pcode) == false) {
|
||||
throw new NotPermissionException(pcode, this.loginKey); // 没有权限抛出异常
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 , 【指定多个,有一个就可以了】
|
||||
* @param pcodeArray .
|
||||
*/
|
||||
public void checkPermissionOr(Object... pcodeArray){
|
||||
Object loginId = getLoginId();
|
||||
List<Object> pcodeList = SaTokenManager.getStp().getPermissionCodeList(loginId, loginKey);
|
||||
for (Object pcode : pcodeArray) {
|
||||
if(pcodeList.contains(pcode) == true) {
|
||||
return; // 有的话提前退出
|
||||
}
|
||||
}
|
||||
if(pcodeArray.length > 0) {
|
||||
throw new NotPermissionException(pcodeArray[0], this.loginKey); // 没有权限抛出异常
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =================== 返回相应key ===================
|
||||
|
||||
/**
|
||||
* 获取key:客户端 tokenName
|
||||
* @return
|
||||
*/
|
||||
public String getKeyTokenName() {
|
||||
return SaTokenManager.getConfig().getTokenName();
|
||||
}
|
||||
/**
|
||||
* 获取key: tokenValue 持久化
|
||||
* @param tokenValue .
|
||||
* @return
|
||||
*/
|
||||
public String getKeyTokenValue(String tokenValue) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + loginKey + ":token:" + tokenValue;
|
||||
}
|
||||
/**
|
||||
* 获取key: id 持久化
|
||||
* @param loginId .
|
||||
* @return
|
||||
*/
|
||||
public String getKeyLoginId(Object loginId) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + loginKey + ":id:" + loginId;
|
||||
}
|
||||
/**
|
||||
* 获取key: session 持久化
|
||||
* @param loginId .
|
||||
* @return .
|
||||
*/
|
||||
public String getKeySession(Object loginId) {
|
||||
return SaTokenManager.getConfig().getTokenName() + ":" + loginKey + ":session:" + loginId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
package cn.dev33.satoken.stp;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* 一个默认的实现
|
||||
* @author kong
|
||||
*/
|
||||
@Service
|
||||
public class StpUtil {
|
||||
|
||||
/**
|
||||
* 底层的 StpLogic 对象
|
||||
*/
|
||||
public static StpLogic stpLogic = new StpLogic("login");
|
||||
|
||||
|
||||
// =================== 获取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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话的token信息:tokenName与tokenValue
|
||||
* @return 一个Map对象
|
||||
*/
|
||||
public static Map<String, String> 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();
|
||||
}
|
||||
|
||||
// =================== 权限验证操作 ===================
|
||||
|
||||
/**
|
||||
* 指定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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
/**
|
||||
* sa-token 工具类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenInsideUtil {
|
||||
|
||||
|
||||
/**
|
||||
* sa-token 版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.5.1";
|
||||
|
||||
/**
|
||||
* sa-token 开源地址
|
||||
*/
|
||||
public static final String GITHUB_URL= "https://github.com/click33/sa-token";
|
||||
|
||||
/**
|
||||
* 打印 sa-token
|
||||
*/
|
||||
public static void printSaToken() {
|
||||
String str =
|
||||
"____ ____ ___ ____ _ _ ____ _ _ \r\n" +
|
||||
"[__ |__| __ | | | |_/ |___ |\\ | \r\n" +
|
||||
"___] | | | |__| | \\_ |___ | \\| \r\n" +
|
||||
"sa-token:" + VERSION_NO + " \r\n" +
|
||||
"GitHub:" + GITHUB_URL; // + "\r\n";
|
||||
System.out.println(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果token为本次请求新创建的,则以此字符串为key存储在当前request中 JUST_CREATED_SAVE_KEY
|
||||
*/
|
||||
public static final String JUST_CREATED_SAVE_KEY = "JUST_CREATED_SAVE_KEY_";
|
||||
|
||||
|
||||
}
|
||||
+174
-52
@@ -1,11 +1,11 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150" style="margin-bottom: 10px;">
|
||||
<img alt="logo" src="https://gitee.com/sz6/sa-token/raw/master/sa-token-doc/doc/logo.png" width="150" height="150">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.5.1</h1>
|
||||
<h4 align="center">一个JavaWeb轻量级权限认证框架,功能全面,上手简单</h4>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.13.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.5.1-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.13.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token/stargazers"><img src="https://img.shields.io/github/stars/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/watchers"><img src="https://img.shields.io/github/watchers/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/network/members"><img src="https://img.shields.io/github/forks/click33/sa-token"></a>
|
||||
@@ -16,76 +16,198 @@
|
||||
---
|
||||
|
||||
|
||||
## 😘 在线资料
|
||||
- ##### ⚡ [官网首页: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、分布式Session等等难题,无一不有着各种优秀框架大行其道
|
||||
|
||||
然而当我们把视线放低,那些最基础的有如:登录认证、权限认证、Session会话等基础问题却一直被两大上古神兽 `Apache Shiro`、`Spring Security` 所把持
|
||||
|
||||
在此并非专门diss此两大框架,诚然两大框架背景强大,历史悠久,其生态也比较齐全。但是它们毕竟已经是十几年前的产物,那是一个还在用 `jsp` 写页面的时代,两大框架的很多功能都是为jsp那一套量身定做。
|
||||
|
||||
在前后台分离已成标配的今天,两大框架的很多设计理念已经比较滞后,已经不能和我们的项目进行无缝适配,很多功能点都需要进行二次封装,甚至找一大堆扩展插件才能集成,已经逐渐不太适合现代化项目的应用
|
||||
|
||||
所以,为什么不能有一个自底向上,从最基础的登录、权限做起,以业务需求为核心,做到开箱即用的轻量级权限认证框架?
|
||||
|
||||
秉承着这个目的,`sa-token` 诞生了!
|
||||
|
||||
|
||||
## Sa-Token 优点?
|
||||
|
||||
sa-token架构设计精简,不引入各种复杂的概念,如丝般顺滑的API调用,大量高级特性统统只需一行代码即可实现
|
||||
- 在sa-token之前,权限认证业务概念纷飞、代码复杂,在sa-token之后,权限认证将会变成:逻辑清晰,架构简单,人人可写
|
||||
- 在sa-token之前,你会在百度上频繁搜索: xx框架如何集成Redis?前后台分离下如何鉴权?踢人下线的原理是什么?在sa-token之后,你将轻松知道这一切的答案
|
||||
- 在sa-token之前,权限认证、分布式会话、单点登录、多账号认证,你需要找不同的框架,在sa-token之后,你只需要这一个框架就足够了
|
||||
|
||||
与其它权限认证框架相比,你将会从以下方面感受到 `sa-token` 的优势:
|
||||
1. **简单** :可零配置启动框架,真正的开箱即用,低成本上手
|
||||
2. **强大** :目前已集成几十项权限相关特性,涵盖了大部分业务场景的解决方案
|
||||
3. **易用** :如丝般顺滑的API调用,大量高级特性统统只需一行代码即可实现
|
||||
4. **高扩展** :几乎所有组件都提供了扩展接口,90%以上的逻辑都可以按需重写
|
||||
|
||||
有了sa-token,你所有的权限认证问题,都不再是问题!
|
||||
|
||||
|
||||
## Sa-Token 能做什么?
|
||||
- **登录验证** —— 轻松登录鉴权,并提供五种细分场景值
|
||||
- **权限验证** —— 适配RBAC权限模型,不同角色不同授权
|
||||
- **Session会话** —— 专业的数据缓存中心
|
||||
- **踢人下线** —— 将违规用户立刻清退下线
|
||||
- **持久层扩展** —— 可集成Redis、Memcached等专业缓存中间件,重启数据不丢失
|
||||
- **分布式会话** —— 提供jwt集成和共享数据中心两种分布式会话方案
|
||||
- **单点登录** —— 一处登录,处处通行
|
||||
- **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- **临时身份切换** —— 将会话身份临时切换为其它账号
|
||||
- **无Cookie模式** —— APP、小程序等前后台分离场景
|
||||
- **同端互斥登录** —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录
|
||||
- **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- **路由拦截式鉴权** —— 设定全局路由拦截,并排除指定路由
|
||||
- **自动续签** —— 提供两种token过期策略,灵活搭配使用,还可自动续签
|
||||
- **会话治理** —— 提供方便灵活的会话查询接口
|
||||
- **组件自动注入** —— 零配置与Spring等框架集成
|
||||
- **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
|
||||
|
||||
|
||||
## 代码示例
|
||||
|
||||
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众多,请恕此处无法为您逐一展示,更多示例请戳官方在线文档
|
||||
|
||||
|
||||
- **没有复杂的封装!不要任何的配置!先写入,后鉴权!只需这两行简单的调用,即可轻松完成系统登录鉴权!**
|
||||
## 迭代模式
|
||||
sa-token的功能提案主要来源于社区,这意味着人人都可以参与到sa-token的功能定制,决定框架的未来走向,
|
||||
如果你有好的想法,可以在issues提出或者加入群一起交流,对于社区的提出的功能要求,主要分为以下几类:
|
||||
- 对框架新增特性功能且比较简单,会在第一时间进行开发
|
||||
- 对框架新增特性功能但比较复杂,会延后几个版本制定相应的计划后进行开发
|
||||
- 与框架设计理念不太相符,或超出权限认证范畴,将会视需求人数决定是否开发
|
||||
|
||||
|
||||
## 🔥 框架设计思想
|
||||
与其它权限认证框架相比,`sa-token`尽力保证两点:
|
||||
- 上手简单:能自动化的配置全部自动化,不让你费脑子
|
||||
- 功能强大:能涵盖的功能全部涵盖,不让你用个框架还要自己给框架打各种补丁
|
||||
## 参与贡献
|
||||
众人拾柴火焰高,万丈高楼众人起!
|
||||
sa-token秉承着开放的思想,欢迎大家贡献代码,为框架添砖加瓦,对框架有卓越贡献者将会出现在贡献者名单里
|
||||
|
||||
|
||||
## 💦️️ 涵盖功能
|
||||
- ⚡ **登录验证** —— 轻松登录鉴权,并提供五种细分场景值
|
||||
- ⚡ **权限验证** —— 拦截违规调用,不同角色不同授权
|
||||
- ⚡ **自定义session会话** —— 专业的数据缓存中心
|
||||
- ⚡ **踢人下线** —— 将违规用户立刻清退下线
|
||||
- ⚡ **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- ⚡ **持久层扩展** —— 可集成redis、MongoDB等专业缓存中间件
|
||||
- ⚡ **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- ⚡ **无cookie模式** —— APP、小程序等前后台分离场景
|
||||
- ⚡ **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- ⚡ **组件自动注入** —— 零配置与Spring等框架集成
|
||||
- ⚡ **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
|
||||
|
||||
|
||||
## 🔨 贡献代码
|
||||
sa-token欢迎大家贡献代码,为框架添砖加瓦
|
||||
1. 在github上fork一份到自己的仓库
|
||||
1. 在gitee或者github上fork一份代码到自己的仓库
|
||||
2. clone自己的仓库到本地电脑
|
||||
3. 在本地电脑修改、commit、push
|
||||
4. 提交pr(点击:New Pull Request)
|
||||
5. 等待合并
|
||||
|
||||
|
||||
## 🌱 建议贡献的地方
|
||||
- 修复源码现有bug,或增加新的实用功能
|
||||
- 完善在线文档,或者修复现有错误之处
|
||||
- 更多demo示例:比如SSM版搭建步骤
|
||||
- 您可以参考项目issues与需求墙进行贡献
|
||||
- 如果更新实用功能,可在文档友情链接处留下自己的推广链接
|
||||
作者寄语:参与贡献不光只有提交代码一个选择,点一个star、提一个issues都是对开源项目的促进,
|
||||
如果框架帮助到了你,欢迎你把框架推荐给你的朋友、同事使用,为sa-token的推广做一份贡献
|
||||
|
||||
|
||||
## 🚀 友情链接
|
||||
[**[ okhttps ]** 一个轻量级http通信框架,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
## 建议贡献的地方
|
||||
目前框架的主要有以下部分需要大家一起参与贡献:
|
||||
- 核心代码:该部分需要开发者了解整个框架的架构,遵循已有代码规范进行bug修复或提交新功能
|
||||
- 文档部分:需要以清晰明了的语句书写文档,力求简单易读,授人以鱼同时更授人以渔
|
||||
- 社区建设:如果框架帮助到了您,希望您可以加入qq群参与交流,对不熟悉框架的新人进行排难解惑
|
||||
- 框架推广:一个优秀的开源项目不能仅靠闭门造车,它还需要一定的推广方案让更多的人一起参与到项目中
|
||||
- 其它部分:您可以参考项目issues与需求墙进行贡献
|
||||
|
||||
|
||||
## 😎 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) ,欢迎你的加入
|
||||
## 贡献者名单
|
||||
[省长](https://gitee.com/sz6)、
|
||||
[RockMan](https://gitee.com/njx33)、
|
||||
[click33](https://github.com/click33)、
|
||||
[AppleOfGray](https://gitee.com/appleOfGray)、
|
||||
[Auster](https://github.com/auster9021)、
|
||||
[ZhuBJ0510](https://gitee.com/zhubj0510)、
|
||||
[legg](https://gitee.com/legg321)、
|
||||
[xiaoshitou](https://gitee.com/smallstoneZ)、
|
||||
[zhangjiaxiaozhuo](https://gitee.com/zhangjiaxiaozhuo)、
|
||||
[离你多远](https://gitee.com/liniduoyuan)
|
||||
|
||||
|
||||
## 知乎专栏
|
||||
- [初识sa-token,一行代码搞定登录授权!](https://zhuanlan.zhihu.com/p/344106099)
|
||||
- [一个登录功能也能玩出这么多花样?sa-token带你轻松搞定多地登录、单地登录、同端互斥登录](https://zhuanlan.zhihu.com/p/344511415)
|
||||
- [浅谈踢人下线的设计思路!(附代码实现方案)](https://zhuanlan.zhihu.com/p/345844002)
|
||||
- 文章已在 [csdn](https://blog.csdn.net/shengzhang_/article/details/112593247)、
|
||||
[掘金](https://juejin.cn/post/6917250126650015751)、
|
||||
[开源中国](https://my.oschina.net/u/3503445/blog/4897816)、
|
||||
[博客园](https://www.cnblogs.com/shengzhang/p/14275558.html)、
|
||||
[知乎](https://zhuanlan.zhihu.com/p/344106099)
|
||||
等平台连载中...欢迎投稿
|
||||
|
||||
|
||||
## 使用sa-token的开源项目
|
||||
[**[ sa-plus]** 一个基于springboot架构的快速开发框架,内置代码生成器](https://gitee.com/sz6/sa-plus)
|
||||
|
||||
如果您的项目使用了sa-token,欢迎提交pr
|
||||
|
||||
|
||||
## 友情链接
|
||||
[**[ okhttps ]** 一个轻量级http通信框架,API设计无比优雅,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
|
||||
|
||||
## 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM)
|
||||
|
||||

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

|
||||
|
||||
<br>
|
||||
|
||||
@@ -8,14 +8,22 @@
|
||||
- **使用**
|
||||
- [登录验证](/use/login-auth)
|
||||
- [权限验证](/use/jur-auth)
|
||||
- [session会话](/use/session)
|
||||
- [Session会话](/use/session)
|
||||
- [踢人下线](/use/kick)
|
||||
- [持久层扩展(集成redis)](/use/dao-extend)
|
||||
- [无cookie模式(前后台分离)](/use/not-cookie)
|
||||
- [持久层扩展(集成Redis)](/use/dao-extend)
|
||||
- [无Cookie模式(前后台分离)](/use/not-cookie)
|
||||
- [模拟他人](/use/mock-person)
|
||||
- [多账号验证](/use/many-account)
|
||||
- [同端互斥登录](/use/mutex-login)
|
||||
- [注解式鉴权](/use/at-check)
|
||||
- [路由拦截式鉴权](/use/route-check)
|
||||
- [花式token](/use/token-style)
|
||||
- [框架配置](/use/config)
|
||||
- [会话治理](/use/search-session)
|
||||
|
||||
- **进阶**
|
||||
- [集群、分布式](/senior/dcs)
|
||||
- [单点登录](/senior/sso)
|
||||
- [多账号验证](/use/many-account)
|
||||
|
||||
- **其它**
|
||||
- [常见问题](/more/common-questions)
|
||||
@@ -24,6 +32,8 @@
|
||||
|
||||
- **附录**
|
||||
- [未登录场景值](/fun/not-login-scene)
|
||||
- [token有效期详解](/fun/token-timeout)
|
||||
- [TokenInfo参数详解](/fun/token-info)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,8 +5,19 @@
|
||||
|
||||
|
||||
## 何为场景值
|
||||
- 在前面的章节中,我们了解到,在会话未登录的情况下尝试获取`loginId`会使框架抛出`NotLoginException`异常,而同为未登录异常却有五种抛出场景的区分 <br>
|
||||
- 那么,如何获取场景值呢?废话少说直接上代码:
|
||||
在前面的章节中,我们了解到,在会话未登录的情况下尝试获取`loginId`会使框架抛出`NotLoginException`异常,而同为未登录异常却有五种抛出场景的区分
|
||||
|
||||
| 场景值 | 对应常量 | 含义说明 |
|
||||
|--- |--- |--- |
|
||||
| -1 | NotLoginException.NOT_TOKEN | 未能从请求中读取到token |
|
||||
| -2 | NotLoginException.INVALID_TOKEN| 已读取到token,但是token无效 |
|
||||
| -3 | NotLoginException.TOKEN_TIMEOUT| 已读取到token,但是token已经过期 |
|
||||
| -4 | NotLoginException.BE_REPLACED| 已读取到token,但是token已被顶下线 |
|
||||
| -5 | NotLoginException.KICK_OUT| 已读取到token,但是token已被踢下线 |
|
||||
|
||||
|
||||
|
||||
那么,如何获取场景值呢?废话少说直接上代码:
|
||||
|
||||
|
||||
``` java
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# SaTokenInfo 参数详解
|
||||
|
||||
token信息Model: 用来描述一个token的常用参数
|
||||
|
||||
``` js
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"data": {
|
||||
"tokenName": "satoken", // token名称
|
||||
"tokenValue": "e67b99f1-3d7a-4a8d-bb2f-e888a0805633", // token值
|
||||
"isLogin": true, // 此token是否已经登录
|
||||
"loginId": "10001", // 此token对应的LoginId,未登录时为null
|
||||
"loginKey": "login", // LoginKey账号体系标识
|
||||
"tokenTimeout": 2591977, // token剩余有效期 (单位: 秒)
|
||||
"sessionTimeout": 2591977, // User-Session剩余有效时间 (单位: 秒)
|
||||
"tokenSessionTimeout": -2, // Token-Session剩余有效时间 (单位: 秒)
|
||||
"tokenActivityTimeout": -1, // token剩余无操作有效时间 (单位: 秒)
|
||||
"loginDevice": "default-device" // 登录设备标识
|
||||
},
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,49 @@
|
||||
# token有效期详解
|
||||
|
||||
<!-- 本篇介绍token有效期的详细用法 -->
|
||||
|
||||
`sa-token` 提供两种token自动过期策略,分别是`timeout`与`activity-timeout`,其详细用法如下:
|
||||
|
||||
|
||||
### timeout
|
||||
1. `timeout`代表token的长久有效期,单位/秒,例如将其配置为`2592000`(30天),代表在30天后,token必定过期,无法继续使用
|
||||
2. `timeout`无法续签,想要继续使用必须重新登录
|
||||
3. `timeout`的值配置为-1后,代表永久有效,不会过期
|
||||
|
||||
|
||||
### activity-timeout
|
||||
1. `activity-timeout`代表临时有效期,单位/秒,例如将其配置为`1800`(30分钟),代表用户如果30分钟无操作,则此token会立即过期
|
||||
2. 如果在30分钟内用户有操作,则会再次续签30分钟,用户如果一直操作则会一直续签,直到连续30分钟无操作,token才会过期
|
||||
3. `activity-timeout`的值配置为-1后,代表永久有效,不会过期,此时也无需频繁续签
|
||||
|
||||
|
||||
### 关于activity-timeout的续签
|
||||
如果`activity-timeout`配置了大于零的值,`sa-token`会在登录时开始计时,在每次直接或间接调用`getLoginId()`时进行一次过期检查与续签操作。
|
||||
此时会有两种情况:
|
||||
1. 一种是会话无操作时间太长,token已经过期,此时框架会抛出`NotLoginException`异常(场景值=-3),
|
||||
2. 另一种则是会话在`activity-timeout`有效期内通过检查,此时token可以成功续签
|
||||
|
||||
|
||||
### 我可以手动续签吗?
|
||||
**可以!**
|
||||
如果框架的自动续签算法无法满足您的业务需求,你可以进行手动续签,`sa-token`提供两个API供你操作:
|
||||
1. `StpUtil.checkActivityTimeout()`: 检查当前token 是否已经[临时过期],如果已经过期则抛出异常
|
||||
2. `StpUtil.updateLastActivityToNow()`: 续签当前token:(将 [最后操作时间] 更新为当前时间戳)
|
||||
|
||||
注意:在手动续签时,即时token已经 [临时过期] 也可续签成功,如果此场景下需要提示续签失败,可采用先检查再续签的形式保证token有效性
|
||||
|
||||
例如以下代码:
|
||||
``` java
|
||||
// 先检查是否已过期
|
||||
StpUtil.checkActivityTimeout();
|
||||
// 检查通过后继续续签
|
||||
StpUtil.updateLastActivityToNow();
|
||||
```
|
||||
|
||||
同时,你还可以关闭框架的自动续签(在配置文件中配置 `autoRenew=false` ),此时续签操作完全由开发者控制,框架不再自动进行任何续签操作
|
||||
|
||||
|
||||
### timeout与activity-timeout可以同时使用吗?
|
||||
**可以同时使用!**
|
||||
两者的认证逻辑彼此独立,互不干扰,可以同时使用。
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
.logo-box {position: absolute;left: 30px;top: 10px;cursor: pointer;color: #000;}
|
||||
.logo-box img {width: 50px;height: 50px;vertical-align: middle;}
|
||||
.logo-box .logo-text {display: inline-block;vertical-align: middle;font-size: 24px;font-weight: 400;}
|
||||
#main {padding-bottom: 100px;}
|
||||
#main h2 {font-size: 1.6rem;}
|
||||
#main h3 {font-size: 1.25rem;}
|
||||
|
||||
.main-box .markdown-section{ padding: 30px 20px;}
|
||||
@media screen and (max-width: 800px) {
|
||||
.logo-box {display: none;}
|
||||
.main-box .markdown-section{max-width: 1000px;}
|
||||
}
|
||||
|
||||
/* ============== 样式优化 ================ */
|
||||
|
||||
/* 背景变黑 */
|
||||
.main-box [data-lang]{padding: 0px !important; border-radius: 10px; overflow: hidden;}
|
||||
.main-box [class^="lang-"]{/* color: red !important; */ border: 0px red solid; padding: 1.2em; background-color: #222; color: #FFF;}
|
||||
.main-box [class^="lang-api"]{color: #FFF;}
|
||||
|
||||
|
||||
.token.string{color: #65B042;}
|
||||
.token.number{color: #2487CC;}
|
||||
.token.punctuation{color: #FFF;}
|
||||
.token.comment{color: #FFF;}
|
||||
|
||||
/* .main-box [class="lang-api"]{color: red;} */
|
||||
|
||||
#main table{margin-left: 25px;}
|
||||
@media screen and (min-width: 800px) {
|
||||
#main table tr th{min-width: 150px;}
|
||||
}
|
||||
+65
-15
@@ -4,13 +4,12 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>sa-token</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Description">
|
||||
<meta name="description" content="sa-token是一个java权限认证框架,功能全面,上手简单,登录验证、权限验证、Session会话、踢人下线、集成Redis、分布式会话、单点登录、前后台分离、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<meta name="keywords" content="sa-token,sa-token框架,sa-token文档,java权限认证">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="keywords" content="sa-token|sa-token框架|sa-token文档|sa-token在线文档|权限认证框架">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,强大、简单、好用,登录验证、权限验证、自定义session会话、踢人下线、持久层扩展、无cookie模式、模拟他人账号、多账号体系、注解式鉴权、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<link rel="stylesheet" href="https://unpkg.com/docsify@4.11.3/lib/themes/vue.css">
|
||||
<link rel="stylesheet" href="./index.css">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="logo.png">
|
||||
<link rel="stylesheet" href="./lib/index.css">
|
||||
<link rel="stylesheet" href="https://unpkg.zhimg.com/docsify@4.11.3/lib/themes/vue.css">
|
||||
</head>
|
||||
<body>
|
||||
<a href="/">
|
||||
@@ -20,6 +19,19 @@
|
||||
</div>
|
||||
</a>
|
||||
<nav>
|
||||
<select onchange="location.href=this.value">
|
||||
<option value="http://sa-token.dev33.cn/doc/index.html">最新版</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.12.1/doc/index.html">v1.12.1</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.12.0/doc/index.html">v1.12.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.11.0/doc/index.html">v1.11.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.10.0/doc/index.html">v1.10.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.9.0/doc/index.html">v1.9.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.8.0/doc/index.html">v1.8.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.7.0/doc/index.html">v1.7.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.6.0/doc/index.html">v1.6.0</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.5.1/doc/index.html">v1.5.1</option>
|
||||
<option value="http://sa-token.dev33.cn/v/v1.4.0/doc/index.html">v1.4.0</option>
|
||||
</select>
|
||||
<a href="/">首页</a>
|
||||
<a href="/doc/">文档</a>
|
||||
<a href="http://sa-app.dev33.cn/wall.html?name=sa-token" target="_blank">需求墙</a>
|
||||
@@ -30,12 +42,12 @@
|
||||
</div>
|
||||
<script>
|
||||
var name = '<img style="width: 50px; height: 50px; vertical-align: middle;" src="logo.png" alt="logo" /> ';
|
||||
name += '<b style="font-size: 24px; vertical-align: middle;">sa-token</b> <sub>v1.5.1</sub>'
|
||||
name += '<b style="font-size: 24px; vertical-align: middle;">sa-token</b> <sub>v1.13.0</sub>'
|
||||
window.$docsify = {
|
||||
name: name, // 名字
|
||||
repo: 'https://github.com/click33/sa-token', // github地址
|
||||
// themeColor: '#06A3D7', // 主题颜色
|
||||
basePath: location.pathname.substr(0, location.pathname.lastIndexOf('/') + 1), // 自动计算项目名字
|
||||
basePath: location.pathname.substr(0, location.pathname.lastIndexOf('/') + 1), // 自动计算项目名字
|
||||
// basePath: '/sa-token-doc/', // 设置文件加载的父路径, 这在一些带项目名部署的文件中非常有效
|
||||
auto2top: true, // 是否在切换页面后回到顶部
|
||||
// coverpage: true, // 开启封面
|
||||
@@ -50,28 +62,31 @@
|
||||
alias: {
|
||||
'/.*/_sidebar.md': '/_sidebar.md'
|
||||
},
|
||||
plugins: [ // 自定义插件
|
||||
plugins: [ // 自定义插件
|
||||
function(hook, vm) {
|
||||
// 解析之后执行
|
||||
hook.afterEach(function(html) {
|
||||
var url = 'https://github.com/click33/sa-token/tree/master/sa-token-doc/doc/' + vm.route.file;
|
||||
var url = 'https://gitee.com/sz6/sa-token/tree/dev/sa-token-doc/doc/' + vm.route.file;
|
||||
var url2 = 'https://github.com/click33/sa-token/tree/dev/sa-token-doc/doc/' + vm.route.file;
|
||||
var footer = [
|
||||
'<br/><br/><br/><br/><br/><br/><br/><hr/>',
|
||||
'<footer>',
|
||||
' <span>发现错误?想参与编辑? <a href="' + url + '" target="_blank">在 GitHub 上编辑此页!</a> </span>',
|
||||
'<span>发现错误?想参与编辑? 在 <a href="' + url + '" target="_blank">Gitee</a> 或 <a href="' + url2 +
|
||||
'" target="_blank">GitHub</a> 上编辑此页!</span>',
|
||||
'</footer>'
|
||||
].join('');
|
||||
return html + footer;
|
||||
});
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script src="https://unpkg.com/docsify@4.9.4/lib/docsify.min.js"></script>
|
||||
<script src="https://unpkg.com/docsify-copy-code@2.1.0/dist/docsify-copy-code.min.js"></script>
|
||||
<script src="https://unpkg.com/prismjs@1.19.0/components/prism-java.min.js"></script>
|
||||
<script src="https://unpkg.zhimg.com/docsify@4.9.4/lib/docsify.min.js"></script>
|
||||
<script src="https://unpkg.zhimg.com/docsify-copy-code@2.1.0/dist/docsify-copy-code.min.js"></script>
|
||||
<script src="https://unpkg.zhimg.com/prismjs@1.19.0/components/prism-java.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
|
||||
<!-- 搜索引擎自动提交 -->
|
||||
<script>
|
||||
(function() {
|
||||
var bp = document.createElement('script');
|
||||
@@ -85,9 +100,44 @@
|
||||
s.parentNode.insertBefore(bp, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- 友盟 -->
|
||||
<div style="height: 0px; overflow: hidden;">
|
||||
<script type="text/javascript" src="https://v1.cnzz.com/z_stat.php?id=1279021391&web_id=1279021391"></script>
|
||||
<script type="text/javascript" src="https://s4.cnzz.com/z_stat.php?id=1279646043&web_id=1279646043"></script>
|
||||
</div>
|
||||
<!-- 百度统计 -->
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?77d7418dd845f98ba1cfee8596eeee3f";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- Gitalk评论 -->
|
||||
<link rel="stylesheet" href="https://unpkg.zhimg.com/gitalk@1.7.0/dist/gitalk.css">
|
||||
<script src="https://unpkg.zhimg.com/docsify@4.11.6/lib/plugins/gitalk.min.js"></script>
|
||||
<script src="https://unpkg.zhimg.com/gitalk@1.7.0/dist/gitalk.min.js"></script>
|
||||
<script>
|
||||
function f5Gitalk() {
|
||||
window.gitalk = new Gitalk({
|
||||
id: location.hash.replace('#', ''),
|
||||
clientID: '19939399448841f818a1',
|
||||
clientSecret: 'af67e0cc14a0f36e171895771c330471cfe36c23',
|
||||
repo: 'sa-token', // 仓库名称
|
||||
owner: 'click33',
|
||||
admin: ['click33'], // 管理员列表
|
||||
// facebook-like distraction free mode
|
||||
distractionFreeMode: false
|
||||
})
|
||||
}
|
||||
f5Gitalk();
|
||||
window.onhashchange = function() {
|
||||
f5Gitalk();
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
body{font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;}
|
||||
.logo-box {position: absolute;left: 30px;top: 10px;cursor: pointer;color: #000;}
|
||||
.logo-box img {width: 50px;height: 50px;vertical-align: middle;}
|
||||
.logo-box .logo-text {display: inline-block;vertical-align: middle;font-size: 24px;font-weight: 400;}
|
||||
#main {padding-bottom: 100px;}
|
||||
#main h2 {font-size: 1.6rem;}
|
||||
#main h3 {font-size: 1.25rem;}
|
||||
|
||||
.main-box .markdown-section{ padding: 30px 20px;}
|
||||
@media screen and (max-width: 800px) {
|
||||
.logo-box {display: none;}
|
||||
.main-box .markdown-section{max-width: 1000px;}
|
||||
}
|
||||
|
||||
/* 左侧树优化 */
|
||||
.sidebar .sidebar-nav>ul>li>p{font-size: 1.2em; margin-top: 10px;}
|
||||
.sidebar ul li a{color: #222;}
|
||||
.sidebar .sidebar-nav>ul>li>ul>li>a{/* color: #222; */font-size: 16px; /* font-weight: 700; */}
|
||||
|
||||
|
||||
/* ============== 样式优化 ================ */
|
||||
|
||||
/* 背景变黑 */
|
||||
.main-box [data-lang]{padding: 0px !important; border-radius: 8px; overflow: hidden;}
|
||||
.main-box [class^="lang-"]{border: 0px red solid; padding: 1.5em 1.2em;/* background-color: #282828; */ background-color: #090300; color: #FFF;}
|
||||
.main-box [data-lang]{overflow: auto;}
|
||||
/* .main-box h2{margin-top: 70px;} */
|
||||
|
||||
/* xml语言样式优化 */
|
||||
.lang-xml .token.comment{color: #CDAB53;}
|
||||
.lang-xml .token.tag *{color: #db2d20;}
|
||||
.lang-xml .token.attr-value{color: #A6E22E;}
|
||||
|
||||
/* java语言样式优化 */
|
||||
.main-box .lang-java{color: #01a252; opacity: 1;}
|
||||
.lang-java .token.keyword{color: #db2d20;}
|
||||
.lang-java .token.namespace,.lang-java .token.namespace *{color: #01A252; opacity: 1;}
|
||||
.lang-java .token.class-name,.lang-java .cm-variable{color: #55b5db; opacity: 1;}
|
||||
.lang-java .token.comment{color: #CDAB53;}
|
||||
.lang-java .token.annotation.punctuation{color: #ddd;}
|
||||
.lang-java .token.punctuation{color: #ddd;}
|
||||
|
||||
/* js语言样式优化 */
|
||||
.main-box .lang-js{color: #01a252;}
|
||||
.lang-js .token.comment{color: #CDAB53;}
|
||||
/* .lang-js .token.string{color: #fded02;} */
|
||||
.lang-js .token.string{color: #ddd;}
|
||||
.lang-js .token.punctuation{color: #ddd;}
|
||||
|
||||
.gt-container{padding: 1.5em; padding-bottom: 100px;}
|
||||
|
||||
/* 调整表格的响应式 */
|
||||
#main table{margin-left: 25px;}
|
||||
@media screen and (min-width: 800px) {
|
||||
#main table tr th{min-width: 150px;}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user