Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d9ed55438 | |||
| 4db058cdf0 | |||
| d776501c3d | |||
| 235e9afcd3 | |||
| 983af6691e | |||
| 6e10bce575 | |||
| 19e81b257e | |||
| a3fa535ddf | |||
| 4ec378e1de | |||
| 9aee951dd6 | |||
| 60bd8dbce9 | |||
| 60a38c81b2 | |||
| 8f14cdc097 | |||
| 1a72945d60 | |||
| ac9fdd5c76 | |||
| ee59f1a2a2 | |||
| 81dbf3394b | |||
| 6c770cd5ce |
@@ -1,339 +1,21 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
MIT License
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
Copyright (c) 2020 www.uviewui.com
|
||||
|
||||
Preamble
|
||||
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:
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
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.
|
||||
@@ -1,56 +1,89 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="http://sa-token.dev33.cn/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" style="margin-bottom: 10px;">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.4.0</h1>
|
||||
<h4 align="center">一个JavaWeb权限认证框架,强大、简单、好用</h4>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.6.0</h1>
|
||||
<h4 align="center">一个JavaWeb轻量级权限认证框架,功能全面,上手简单</h4>
|
||||
<h4 align="center">
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.4.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/language-java-2B9939"></a>
|
||||
<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.6.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token/stargazers"><img src="https://img.shields.io/github/stars/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/watchers"><img src="https://img.shields.io/github/watchers/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/network/members"><img src="https://img.shields.io/github/forks/click33/sa-token"></a>
|
||||
<a href="https://github.com/click33/sa-token/issues"><img src="https://img.shields.io/github/issues/click33/sa-token.svg"></a>
|
||||
<a href="https://github.com/click33/sa-token/blob/master/LICENSE"><img src="https://img.shields.io/github/license/click33/sa-token.svg"></a>
|
||||
</h4>
|
||||
|
||||
## sa-token 😋
|
||||
- 在线文档:[http://sa-token.dev33.cn/](http://sa-token.dev33.cn/)
|
||||
- 需求提交:我们深知一个优秀的项目需要海纳百川,[点我在线提交需求](http://sa-app.dev33.cn/wall.html?name=sa-token)
|
||||
- 开源不易,求鼓励,**点个`star`吧**
|
||||
|
||||
## 框架优点
|
||||
与其它权限认证框架相比,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吧](https://github.com/click33/sa-token)
|
||||
|
||||
|
||||
## ⭐ sa-token是什么?
|
||||
- **sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,有多简单呢?以登录验证为例,你只需要:**
|
||||
``` java
|
||||
// 在登录时写入当前会话的账号id
|
||||
StpUtil.setLoginId(10001);
|
||||
```
|
||||
|
||||
|
||||
- **然后在任意需要验证登录权限的地方:**
|
||||
``` java
|
||||
// 检测是否登录 --- 如果当前会话未登录,下面这句代码会抛出 `NotLoginException`异常
|
||||
StpUtil.checkLogin();
|
||||
```
|
||||
|
||||
|
||||
- **没有复杂的封装!不要任何的配置!先写入,后鉴权!只需这两行简单的调用,即可轻松完成系统登录鉴权!**
|
||||
|
||||
|
||||
## 🔥 框架设计思想
|
||||
与其它权限认证框架相比,`sa-token`尽力保证两点:
|
||||
- 上手简单:能自动化的配置全部自动化,不让你费脑子
|
||||
- 功能强大:能涵盖的功能全部涵盖,不让你用个框架还要自己给框架打各种补丁
|
||||
|
||||
## 涵盖功能
|
||||
- 登录验证
|
||||
- 权限验证
|
||||
- 自定义session会话
|
||||
- 踢人下线
|
||||
- 模拟他人账号
|
||||
- 持久层扩展(集成redis)
|
||||
- 多账号认证体系(比如一个商城项目的user表和admin表)
|
||||
- 无cookie模式(APP、小程序等前后台分离场景)
|
||||
- 注解式鉴权(优雅的将鉴权与业务代码分离)
|
||||
- 零配置与Spring等框架集成
|
||||
- ...
|
||||
|
||||
## 贡献代码
|
||||
## 💦️️ 涵盖功能
|
||||
- ⚡ **登录验证** —— 轻松登录鉴权,并提供五种细分场景值
|
||||
- ⚡ **权限验证** —— 拦截违规调用,不同角色不同授权
|
||||
- ⚡ **自定义session会话** —— 专业的数据缓存中心
|
||||
- ⚡ **踢人下线** —— 将违规用户立刻清退下线
|
||||
- ⚡ **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- ⚡ **持久层扩展** —— 可集成redis、MongoDB等专业缓存中间件
|
||||
- ⚡ **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- ⚡ **无cookie模式** —— APP、小程序等前后台分离场景
|
||||
- ⚡ **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- ⚡ **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- ⚡ **组件自动注入** —— 零配置与Spring等框架集成
|
||||
- ⚡ **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
|
||||
|
||||
|
||||
## 🔨 贡献代码
|
||||
sa-token欢迎大家贡献代码,为框架添砖加瓦
|
||||
1. 在github上fork一份到自己的仓库
|
||||
2. clone自己的仓库到本地电脑
|
||||
3. 在本地电脑修改、commit、push
|
||||
4. 提交pr(点击:New Pull Request)(提交pr前请保证自己fork的仓库是最新版本,如若不是先强制更新一下)
|
||||
4. 提交pr(点击:New Pull Request)
|
||||
5. 等待合并
|
||||
|
||||
## 建议贡献的地方
|
||||
|
||||
## 🌱 建议贡献的地方
|
||||
- 修复源码现有bug,或增加新的实用功能
|
||||
- 完善在线文档,或者修复现有错误之处
|
||||
- 更多demo示例:比如SSM版搭建步骤
|
||||
- 您可以参考项目issues与需求墙进行贡献
|
||||
- 如果更新实用功能,可在文档友情链接处留下自己的推广链接
|
||||
|
||||
|
||||
## 交流群
|
||||
## 🚀 友情链接
|
||||
[**[ okhttps ]** 一个轻量级http通信框架,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
|
||||
|
||||
## 😎 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) ,欢迎你的加入
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<factorypath>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-web/2.0.0.RELEASE/spring-boot-starter-web-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter/2.0.0.RELEASE/spring-boot-starter-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot/2.0.0.RELEASE/spring-boot-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-autoconfigure/2.0.0.RELEASE/spring-boot-autoconfigure-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-logging/2.0.0.RELEASE/spring-boot-starter-logging-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-to-slf4j/2.10.0/log4j-to-slf4j-2.10.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-api/2.10.0/log4j-api-2.10.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-core/5.0.4.RELEASE/spring-core-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jcl/5.0.4.RELEASE/spring-jcl-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/yaml/snakeyaml/1.19/snakeyaml-1.19.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-json/2.0.0.RELEASE/spring-boot-starter-json-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.9.4/jackson-databind-2.9.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.9.0/jackson-annotations-2.9.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.9.4/jackson-core-2.9.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.9.4/jackson-datatype-jdk8-2.9.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.9.4/jackson-datatype-jsr310-2.9.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/module/jackson-module-parameter-names/2.9.4/jackson-module-parameter-names-2.9.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-tomcat/2.0.0.RELEASE/spring-boot-starter-tomcat-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-core/8.5.28/tomcat-embed-core-8.5.28.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-el/8.5.28/tomcat-embed-el-8.5.28.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-websocket/8.5.28/tomcat-embed-websocket-8.5.28.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/hibernate/validator/hibernate-validator/6.0.7.Final/hibernate-validator-6.0.7.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/jboss/logging/jboss-logging/3.3.2.Final/jboss-logging-3.3.2.Final.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/classmate/1.3.4/classmate-1.3.4.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-web/5.0.4.RELEASE/spring-web-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-beans/5.0.4.RELEASE/spring-beans-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-webmvc/5.0.4.RELEASE/spring-webmvc-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context/5.0.4.RELEASE/spring-context-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-expression/5.0.4.RELEASE/spring-expression-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-aop/2.0.0.RELEASE/spring-boot-starter-aop-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-aop/5.0.4.RELEASE/spring-aop-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/aspectj/aspectjweaver/1.8.13/aspectjweaver-1.8.13.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/cn/dev33/sa-token/1.6.0/sa-token-1.6.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-redis/1.4.7.RELEASE/spring-boot-starter-redis-1.4.7.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-redis/2.0.5.RELEASE/spring-data-redis-2.0.5.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-keyvalue/2.0.5.RELEASE/spring-data-keyvalue-2.0.5.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/data/spring-data-commons/2.0.5.RELEASE/spring-data-commons-2.0.5.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-tx/5.0.4.RELEASE/spring-tx-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-oxm/5.0.4.RELEASE/spring-oxm-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context-support/5.0.4.RELEASE/spring-context-support-5.0.4.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/redis/clients/jedis/2.9.0/jedis-2.9.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-pool2/2.5.0/commons-pool2-2.5.0.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-configuration-processor/2.0.0.RELEASE/spring-boot-configuration-processor-2.0.0.RELEASE.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="PLUGIN" id="net.harawata.mybatipse" enabled="false" runInBatchMode="false"/>
|
||||
</factorypath>
|
||||
@@ -29,14 +29,14 @@
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dev</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>1.6.0</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>1.6.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot整合redis -->
|
||||
@@ -46,6 +46,21 @@
|
||||
<version>RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!-- @ConfigurationProperties -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<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,21 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
|
||||
|
||||
/**
|
||||
* 继承sa-token行为Bean默认实现, 重写部分逻辑
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class MySaTokenAction extends SaTokenActionDefaultImpl {
|
||||
|
||||
// 重写token生成策略
|
||||
// @Override
|
||||
// public String createToken(Object loginId, String loginKey) {
|
||||
// return SaTokenInsideUtil.getRandomString(60);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
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;
|
||||
|
||||
@@ -15,9 +13,9 @@ import cn.dev33.satoken.config.SaTokenConfig;
|
||||
@Configuration
|
||||
public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
|
||||
// 获取配置Bean (以代码的方式配置sa-token)
|
||||
@Primary
|
||||
@Bean(name="MySaTokenConfig")
|
||||
// 获取配置Bean (以代码的方式配置sa-token, 此配置会覆盖yml中的配置 )
|
||||
// @Primary
|
||||
// @Bean(name="MySaTokenConfig")
|
||||
public SaTokenConfig getSaTokenConfig() {
|
||||
SaTokenConfig config = new SaTokenConfig();
|
||||
config.setTokenName("satoken"); // token名称 (同时也是cookie名称)
|
||||
@@ -26,6 +24,7 @@ public class MySaTokenConfig implements WebMvcConfigurer {
|
||||
config.setIsReadBody(true); // 是否尝试从请求体里读取token
|
||||
config.setIsReadHead(true); // 是否尝试从header里读取token
|
||||
config.setIsReadCookie(true); // 是否尝试从cookie里读取token
|
||||
config.setTokenStyle("uuid"); // token风格
|
||||
config.setIsV(true); // 是否在初始化配置时打印版本字符画
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ 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.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
@@ -15,7 +14,7 @@ import cn.dev33.satoken.session.SaSession;
|
||||
/**
|
||||
* sa-token持久层的实现类 , 基于redis
|
||||
*/
|
||||
// @Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token与redis的集成
|
||||
//@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token与redis的集成
|
||||
public class SaTokenDaoRedis implements SaTokenDao {
|
||||
|
||||
|
||||
@@ -46,6 +45,16 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
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) {
|
||||
@@ -77,10 +86,13 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
|
||||
// 删除一个指定的session
|
||||
@Override
|
||||
public void delSaSession(String sessionId) {
|
||||
public void deleteSaSession(String sessionId) {
|
||||
redisTemplate.delete(sessionId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -24,6 +24,9 @@ public class GlobalException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
|
||||
@@ -35,7 +38,8 @@ public class GlobalException {
|
||||
// 不同异常返回不同状态码
|
||||
AjaxJson aj = null;
|
||||
if (e instanceof NotLoginException) { // 如果是未登录异常
|
||||
aj = AjaxJson.getNotLogin();
|
||||
NotLoginException ee = (NotLoginException) e;
|
||||
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
|
||||
} else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
|
||||
@@ -50,5 +54,41 @@ public class GlobalException {
|
||||
// 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);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -112,10 +112,48 @@ public class TestController {
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
// 测试注解式鉴权, 浏览器访问: http://localhost:8081/test/getInfo
|
||||
@SaCheckLogin // 注解式鉴权:当前会话必须登录才能通过
|
||||
@RequestMapping("getInfo")
|
||||
public AjaxJson getInfo() {
|
||||
return AjaxJson.getSuccessData("用户信息");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 测试踢人下线 浏览器访问: http://localhost:8081/test/kickOut
|
||||
@RequestMapping("kickOut")
|
||||
public AjaxJson kickOut() {
|
||||
// 先登录上
|
||||
StpUtil.setLoginId(10001);
|
||||
// 清退下线
|
||||
// StpUtil.logoutByLoginId(10001);
|
||||
// 踢下线
|
||||
StpUtil.kickoutByLoginId(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"));
|
||||
|
||||
return AjaxJson.getSuccess();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ spring:
|
||||
is-read-head: true
|
||||
# 是否尝试从cookie里读取token
|
||||
is-read-cookie: true
|
||||
# token风格
|
||||
token-style: uuid
|
||||
# 是否在初始化配置时打印版本字符画
|
||||
is-v: true
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dev</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.4.0</version>
|
||||
<version>1.6.0</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
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.SaTokenDaoDefault;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
@@ -38,7 +42,6 @@ public class SaTokenManager {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 持久化 Bean
|
||||
*/
|
||||
@@ -54,11 +57,10 @@ public class SaTokenManager {
|
||||
}
|
||||
public synchronized static void initDao() {
|
||||
if (dao == null) {
|
||||
setDao(new SaTokenDaoDefault());
|
||||
setDao(new SaTokenDaoDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 权限认证 Bean
|
||||
*/
|
||||
@@ -78,6 +80,45 @@ public class SaTokenManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
initSaCookieOper();
|
||||
}
|
||||
return saCookieOper;
|
||||
}
|
||||
public static void setSaCookieOper(SaCookieOper saCookieOper) {
|
||||
SaTokenManager.saCookieOper = saCookieOper;
|
||||
}
|
||||
public synchronized static void initSaCookieOper() {
|
||||
if (saCookieOper == null) {
|
||||
setSaCookieOper(new SaCookieOperDefaultImpl());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
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);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package cn.dev33.satoken.action;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
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) {
|
||||
// 生成各种花式token
|
||||
String tokenStyle = SaTokenManager.getConfig().getTokenStyle();
|
||||
// uuid
|
||||
if(tokenStyle.equals("uuid")) {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
// 简单uuid (不带下划线)
|
||||
else if(tokenStyle.equals("simple-uuid")) {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
// 32位随机字符串
|
||||
else if(tokenStyle.equals("random-32")) {
|
||||
return SaTokenInsideUtil.getRandomString(32);
|
||||
}
|
||||
// 64位随机字符串
|
||||
else if(tokenStyle.equals("random-64")) {
|
||||
return SaTokenInsideUtil.getRandomString(64);
|
||||
}
|
||||
// 128位随机字符串
|
||||
else if(tokenStyle.equals("random-128")) {
|
||||
return SaTokenInsideUtil.getRandomString(128);
|
||||
}
|
||||
// tik风格
|
||||
else if(tokenStyle.equals("tik")) {
|
||||
return SaTokenInsideUtil.getRandomString(2) + "_" + SaTokenInsideUtil.getRandomString(14) + "_" + SaTokenInsideUtil.getRandomString(16) + "__";
|
||||
}
|
||||
// 默认
|
||||
else {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ public class SaTokenConfig {
|
||||
private Boolean isReadBody = true; // 是否尝试从请求体里读取token
|
||||
private Boolean isReadHead = true; // 是否尝试从header里读取token
|
||||
private Boolean isReadCookie = true; // 是否尝试从cookie里读取token
|
||||
private String tokenStyle = "uuid"; // token风格
|
||||
|
||||
private Boolean isV = true; // 是否在初始化配置时打印版本字符画
|
||||
|
||||
@@ -102,7 +103,21 @@ public class SaTokenConfig {
|
||||
public void setIsReadBody(Boolean isReadBody) {
|
||||
this.isReadBody = isReadBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return tokenStyle
|
||||
*/
|
||||
public String getTokenStyle() {
|
||||
return tokenStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenStyle 要设置的 tokenStyle
|
||||
*/
|
||||
public void setTokenStyle(String tokenStyle) {
|
||||
this.tokenStyle = tokenStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isV
|
||||
*/
|
||||
@@ -124,12 +139,16 @@ public class SaTokenConfig {
|
||||
public String toString() {
|
||||
return "SaTokenConfig [tokenName=" + tokenName + ", timeout=" + timeout + ", isShare=" + isShare
|
||||
+ ", isReadBody=" + isReadBody + ", isReadHead=" + isReadHead + ", isReadCookie=" + isReadCookie
|
||||
+ ", isV=" + isV + "]";
|
||||
+ ", tokenStyle=" + tokenStyle + ", isV=" + isV + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
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.SaCookieUtil;
|
||||
|
||||
/**
|
||||
* sa-token 对cookie的相关操作 接口实现类
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaCookieOperDefaultImpl implements SaCookieOper {
|
||||
|
||||
/**
|
||||
* 获取指定cookie
|
||||
*/
|
||||
public Cookie getCookie(HttpServletRequest request, String cookieName) {
|
||||
return SaCookieUtil.getCookie(request, cookieName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加cookie
|
||||
*/
|
||||
public void addCookie(HttpServletResponse response, String name, String value, String path, int timeout) {
|
||||
SaCookieUtil.addCookie(response, name, value, path, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除cookie
|
||||
*/
|
||||
public void delCookie(HttpServletRequest request, HttpServletResponse response, String name) {
|
||||
SaCookieUtil.delCookie(request, response, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改cookie的value值
|
||||
*/
|
||||
public void updateCookie(HttpServletRequest request, HttpServletResponse response, String name, String value) {
|
||||
SaCookieUtil.updateCookie(request, response, name, value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,13 @@ public interface SaTokenDao {
|
||||
*/
|
||||
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 键名称
|
||||
@@ -57,7 +64,7 @@ public interface SaTokenDao {
|
||||
* 删除一个指定的session
|
||||
* @param sessionId sessionId
|
||||
*/
|
||||
public void delSaSession(String sessionId);
|
||||
public void deleteSaSession(String sessionId);
|
||||
|
||||
|
||||
}
|
||||
|
||||
+7
-2
@@ -10,7 +10,7 @@ import cn.dev33.satoken.session.SaSession;
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaTokenDaoDefault implements SaTokenDao {
|
||||
public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
|
||||
/**
|
||||
* 所有数据集合
|
||||
@@ -28,6 +28,11 @@ public class SaTokenDaoDefault implements SaTokenDao {
|
||||
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);
|
||||
@@ -50,7 +55,7 @@ public class SaTokenDaoDefault implements SaTokenDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delSaSession(String sessionId) {
|
||||
public void deleteSaSession(String sessionId) {
|
||||
dataMap.remove(sessionId);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 没有登陆抛出的异常
|
||||
@@ -14,35 +15,120 @@ public class NotLoginException extends RuntimeException {
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290130142L;
|
||||
|
||||
|
||||
// ------------------- 异常类型常量 --------------------
|
||||
|
||||
/*
|
||||
* 这里简述一下为什么要把常量设计为String类型
|
||||
* 因为loginId刚取出的时候类型为String,为了避免两者相比较时不必要的类型转换带来的性能消耗,故在此直接将常量类型设计为String
|
||||
*/
|
||||
|
||||
/** 表示未提供token */
|
||||
public static final String NOT_TOKEN = "-1";
|
||||
public static final String NOT_TOKEN_MESSAGE = "未提供token";
|
||||
|
||||
/** 表示token无效 */
|
||||
public static final String INVALID_TOKEN = "-2";
|
||||
public static final String INVALID_TOKEN_MESSAGE = "token无效";
|
||||
|
||||
/** 表示token已过期 */
|
||||
public static final String TOKEN_TIMEOUT = "-3";
|
||||
public static final String TOKEN_TIMEOUT_MESSAGE = "token已过期";
|
||||
|
||||
/** 表示token已被顶下线 */
|
||||
public static final String BE_REPLACED = "-4";
|
||||
public static final String BE_REPLACED_MESSAGE = "token已被顶下线";
|
||||
|
||||
/** 表示token已被踢下线 */
|
||||
public static final String KICK_OUT = "-5";
|
||||
public static final String KICK_OUT_MESSAGE = "token已被踢下线";
|
||||
|
||||
/** 默认的提示语 */
|
||||
public static final String DEFAULT_MESSAGE = "当前会话未登录";
|
||||
|
||||
|
||||
/**
|
||||
* 代表异常token的标志集合
|
||||
*/
|
||||
public static final List<String> ABNORMAL_LIST = Arrays.asList(NOT_TOKEN, INVALID_TOKEN, TOKEN_TIMEOUT, BE_REPLACED, KICK_OUT);
|
||||
|
||||
|
||||
/**
|
||||
* login_key
|
||||
* 异常类型
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 获取异常类型
|
||||
* @return
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* loginKey
|
||||
*/
|
||||
private String loginKey;
|
||||
/**
|
||||
* 获得login_key
|
||||
* 获得loginKey
|
||||
* @return login_key
|
||||
*/
|
||||
public String getLoginKey() {
|
||||
return loginKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// /**
|
||||
// * 创建一个
|
||||
// */
|
||||
// public NotLoginException() {
|
||||
// this(StpUtil.stpLogic.loginKey);
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* 创建一个
|
||||
* @param message 异常消息
|
||||
* @param loginKey loginKey
|
||||
* @param type 类型
|
||||
*/
|
||||
public NotLoginException() {
|
||||
this(StpUtil.stpLogic.loginKey);
|
||||
public NotLoginException(String message, String loginKey, String type) {
|
||||
// 这里到底要不要拼接上login_key呢?纠结
|
||||
super(message);
|
||||
this.loginKey = loginKey;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个
|
||||
* @param loginKey login_key
|
||||
* 静态方法构建一个NotLoginException
|
||||
* @param loginKey loginKey
|
||||
* @param type 场景类型
|
||||
* @return 构建完毕的异常对象
|
||||
*/
|
||||
public NotLoginException(String loginKey) {
|
||||
// 这里到底要不要拼接上login_key呢?纠结
|
||||
super("当前会话未登录");
|
||||
this.loginKey = loginKey;
|
||||
public static NotLoginException newInstance(String loginKey, String type) {
|
||||
String message = null;
|
||||
if(type.equals(NOT_TOKEN)) {
|
||||
message = NOT_TOKEN_MESSAGE;
|
||||
}
|
||||
else if(type.equals(INVALID_TOKEN)) {
|
||||
message = INVALID_TOKEN_MESSAGE;
|
||||
}
|
||||
else if(type.equals(TOKEN_TIMEOUT)) {
|
||||
message = TOKEN_TIMEOUT_MESSAGE;
|
||||
}
|
||||
else if(type.equals(BE_REPLACED)) {
|
||||
message = BE_REPLACED_MESSAGE;
|
||||
}
|
||||
else if(type.equals(KICK_OUT)) {
|
||||
message = KICK_OUT_MESSAGE;
|
||||
}
|
||||
else {
|
||||
message = DEFAULT_MESSAGE;
|
||||
}
|
||||
return new NotLoginException(message, loginKey, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class SaSession implements Serializable {
|
||||
|
||||
/**
|
||||
* 构建一个 session对象
|
||||
* @param id
|
||||
* @param id sessionId
|
||||
*/
|
||||
public SaSession(String id) {
|
||||
this.id = id;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class SaSessionCustomUtil {
|
||||
* 获取指定key的session
|
||||
* @param sessionId key
|
||||
* @param isCreate 如果没有,是否新建并返回
|
||||
* @return
|
||||
* @return SaSession
|
||||
*/
|
||||
public static SaSession getSessionById(String sessionId, boolean isCreate) {
|
||||
SaSession session = SaTokenManager.getDao().getSaSession(getSessionKey(sessionId));
|
||||
@@ -53,8 +53,8 @@ public class SaSessionCustomUtil {
|
||||
* 删除指定key的session
|
||||
* @param sessionId 删除指定key
|
||||
*/
|
||||
public static void delSessionById(String sessionId) {
|
||||
SaTokenManager.getDao().delSaSession(getSessionKey(sessionId));
|
||||
public static void deleteSessionById(String sessionId) {
|
||||
SaTokenManager.getDao().deleteSaSession(getSessionKey(sessionId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.SaTokenManager;
|
||||
import cn.dev33.satoken.action.SaTokenAction;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.cookie.SaCookieOper;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
@@ -21,7 +23,7 @@ public class SpringSaToken {
|
||||
|
||||
/**
|
||||
* 获取配置Bean
|
||||
* @return
|
||||
* @return .
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix="spring.sa-token")
|
||||
@@ -55,6 +57,24 @@ public class SpringSaToken {
|
||||
public void setStp(StpInterface stp){
|
||||
SaTokenManager.setStp(stp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入Cookie操作Bean
|
||||
* @param saCookieOper .
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaCookieOper(SaCookieOper saCookieOper){
|
||||
SaTokenManager.setSaCookieOper(saCookieOper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入框架行为Bean
|
||||
* @param sta .
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSta(SaTokenAction sta){
|
||||
SaTokenManager.setSta(sta);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public interface StpInterface {
|
||||
* 返回指定login_id所拥有的权限码集合
|
||||
* @param loginId 账号id
|
||||
* @param loginKey 具体的stp标识
|
||||
* @return
|
||||
* @return .
|
||||
*/
|
||||
public List<Object> getPermissionCodeList(Object loginId, String loginKey);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package cn.dev33.satoken.stp;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -14,9 +13,7 @@ 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.SaCookieUtil;
|
||||
import cn.dev33.satoken.util.SaTokenInsideUtil;
|
||||
import cn.dev33.satoken.util.SpringMvcUtil;
|
||||
|
||||
/**
|
||||
* sa-token 权限验证,逻辑 实现类
|
||||
@@ -42,13 +39,21 @@ public class StpLogic {
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
/**
|
||||
* 返回token名称
|
||||
* @return 此StpLogic的token名称
|
||||
*/
|
||||
public String getTokenName() {
|
||||
return getKeyTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成一个tokenValue
|
||||
* @param loginId loginId
|
||||
* @return 生成的tokenValue
|
||||
*/
|
||||
public String randomTokenValue() {
|
||||
return UUID.randomUUID().toString();
|
||||
public String randomTokenValue(Object loginId) {
|
||||
return SaTokenManager.getSta().createToken(loginId, loginKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,9 +62,9 @@ public class StpLogic {
|
||||
*/
|
||||
public String getTokenValue(){
|
||||
// 0、获取相应对象
|
||||
HttpServletRequest request = SpringMvcUtil.getRequest();
|
||||
HttpServletRequest request = SaTokenManager.getSta().getCurrRequest();
|
||||
SaTokenConfig config = SaTokenManager.getConfig();
|
||||
String keyTokenName = getKeyTokenName();
|
||||
String keyTokenName = getTokenName();
|
||||
|
||||
// 1、尝试从request里读取
|
||||
if(request.getAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY) != null) {
|
||||
@@ -81,7 +86,7 @@ public class StpLogic {
|
||||
}
|
||||
// 4、尝试从cookie里读取
|
||||
if(config.getIsReadCookie() == true){
|
||||
Cookie cookie = SaCookieUtil.getCookie(request, keyTokenName);
|
||||
Cookie cookie = SaTokenManager.getSaCookieOper().getCookie(request, keyTokenName);
|
||||
if(cookie != null){
|
||||
String tokenValue = cookie.getValue();
|
||||
if(tokenValue != null) {
|
||||
@@ -96,7 +101,7 @@ public class StpLogic {
|
||||
/**
|
||||
* 获取指定id的tokenValue
|
||||
* @param loginId .
|
||||
* @return
|
||||
* @return .
|
||||
*/
|
||||
public String getTokenValueByLoginId(Object loginId) {
|
||||
return SaTokenManager.getDao().getValue(getKeyLoginId(loginId));
|
||||
@@ -108,7 +113,7 @@ public class StpLogic {
|
||||
*/
|
||||
public Map<String, String> getTokenInfo() {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("tokenName", getKeyTokenName());
|
||||
map.put("tokenName", getTokenName());
|
||||
map.put("tokenValue", getTokenValue());
|
||||
return map;
|
||||
}
|
||||
@@ -123,19 +128,20 @@ public class StpLogic {
|
||||
public void setLoginId(Object loginId) {
|
||||
|
||||
// 1、获取相应对象
|
||||
HttpServletRequest request = SpringMvcUtil.getRequest();
|
||||
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();
|
||||
tokenValue = randomTokenValue(loginId);
|
||||
} else {
|
||||
// 不为null, 并且配置不共享,则删掉原来,并且创建新的
|
||||
// 不为null, 并且配置不共享,则:将原来的标记为[被顶替]
|
||||
if(config.getIsShare() == false){
|
||||
dao.delKey(getKeyTokenValue(tokenValue));
|
||||
tokenValue = randomTokenValue();
|
||||
// dao.delKey(getKeyTokenValue(tokenValue));
|
||||
dao.updateValue(getKeyTokenValue(tokenValue), NotLoginException.BE_REPLACED);
|
||||
tokenValue = randomTokenValue(loginId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +150,7 @@ public class StpLogic {
|
||||
dao.setValue(getKeyLoginId(loginId), tokenValue, config.getTimeout()); // uid -> token
|
||||
request.setAttribute(SaTokenInsideUtil.JUST_CREATED_SAVE_KEY, tokenValue); // 保存到本次request里
|
||||
if(config.getIsReadCookie() == true){
|
||||
SaCookieUtil.addCookie(SpringMvcUtil.getResponse(), getKeyTokenName(), tokenValue, "/", (int)config.getTimeout()); // cookie注入
|
||||
SaTokenManager.getSaCookieOper().addCookie(SaTokenManager.getSta().getResponse(), getTokenName(), tokenValue, "/", (int)config.getTimeout()); // cookie注入
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,18 +158,31 @@ public class StpLogic {
|
||||
* 当前会话注销登录
|
||||
*/
|
||||
public void logout() {
|
||||
Object loginId = getLoginIdDefaultNull();
|
||||
if(loginId != null) {
|
||||
logoutByLoginId(loginId);
|
||||
// 清除cookie
|
||||
if(SaTokenManager.getConfig().getIsReadCookie() == true){
|
||||
SaCookieUtil.delCookie(SpringMvcUtil.getRequest(), SpringMvcUtil.getResponse(), getKeyTokenName());
|
||||
}
|
||||
// 如果连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的会话注销登录(踢人下线)
|
||||
* 指定loginId的会话注销登录(清退下线)
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public void logoutByLoginId(Object loginId) {
|
||||
@@ -177,8 +196,25 @@ public class StpLogic {
|
||||
// 清除相关数据
|
||||
SaTokenManager.getDao().delKey(getKeyTokenValue(tokenValue)); // 清除token-id键值对
|
||||
SaTokenManager.getDao().delKey(getKeyLoginId(loginId)); // 清除id-token键值对
|
||||
SaTokenManager.getDao().delKey(getKeySession(loginId)); // 清除其session
|
||||
// SaCookieUtil.delCookie(SpringMVCUtil.getRequest(), SpringMVCUtil.getResponse(), getKey_tokenName()); // 清除cookie
|
||||
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
|
||||
}
|
||||
|
||||
// 查询相关
|
||||
@@ -188,6 +224,7 @@ public class StpLogic {
|
||||
* @return 是否已登录
|
||||
*/
|
||||
public boolean isLogin() {
|
||||
// 判断条件:不为null,并且不在异常项集合里
|
||||
return getLoginIdDefaultNull() != null;
|
||||
}
|
||||
|
||||
@@ -203,24 +240,45 @@ public class StpLogic {
|
||||
* @return .
|
||||
*/
|
||||
public Object getLoginId() {
|
||||
Object loginId = getLoginIdDefaultNull();
|
||||
if(loginId == null) {
|
||||
throw new NotLoginException(this.loginKey);
|
||||
// 如果获取不到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
|
||||
* @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());
|
||||
}
|
||||
@@ -235,17 +293,21 @@ public class StpLogic {
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 如果未登录,则返回null
|
||||
* @return
|
||||
* @return .
|
||||
*/
|
||||
public Object getLoginIdDefaultNull() {
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue != null) {
|
||||
Object loginId = SaTokenManager.getDao().getValue(getKeyTokenValue(tokenValue));
|
||||
if(loginId != null) {
|
||||
return loginId;
|
||||
}
|
||||
// 如果连token都是空的,则直接返回
|
||||
String tokenValue = getTokenValue();
|
||||
if(tokenValue == null) {
|
||||
return 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +320,7 @@ public class StpLogic {
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 并转换为int
|
||||
* @return
|
||||
* @return .
|
||||
*/
|
||||
public int getLoginIdAsInt() {
|
||||
// Object loginId = getLoginId();
|
||||
@@ -270,7 +332,7 @@ public class StpLogic {
|
||||
|
||||
/**
|
||||
* 获取当前会话登录id, 并转换为long
|
||||
* @return
|
||||
* @return .
|
||||
*/
|
||||
public long getLoginIdAsLong() {
|
||||
// Object loginId = getLoginId();
|
||||
@@ -328,7 +390,7 @@ public class StpLogic {
|
||||
* @return .
|
||||
*/
|
||||
public SaSession getSessionByLoginId(Object loginId) {
|
||||
return getSessionByLoginId(getKeySession(loginId), false);
|
||||
return getSessionByLoginId(loginId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,7 +398,7 @@ public class StpLogic {
|
||||
* @return
|
||||
*/
|
||||
public SaSession getSession() {
|
||||
return getSessionBySessionId(getKeySession(getLoginId()), true);
|
||||
return getSessionByLoginId(getLoginId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,13 @@ public class StpUtil {
|
||||
|
||||
// =================== 获取token 相关 ===================
|
||||
|
||||
/**
|
||||
* 返回token名称
|
||||
* @return 此StpLogic的token名称
|
||||
*/
|
||||
public static String getTokenName() {
|
||||
return stpLogic.getTokenName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前tokenValue
|
||||
@@ -65,13 +72,21 @@ public class StpUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定loginId的会话注销登录(踢人下线)
|
||||
* 指定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);
|
||||
}
|
||||
|
||||
// 查询相关
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,92 +4,90 @@ import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* cookie工具类
|
||||
* cookie操作工具类
|
||||
*
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
public class SaCookieUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定cookie .
|
||||
* @param request .
|
||||
*
|
||||
* @param request .
|
||||
* @param cookieName .
|
||||
* @return .
|
||||
*/
|
||||
public static Cookie getCookie(HttpServletRequest request, String cookieName) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if(cookies != null) {
|
||||
for(int i = 0; i < cookies.length; i++) {
|
||||
Cookie cookie = cookies[i];
|
||||
if(cookie != null && cookieName.equals(cookie.getName())) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static Cookie getCookie(HttpServletRequest request, String cookieName) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie != null && cookieName.equals(cookie.getName())) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加cookie
|
||||
*
|
||||
* @param response .
|
||||
* @param name .
|
||||
* @param value .
|
||||
* @param path .
|
||||
* @param timeout .
|
||||
*/
|
||||
public static void addCookie(HttpServletResponse response, String name, String value, String path, int timeout) {
|
||||
Cookie cookie = new Cookie(name, value);
|
||||
if (path == null) {
|
||||
path = "/";
|
||||
}
|
||||
cookie.setPath(path);
|
||||
cookie.setMaxAge(timeout);
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加cookie
|
||||
* @param response .
|
||||
* @param name .
|
||||
* @param value .
|
||||
* @param path .
|
||||
* @param timeout .
|
||||
*/
|
||||
public static void addCookie(HttpServletResponse response,String name,String value,String path,int timeout) {
|
||||
Cookie cookie = new Cookie(name, value);
|
||||
if(path == null) {
|
||||
path = "/";
|
||||
}
|
||||
cookie.setPath(path);
|
||||
cookie.setMaxAge(timeout);
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
/**
|
||||
* 删除cookie .
|
||||
*
|
||||
* @param request .
|
||||
* @param response .
|
||||
* @param name .
|
||||
*/
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改cookie的value值
|
||||
*
|
||||
* @param request .
|
||||
* @param response .
|
||||
* @param name .
|
||||
* @param value .
|
||||
*/
|
||||
public static void updateCookie(HttpServletRequest request, HttpServletResponse response, String name,
|
||||
String value) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie != null && (name).equals(cookie.getName())) {
|
||||
addCookie(response, name, value, cookie.getPath(), cookie.getMaxAge());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除cookie .
|
||||
* @param request .
|
||||
* @param response .
|
||||
* @param name .
|
||||
*/
|
||||
public static void delCookie(HttpServletRequest request,HttpServletResponse response,String name) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if(cookies != null){
|
||||
for(Cookie cookie : cookies) {
|
||||
if(cookies != null && (name).equals(cookie.getName())) {
|
||||
addCookie(response,name,null,null,0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改cookie的value值
|
||||
* @param request .
|
||||
* @param response .
|
||||
* @param name .
|
||||
* @param value .
|
||||
*/
|
||||
public static void updateCookie(HttpServletRequest request,HttpServletResponse response,String name,String value) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if(cookies != null){
|
||||
for(Cookie cookie : cookies) {
|
||||
if(cookies != null && (name).equals(cookie.getName())) {
|
||||
addCookie(response,name,value,cookie.getPath(),cookie.getMaxAge());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package cn.dev33.satoken.util;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* sa-token 工具类
|
||||
* @author kong
|
||||
@@ -11,7 +13,7 @@ public class SaTokenInsideUtil {
|
||||
/**
|
||||
* sa-token 版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.4.0";
|
||||
public static final String VERSION_NO = "v1.6.0";
|
||||
|
||||
/**
|
||||
* sa-token 开源地址
|
||||
@@ -36,5 +38,21 @@ public class SaTokenInsideUtil {
|
||||
*/
|
||||
public static final String JUST_CREATED_SAVE_KEY = "JUST_CREATED_SAVE_KEY_";
|
||||
|
||||
/**
|
||||
* 生成指定长度的随机字符串
|
||||
* @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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+59
-28
@@ -1,11 +1,11 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="http://sa-token.dev33.cn/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" style="margin-bottom: 10px;">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.4.0</h1>
|
||||
<h4 align="center">一个JavaWeb权限认证框架,强大、简单、好用</h4>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sa-token v1.6.0</h1>
|
||||
<h4 align="center">一个JavaWeb轻量级权限认证框架,功能全面,上手简单</h4>
|
||||
<h4 align="center">
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/sa--token-v1.4.0-2B9939"></a>
|
||||
<a href="https://github.com/click33/sa-token"><img src="https://img.shields.io/badge/language-java-2B9939"></a>
|
||||
<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.6.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>
|
||||
@@ -14,45 +14,76 @@
|
||||
</h4>
|
||||
|
||||
---
|
||||
|
||||
## sa-token 😋
|
||||
- 在线文档:[http://sa-token.dev33.cn/](http://sa-token.dev33.cn/)
|
||||
- 需求提交:我们深知一个优秀的项目需要海纳百川,[点我在线提交需求](http://sa-app.dev33.cn/wall.html?name=sa-token)
|
||||
- 开源不易,求鼓励,**点个`star`吧**
|
||||
|
||||
## 框架优点
|
||||
与其它权限认证框架相比,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吧](https://github.com/click33/sa-token)
|
||||
|
||||
|
||||
## ⭐ sa-token是什么?
|
||||
- **sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,有多简单呢?以登录验证为例,你只需要:**
|
||||
``` java
|
||||
// 在登录时写入当前会话的账号id
|
||||
StpUtil.setLoginId(10001);
|
||||
```
|
||||
|
||||
|
||||
- **然后在任意需要验证登录权限的地方:**
|
||||
``` java
|
||||
// 检测是否登录 --- 如果当前会话未登录,下面这句代码会抛出 `NotLoginException`异常
|
||||
StpUtil.checkLogin();
|
||||
```
|
||||
|
||||
|
||||
- **没有复杂的封装!不要任何的配置!先写入,后鉴权!只需这两行简单的调用,即可轻松完成系统登录鉴权!**
|
||||
|
||||
|
||||
## 🔥 框架设计思想
|
||||
与其它权限认证框架相比,`sa-token`尽力保证两点:
|
||||
- 上手简单:能自动化的配置全部自动化,不让你费脑子
|
||||
- 功能强大:能涵盖的功能全部涵盖,不让你用个框架还要自己给框架打各种补丁
|
||||
|
||||
## 涵盖功能
|
||||
- 登录验证
|
||||
- 权限验证
|
||||
- 自定义session会话
|
||||
- 踢人下线
|
||||
- 模拟他人账号
|
||||
- 持久层扩展(集成redis)
|
||||
- 多账号认证体系(比如一个商城项目的user表和admin表)
|
||||
- 无cookie模式(APP、小程序等前后台分离场景)
|
||||
- 注解式鉴权(优雅的将鉴权与业务代码分离)
|
||||
- 零配置与Spring等框架集成
|
||||
- ...
|
||||
|
||||
## 贡献代码
|
||||
## 💦️️ 涵盖功能
|
||||
- ⚡ **登录验证** —— 轻松登录鉴权,并提供五种细分场景值
|
||||
- ⚡ **权限验证** —— 拦截违规调用,不同角色不同授权
|
||||
- ⚡ **自定义session会话** —— 专业的数据缓存中心
|
||||
- ⚡ **踢人下线** —— 将违规用户立刻清退下线
|
||||
- ⚡ **模拟他人账号** —— 实时操作任意用户状态数据
|
||||
- ⚡ **持久层扩展** —— 可集成redis、MongoDB等专业缓存中间件
|
||||
- ⚡ **多账号认证体系** —— 比如一个商城项目的user表和admin表分开鉴权
|
||||
- ⚡ **无cookie模式** —— APP、小程序等前后台分离场景
|
||||
- ⚡ **注解式鉴权** —— 优雅的将鉴权与业务代码分离
|
||||
- ⚡ **花式token生成** —— 内置六种token风格,还可自定义token生成策略
|
||||
- ⚡ **组件自动注入** —— 零配置与Spring等框架集成
|
||||
- ⚡ **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
|
||||
|
||||
|
||||
## 🔨 贡献代码
|
||||
sa-token欢迎大家贡献代码,为框架添砖加瓦
|
||||
1. 在github上fork一份到自己的仓库
|
||||
2. clone自己的仓库到本地电脑
|
||||
3. 在本地电脑修改、commit、push
|
||||
4. 提交pr(点击:New Pull Request)(提交pr前请保证自己fork的仓库是最新版本,如若不是先强制更新一下)
|
||||
4. 提交pr(点击:New Pull Request)
|
||||
5. 等待合并
|
||||
|
||||
## 建议贡献的地方
|
||||
|
||||
## 🌱 建议贡献的地方
|
||||
- 修复源码现有bug,或增加新的实用功能
|
||||
- 完善在线文档,或者修复现有错误之处
|
||||
- 更多demo示例:比如SSM版搭建步骤
|
||||
- 您可以参考项目issues与需求墙进行贡献
|
||||
- 如果更新实用功能,可在文档友情链接处留下自己的推广链接
|
||||
|
||||
|
||||
## 交流群
|
||||
## 🚀 友情链接
|
||||
[**[ okhttps ]** 一个轻量级http通信框架,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||
|
||||
|
||||
## 😎 交流群
|
||||
QQ交流群:[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM) ,欢迎你的加入
|
||||
|
||||
|
||||
|
||||
@@ -15,18 +15,18 @@
|
||||
- [模拟他人](/use/mock-person)
|
||||
- [多账号验证](/use/many-account)
|
||||
- [注解式鉴权](/use/at-check)
|
||||
- [花式token](/use/token-style)
|
||||
- [框架配置](/use/config)
|
||||
|
||||
- **其它**
|
||||
- [常见问题](/more/common-questions)
|
||||
- [友情链接](/more/link)
|
||||
- [更新日志](/more/update-log)
|
||||
<!-- - [常见问题](/c1) -->
|
||||
<!-- - [常用函数](/c1) -->
|
||||
<!-- - [插件](/c1) -->
|
||||
<!-- - [关于](/c1) -->
|
||||
<!-- - eclipse插件 -->
|
||||
<!-- - idea插件 -->
|
||||
|
||||
- **附录**
|
||||
- [未登录场景值](/fun/not-login-scene)
|
||||
|
||||
|
||||
|
||||
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
|
||||
<p style="text-align: center;">----- 到底线了 -----</p>
|
||||
@@ -0,0 +1,49 @@
|
||||
# NotLoginException 场景值
|
||||
|
||||
本篇介绍如何根据`NotLoginException`异常的场景值,来定制化处理未登录的逻辑 <br/>
|
||||
应用场景举例:未登录、被顶下线、被踢下线等场景需要不同方式来处理
|
||||
|
||||
|
||||
## 何为场景值
|
||||
- 在前面的章节中,我们了解到,在会话未登录的情况下尝试获取`loginId`会使框架抛出`NotLoginException`异常,而同为未登录异常却有五种抛出场景的区分 <br>
|
||||
- 那么,如何获取场景值呢?废话少说直接上代码:
|
||||
|
||||
|
||||
``` java
|
||||
// 全局异常拦截(拦截项目中的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);
|
||||
}
|
||||
```
|
||||
|
||||
<br/>
|
||||
注意:以上代码并非处理逻辑的最佳方式,只为以最简单的代码演示出场景值的获取与应用,大家可以根据自己的项目需求来定制化处理
|
||||
|
||||
@@ -10,3 +10,23 @@
|
||||
.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: #282828; 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;}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
<meta name="description" content="Description">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="keywords" content="sa-token|sa-token框架|sa-token文档|sa-token在线文档|权限认证框架">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,强大、简单、好用,登录验证、权限验证、自定义session会话、踢人下线、持久层扩展、无cookie模式、模拟他人账号、多账号体系、注解式鉴权、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,强大、简单、好用,登录验证、权限验证、自定义session会话、踢人下线、持久层扩展、无cookie模式、模拟他人账号、多账号体系、注解式鉴权、花式token、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">
|
||||
@@ -20,6 +20,10 @@
|
||||
</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.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,7 +34,7 @@
|
||||
</div>
|
||||
<script>
|
||||
var name = '<img style="width: 50px; height: 50px; vertical-align: middle;" src="logo.png" alt="logo" /> ';
|
||||
name += '<b style="font-size: 24px; vertical-align: middle;">sa-token</b> <sub>v1.4.0</sub>'
|
||||
name += '<b style="font-size: 24px; vertical-align: middle;">sa-token</b> <sub>v1.6.0</sub>'
|
||||
window.$docsify = {
|
||||
name: name, // 名字
|
||||
repo: 'https://github.com/click33/sa-token', // github地址
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# 更新日志
|
||||
|
||||
|
||||
### 2020-12-17 @v1.6.0
|
||||
- 新增:花式token生成方案 **[重要]**
|
||||
- 优化:优化`readme.md`
|
||||
- 修复:修复`SaCookieOper`与`SaTokenAction`无法自动注入的问题
|
||||
|
||||
|
||||
### 2020-12-16 @v1.5.1
|
||||
- 新增:细化未登录异常类型,提供五种场景值:未提供token、token无效、token已过期 、token已被顶下线、token已被踢下线 **[重要]**
|
||||
- 修复:修复`StpUtil.getSessionByLoginId(String loginId)`方法转换key出错的bug,感谢群友 @(#°Д°)、@一米阳光 发现的bug
|
||||
- 优化:修改方法`StpUtil.getSessionByLoginId(Object loginId)`的isCreate值默认为true
|
||||
- 修改:`方法delSaSession`修改为`deleteSaSession`,更加语义化的函数名称
|
||||
- 新增:新增`StpUtil.getTokenName()`方法,更语义化的获取tokenName
|
||||
- 新增:新增`SaTokenAction`框架行为Bean,方便重写逻辑
|
||||
- 优化:`Cookie操作`改为接口代理模式,使其可以被重写
|
||||
- 优化:文档里集成redis部分增加redis的pom依赖示例
|
||||
- 修复:登录验证-> `StpUtil.getLoginId_defaultNull()` 修复方法名错误的问题
|
||||
- 优化:优化`readme.md`
|
||||
- 升级:开源协议修改为`MIT`
|
||||
|
||||
|
||||
### 2020-9-7 @v1.4.0
|
||||
- 优化:修改一些函数、变量名称,使其更符合阿里java代码规范
|
||||
- 优化:`tokenValue`的读取优先级改为:`request` > `body` > `header` > `cookie`
|
||||
@@ -8,7 +29,8 @@
|
||||
- 新增:新增`getSessionByLoginId(Object loginId, boolean isCreate)`方法
|
||||
- 修复:修复文档部分错误,修正群号码
|
||||
|
||||
### 2020-5-2 @v1.0.3
|
||||
|
||||
### 2020-5-2 @v1.3.0
|
||||
- 新增:新增 `StpUtil.checkLogin()` 方法,更符合语义化的鉴权方法
|
||||
- 新增:注册拦截器时可设置 `StpLogic` ,方便不同模块不同鉴权方式
|
||||
- 新增:抛出异常时增加 `loginKey` 区分,方便多账号体系鉴权处理
|
||||
@@ -16,13 +38,16 @@
|
||||
- 修复:修复文档部分不正确之处
|
||||
- 新增:新增文档的友情链接
|
||||
|
||||
### 2020-3-7 @v1.0.2
|
||||
|
||||
### 2020-3-7 @v1.2.0
|
||||
- 新增:新增注解式验证,可在路由方法中使用注解进行权限验证
|
||||
- 参考:[注解式验证](use/at-check)
|
||||
|
||||
### 2020-2-12 @v1.0.1
|
||||
|
||||
### 2020-2-12 @v1.1.0
|
||||
- 修复:修复`StpUtil.getLoginId(T defaultValue)`取值转换错误的bug
|
||||
|
||||
|
||||
### 2020-2-4 @v1.0.0
|
||||
- 第一个版本出炉
|
||||
- GitHub:[https://github.com/click33/sa-token](https://github.com/click33/sa-token)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>1.6.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
|
||||
## jar包下载
|
||||
[点击下载:sa-token-1.4.0.jar](https://color-test.oss-cn-qingdao.aliyuncs.com/sa-token/sa-token-1.4.0.jar)
|
||||
[点击下载:sa-token-1.6.0.jar](https://oss.dev33.cn/sa-token/sa-token-1.6.0.jar)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>1.6.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
| isReadBody | Boolean | true | 是否尝试从请求体里读取token |
|
||||
| isReadHead | Boolean | true | 是否尝试从header里读取token |
|
||||
| isReadCookie | Boolean | true | 是否尝试从cookie里读取token |
|
||||
| tokenStyle | String | uuid | token风格, 参考:[花式token](/use/token-style) |
|
||||
| isV | Boolean | true | 是否在初始化配置时打印版本字符画 |
|
||||
|
||||
|
||||
@@ -37,6 +38,7 @@
|
||||
config.setIsReadBody(true); // 是否尝试从请求体里读取token
|
||||
config.setIsReadHead(true); // 是否尝试从header里读取token
|
||||
config.setIsReadCookie(true); // 是否尝试从cookie里读取token
|
||||
config.setTokenStyle("uuid"); // token风格
|
||||
config.setIsV(true); // 是否在初始化配置时打印版本字符画
|
||||
return config;
|
||||
}
|
||||
@@ -62,6 +64,8 @@ spring:
|
||||
is-read-head: true
|
||||
# 是否尝试从cookie里读取token
|
||||
is-read-cookie: true
|
||||
# token风格
|
||||
token-style: uuid
|
||||
# 是否在初始化配置时打印版本字符画
|
||||
is-v: true
|
||||
```
|
||||
|
||||
@@ -5,8 +5,20 @@
|
||||
- 你需要做的就是重写`sa-token`的dao层实现方式,参考以下方案:
|
||||
|
||||
|
||||
## 具体代码
|
||||
- 新建文件`SaTokenDaoRedis.java`,实现接口`SaTokenDao`, 并加上注解`@Component`,保证此类被springboot扫描到
|
||||
|
||||
### 1. 首先在pom.xml引入依赖
|
||||
``` xml
|
||||
<!-- SpringBoot整合redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-redis</artifactId>
|
||||
<version>RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
### 2. 实现SaTokenDao接口
|
||||
新建文件`SaTokenDaoRedis.java`,实现接口`SaTokenDao`, 并加上注解`@Component`,保证此类被springboot扫描到
|
||||
- 代码参考:
|
||||
|
||||
```java
|
||||
@@ -19,7 +31,6 @@ 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.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -29,7 +40,7 @@ import cn.dev33.satoken.session.SaSession;
|
||||
/**
|
||||
* sa-token持久层的实现类 , 基于redis
|
||||
*/
|
||||
@Component // 保证此类被springboot扫描,即可完成sa-token与redis的集成
|
||||
@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token与redis的集成
|
||||
public class SaTokenDaoRedis implements SaTokenDao {
|
||||
|
||||
|
||||
@@ -42,10 +53,8 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
@Autowired
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void setRedisTemplate(RedisTemplate redisTemplate) {
|
||||
RedisSerializer stringSerializer = new StringRedisSerializer();
|
||||
redisTemplate.setKeySerializer(stringSerializer);
|
||||
JdkSerializationRedisSerializer jrSerializer = new JdkSerializationRedisSerializer();
|
||||
redisTemplate.setValueSerializer(jrSerializer);
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
@@ -62,6 +71,16 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
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) {
|
||||
@@ -93,12 +112,10 @@ public class SaTokenDaoRedis implements SaTokenDao {
|
||||
|
||||
// 删除一个指定的session
|
||||
@Override
|
||||
public void delSaSession(String sessionId) {
|
||||
public void deleteSaSession(String sessionId) {
|
||||
redisTemplate.delete(sessionId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
## 具体API
|
||||
|
||||
#### StpUtil.logoutByLoginId(Object loginId)
|
||||
- 让指定loginId的会话注销登录(清退下线)
|
||||
|
||||
#### StpUtil.kickoutByLoginId(Object loginId)
|
||||
- 让指定loginId的会话注销登录(踢人下线)
|
||||
|
||||
|
||||
## 详解
|
||||
- `logoutByLoginId` 和 `kickoutByLoginId` 都可以将用户强制下线,不同点在于:
|
||||
- `logoutByLoginId` 是将人清退,用户得到的提示是 [token无效] ,对于失效原因尚未可知 (NotLoginException场景值为-2)
|
||||
- `kickoutByLoginId` 是将人踢下线,用户可得到明确提示 [已被踢下线] (NotLoginException场景值为-5)
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#### StpUtil.checkLogin()
|
||||
- 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException`
|
||||
- 扩展:`NotLoginException` 对象可通过 `getLoginKey()` 方法获取具体是哪个 `StpLogic` 抛出的异常
|
||||
- 扩展:`NotLoginException` 对象可通过 `getType()` 方法获取具体的场景值,详细参考章节:[未登录场景值](/fun/not-login-scene)
|
||||
|
||||
#### StpUtil.getLoginId()
|
||||
- 获取当前会话登录id, 如果未登录,则抛出异常:`NotLoginException`
|
||||
@@ -36,9 +37,10 @@
|
||||
#### StpUtil.getLoginId(T defaultValue)
|
||||
- 获取当前会话登录id, 如果未登录,则返回默认值 (`defaultValue`可以为任意类型)
|
||||
- 类似API还有:
|
||||
- `StpUtil.getLoginId_defaultNull()` 获取当前会话登录id, 如果未登录,则返回null
|
||||
- `StpUtil.getLoginIdDefaultNull()` 获取当前会话登录id, 如果未登录,则返回null
|
||||
|
||||
#### getLoginIdByToken(String tokenValue)
|
||||
#### StpUtil.getLoginIdByToken(String tokenValue)
|
||||
- 获取指定token对应的登录id,如果未登录,则返回 null
|
||||
|
||||
|
||||
#### StpUtil.getTokenName()
|
||||
- 获取当前`StpLogic`的token名称
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
# 花式token
|
||||
|
||||
本篇介绍token生成的各种风格,以及自定义token生成策略
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 内置风格
|
||||
|
||||
- sa-token默认的token生成策略是uuid风格, 其模样类似于:`623368f0-ae5e-4475-a53f-93e4225f16ae`<br>
|
||||
- 如果你对这种风格不太感冒,还可以将token生成设置为其他风格,
|
||||
- 怎么设置呢?只需要在yml配置文件里设置 `spring.sa-token.token-style=风格类型` 即可,其有多种取值: <br>
|
||||
|
||||
|
||||
1. token-style=`uuid`,uuid风格 **(默认风格)**
|
||||
``` html
|
||||
623368f0-ae5e-4475-a53f-93e4225f16ae
|
||||
```
|
||||
|
||||
2. token-style=`simple-uuid`,同上,uuid风格, 只不过去掉了下划线:
|
||||
``` html
|
||||
6fd4221395024b5f87edd34bc3258ee8
|
||||
```
|
||||
|
||||
3. token-style=`random-32`,随机32位字符串:
|
||||
``` html
|
||||
qEjyPsEA1Bkc9dr8YP6okFr5umCZNR6W
|
||||
```
|
||||
|
||||
4. token-style=`random-64`,随机64位字符串:
|
||||
``` html
|
||||
v4ueNLEpPwMtmOPMBtOOeIQsvP8z9gkMgIVibTUVjkrNrlfra5CGwQkViDjO8jcc
|
||||
```
|
||||
|
||||
5. token-style=`random-128`,随机128位字符串:
|
||||
``` html
|
||||
nojYPmcEtrFEaN0Otpssa8I8jpk8FO53UcMZkCP9qyoHaDbKS6dxoRPky9c6QlftQ0pdzxRGXsKZmUSrPeZBOD6kJFfmfgiRyUmYWcj4WU4SSP2ilakWN1HYnIuX0Olj
|
||||
```
|
||||
|
||||
6. token-style=`tik`,tik风格:
|
||||
``` html
|
||||
gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__
|
||||
```
|
||||
|
||||
|
||||
## 自定义token生成策略
|
||||
- 如果你觉着以上风格都不是你喜欢的类型,那么你还可以**自定义token生成策略**,来定制化token生成风格 <br>
|
||||
- 怎么做呢?只需要重写`SaTokenAction`接口的`createToken`方法即可
|
||||
|
||||
|
||||
#### 参考步骤如下:
|
||||
1. 新建文件`MySaTokenAction.java`,继承`SaTokenActionDefaultImpl`默认实现类, 并添加上注解`@Component`,保证此类被`springboot`扫描到
|
||||
``` java
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
|
||||
|
||||
/**
|
||||
* 继承sa-token行为Bean默认实现, 重写部分逻辑
|
||||
*/
|
||||
@Component
|
||||
public class MySaTokenAction extends SaTokenActionDefaultImpl {
|
||||
// 重写token生成策略
|
||||
@Override
|
||||
public String createToken(Object loginId, String loginKey) {
|
||||
return SaTokenInsideUtil.getRandomString(60); // 随机60位字符串
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. 再次调用 `StpUtil.setLoginId(10001)`方法进行登录,观察其生成的token样式
|
||||
``` html
|
||||
gfuPSwZsnUhwgz08GTCH4wOgasWtc3odP4HLwXJ7NDGOximTvT4OlW19zeLH
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 以雪花算法生成token
|
||||
在此再举一个例子,以`自定义token生成策略`的方式集成`雪花算法`来生成token
|
||||
|
||||
1. 首先我们需要找一个合适的类库,帮助我们生成雪花算法唯一id,在此推荐 [Hutool](https://hutool.cn/docs/#/) ,在`pom.xml`里添加依赖:
|
||||
``` xml
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.5.4</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
2. 同上,我们需要新建文件`MySaTokenAction.java`,继承`SaTokenActionDefaultImpl`默认实现类, 并添加上注解`@Component`,保证此类被`springboot`扫描到
|
||||
``` java
|
||||
package com.pj.satoken;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
|
||||
/**
|
||||
* 继承sa-token行为Bean默认实现, 重写部分逻辑
|
||||
*/
|
||||
@Component
|
||||
public class MySaTokenAction extends SaTokenActionDefaultImpl {
|
||||
// 重写token生成策略
|
||||
@Override
|
||||
public String createToken(Object loginId, String loginKey) {
|
||||
return IdUtil.getSnowflake(1, 1).nextIdStr(); // 以雪花算法生成token
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. 再次调用 `StpUtil.setLoginId(10001)`方法进行登录,观察其生成的token样式:
|
||||
``` html
|
||||
1339604338175250432
|
||||
```
|
||||
@@ -66,3 +66,14 @@ footer a:hover{text-decoration: underline;}
|
||||
footer{position: static; line-height: 40px;}
|
||||
}
|
||||
|
||||
/* 闪光背景 */
|
||||
.main-box{
|
||||
background-size: 500%;
|
||||
background-image: linear-gradient(125deg,#BFFEBE,#F6F8B5,#FCD0B3,#BFB6F8,#E8D8B3);
|
||||
animation: bganimation 15s infinite;
|
||||
}
|
||||
@keyframes bganimation{
|
||||
0%{background-position: 0% 50%;}
|
||||
50%{background-position: 100% 50%;}
|
||||
100%{background-position: 0% 50%;}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
<meta name="description" content="Description">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="keywords" content="sa-token|sa-token框架|sa-token文档|sa-token在线文档|权限认证框架">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,强大、简单、好用,登录验证、权限验证、自定义session会话、踢人下线、持久层扩展、无cookie模式、模拟他人账号、多账号体系、注解式鉴权、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<meta name="description" content="sa-token是一个JavaWeb权限认证框架,强大、简单、好用,登录验证、权限验证、自定义session会话、踢人下线、持久层扩展、无cookie模式、模拟他人账号、多账号体系、注解式鉴权、花式token、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
|
||||
<link rel="stylesheet" href="https://unpkg.com/docsify@4.11.3/lib/themes/vue.css">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="doc/logo.png">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
@@ -43,10 +43,10 @@
|
||||
<!-- 内容部分 -->
|
||||
<div class="main-box">
|
||||
<div class="content-box">
|
||||
<h1>sa-token<small>v1.4.0</small></h1>
|
||||
<div class="sub-title">一个JavaWeb权限认证框架,强大、简单、好用</div>
|
||||
<h1>sa-token<small>v1.6.0</small></h1>
|
||||
<div class="sub-title">一个JavaWeb轻量级权限认证框架,功能全面,上手简单</div>
|
||||
<!-- <p>0配置开箱即用,低学习成本</p> -->
|
||||
<p>登录验证、权限验证、自定义session会话、踢人下线、持久层扩展、无cookie模式、模拟他人账号、多账号体系、注解式鉴权、Spring集成...</p>
|
||||
<p>登录验证、权限验证、自定义session会话、踢人下线、持久层扩展、无cookie模式、模拟他人账号、多账号体系、注解式鉴权、花式token、Spring集成...</p>
|
||||
<p>零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有</p>
|
||||
<div class="btn-box">
|
||||
<a href="https://github.com/click33/sa-token" target="_blank">GitHub</a>
|
||||
|
||||
Reference in New Issue
Block a user