1
0
mirror of synced 2026-05-22 22:53:16 +00:00

Compare commits

..

169 Commits

Author SHA1 Message Date
click33 ad837c9640 Merge branch 'master' of https://gitee.com/dromara/sa-token into dev
# Conflicts:
#	sa-token-demo/sa-token-demo-solon/src/main/java/com/pj/satoken/custom_annotation/handler/CheckAccountHandler.java
2026-03-09 08:16:58 +08:00
click33 aa3f77bb3c chore: 修正注释 2026-03-08 20:18:03 +08:00
click33 80c7362bfa feat: 新增 sa-token-alone-redis-by-spring-boot4 插件,实现 SpringBoot4 下权限缓存与数据缓存分离 2026-03-08 20:14:47 +08:00
click33 91dd4bb30a docs: v1.45.0 更新 2026-03-08 20:13:36 +08:00
click33 f4a7ef6e00 chore: 完善 pom.xml 信息 2026-03-08 20:08:24 +08:00
click33 79245edb5b chore: 统一 POM 的 name 与 artifactId,并补充 description 2026-03-08 14:47:36 +08:00
click33 98f4de6514 docs: 补全 StpUtil API 说明文档 2026-03-08 13:35:30 +08:00
click33 024d6e0491 docs: 优化 redis 引入章节相关说明 2026-03-08 13:21:01 +08:00
click33 5c675d0a37 docs: 修正全局的 redis 相关集成引入说明 2026-03-08 13:01:03 +08:00
click33 1858306fde docs: 添加 SpringBoot4 引入说明 2026-03-08 12:48:13 +08:00
click33 4c69912af0 docs: 添加 loveqq-boot 引入说明 2026-03-08 12:29:15 +08:00
click33 a24a5b2271 docs: 新增 json 序列化插件展示说明 2026-03-08 12:16:24 +08:00
click33 0e551cfd69 docs: 补全缺失目录说明 2026-03-08 12:15:43 +08:00
click33 2d37f76a42 docs: 新增 replacedLoginExitMode 配置项功能说明 2026-03-08 11:33:41 +08:00
click33 643f422ac8 release: v1.45.0 2026-03-08 04:09:40 +08:00
click33 3f9b07c25e AI: 新增 skills/upgrade-version/SKILL.md,用于一键更新版本号 2026-03-08 04:07:42 +08:00
click33 6cbf628c0f Merge branch 'dev' of https://github.com/dromara/Sa-Token into dev 2026-03-08 03:17:56 +08:00
click33 33047fd760 AI: 优化 commit-message/SKILL.md 2026-03-08 03:17:41 +08:00
click33 3f3263fe33 Merge pull request #792 from xuxiaowei-com-cn/xuxiaowei/Maven-Central-Portal
🔧 use Maven Central Portal
2026-03-08 03:15:06 +08:00
click33 e966f6cec1 Merge branch 'dev' into xuxiaowei/Maven-Central-Portal 2026-03-08 03:14:46 +08:00
click33 d3bdd6319c AI: 新增 skills/README.md 文档 2026-03-05 14:44:58 +08:00
click33 22e4d7e80b AI: 新增 commit-message skill,用于根据 git 变更生成符合项目规范的 commit message 2026-03-05 14:30:32 +08:00
click33 890a8f860e chore: 升级 flatten-maven-plugin 至 1.7.3 2026-03-05 14:22:06 +08:00
click33 f9ebc52a39 refactor: 移除冗余导包 2026-03-02 09:21:35 +08:00
click33 34c6b752d7 AI: 新增 skills/remove-redundancy-import/SKILL.md,用于检查项目中的java类无效冗余导包信息并移除 2026-03-02 09:21:18 +08:00
click33 46ab4d7849 Merge pull request #889 from aotemiao/fix/dubbo-context-cleanup
refactor: SaTokenDubboContextFilter 改为使用 SaTokenContextDubboUtil 清理上下文
2026-03-02 08:04:31 +08:00
click33 bd21035cf1 Merge pull request #851 from coder-xiaomo/dev
fix: 修正一处代码注释错误:SaTokenDao 注释中 数据有效期 应为 小于等于-2 (掉了等于)
2026-03-02 07:57:06 +08:00
click33 465dfaddeb Merge pull request #827 from AdRainty/dev
fix: SaOAuth2Strategy中removeGrantTypeHandler的引用有误
2026-03-02 07:54:31 +08:00
click33 af81ccac75 Merge pull request #816 from font-C/dev
fix: Bearer 全局统一大小写。
2026-03-02 07:48:58 +08:00
click33 b97e60661d Merge pull request #807 from GodBlessWorld/dev
文档:更正一些专业技术术语及清晰表述问题
2026-03-02 07:37:45 +08:00
click33 5e0ca81b9e Merge branch 'dev' into dev 2026-03-02 07:37:33 +08:00
click33 1f4ff1ede4 Merge pull request #793 from 1411430556/patch-1
Update LICENSE
2026-03-02 07:29:41 +08:00
click33 d75d5de438 Merge pull request #907 from 07heco/perf/optimize-pattern-cache-and-eliminate-magic-value
refactor: optimize constant specification and encapsulation for StrFormatter
2026-03-02 07:07:25 +08:00
click33 ab362c87e0 fix: 修复 StpUtil.getLoginIdByTokenNotThinkFreeze 方法缺少 static 的问题 2026-03-02 05:14:26 +08:00
刘潇 0c23371058 !356 添加 sa-token-snack4
Merge pull request !356 from 西东/dev
2026-03-01 21:08:05 +00:00
刘潇 197ba44ce1 !355 修复sa-token-oauth2组件使用sa-token-fastjson2序列化导致的类型转换问题
Merge pull request !355 from 陆健伟/dev
2026-03-01 21:06:48 +00:00
刘潇 c8a7b50cb8 !354 docs: 订正文档错别字
Merge pull request !354 from Uncarbon/N/A
2026-03-01 21:05:41 +00:00
刘潇 9715526c34 !351 loveqq-framework版本更新
Merge pull request !351 from kfyty/dev
2026-03-01 21:04:21 +00:00
click33 22302760c8 refactor: 重命名 SaRepeatLoginsMode -> SaReplacedLoginExitMode 2026-03-02 04:59:09 +08:00
刘潇 466c215506 !349 添加重复登录处理策略,当同一账号不允许多客户端同时登录时支持选择是踢人下线还是拦截本次登录
Merge pull request !349 from 石泽旭/dev
2026-03-01 19:37:12 +00:00
刘潇 7e1d810667 !347 文档内代码示例有点小问题
Merge pull request !347 from Tang/N/A
2026-03-01 19:17:17 +00:00
click33 809232d656 docs: 同步最新文章列表、赞助者名单 2026-03-01 21:34:44 +08:00
click33 2c442f4c0a memo: 备忘录重构为专门的文件夹 2026-03-01 12:10:22 +08:00
click33 dbbb47a1a9 refactor: 优化项目构建配置 2026-03-01 12:09:33 +08:00
click33 4b176d49d5 refactor: 优化 OAuth2 模块在请求中读取 Client 信息算法 2026-03-01 12:08:46 +08:00
刘潇 750c346353 !346 修改ClientIdSecretModel的读取构建逻辑
Merge pull request !346 from ziy001/dev
2026-02-28 22:39:02 +00:00
click33 1a37efef22 docs: 为 sa-token-sso 模块定义 STS 协议 2026-02-28 21:29:22 +08:00
click33 d9168711a0 AI: 新增 SKILL: organize-update-log ,用于格式化整理版本更新日志信息 2026-02-28 05:02:02 +08:00
click33 1a89c68e68 chore: 修复注释错别字 2026-02-28 04:49:02 +08:00
click33 22e9ee80d0 refactor: 优化模块依赖关系 2026-02-28 03:24:24 +08:00
07heco 80f132c1f8 refactor: optimize constant specification and encapsulation for StrFormatter 2026-02-27 18:42:08 +08:00
click33 6a96d2fabd refactor: 重构模块依赖层级 2026-02-27 12:14:01 +08:00
click33 f40f0d3adf refactor: sa-token-dependencies 重构为 sa-token-basic-dependencies 2026-02-27 10:04:44 +08:00
click33 6398ef4ca6 refactor: 重构 springboot reactor 相关集成包,优化依赖关系 2026-02-27 06:18:06 +08:00
click33 e8fe3a83c7 demo: 新增 sa-token-demo-webflux-springboot4 示例 2026-02-27 03:47:12 +08:00
click33 8c0177c598 feat: 新增 sa-token-reactor-spring-boot4-starter 集成包 2026-02-27 03:45:54 +08:00
click33 fc27e2a49d refactor: 移除 sa-token-spring-boot-autoconfig 模块 2026-02-27 03:22:14 +08:00
click33 7c95ec51fa refactor: 重构 springboot 相关集成包,优化依赖关系 2026-02-27 00:25:12 +08:00
click33 9b7efbbcc8 demo: 新增 SpringBoot4 整合 demo 示例 2026-02-26 01:22:32 +08:00
click33 2d671dc783 feat: 新增 sa-token-spring-boot4-starter 集成包 2026-02-26 01:19:41 +08:00
click33 f6c5131ad2 test: 新增 sa-token-jackson3 单元测试 2026-02-25 19:55:09 +08:00
click33 9c07f7a007 feat: 添加 sa-token-jackson3 插件 2026-02-25 19:13:18 +08:00
click33 c2becdf498 docs: 公众号文章列表同步 2026-02-23 16:42:26 +08:00
click33 defb2a0052 docs: 优化 readme 2026-02-20 19:29:15 +08:00
click33 007bf2aa47 docs: 优化 readme. 2026-02-20 19:19:31 +08:00
click33 7725322002 docs: 优化案例库展示 2026-02-20 19:00:24 +08:00
click33 91f97089b5 chore: 增加忽略 .vscode 目录 2026-02-20 19:00:03 +08:00
click33 ef9a0ac3d4 Merge branch 'dev' of https://gitee.com/dromara/sa-token into dev 2026-02-16 01:38:27 +08:00
click33 60a348e903 docs: 同步最新博客链接 2026-02-16 01:37:44 +08:00
刘潇 2eeb47034b !358 update sa-token-doc/start/maven-pull.md.
Merge pull request !358 from QuLing/N/A
2026-02-10 04:30:24 +00:00
QuLing c83ccee894 update sa-token-doc/start/maven-pull.md.
通过新开项目下载依赖后,解决原父子项目无法下载依赖的问题

Signed-off-by: QuLing <x2995924975@163.com>
2026-02-10 04:29:27 +00:00
click33 eba5294beb docs: 同步最新博客列表、赞助者名单 2026-02-09 23:48:36 +08:00
click33 96cb8c5ad3 docs: 添加 Sa-Token 内容合作者群 2026-02-09 02:19:19 +08:00
click33 a552e84488 docs: 优化框架 Slogan 2026-02-08 01:21:55 +08:00
click33 82158d1ee1 docs: 优化 slogan 2026-02-06 18:36:16 +08:00
click33 26c4957020 docs: 新增 《Gitee 2025年度开源项目 Web应用开发 Top 2》 证书展示 2026-02-05 23:59:56 +08:00
click33 49ac796460 docs: 完善网站底部版权栏 2026-02-05 03:56:42 +08:00
click33 9f8a2b29ba docs: 错别字修复 2026-02-05 03:13:03 +08:00
click33 e608272f3f docs: 错别字修复 /sso/sso-questions.md 2026-02-05 02:59:50 +08:00
click33 8be310be15 docs: 错别字修复 2026-02-05 02:50:30 +08:00
click33 9090eddd8d docs: 框架博客展示 2026-02-04 23:56:17 +08:00
click33 34c0775fa2 docs: 同步博客列表 2026-02-04 19:20:29 +08:00
click33 4c737cb342 docs: 同步最新赞助者名单 2026-02-04 19:19:42 +08:00
click33 2294d89db6 docs: 更换 GitCode 为 AtomGit 2026-02-03 17:09:34 +08:00
click33 9444520ad3 docs: 同步最新赞助者名单 2026-02-03 17:02:38 +08:00
click33 d0135299d1 docs: 同步开源案例 2026-02-03 16:45:16 +08:00
click33 dce14ce384 docs: 增加 '/up/*', '/micro/*', '/plugin/*' 章节锁定 2026-02-03 16:44:58 +08:00
click33 150781e94f docs: 更换 gitcode 为 atomgit 2026-02-03 16:43:59 +08:00
click33 192a66aed2 docs: 更换qq群链接 2026-01-29 14:49:04 +08:00
click33 7c4865d0e1 docs: 在线文档地址新增渠道参数 2026-01-27 13:40:28 +08:00
click33 f431473c90 docs: 增加部分章节锁定功能 2026-01-27 13:38:02 +08:00
click33 6387b7eede docs: readme & 文档 细节优化 2026-01-25 14:55:13 +08:00
click33 7eb2772484 docs: 优化 README.md 2026-01-25 14:45:35 +08:00
click33 21df9682b6 docs: 文档细节优化 2026-01-25 14:35:30 +08:00
click33 b707205607 docs: 优化readme 2026-01-25 14:18:20 +08:00
click33 d4a3eef5c7 docs: 文档优化 2026-01-24 11:34:44 +08:00
click33 0e45a9067a docs: 更改部分项目展示 logo 2026-01-24 11:03:53 +08:00
click33 b3fbfa35d4 docs: 同步最新 dromara 项目列表 2026-01-24 09:46:03 +08:00
click33 18f8ed74ff docs: 更换微信群聊展示图 2026-01-24 07:39:38 +08:00
click33 ac74945004 docs: 增加微信群聊信息展示 2026-01-24 07:35:21 +08:00
click33 2fc76c767e docs: 新增赞赏码展示 2026-01-24 05:30:22 +08:00
click33 cefa368f4d docs: 文档首页新增 stars 对比图 2026-01-24 04:19:42 +08:00
click33 84795e9014 docs: 新增解决跨域专题文章 2026-01-24 04:18:28 +08:00
click33 f8d57189e0 docs: 移除 Gitee 2025 投票提示 2026-01-24 04:17:54 +08:00
aotemiao 43ae08bd3c refactor: SaTokenDubboContextFilter 改为使用 SaTokenContextDubboUtil 清理上下文 2026-01-06 08:41:12 +08:00
click33 4cbb0849a7 perf: 优化 readme 2025-12-30 22:59:06 +08:00
click33 b1723e5424 refactor: 注释优化 2025-12-29 02:08:12 +08:00
click33 196d34be20 docs: team.md 优化 2025-12-29 02:00:20 +08:00
click33 ff3ce7daa5 docs: 图片地址更换为本地文件 (附录篇) 2025-12-29 01:44:28 +08:00
click33 f61d8369ac docs: 图片地址更换为本地文件 (插件篇) 2025-12-29 01:19:15 +08:00
click33 a27739f344 docs: 图片地址更换为本地文件 (微服务篇) 2025-12-29 01:02:23 +08:00
click33 9378da7a91 docs: 图片地址更换为本地文件 (OAuth2篇) 2025-12-29 00:41:34 +08:00
click33 47e8e91056 docs: 图片地址更换为本地文件 (SSO篇) 2025-12-29 00:25:06 +08:00
click33 e7c09e81d7 docs: 图片地址更换为本地文件 (深入篇) 2025-12-29 00:03:35 +08:00
click33 6cd765ce91 docs: 图片地址更换为本地文件 (基础篇) 2025-12-28 23:49:34 +08:00
click33 d646099127 docs: index.html & doc.html 引用图片替换为本地 2025-12-28 22:44:15 +08:00
click33 5708657f86 docs: 文档优化 [路由拦截鉴权] 篇 2025-12-28 17:53:30 +08:00
click33 7e9201584d docs: 文档优化 [权限认证] 篇 2025-12-28 12:32:02 +08:00
click33 a6a1b9b865 docs: 文档优化 [登录认证] 篇 2025-12-27 16:47:49 +08:00
click33 4b7e6d651f doc: 补全登记企业信息 2025-12-27 16:46:37 +08:00
click33 f6d6e50a16 docs: 补全企业登记案例 2025-12-26 12:26:46 +08:00
click33 fdead8cfa6 docs: 补全全局策略说明 2025-12-25 20:18:24 +08:00
click33 aa15d56082 docs: 优化文档排版 2025-12-25 01:37:41 +08:00
click33 30ffe1713f docs: 调整主题色块顺序,使之切换时对比更突出 2025-12-24 23:43:26 +08:00
click33 74db9e997e docs: 文档主题切换增加水滴特效 2025-12-24 23:31:40 +08:00
noear 6264c37780 feat: 添加 sa-token-snack4 2025-12-24 21:51:43 +08:00
click33 f4fa77edd0 docs: 补全数据结构说明 2025-12-24 01:14:50 +08:00
click33 2cf899e019 docs: 增加 Same-Token 同源系统认证图示说明 2025-12-22 20:37:46 +08:00
click33 c625a29df8 docs: 子服务外网隔离章节增加示意图 2025-12-22 17:42:27 +08:00
noear d0c2ee7ffd feat: 添加 sa-token-snack4 2025-12-09 17:16:33 +08:00
click33 39b468b150 docs: 功能结构图增加点击事件跳转到对应功能文档 2025-12-01 01:00:17 +08:00
click33 a16552c3fe docs(sso): 单点登录消息推送模块增加 msgType 参数说明 2025-11-18 12:03:30 +08:00
click33 ed35640765 docs: 目录树增加专门栏目记录项目架构设计 2025-11-18 11:39:59 +08:00
click33 b85b2815ea docs: 添加 SSO 模块视频讲解链接 2025-11-12 15:53:22 +08:00
click33 5b8b99e0d0 feat(sso): 新增 sso-server 前后端分离模式下 平台中心模式 demo 示例 2025-11-11 14:51:38 +08:00
click33 75256e3329 docs: 修正部分信息 2025-11-10 22:55:42 +08:00
noear a59c42802c feat: 添加 sa-token-snack4 2025-11-06 17:54:19 +08:00
徐晓伟 6a21591e61 🔧 use Maven Central Portal
https://github.com/alibaba/nacos/issues/13358

settings.xml servers use:

<server>
  <id>central</id>
  <username>${CENTRAL_USERNAME}</username>
  <password>${CENTRAL_PASSWORD}</password>
</server>
2025-10-30 16:42:40 +08:00
noear 27c1a0b1a2 Merge branch 'dev' of https://gitee.com/noear_admin/sa-token into dev 2025-10-27 22:08:50 +08:00
noear 7a534d5aa4 feat: 添加 sa-token-snack4 2025-10-27 22:08:01 +08:00
click33 43728fb6cc docs(sso): 补全 SSO 模块内置消息处理器相关文档 2025-10-23 18:41:40 +08:00
lujw 3c558c2472 修复sa-token-oauth2组件使用sa-token-fastjson2序列化导致的类型转换问题 2025-10-23 17:02:58 +08:00
Uncarbon ae1bd30245 docs: 订正文档错别字
Signed-off-by: Uncarbon <4840454+uncarbon97@user.noreply.gitee.com>
2025-10-23 02:15:04 +00:00
click33 a53874025e Merge branch 'dev' of https://gitee.com/dromara/sa-token into dev 2025-10-21 17:49:05 +08:00
click33 7a63be81d4 fix(docs): 修复不合适的文章标题 2025-10-21 17:47:52 +08:00
刘潇 4a16556a56 !352 update sa-token-doc/sso/sso-check-domain.md.
Merge pull request !352 from 吴金川/N/A
2025-10-10 05:52:14 +00:00
吴金川 5051f430c0 update sa-token-doc/sso/sso-check-domain.md.
Signed-off-by: 吴金川 <wujinchuan@126.com>
2025-10-10 01:38:49 +00:00
click33 fa76826ee0 docs: 修复文档不正确描述 2025-10-04 18:39:16 +08:00
小墨 4bbce74570 fix: 修正一处代码注释错误:SaTokenDao 注释中 数据有效期 应为 小于等于-2 (掉了等于) 2025-09-29 05:24:44 +00:00
click33 622cf3f3b5 docs(sso): 修复不正确的API说明 2025-09-20 13:33:48 +08:00
click33 0bebcd4b02 Merge branch 'dev' of https://gitee.com/dromara/sa-token into dev 2025-09-20 12:57:23 +08:00
click33 e1f25ccae8 docs: 更新赞助者名单 2025-09-20 12:54:38 +08:00
刘潇 ac6f06fb37 !348 refactor: sa-token-solon-plugin 优化 Gateway 接口的处理(避免使用路由接口)
Merge pull request !348 from 西东/dev
2025-09-04 06:26:37 +00:00
kfyty725 03613bf7a3 opt:loveqq-framework版本更新 2025-08-15 15:28:47 +08:00
AdRainty e4da3d8e7b Update SaOAuth2Strategy.java
removeGrantTypeHandler的引用有误
2025-08-06 15:59:53 +08:00
石泽旭 79f772ccc8 --添加重复登录处理策略,当不允许多客户端同时登录时支持选择是踢人下线还是拦截本次登录 2025-08-05 13:48:35 +08:00
石泽旭 312ca71c47 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java
2025-08-05 13:47:22 +08:00
石泽旭 ac2e1d0abd --添加重复登录处理策略,当不允许多客户端同时登录时支持选择是踢人下线还是拦截本次登录 2025-08-05 13:46:40 +08:00
石泽旭 808c3cdc0f --添加重复登录处理策略,当不允许多客户端同时登录时支持选择是踢人下线还是拦截本次登录 2025-07-31 15:07:20 +08:00
noear 4036788426 refactor: sa-token-solon-plugin 优化 Gateway 接口的处理(避免使用路由接口) 2025-07-29 11:19:25 +08:00
font-C f57303f7ec fix: Bearer 全局统一大小写。 2025-07-16 02:46:01 +00:00
Tang 2919188470 文档内代码示例有点小问题
文档示例代码中注释与方法有冲突,差点给带进去了

Signed-off-by: Tang <tang_0416@126.com>
2025-07-15 02:06:11 +00:00
ziy 5c33c83391 修改ClientIdSecretModel的读取构建逻辑 2025-07-13 14:39:06 +08:00
COYG⚡️ 39a986071d Merge branch 'dev' into patch-1 2025-07-10 18:18:00 +08:00
sudongdong01 072ae7c787 文档:更正一些专业技术术语及清晰表述问题 2025-06-19 17:04:29 +08:00
刘潇 eefb5e5f8e !342 fix: 兼容父类SaAnnotationHandlerInterface#checkMethod
Merge pull request !342 from 最后/N/A
2025-06-08 10:32:09 +00:00
最后 f75b6595dd fix: 兼容父类SaAnnotationHandlerInterface#checkMethod
fix:  兼容父类SaAnnotationHandlerInterface#checkMethod

Signed-off-by: 最后 <244387066@qq.com>
2025-06-08 01:34:55 +00:00
click33 b0d42821d7 docs: 完善文档 2025-06-07 11:59:57 +08:00
duolaameng 2f6390a211 Update LICENSE
完善版权日期
2025-05-22 12:22:14 +08:00
349 changed files with 10163 additions and 2720 deletions
+87
View File
@@ -0,0 +1,87 @@
# Cursor Agent Skills
本目录存放用于辅助 Sa-Token 项目开发的 Cursor Agent Skills。这些 skills 封装了项目特定的知识和操作规范,可在对话中直接调用。
## Skill 列表
| Skill 名称 | 功能描述 | 使用场景 | 入口文件 |
|-----------|---------|---------|---------|
| `commit-message` | 根据 git 变更生成符合 Sa-Token 项目风格的 commit message | 生成提交信息、写 commit message | [SKILL.md](commit-message/SKILL.md) |
| `organize-update-log` | 根据 git 提交记录生成符合项目规范的更新日志内容 | 整理更新日志、分析版本变更 | [SKILL.md](organize-update-log/SKILL.md) |
| `remove-redundancy-import` | 检查并移除 Java 类中未被引用的冗余 import | 清理冗余导包、优化 import | [SKILL.md](remove-redundancy-import/SKILL.md) |
| `upgrade-version` | 将项目版本号从旧版本升级到新版本,批量修改 pom、常量、Demo 及文档 | 升级版本、修改版本号、version bump | [SKILL.md](upgrade-version/SKILL.md) |
### 详细说明
#### commit-message
根据当前 git 变更(staged 或 unstaged),生成符合 [Conventional Commits](https://www.conventionalcommits.org/) 格式、以中文为主的 commit message。支持识别新增文件、修复 bug、重构等多种变更类型。
#### organize-update-log
根据 git 提交记录,生成符合 `sa-token-doc/more/update-log.md` 格式的更新日志内容。自动分类到插件、starter、重构、Solon、示例、文档等版块。
#### remove-redundancy-import
扫描项目中所有 Java 类,检测未被引用的冗余 import,生成清理计划供审阅,确认后执行移除。支持通过内置 Python 脚本快速扫描。
#### upgrade-version
将 Sa-Token 项目版本号从旧版本升级到新版本。批量修改根 POM、BOM、SaTokenConsts、所有 Demo 子项目 pom.xml 及文档(README、index.html、doc.html、new-version.md 等)中的版本引用。明确排除历史记录、@since 标注、更新日志等不应修改的文件。
## 快速使用
在 Cursor 对话中,直接描述你的需求即可自动触发相应 skill:
```
用户:帮我生成 commit message
→ 自动使用 commit-message skill 分析 git 变更并生成提交信息
用户:整理一下更新日志
→ 自动使用 organize-update-log skill 生成更新日志
用户:清理一下冗余 import
→ 自动使用 remove-redundancy-import skill 扫描并清理未使用的 import
用户:把版本从 v1.44.0 升级到 v1.45.0
→ 自动使用 upgrade-version skill 批量修改版本号
```
## 新增 Skill 维护指南
当新增 skill 时,请同步更新本 README 文件,保持 skill 列表的完整性。
### Skill 目录结构规范
每个 skill 应创建独立的子目录,结构如下:
```
.cursor/skills/
├── README.md # 本文件
└── skill-name/ # skill 目录(小写,短横线分隔)
├── SKILL.md # skill 主文件(必须包含 YAML 元数据和使用说明)
├── examples.md # 使用示例(可选)
├── reference.md # 参考文档(可选)
└── scan_redundant_imports.py # 辅助脚本(如需要)
```
### SKILL.md 文件格式
每个 `SKILL.md` 必须包含 YAML Front Matter
```yaml
---
name: skill-name
description: 简要描述 skill 的功能和使用场景
---
```
### 更新 README 清单
新增 skill 后,请在本文件中:
1.**Skill 列表** 表格中添加新行
2.**详细说明** 小节添加对应的描述段落
3. (可选)在 **快速使用** 中添加使用示例
## 注意事项
- 所有 skill 遵循 Sa-Token 项目特定的规范和风格
- 部分 skill(如 `remove-redundancy-import`)在执行前需要用户确认
- 可参考每个 skill 目录下的 `examples.md``reference.md` 获取更多使用帮助
+110
View File
@@ -0,0 +1,110 @@
---
name: commit-message
description: 根据 git 变更生成符合 Sa-Token 项目风格的 commit message。遵循 Conventional Commits 格式,以中文为主。当用户要求生成提交信息、写 commit message、或根据变更生成提交说明时使用。
---
# 生成 Commit Message
根据当前 git 变更(staged 或 unstaged),生成符合 Sa-Token 项目规范的 commit message。
## 使用时机
- 用户要求生成 commit message
- 用户要求根据变更写提交说明
- 用户说「帮我写个 commit」「生成提交信息」等
## 工作流程
### 第一步:获取变更内容
```bash
git status
git diff --staged
git diff
```
**必须包含的变更范围**
- **staged 变更**`git diff --staged`
- **unstaged 变更**:若无 staged,则用 `git diff` 查看工作区修改
- **未跟踪文件**`git status` 中的 Untracked files 也要纳入分析,生成 commit message 时需一并考虑
若存在未跟踪的新增文件(如新 skill、新配置等),应在 message 中体现,或给出「包含全部变更」与「仅已修改文件」两种方案供用户选择。
### 第二步:分析变更类型
根据变更内容选择 type 前缀:
| type | 适用场景 |
|------|----------|
| feat | 新增功能、新模块、新插件 |
| fix | 修复 bug、修正错误 |
| refactor | 重构、优化结构、重命名、移除冗余 |
| perf | 性能优化(与 refactor 区分:侧重性能) |
| docs | 文档更新、README、错别字、同步链接 |
| style | 代码格式调整(缩进、空格等,不影响逻辑) |
| chore | 构建配置、.gitignore、注释修复、依赖更新 |
| test | 单元测试、测试用例 |
| demo | 示例项目、demo 相关 |
| memo | 备忘录、内部记录 |
| revert | 回滚某次提交 |
| AI | AI 创建的 skill、规则等 |
### 第三步:撰写描述
**基础格式**`type: 简短描述``type(scope): 简短描述`
**scope 可选**:涉及特定模块时使用,如 `feat(sign)``fix(oauth2)``refactor(dependencies)`
**规范**
- **50 字规则**subject 不超过 50 字符,保证在 git log 中完整显示
- **命令式语气**:用「修复」「新增」「优化」,不用「修复了」「新增了」
- **说明「做了什么」**:清晰表达变更内容,必要时说明「为什么」
- **以中文为主**:技术术语可保留英文(如 `StrFormatter``sa-token-jackson3`
- **动词开头**:新增、修复、优化、重构、移除、同步、订正 等
**可选 Body/Footer**(重要变更时):
- Body:详细说明变更背景、动机,每行不超过 72 字符
- Footer:关联 Issue,如 `Fixes #123``merge: [pr N](url)`
### 第四步:输出
直接输出可复制的 commit message。若有多条合理方案,可给出 1~2 个备选。
## 格式示例
**简单提交**(常用):
```
feat: 添加 sa-token-jackson3 插件
fix(sign): 修复签名校验在空参数时的空指针
```
**带 scope**
```
refactor(dependencies): 重构模块依赖层级
perf(oauth2): 优化 Client 信息读取算法
```
**带 body**(复杂变更):
```
feat: 新增重复登录处理策略
当同一账号不允许多客户端同时登录时,支持选择踢人下线或拦截本次登录。
```
## 参考资源
- 示例:详见 [examples.md](examples.md)
- 规范详解:详见 [reference.md](reference.md)
## 快速对照
| 变更内容 | 示例输出 |
|----------|----------|
| 新增插件 | `feat: 添加 sa-token-jackson3 插件` |
| 修复 bug | `fix: 修复 StpUtil.getLoginIdByTokenNotThinkFreeze 方法缺少 static 的问题` |
| 性能优化 | `perf: 优化 StrFormatter 常量封装` |
| 重构模块 | `refactor: 重构模块依赖层级` |
| 移除冗余 | `refactor: 移除冗余导包` |
| 文档更新 | `docs: 同步最新文章列表、赞助者名单` |
| 注释修复 | `chore: 修复注释错别字` |
| 新增 skill | `AI: 新增 skills/commit-message/SKILL.md,用于根据 git 变更生成符合项目风格的 commit message` |
+97
View File
@@ -0,0 +1,97 @@
# Commit Message 示例
基于 Sa-Token 项目近期提交整理,遵循 Conventional Commits + 50/72 规则。
## feat - 新增功能
```
feat: 添加 sa-token-jackson3 插件
feat: 新增 sa-token-spring-boot4-starter 集成包
feat: 新增 sa-token-reactor-spring-boot4-starter 集成包
feat(sign): 新增签名模板自定义能力
```
## fix - 修复问题
```
fix: 修复 StpUtil.getLoginIdByTokenNotThinkFreeze 方法缺少 static 的问题
fix: 修正一处代码注释错误:SaTokenDao 注释中 数据有效期 应为 小于等于-2 (掉了等于)
fix: Bearer 全局统一大小写
fix: SaOAuth2Strategy中removeGrantTypeHandler的引用有误
```
## refactor - 重构/优化
```
refactor: 移除冗余导包
refactor: 重命名 SaRepeatLoginsMode -> SaReplacedLoginExitMode
refactor: 优化项目构建配置
refactor: 优化 OAuth2 模块在请求中读取 Client 信息算法
refactor: 优化模块依赖关系
refactor: 重构模块依赖层级
refactor: sa-token-dependencies 重构为 sa-token-basic-dependencies
refactor: SaTokenDubboContextFilter 改为使用 SaTokenContextDubboUtil 清理上下文
```
## perf - 性能优化
```
perf: 优化 StrFormatter 常量规范与封装
perf: 优化 pattern 缓存,消除魔法值
```
## docs - 文档
```
docs: 订正文档错别字
docs: 同步最新文章列表、赞助者名单
docs: 为 sa-token-sso 模块定义 STS 协议
docs: 优化 readme
docs: 同步最新博客链接
```
## chore - 杂项
```
chore: 修复注释错别字
chore: 增加忽略 .vscode 目录
```
## demo - 示例
```
demo: 新增 sa-token-demo-webflux-springboot4 示例
demo: 新增 SpringBoot4 整合 demo 示例
```
## test - 测试
```
test: 新增 sa-token-jackson3 单元测试
```
## memo - 备忘录
```
memo: 备忘录重构为专门的文件夹
```
## style - 代码格式
```
style: 统一代码缩进与空格
style: 修复 ESLint 警告
```
## revert - 回滚
```
revert: feat(sign): 新增签名模板自定义能力
```
## AI - AI 相关
```
AI: 新增 skills/remove-redundancy-import/SKILL.md,用于检查项目中的java类无效冗余导包信息并移除
AI: 新增 SKILL: organize-update-log ,用于格式化整理版本更新日志信息
```
@@ -0,0 +1,38 @@
# Commit Message 规范参考
基于 Conventional Commits 与业界最佳实践整理。
## 核心规则
| 规则 | 说明 |
|------|------|
| 50 字规则 | subject 不超过 50 字符,便于 git log 完整显示 |
| 72 字规则 | body 每行不超过 72 字符,便于阅读与 diff |
| 命令式语气 | 用「修复」「新增」而非「修复了」「新增了」 |
| 说明动机 | 重要变更在 body 中说明「为什么」而不仅是「做了什么」 |
## 格式结构
```
<type>[(<scope>)]: <subject>
[optional body]
[optional footer(s)]
```
- **subject**:必填,简明扼要
- **body**:可选,详细说明
- **footer**:可选,如 `Fixes #123``BREAKING CHANGE: xxx`
## 类型速查
- **feat**:新功能
- **fix**:修复 bug
- **refactor**:重构(结构、逻辑)
- **perf**:性能优化
- **docs**:文档
- **style**:格式(不影响逻辑)
- **chore**:构建、配置、杂项
- **test**:测试
- **revert**:回滚
+123
View File
@@ -0,0 +1,123 @@
---
name: organize-update-log
description: 根据 git 提交记录生成符合 Sa-Token 项目规范的更新日志内容。适用于分析指定版本之后的提交、提取变更并格式化为 update-log.md 风格。当用户需要生成更新日志、整理版本变更、或分析 release 之后的提交时使用。
---
# 整理更新日志
根据 git 提交记录,生成符合 `sa-token-doc/more/update-log.md` 格式的更新日志内容。
## 使用时机
- 用户要求生成/整理更新日志
- 用户要求分析「某版本之后」的提交变更
- 用户要求将 git 提交格式化为更新日志风格
- 准备发布新版本前整理 changelog
## 工作流程
### 第一步:确定基准版本
1. 询问用户基准版本(如 `v1.44.0`),或从上下文推断
2. 查找该版本的发布提交:
-`SaTokenConsts.java``pom.xml` 中搜索版本号
- 或执行:`git log --oneline --all -- sa-token-core/src/main/java/cn/dev33/satoken/util/SaTokenConsts.java` 查找含 `release vX.X.X` 的提交
3. 记录基准提交 hash(如 `7bde74bc`
### 第二步:获取提交列表
执行:
```bash
git log <基准提交>..HEAD --oneline --format="%h %s"
```
可选,获取更详细的变更文件:
```bash
git log <基准提交>..HEAD --stat --format="=== %h %s ==="
```
### 第三步:分类与映射
将每条提交按以下规则归类到对应板块:
| 提交关键词/内容 | 归属板块 |
|----------------|----------|
| feat.*jackson、plugin、插件 | 插件 |
| feat.*starter、spring-boot、reactor | starter |
| refactor.*依赖、dependencies、模块 | 重构 |
| refactor.*solon、gateway | Solon(单独列出) |
| fix.*dubbo、dubbo3 | 插件 |
| demo.*、示例 | 示例 或 starter |
| docs.*、文档 | 文档 |
| chore、.gitignore、.vscode | 其它 |
| merge.*loveqq、maven-pull | 其它(含 PR 链接) |
### 第四步:动作词映射
根据提交类型选择正确的动作词:
| 提交类型 | 动作词 | 示例 |
|----------|--------|------|
| feat、新增 | 新增 | 新增 `sa-token-jackson3` 插件 |
| fix、修复 | 修复 | 修复 Maven 父子项目依赖下载问题 |
| refactor、重构 | 重构 / 移除 | 重构模块依赖层级;移除 xxx 模块 |
| 优化、perf | 优化 | 优化 Gateway 接口处理 |
| 拆分 | 拆分 | (少见) |
| 文档更新 | 同步/新增/优化/修复 | 按具体内容选择 |
### 第五步:按格式输出
使用下方模板生成最终内容。详见 [format-reference.md](format-reference.md)。
## 输出模板
```markdown
### vX.X.X @YYYY-M-D(或:开发中 / 未发布)
- 插件:
- 新增:xxx。 **[重要]**(如适用)
- 修复:xxx。merge: [pr N](https://gitee.com/dromara/sa-token/pulls/N)(如适用)
- starter
- 新增:xxx。
- 重构:
- 重构:xxx。
- 移除:xxx。
- Solon:(如有)
- 优化:xxx。merge: [pr N](url)
- 示例:(如有)
- 新增:xxx。
- 文档:
- 同步:xxx。
- 新增:xxx。
- 优化:xxx。
- 修复:xxx。
- 其它:
- 新增/修复/优化:xxx。
```
## 格式规则
1. **层级**:一级用 `-`,二级用 ` -`Tab + 短横线)
2. **动作词**:每条以「新增」「修复」「重构」「优化」「移除」「同步」等开头
3. **重要标记**:对用户影响大的变更加 `**[重要]**`
4. **PR/Issue 链接**:提交信息含 `!358``pr 340` 等时,补充 `merge: [pr N](https://gitee.com/dromara/sa-token/pulls/N)`
5. **代码/模块名**:用反引号包裹,如 `` `sa-token-jackson3` ``
6. **合并同类**:多条相似文档类提交可合并为一条(如「同步公众号、博客、赞助者名单」)
## 常见板块
- **core**:核心逻辑、API、配置变更
- **SSO**:单点登录相关
- **OAuth2**OAuth2 相关
- **插件**:插件包(jackson、dubbo、redis 等)
- **starter**Spring Boot / Reactor 等 starter
- **示例**demo 项目
- **文档**:文档、README、错别字
- **其它**:其它杂项
## 注意事项
- 合并提交(Merge branch)可忽略,只保留实际变更的提交
- 纯文档/错别字可适度合并,避免条目过多
- 版本号未发布时,可写 `v1.45.0(开发中)``未发布`
- 输出为可直接粘贴到 `update-log.md` 的 Markdown 片段
@@ -0,0 +1,108 @@
# 更新日志格式参考
本文档提供 `sa-token-doc/more/update-log.md` 的格式细节与示例,供生成更新日志时参考。
## 版本标题格式
```markdown
### v1.44.0 @2025-6-7
```
- 版本号:`v` + 主.次.修订
- 日期:`@YYYY-M-D``@YYYY-M-DD`
- 未发布时:`v1.45.0(开发中)``v1.45.0(未发布)`
## 板块结构
```
- 板块名:
- 动作词:具体描述。 **[重要]**(可选) merge: [pr N](url)(可选)
```
- 一级:`- 板块名:`
- 二级:` - 动作词:描述。`Tab 缩进)
## 动作词
| 动作词 | 含义 | 使用场景 |
|--------|------|----------|
| 新增 | 新功能、新模块 | feat、新增插件、新 starter |
| 修复 | Bug 修复 | fix |
| 重构 | 结构调整 | refactor |
| 优化 | 改进、优化 | 优化、perf |
| 移除 | 删除模块/功能 | 删除、移除 |
| 拆分 | 模块拆分 | 拆分 |
| 同步 | 内容同步 | 文档、赞助者、博客列表 |
| 补全 | 补充内容 | 补全文档、测试 |
| 升级 | 升级、变更 | 升级 API、模块 |
## 链接格式
**PR**
```markdown
merge: [pr 340](https://gitee.com/dromara/sa-token/pulls/340)
```
**Issue**
```markdown
fix: [#IA6ZK0](https://gitee.com/dromara/sa-token/issues/IA6ZK0)
```
## 重要标记
对用户影响较大的变更加 `**[重要]**`,通常放在句末、链接前:
```markdown
- 新增:新增 `sa-token-spring-boot4-starter` 集成包。 **[重要]**
- 新增:loveqq-framework 启动器集成。merge: [pr 340](url)
```
## 完整示例
```markdown
### v1.45.0(开发中)
- 插件:
- 新增:新增 `sa-token-jackson3` 插件,用于 Jackson 3 的 JSON 解析。 **[重要]**
- 新增:新增 `sa-token-jackson3` 单元测试。
- starter
- 新增:新增 `sa-token-spring-boot4-starter` 集成包,支持 Spring Boot 4。 **[重要]**
- 新增:新增 `sa-token-reactor-spring-boot4-starter` 集成包,支持 WebFlux + Spring Boot 4。 **[重要]**
- 新增:新增 `sa-token-demo-webflux-springboot4` 示例。
- 新增:新增 Spring Boot 4 整合 demo 示例。
- 重构:
- 重构:`sa-token-dependencies` 重构为 `sa-token-basic-dependencies`**[重要]**
- 重构:重构 Spring Boot 相关集成包,优化依赖关系。
- 移除:移除 `sa-token-spring-boot-autoconfig` 模块,相关逻辑迁移至各 starter 内。 **[重要]**
- 重构:重构模块依赖层级,新增 `sa-token-special-dependencies`
- Solon
- 优化:`sa-token-solon-plugin` 优化 Gateway 接口的处理,避免使用路由接口。merge: [pr 348](https://gitee.com/dromara/sa-token/pulls/348)
- 其它:
- 新增:loveqq-framework 启动器集成。merge: [pr 340](https://gitee.com/dromara/sa-token/pulls/340)
- 修复:修复 Maven 父子项目无法下载依赖的问题。merge: [pr 358](https://gitee.com/dromara/sa-token/pulls/358)
- 文档:
- 同步:同步公众号文章列表、博客列表、赞助者名单。
- 新增:新增《Gitee 2025年度开源项目 Web应用开发 Top 2》证书展示。
- 优化:优化框架 Slogan、README、案例库展示。
- 修复:错别字修复;文档图片地址更换为本地文件。
- 其它:
- 新增:增加忽略 .vscode 目录。
- 优化:注释优化。
```
## 提交信息到条目的映射示例
| 提交信息 | 生成条目 |
|----------|----------|
| `feat: 添加 sa-token-jackson3 插件` | 新增:新增 `sa-token-jackson3` 插件,用于 Jackson 3 的 JSON 解析。 **[重要]** |
| `refactor: 移除 sa-token-spring-boot-autoconfig 模块` | 移除:移除 `sa-token-spring-boot-autoconfig` 模块,相关逻辑迁移至各 starter 内。 **[重要]** |
| `docs: 同步最新赞助者名单` | 同步:同步赞助者名单。 |
| `!358 update maven-pull.md` | 修复:修复 Maven 父子项目无法下载依赖的问题。merge: [pr 358](url) |
## 文档类合并建议
以下类型可合并为一条:
- 同步公众号、博客、赞助者名单 → 「同步:同步公众号文章列表、博客列表、赞助者名单。」
- 多条例错别字修复 → 「修复:错别字修复。」
- 多篇文档图片本地化 → 「修复:文档图片地址更换为本地文件(基础篇、深入篇、SSO篇等)。」
@@ -0,0 +1,76 @@
---
name: remove-redundancy-import
description: 检查 Java 类中未被引用的冗余 import 并移除。先输出待审阅计划,用户确认后执行。适用于用户要求清理冗余导包、优化 import、或执行 remove-redundancy-import 时使用。
---
# 移除冗余 import
检查项目中所有 Java 类的未使用 import,生成清理计划供用户审阅,确认后执行移除。
## 使用时机
- 用户要求清理冗余导包
- 用户要求优化 Java import
- 用户明确执行 `remove-redundancy-import` 或提及本 Skill 名称
## 强制流程
**必须先输出计划,用户确认后再执行移除。** 不得在未审阅的情况下直接修改文件。
## 工作流程
### 第一步:扫描与解析
**优先使用内置脚本**:在 Skill 目录下的 [scan_redundant_imports.py](scan_redundant_imports.py) 已实现完整扫描逻辑,可直接复用。
```bash
# 在项目根目录执行
python .cursor/skills/remove-redundancy-import/scan_redundant_imports.py
# 或指定扫描根路径
python .cursor/skills/remove-redundancy-import/scan_redundant_imports.py .
```
脚本输出格式:`文件路径 | 冗余import1; import2 | 数量`,末尾两行为 `TOTAL_FILES:N``TOTAL_IMPORTS:M`
**若无 Python 环境**,可手动执行:
1. 使用 `Glob` 查找项目内所有 `**/*.java` 文件
2. 对每个文件:提取 `package``import`,按 [reference.md](reference.md) 判定是否被使用
3. 汇总存在冗余 import 的文件及列表
### 第二步:输出计划
使用下方模板生成计划报告,等待用户确认:
```markdown
## 冗余 import 清理计划
| 文件 | 待移除 import | 数量 |
|------|---------------|------|
| path/to/Foo.java | `java.util.Date`, `java.sql.Timestamp` | 2 |
| ... | ... | ... |
**共 N 个文件,M 处冗余 import。确认后执行移除。**
```
### 第三步:执行移除
用户确认后,对计划中的每个文件使用 `StrReplace` 移除对应 import 行:
- 逐行移除,每行格式为 `import ...;``import static ...;`
- 若某 import 后紧跟空行,可一并移除空行以保持格式整洁
- 移除后确认文件无语法错误
## 检测规则概要
- **普通 import**:取最后一段类名(如 `java.util.List``List`),在类体中搜索 `\bList\b`
- **static import**:取方法/字段名,在类体中搜索
- **同包冗余**:import 的包与当前文件 `package` 相同则视为冗余
- **通配符**`import pkg.*` 跳过,不自动处理
详见 [reference.md](reference.md)。
## 注意事项
- 通配符 import 无法可靠判断,一律跳过
- 注解中的类型引用采用保守策略,宁可漏检不误删
- 移除后建议用户运行 `mvn compile` 验证
@@ -0,0 +1,63 @@
# 冗余 import 检测规则
## 解析步骤
### 1. 提取 package
匹配 `package\s+([\w.]+)\s*;`,得到当前文件所在包。
### 2. 提取 import
匹配以下模式(每行一条):
- `import\s+([\w.]+)\s*;` — 普通 import
- `import\s+static\s+([\w.]+)\s*;` — static 导入类
- `import\s+static\s+([\w.]+)\.(\w+)\s*;` — static 导入成员(方法/字段)
- `import\s+[\w.]+\s*\.\s*\*\s*;` — 通配符,**跳过不处理**
### 3. 确定简单名(Simple Name
| import 类型 | 示例 | 简单名 |
|-------------|------|--------|
| 普通类 | `import java.util.List;` | `List` |
| 内部类 | `import pkg.Outer.Inner;` | `Inner` |
| static 类 | `import static pkg.Utils;` | `Utils` |
| static 成员 | `import static pkg.Utils.foo;` | `foo` |
### 4. 同包冗余
`import x.y.Z` 的包 `x.y` 与当前文件 `package x.y` 相同,则该 import 冗余(同包无需导入)。
### 5. 使用检测
在**类体**`package` 和所有 `import` 之后)中搜索:
- 使用正则 `\bSimpleName\b` 匹配整词,避免误匹配子串
- 排除:注释、字符串字面量中的出现
- 若未找到匹配,则该 import 视为未使用
## 边界情况
| 情况 | 处理方式 |
|------|----------|
| `import pkg.*;` | 跳过,不自动移除 |
| 注解中的类型 `@Foo` | 若 `Foo` 为 import 的简单名,视为已使用 |
| 泛型 `List<String>` | `List` 会匹配,视为已使用 |
| 同名类(如 `java.util.Date``java.sql.Date`) | 两 import 都保留;若仅一个被使用,只移除未使用的 |
| Javadoc `@param` 中的类型 | 保守:若不确定则保留 |
## 正则参考
```
// package
package\s+([\w.]+)\s*;
// 普通 import(非通配符)
import\s+(?!static)([\w.]+)\s*;
// static import 成员
import\s+static\s+[\w.]+\.(\w+)\s*;
// static import 类
import\s+static\s+([\w.]+)\s*;
```
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
冗余 import 扫描脚本
按 reference.md 规则扫描项目内 Java 文件,输出待移除的冗余 import 列表。
用法:在项目根目录执行 python scan_redundant_imports.py
"""
import os
import re
import sys
def get_simple_name(imp: str) -> str | None:
"""从 import 行提取简单名(用于类体搜索)"""
m = re.match(r'import\s+static\s+[\w.]+\.(\w+)\s*;', imp)
if m:
return m.group(1)
m = re.match(r'import\s+(?:static\s+)?([\w.]+)\s*;', imp)
if m:
return m.group(1).split('.')[-1]
return None
def get_import_full(imp: str) -> str:
"""提取 import 的完整限定名"""
m = re.match(r'import\s+(?:static\s+)?([\w.]+)\s*;', imp)
return m.group(1).strip() if m else ''
def get_import_package(imp: str) -> str:
"""提取 import 所在包(用于同包冗余判断)"""
m = re.match(r'import\s+(?:static\s+)?([\w.]+)\s*;', imp)
if m:
parts = m.group(1).split('.')
return '.'.join(parts[:-1]) if len(parts) > 1 else ''
return ''
def find_class_body_start(content: str) -> int:
"""找到类体起始位置(最后一个 import 之后)"""
last = 0
for m in re.finditer(r'import\s+(?:static\s+)?[\w.]+\s*;', content):
last = m.end()
return last
def main() -> None:
root = sys.argv[1] if len(sys.argv) > 1 else '.'
skip_dirs = {'target', 'build', '.git', 'node_modules'}
results = []
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if d not in skip_dirs]
for f in filenames:
if not f.endswith('.java'):
continue
path = os.path.join(dirpath, f).replace('\\', '/')
try:
with open(path, 'r', encoding='utf-8', errors='ignore') as fp:
content = fp.read()
except OSError:
continue
pkg_match = re.search(r'package\s+([\w.]+)\s*;', content)
file_pkg = pkg_match.group(1) if pkg_match else ''
imports = re.findall(r'import\s+(?:static\s+)?[\w.]+\s*;', content)
imports = [i for i in imports if '*;' not in i and '.*' not in i]
body_start = find_class_body_start(content)
body = content[body_start:]
redundant = []
for imp in imports:
simple = get_simple_name(imp)
if not simple:
continue
imp_full = get_import_full(imp)
imp_pkg = get_import_package(imp)
if imp_pkg and imp_pkg == file_pkg:
redundant.append(imp_full)
continue
if not re.search(r'\b' + re.escape(simple) + r'\b', body):
redundant.append(imp_full)
if redundant:
results.append((path, redundant))
for path, red in results:
print(f"{path} | {'; '.join(red)} | {len(red)}")
print("TOTAL_FILES:" + str(len(results)))
print("TOTAL_IMPORTS:" + str(sum(len(r[1]) for r in results)))
if __name__ == '__main__':
main()
+77
View File
@@ -0,0 +1,77 @@
---
name: upgrade-version
description: 将 Sa-Token 项目版本号升级到指定新版本。每次调用时先读取当前版本并提示用户,待用户输入目标版本后再执行批量修改。修改范围:pom.xml、核心常量、Demo 子项目及文档。当用户要求升级版本、修改版本号、或 version bump 时使用。
---
# Sa-Token 版本升级
将项目版本号升级到用户指定的新版本。每次调用时**先读取当前版本并询问目标版本**,用户确认后再批量修改核心构建、Demo 项目及文档中的版本引用。
## 使用时机
- 用户要求升级项目版本、修改版本号
- 用户说「版本从 vX.Y.Z 升级到 vX.Y.Z」「bump version」等
## 工作流程
### 第零步:询问目标版本(必须执行,不得跳过)
1. **读取当前版本**:从 `pom.xml``<revision>``SaTokenConsts.java``VERSION_NO` 中读取当前版本号
2. **提示用户**:明确告知「当前版本号是:xxx」
3. **等待输入**:询问「请输入要升级到的目标版本号(如 1.46.0):」
4. **确认后再执行**:**必须**等用户明确回复目标版本号后,才能执行后续修改步骤。若用户仅说「升级版本」而未给出目标版本,先完成本步骤再继续
### 第一步:核心构建配置(3 个文件)
使用「当前版本」「目标版本」进行替换:
| 文件 | 修改内容 |
|------|----------|
| `pom.xml` | `<revision>当前版本</revision>` → 目标版本 |
| `sa-token-bom/pom.xml` | `<revision>当前版本</revision>` → 目标版本 |
| `sa-token-core/.../SaTokenConsts.java` | `VERSION_NO = "v当前版本"``"v目标版本"` |
### 第二步:Demo 项目(sa-token-demo 下所有 pom.xml
-`<sa-token.version>当前版本</sa-token.version>` 改为目标版本
- **sa-token-demo-bom-import** 额外修改:`<dependencyManagement>``sa-token-bom``<version>当前版本</version>` → 目标版本
**查找方式**`grep "当前版本" sa-token-demo --output-mode files_with_matches` 定位所有需修改的 pom.xml。
### 第三步:文档(6 个文件)
| 文件 | 修改内容 |
|------|----------|
| `README.md` | 标题 `v当前版本``v目标版本`Maven 依赖 `<version>当前版本</version>` → 目标版本 |
| `sa-token-doc/README.md` | 标题 `v当前版本``v目标版本` |
| `sa-token-doc/index.html` | `<small>v当前版本</small>``v目标版本` |
| `sa-token-doc/doc.html` | `<sub>v当前版本</sub>``saTokenTopVersion = '当前版本'` → 目标版本 |
| `sa-token-doc/start/new-version.md` | 文案及 Maven 示例中的当前版本 → 目标版本 |
### 第四步:不修改的文件
以下为历史记录或示例,**保持原样**:
- `.cursor/skills/` 下的示例(format-reference.md、SKILL.md 等)
- `MEMO/` 下的历史备忘录
- `sa-token-core/.../*.java` 中的 `@since X.Y.Z`(表示 API 引入版本,不随发布升级)
- `sa-token-doc/more/update-log.md`:更新日志应**新增**新版本条目,而非修改旧条目
- `sa-token-doc/more/blog.md`:历史博客链接
## 替换规则
- **pom.xml**`当前版本``目标版本`
- **Java**`"v当前版本"``"v目标版本"`
- **HTML/MD**`v当前版本``当前版本` 按上下文分别替换为目标版本
## 执行顺序建议
1. 第零步:读取当前版本 → 提示用户 → 等待用户输入目标版本
2. 根 POM、sa-token-bom
3. SaTokenConsts.java
4. 批量修改 Demo pom.xml
5. 修改文档
## 验证
执行完成后,用 `grep "被替换的版本号"` 在项目根目录搜索,确认仅剩「不修改」列表中的文件仍含该版本(即修改已生效)。
+2
View File
@@ -12,7 +12,9 @@ unpackage/
/.factorypath
.idea/
.vscode/
sa-token-three-plugin/
sa-token-doc/big-file/
.flattened-pom.xml
+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2011-2019 hubin.
Copyright 2011-Present hubin.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -0,0 +1,41 @@
## 未完成目标
### 1、尝试将所有 `<properties>` 依赖版本号定义在同一个 pom.xml 里。 **[❌失败]**
**尝试1:将所有 `<properties>` 定义在 `sa-token-dependencies` 里:**
结果: 无法在 `sa-token-spring-boot2/3/4-dependencies` 中引用这些 `<properties>`,因为 `<dependencyManagement> <dependencies> <scope>import</scope>` 只会导入目标的 `<dependencyManagement>` 版本号定义,不会导入目标的 `<properties>` 属性。
`<properties>` 只会在 父子结构中向下传递,不会在 `<dependencyManagement> <dependencies> <scope>import</scope>` 中传递。
**尝试2:将所有 `<properties>` 定义在 `sa-token-parent` 里:**
结果:在 `sa-token-dependencies` 里无法引用这些 `<properties>`,因为 `sa-token-parent` 不是 `sa-token-dependencies` 的父模块。
`sa-token-parent` 定义为 `sa-token-dependencies` 的父模块行吗?
不行,因为在 `sa-token-parent` 通过 `<dependencyManagement> <dependencies> <scope>import</scope>` 导入了 `sa-token-dependencies`,如果再把 `sa-token-parent` 定义为 `sa-token-dependencies` 的父模块,会造成循环依赖。
执行 `mvn package` 打包时,maven 会直接报错:
```
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[ERROR] The dependencies of type=pom and with scope=import form a cycle: cn.dev33:sa-token-parent:1.44.0 -> cn.dev33:sa-token-basic-dependencies:1.44.0 -> cn.dev33:sa-token-basic-dependencies:1.44.0 @ cn.dev33:sa-token-basic-dependencies:1.44.0
@
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR] The project cn.dev33:sa-token-parent:1.44.0 (E:\work\project-yun\sa-token\pom.xml) has 1 error
[ERROR] The dependencies of type=pom and with scope=import form a cycle: cn.dev33:sa-token-parent:1.44.0 -> cn.dev33:sa-token-basic-dependencies:1.44.0 -> cn.dev33:sa-token-basic-dependencies:1.44.0 @ cn.dev33:sa-token-basic-dependencies:1.44.0
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException
```
+133
View File
@@ -0,0 +1,133 @@
2026-3-1 调试记录
启动 SaOAuth2ServerApplication,报错空指针:SaOAuth2ServerController 文件的 SaOAuth2Strategy.instance.notLoginView 空指针,SaOAuth2Strategy.instance 为 null
SaOAuth2Strategy.instance 的定义为:
public static final SaOAuth2Strategy instance = new SaOAuth2Strategy();
看代码是无论如何也不可能空指针的,诡异。
在 main 方法第一句加上测试
@SpringBootApplication
public class SaOAuth2ServerApplication {
public static void main(String[] args) {
System.out.println(SaOAuth2Strategy.instance);
SpringApplication.run(SaOAuth2ServerApplication.class, args);
System.out.println("\nSa-Token-OAuth2 Server端启动成功,配置如下:");
System.out.println(SaOAuth2Manager.getServerConfig());
}
}
打印居然为 null。
询问 AI,解释的乱七八糟,没有参考价值。
然后在根目录执行 mvn clean,居然无法成功。sa-token-test 模块无法 clean 。
报错 test 依赖不存在
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
最后必须在 dependencyManagement 加上这个才行
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.18</version>
</dependency>
可是就算不加,我也已经在 sa-token-spring-boot2-dependencies 中定义这个依赖了呀,为什么 在 sa-token-test 中无法 import spring-boot-starter-test
加了后,mvn clean 执行成功了
但是 mvn package 又开始无法打包。
可以昨天我明明能打包成功的啊?今天好像就变动了一下 sa-token-test 中的依赖配置。这有什么影响吗?
而且打包报错信息居然是:sa-token-jboot-plugin 插件中 javax.servlet.http.HttpServletRequest 无法转换为 HttpServletRequest
什么东西啊。
抓头挠腮解决不了。
这个插件已经十几个版本没有变动过代码了,代码不变,打包环境不变,命令不变,今天就突然报这种莫名其妙的错误,无奈,只能先去除这个插件,不让它参与打包。
继续打包,又开始报错:
sa-token-jfinal-plugin 中 cn.dev33.satoken.context.SaTokenContext 无法转换为 SaTokenContext。
这一瞬间我怀疑自己正处于梦中。
纠结了半分钟,继续去除此插件,继续打包。
打包成功了。
启动 SaOAuth2ServerApplication,启动成功,SaOAuth2ServerController 文件的 SaOAuth2Strategy.instance.notLoginView 空指针问题,消失了。
请问中间的这几个报错和这个空指针有任何关联吗?我请问呢?
注:以上所有叙述均为最后打包成功后进行回忆,可能细节上略有偏差。
两小时后:
本来可以运行成功的代码,只要一改子模块的代码就无法再运行成功,报错:java: 无法访问SaRequest。
试了好多解决方案,不行。
---
吃了两份炉盖香酥鸡饼,原来人在压力大的时候真的需要补充能量。
---
继续报错:
Maven 资源编译器: 模块 'sa-token-oauth2' 所需的 Maven 项目配置不可用。仅当从 IDE 启动外部构建时,才支持 Maven 项目编译。
sa-token-jwt、sso、sign 等模块均出现此问题
最后:
把项目删掉,重新下载一份,导入
项目可以运行成功了,但是每次修改子模块,在 demo 示例里无法实时起作用。需要 mvn clean install 才能看到效果。
最后:
取消勾选 maven 配置项:Delegate IDE build/run actions to Maven
一切问题解决,包括最上面的诡异调试现象也消失了。
idea,你给老子爬
+344
View File
@@ -0,0 +1,344 @@
<!-- sa-token-bom -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-bom</artifactId>
<version>1.45.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- sa-token-core -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-spring-boot-starter -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-spring-boot3-starter -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-spring-boot4-starter -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot4-starter</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-reactor-spring-boot-starter -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-reactor-spring-boot3-starter -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-reactor-spring-boot4-starter -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot4-starter</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-jboot-plugin -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jboot-plugin</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-jfinal-plugin -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jfinal-plugin</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-loveqq-boot-starter -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-loveqq-boot-starter</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-servlet -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-servlet</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-jakarta-servlet -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jakarta-servlet</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-plugin -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-plugin</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-alone-redis -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-alone-redis</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-redis-template -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-redis-template-jdk-serializer -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template-jdk-serializer</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-redis-jackson -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-redisson -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redisson</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-redisson-spring-boot-starter -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redisson-spring-boot-starter</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-redisx -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redisx</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-hutool-timed-cache -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-hutool-timed-cache</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-caffeine -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-caffeine</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-jackson -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jackson</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-jackson3 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jackson3</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-fastjson -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-fastjson</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-fastjson2 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-fastjson2</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-snack3 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-snack3</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-snack4 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-snack4</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-serializer-features -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-serializer-features</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-thymeleaf -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-thymeleaf</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-freemarker -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-freemarker</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-dubbo -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dubbo</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-dubbo3 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dubbo3</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-grpc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-grpc</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-forest -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-forest</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-okhttps -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-okhttps</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-temp-jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-temp-jwt</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-oauth2 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-oauth2</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-apikey -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-apikey</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-sign -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sign</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-quick-login -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-quick-login</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-sso -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sso</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-spring-aop -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-aop</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-spring-el -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-el</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-spring-boot-webmvc-reactor-v2v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-spring-boot-reactor-v2v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-reactor-v2v3v4-common</artifactId>
<version>1.45.0</version>
</dependency>
<!-- sa-token-spring-boot-webmvc-v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-webmvc-v3v4-common</artifactId>
<version>1.45.0</version>
</dependency>
+88 -49
View File
@@ -1,29 +1,29 @@
<p align="center">
<img alt="logo" src="https://sa-token.cc/logo.png" width="150" height="150">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.44.0</h1>
<h4 align="center">开源、免费、轻量级 Java 权限认证框架,让鉴权变得简单、优雅!</h4>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.45.0</h1>
<h4 align="center">开源、免费、一站式 java 权限认证框架,让鉴权变得简单、优雅! </h4>
<p align="center">
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg?theme=gvp"></a>
<a href="https://gitee.com/dromara/sa-token/members"><img src="https://gitee.com/dromara/sa-token/badge/fork.svg?theme=gvp"></a>
<a href="https://gitcode.com/dromara/sa-token/stargazers"><img src="https://gitcode.com/dromara/Sa-Token/star/badge.svg"></a>
<a href="https://atomgit.com/dromara/sa-token/stargazers"><img src="https://atomgit.com/dromara/Sa-Token/star/badge.svg"></a>
<a href="https://github.com/dromara/sa-token/stargazers"><img src="https://img.shields.io/github/stars/dromara/sa-token?style=flat-square&logo=GitHub"></a>
<a href="https://github.com/dromara/sa-token/network/members"><img src="https://img.shields.io/github/forks/dromara/sa-token?style=flat-square&logo=GitHub"></a>
<a href="https://github.com/dromara/sa-token/watchers"><img src="https://img.shields.io/github/watchers/dromara/sa-token?style=flat-square&logo=GitHub"></a>
<!-- <a href="https://github.com/dromara/sa-token/watchers"><img src="https://img.shields.io/github/watchers/dromara/sa-token?style=flat-square&logo=GitHub"></a> -->
<!-- <a href="https://github.com/dromara/sa-token/issues"><img src="https://img.shields.io/github/issues/dromara/sa-token.svg?style=flat-square&logo=GitHub"></a> -->
<a href="https://github.com/dromara/sa-token/blob/master/LICENSE"><img src="https://img.shields.io/github/license/dromara/sa-token.svg?style=flat-square"></a>
</p>
<!-- <p align="center">学习测试请拉取 master 分支,dev 是在开发分支 (在根目录执行 `git checkout master`)</p> -->
<p align="center"><a href="https://sa-token.cc" target="_blank">在线文档:https://sa-token.cc</a></p>
<p align="center"><a href="https://sa-token.cc?way=readme" target="_blank">在线文档:https://sa-token.cc</a></p>
---
### Sa-Token 介绍
### 🛠️ Sa-Token 介绍
Sa-Token 是一个轻量级 Java 权限认证框架,目前拥有五大核心模块:登录认证、权限认证、单点登录、OAuth2.0、微服务鉴权。
![sa-token-jss](https://oss.dev33.cn/sa-token/doc/home/sa-token-jss--tran.png)
![sa-token-jss](https://sa-token.cc/big-file/index/intro/sa-token-jss--tran.png)
要在 SpringBoot 项目中使用 Sa-Token,你只需要在 pom.xml 中引入依赖:
@@ -32,11 +32,11 @@ Sa-Token 是一个轻量级 Java 权限认证框架,目前拥有五大核心
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.44.0</version>
<version>1.45.0</version>
</dependency>
```
除了 SpringBoot2、Sa-Token 还为 SpringBoot3、Solon、JFinal 等常见 Web 框架提供集成包,做到真正的开箱即用。
除了支持 SpringBoot2、Sa-Token 还为 SpringBoot3、Solon、JFinal 等常见 Web 框架提供集成包,做到真正的开箱即用。
<details>
@@ -144,59 +144,95 @@ registry.addInterceptor(new SaInterceptor(handler -> {
### SSO 单点登录
### 🍃 SSO 单点登录
Sa-Token SSO 分为三种模式,解决同域、跨域、共享Redis、跨Redis、前后端一体、前后端分离……等不同架构下的 SSO 接入问题
Sa-Token SSO 分为三种模式,解决`同域、跨域、共享Redis、跨Redis、前后端一体、前后端分离、纯 js、vue2、vue3、java 项目、非 java 项目` 等架构下的 SSO 认证需求
![sa-token-jss](https://oss.dev33.cn/sa-token/doc/home/sa-token-sso--white.png)
![sa-token-jss](https://sa-token.cc/big-file/doc/sso/sa-token-sso--white.png)
| 系统架构 | 采用模式 | 简介 | 文档链接 |
| :-------- | :-------- | :-------- | :-------- |
| 前端同域 + 后端同 Redis | 模式一 | 共享Cookie同步会话 | [文档](https://sa-token.cc/doc.html#/sso/sso-type1)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso1-client) |
| 前端同域 + 后端同 Redis | 模式 | URL重定向传播会话 | [文档](https://sa-token.cc/doc.html#/sso/sso-type2)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso2-client) |
| 前端不同域 + 后端同Redis | 模式 | Http请求获取会话 | [文档](https://sa-token.cc/doc.html#/sso/sso-type3)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso3-client) |
| 系统架构 | 采用模式 | 简介 | 文档链接 |
| :-------- | :-------- |:----------------| :-------- |
| 前端同域 + 后端同 Redis | 模式 | 共享Cookie同步会话 | [文档](https://sa-token.cc/doc.html#/sso/sso-type1)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso1-client) |
| 前端不同域 + 后端同 Redis | 模式 | URL重定向传播会话 | [文档](https://sa-token.cc/doc.html#/sso/sso-type2)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso2-client) |
| 前端不同域 + 后端 不同Redis | 模式三 | HTTP请求获取会话 | [文档](https://sa-token.cc/doc.html#/sso/sso-type3)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso3-client) |
1. 前端同域:就是指多个系统可以部署在同一个主域名之下,比如:`c1.domain.com`、`c2.domain.com`、`c3.domain.com`
2. 后端同Redis:就是指多个系统可以连接同一个Redis。(此处并非要所有项目数据都放在一个Redis中,Sa-Token提供 **`[权限缓存与业务缓存分离]`** 的解决方案)
3. 如果无法做到前端同域,也无法做到后端同Redis,可以走模式三Http请求校验 ticket 获取会话。
4. 提供 NoSdk 模式示例,不使用 Sa-Token 的系统也可以对接。
5. 提供 sso-server 接口文档,不使用 java 语言的系统也可以对接
6. 提供前后端分离整合方案:无论是 sso-server 还是 sso-client 前后端分离都可以整合
7. 提供安全校验:域名校验、ticket校验、参数签名校验,有效防 ticket 劫持,防请求重放等攻击
8. 参数防丢:笔者曾试验多个SSO框架,均有参数丢失情况,比如登录前是:`http://a.com?id=1&name=2`,登录成功后就变成了:`http://a.com?id=1`Sa-Token-SSO 内有专门算法保证了参数不丢失,登录成功后精准原路返回。
9. 提供用户数据同步/迁移方案的建议:开发前统一迁移、运行时实时数据同步、根据关联字段匹配、根据 center_id 字段匹配等。
10. 提供直接可运行的 demo 示例,帮助你快速熟悉 SSO 大致登录流程。
2. 后端同 Redis:就是指多个系统可以连接同一个 Redis,共享会话数据。
3. 如果无法做到前端同域后端同 Redis,可以走托底的模式三Http请求校验 ticket 获取会话。
4. 提供NoSdk 模式示例 + sso-server 接口文档,非 Sa-Token 项目、非 java 项目也可以对接。
5. 提供:多重安全校验:域名校验、ticket校验、参数签名校验,有效防 ticket 劫持,防请求重放等攻击
6. 提供:大量实战痛点教学:sso-server 前后端分离设计、sso-client 前后端分离设计、用户数据同步/迁移方案设计
7. 提供:直接可运行的 demo 示例,助你快速熟悉 SSO 大致登录流程
8. 提供:深度细节优化,参数防丢:笔者曾试验多个SSO框架,均有参数丢失情况,比如登录前是:`http://a.com?id=1&name=2`,登录成功后就变成了:`http://a.com?id=1`Sa-Token-SSO 内有专门算法保证了参数不丢失,登录成功后精准原路返回。
### OAuth2 授权认证
Sa-Token-OAuth2 模块分为四种授权模式,解决不同场景下的授权需求
| 授权模式 | 简介 |
| :-------- | :-------- |
| 授权码(Authorization Code | OAuth2.0 标准授权步骤,Server 端向 Client 端下放 Code 码,Client 端再用 Code 码换取授权 Token |
| 隐藏式(Implicit) | 无法使用授权码模式时的备用选择,Server 端使用 URL 重定向方式直接将 Token 下放到 Client 端页面 |
| 密码式(Password | Client直接拿着用户的账号密码换取授权 Token |
| 客户端凭证(Client Credentials| Server 端针对 Client 级别的 Token,代表应用自身的资源授权 |
### 🍂 OAuth2 授权认证
Sa-Token OAuth2 模块分为四种授权模式,解决不同场景下的授权需求
| 授权模式 | 简介 |
| :-------- | :-------- |
| 授权码式 | OAuth2 标准授权步骤,server 端下放 codeclient 端获取 code 码兑换 access_token |
| 隐藏式 | 备用选择,server 端使用 URL 重定向方式直接将 access_token 下放到 client 端页面 |
| 密码式 | client 直接拿着用户的账号密码换取授权 access_token |
| 客户端凭证式 | server 端针对 client 级别的 client_token,代表应用自身的资源授权 |
详细参考文档:[https://sa-token.cc/doc.html#/oauth2/readme](https://sa-token.cc/doc.html#/oauth2/readme)
### 开源集成案例
### 📖❓ 疑问解答
**1、Sa-Token 功能全不全?**
七年磨一剑:五大核心模块(登录、鉴权、SSO、OAuth2、微服务) + 众多实用插件 (短 token、jwt 集成、API 参数签名、API Key 秘钥授权...) 我们提供的不只是权限认证,我们提供的是一站式解决方案。
**2、Sa-Token 好不好学?**
中文文档 + 中文代码注释 + 中文交流社区 + 大量实战案例博客 + 多个视频教程 + 大量优秀开源项目集成案例。
**3、Sa-Token 用的人多不多?**
截止统计日 (2026-1-25) 起,Sa-Token 在:
- Gitee 关注量达到 48627 Star,位列平台所有推荐项目排行榜第一名。
- GitHub 关注量达到 18523 Star,是主要竞争框架 Spring Security 的 1.97 倍,Apache Shiro 的 4.19 倍。
- 25+ 微信粉丝群 (500人)8+ QQ粉丝群 (1000人 or 2000人) ,在线文档访问量月PV 20万+。
这是众多开发者用脚投票的数据,相信这些数据比任何言语都能证明 Sa-Token 的热度。
**4、Sa-Token 有哪些权威认证?**
曾获荣誉包括但不限于:Gitee GVP 最有价值开源项目、GitCode G-Star 优质开源项目、OSCHINA 2021 人气指数 TOP 30 开源项目、OSCHINA 2022 年度最火热中国开源项目社区之一、开放原子基金会2023快速成长开源项目、 Dromara 组织顶尖项目(之一)、可信开源社区共同体预备成员、所在开源社区 “Dromara” 荣获《2024中国互联网发展创新与投资大赛(开源)》二等奖。 Gitee High Star 计划项目(5000+star)。Gitee 2025年度开源项目 Web应用开发 Top 2。
**5、Sa-Token 收费吗?**
Sa-Token 采用 Apache-2.0 开源协议,承诺框架本身与在线文档永久免费开放。当然如果您有心赞助 Sa-Token,我们也不回避:[赞助链接](https://sa-token.cc/doc.html#/more/sa-token-donate)。
我们将定期同步赞助者名单到在线文档展示。(您需要注意的一点是:该赞助仅为友情赞助,不提供任何商业交换)
### 🚀 优秀开源集成案例
- [[ Snowy ]](https://gitee.com/xiaonuobase/snowy):国内首个国密前后分离快速开发平台,采用 Vue3 + Vite + SpringBoot + Mp + HuTool + SaToken。
- [[ RuoYi-Vue-Plus ]](https://gitee.com/dromara/RuoYi-Vue-Plus):重写RuoYi-Vue所有功能 集成 Sa-Token、Mybatis-Plus、Xxl-Job、knife4j、OSS 定期同步。
- [[ Smart-Admin ]](https://gitee.com/lab1024/smart-admin)SmartAdmin 国内首个以「高质量代码」为核心,「简洁、高效、安全」中后台快速开发平台。
- [[ 橙单 ]](https://gitee.com/orangeform/orange-admin) 橙单中台化低代码生成器。可完整支持多应用、多租户、多渠道、工作流、框架技术栈自由组合等。
- [[ 灯灯 ]](https://gitee.com/dromara/lamp-cloud) 专注于多租户解决方案的中后台快速开发平台。支持独立数据库、共享数据架构 和 非租户模式 ✨
- [[ 拾壹博客 ]](https://gitee.com/quequnlong/shiyi-blog):一款 vue + springboot 前后端分离的博客系统。
- [[ Snowy ]](https://gitee.com/xiaonuobase/snowy):国内首个国密前后分离快速开发平台,采用 Vue3 + AntDesignVue3 + Vite + SpringBoot + Mp + HuTool + SaToken。
- [[ RuoYi-Vue-Plus ]](https://gitee.com/dromara/RuoYi-Vue-Plus):重写RuoYi-Vue所有功能 集成 Sa-Token+Mybatis-Plus+Jackson+Xxl-Job+knife4j+Hutool+OSS 定期同步
- [[Smart-Admin]](https://gitee.com/lab1024/smart-admin)SmartAdmin国内首个以「高质量代码」为核心,「简洁、高效、安全」中后台快速开发平台;
- [[ 灯灯 ]](https://gitee.com/dromara/lamp-cloud) 专注于多租户解决方案的微服务中后台快速开发平台。租户模式支持独立数据库(DATASOURCE模式)、共享数据架构(COLUMN模式) 和 非租户模式(NONE模式)✨
- [[ EasyAdmin ]](https://gitee.com/lakernote/easy-admin):一个基于SpringBoot2 + Sa-Token + Mybatis-Plus + Snakerflow + Layui 的后台管理系统,灵活多变可前后端分离,也可单体,内置代码生成器、权限管理、工作流引擎等
- [[ sa-admin-server ]](https://gitee.com/wlf213/sa-admin-server) 基于 sa-admin-ui 的后台管理开发脚手架。
还有更多优秀开源案例无法逐一展示,请参考:[Awesome-Sa-Token](https://gitee.com/sa-token/awesome-sa-token)
### 友情链接
### 🔗 友情链接
- [[ OkHttps ]](https://gitee.com/ejlchina-zhxu/okhttps):轻量级 http 通信框架,API无比优雅,支持 WebSocket、Stomp 协议
- [[ Forest ]](https://gitee.com/dromara/forest):声明式与编程式双修,让天下没有难以发送的 HTTP 请求
- [[ Bean Searcher ]](https://github.com/ejlchina/bean-searcher):专注高级查询的只读 ORM,使一行代码实现复杂列表检索!
@@ -209,31 +245,34 @@ Sa-Token-OAuth2 模块分为四种授权模式,解决不同场景下的授权
### 代码托管
### 📦 代码托管
- Gitee[https://gitee.com/dromara/sa-token](https://gitee.com/dromara/sa-token)
- GitHub[https://github.com/dromara/sa-token](https://github.com/dromara/sa-token)
- GitCode[https://gitcode.com/dromara/sa-token](https://gitcode.com/dromara/sa-token)
- AtomGit[https://atomgit.com/dromara/sa-token](https://atomgit.com/dromara/sa-token)
### 交流群
### 💬 交流群
<!-- QQ交流群:685792424 [点击加入](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Y05Ld4125W92YSwZ0gA8e3RhG9Q4Vsfx&authKey=IomXuIuhP9g8G7l%2ByfkrRsS7i%2Fna0lIBpkTXxx%2BQEaz0NNEyJq00kgeiC4dUyNLS&noverify=0&group_code=685792424)-->
QQ交流群:823181187 [点击加入](https://qm.qq.com/q/EBIJVZBVGE)
QQ交流群:1081649142 [点击加入](https://qm.qq.com/q/SCAaZ6Ros2)
微信交流群:
<!-- <img src="https://oss.dev33.cn/sa-token/qr/wx-qr-m-400k.png" width="230px" title="微信群" /> -->
<img src="https://oss.dev33.cn/sa-token/qr/i-wx-qr2.png" width="230px" title="微信群" />
<img src="https://sa-token.cc/big-file/contact/i-wx-qr2.jpg" width="230px" title="微信群" />
(扫码添加微信备注:sa-token,邀您加入群聊)
PS扫码添加微信 (备注:sa-token),邀您加入群聊
<br>
<img class="s-w" src="http://sa-token.cc/big-file/contact/show/wx-group-show3--liubai.png" style="max-width: 50%;" alt="微信群" />
加入群聊的好处:
- 第一时间收到框架更新通知。
- 第一时间收到框架 bug 通知。
- 第一时间收到新增开源案例通知。
- 和众多大佬一起互相 (huá shuǐ) 交流 (mō yú)。
- 和众多大佬一起互相 (huá shuǐ) 交流 (mō yú) 🖐️🐟️
+1
View File
@@ -24,6 +24,7 @@ cd sa-token-demo-solon & call mvn clean & cd ..
cd sa-token-demo-solon-redisson & call mvn clean & cd ..
cd sa-token-demo-springboot & call mvn clean & cd ..
cd sa-token-demo-springboot3-redis & call mvn clean & cd ..
cd sa-token-demo-springboot4-redis & call mvn clean & cd ..
cd sa-token-demo-springboot-low-version & call mvn clean & cd ..
cd sa-token-demo-springboot-redis & call mvn clean & cd ..
cd sa-token-demo-springboot-redisson & call mvn clean & cd ..
+140 -85
View File
@@ -19,6 +19,7 @@
<!-- 所有模块 -->
<modules>
<module>sa-token-dependencies</module>
<module>sa-token-special-dependencies</module>
<module>sa-token-bom</module>
<module>sa-token-core</module>
<module>sa-token-starter</module>
@@ -37,10 +38,14 @@
<!-- 一些属性 -->
<properties>
<revision>1.44.0</revision>
<revision>1.45.0</revision>
<jdk.version>1.8</jdk.version>
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
<!-- Maven GPG Plugin & Maven Central Portal -->
<maven-gpg-plugin.version>3.2.8</maven-gpg-plugin.version>
<central.publishing.maven.version>0.10.0</central.publishing.maven.version>
</properties>
<!-- 仓库信息 -->
@@ -54,20 +59,11 @@
<!-- 作者信息 -->
<developers>
<developer>
<name>shengzhang</name>
<name>click33</name>
<email>2393584716@qq.com</email>
</developer>
</developers>
<!-- 父仓库 -->
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
<relativePath/>
</parent>
<!-- 仓库依赖 -->
<dependencies>
@@ -75,7 +71,11 @@
<dependencyManagement>
<dependencies>
<!--
导入 sa-token-dependencies 所有版本定义,并传导到每个子项目。
需要注意的是:该 import 只会导入 <dependencyManagement> 部分,而不会导入 <dependencies> 部分和 <properties> 部分。
-->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dependencies</artifactId>
@@ -89,79 +89,133 @@
<!-- 项目构建 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<!-- 统一生成聚合文档,解决 mvn package 时控制台发出 javadoc 警告的问题 -->
<aggregate>true</aggregate>
<!-- 忽略部分 error 和 warning -->
<failOnError>false</failOnError>
<failOnWarnings>false</failOnWarnings>
<additionalOptions>-Xdoclint:none</additionalOptions>
<detectLinks>false</detectLinks>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>aggregate</goal>
</goals>
</execution>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
</execution>
</executions>
</plugin>
<plugins>
<!-- 统一版本号管理 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.2.7</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<!-- Source -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<attach>true</attach>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 源码编译 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- API 文档 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.12.0</version>
<configuration>
<!-- 统一生成聚合文档,解决 mvn package 时控制台发出 javadoc 警告的问题 -->
<!-- <aggregate>true</aggregate> -->
<!-- 忽略部分 error 和 warning -->
<failOnError>false</failOnError>
<failOnWarnings>false</failOnWarnings>
<additionalOptions>-Xdoclint:none</additionalOptions>
<detectLinks>false</detectLinks>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>aggregate</goal>
</goals>
</execution>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<doclint>none</doclint>
</configuration>
</execution>
</executions>
</plugin>
<!-- flatten 统一版本号管理 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.7.3</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- gpg 签名 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven-gpg-plugin.version}</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 新版 Central Portal 中央仓库上传 -->
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>${central.publishing.maven.version}</version>
<extensions>true</extensions>
<configuration>
<!-- 必须与 settings.xml 中 server 的 id 一致 -->
<publishingServerId>central</publishingServerId>
<!-- 是否自动发布。设为 true 后,上传完成无需手动点击发布 -->
<!-- <autoPublish>true</autoPublish> -->
<!-- 等待直到发布完成,让构建过程等待最终结果 -->
<!-- <waitUntil>published</waitUntil> -->
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
@@ -188,7 +242,8 @@
</configuration>
</plugin>
</plugins>
</pluginManagement>
</pluginManagement>
</build>
</project>
+100 -13
View File
@@ -13,9 +13,39 @@
<url>https://github.com/dromara/sa-token</url>
<properties>
<revision>1.44.0</revision>
<revision>1.45.0</revision>
<!-- Maven GPG Plugin & Maven Central Portal -->
<maven-gpg-plugin.version>3.2.8</maven-gpg-plugin.version>
<central.publishing.maven.version>0.10.0</central.publishing.maven.version>
</properties>
<!-- 开源协议 apache 2.0 -->
<licenses>
<license>
<name>Apache 2</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
<comments>A business-friendly OSS license</comments>
</license>
</licenses>
<!-- 仓库信息 -->
<scm>
<tag>master</tag>
<url>https://github.com/dromara/sa-token.git</url>
<connection>scm:git:https://github.com/dromara/sa-token.git</connection>
<developerConnection>scm:git:https://github.com/dromara/sa-token.git</developerConnection>
</scm>
<!-- 作者信息 -->
<developers>
<developer>
<name>click33</name>
<email>2393584716@qq.com</email>
</developer>
</developers>
<dependencyManagement>
<dependencies>
<!-- sa-token 核心 -->
@@ -51,6 +81,11 @@
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot4-starter</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-servlet</artifactId>
@@ -68,7 +103,17 @@
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-autoconfig</artifactId>
<artifactId>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-reactor-v2v3v4-common</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-webmvc-v3v4-common</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
@@ -81,6 +126,11 @@
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot4-starter</artifactId>
<version>${revision}</version>
</dependency>
<!-- endregion-->
<!-- region sa-token-plugin -->
@@ -94,6 +144,11 @@
<artifactId>sa-token-alone-redis</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-alone-redis-by-spring-boot4</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dubbo</artifactId>
@@ -119,6 +174,11 @@
<artifactId>sa-token-jackson</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jackson3</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-fastjson</artifactId>
@@ -239,21 +299,15 @@
</dependencies>
</dependencyManagement>
<!-- 父仓库 -->
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
<relativePath/>
</parent>
<!-- 项目构建 -->
<build>
<plugins>
<!-- 源码编译 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<version>3.15.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@@ -261,11 +315,11 @@
</configuration>
</plugin>
<!-- 统一版本号管理 -->
<!-- flatten 统一版本号管理 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.2.7</version>
<version>1.7.3</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
@@ -288,7 +342,40 @@
</executions>
</plugin>
<!-- gpg 签名 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven-gpg-plugin.version}</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 新版 Central Portal 中央仓库上传 -->
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>${central.publishing.maven.version}</version>
<extensions>true</extensions>
<configuration>
<!-- 必须与 settings.xml 中 server 的 id 一致 -->
<publishingServerId>central</publishingServerId>
<!-- 是否自动发布。设为 true 后,上传完成无需手动点击发布 -->
<!-- <autoPublish>true</autoPublish> -->
<!-- 等待直到发布完成,让构建过程等待最终结果 -->
<!-- <waitUntil>published</waitUntil> -->
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
+1 -1
View File
@@ -16,7 +16,7 @@
<description>A Java Web lightweight authority authentication framework, comprehensive function, easy to use</description>
<dependencies>
<!-- Zero dependence -->
<!-- Zero Dependence -->
</dependencies>
@@ -15,7 +15,6 @@
*/
package cn.dev33.satoken.application;
import cn.dev33.satoken.fun.SaRetFunction;
import cn.dev33.satoken.fun.SaRetGenericFunction;
/**
@@ -17,6 +17,7 @@ package cn.dev33.satoken.config;
import cn.dev33.satoken.stp.parameter.enums.SaLogoutMode;
import cn.dev33.satoken.stp.parameter.enums.SaLogoutRange;
import cn.dev33.satoken.stp.parameter.enums.SaReplacedLoginExitMode;
import cn.dev33.satoken.stp.parameter.enums.SaReplacedRange;
import cn.dev33.satoken.util.SaFoxUtil;
@@ -64,6 +65,11 @@ public class SaTokenConfig implements Serializable {
*/
private Boolean isShare = false;
/**
* 在 isConcurrent=false 时,决定新旧设备谁将放弃会话 (OLD_DEVICE=旧设备下线,新设备登录成功, NEW_DEVICE=新设备登录失败,旧设备维持在线)
*/
private SaReplacedLoginExitMode replacedLoginExitMode = SaReplacedLoginExitMode.OLD_DEVICE;
/**
* 当 isConcurrent=false 时,顶人下线的范围 (CURR_DEVICE_TYPE=当前指定的设备类型端, ALL_DEVICE_TYPE=所有设备类型端)
*/
@@ -713,6 +719,22 @@ public class SaTokenConfig implements Serializable {
return this;
}
/**
* @return 在 isConcurrent=false 时,决定新旧设备谁将放弃会话 (OLD_DEVICE=旧设备下线,新设备登录成功, NEW_DEVICE=新设备登录失败,旧设备维持在线)
*/
public SaReplacedLoginExitMode getReplacedLoginExitMode() {
return replacedLoginExitMode;
}
/**
* @param replacedLoginExitMode 在 isConcurrent=false 时,决定新旧设备谁将放弃会话 (OLD_DEVICE=旧设备下线,新设备登录成功, NEW_DEVICE=新设备登录失败,旧设备维持在线)
* @return 对象自身
*/
public SaTokenConfig setReplacedLoginExitMode(SaReplacedLoginExitMode replacedLoginExitMode) {
this.replacedLoginExitMode = replacedLoginExitMode;
return this;
}
/**
* 获取 当 isConcurrent=false 时,顶人下线的范围 (CURR_DEVICE_TYPE=当前指定的设备类型端 ALL_DEVICE_TYPE=所有设备类型端)
*
@@ -860,6 +882,7 @@ public class SaTokenConfig implements Serializable {
+ ", isConcurrent=" + isConcurrent
+ ", isShare=" + isShare
+ ", replacedRange=" + replacedRange
+ ", replacedLoginExitMode=" + replacedLoginExitMode
+ ", maxLoginCount=" + maxLoginCount
+ ", overflowLogoutMode=" + overflowLogoutMode
+ ", maxTryTimes=" + maxTryTimes
@@ -25,7 +25,7 @@ import cn.dev33.satoken.context.model.SaTokenContextModelBox;
*
* <p>
* 使用 [ ThreadLocal 版本 ] 上下文处理器需要在全局过滤器或者拦截器内率先调用
* SaTokenContextForThreadLocalStorage.setBox(req, res, sto) 初始化上下文
* SaTokenContextForThreadLocalStaff.setBox(req, res, sto) 初始化上下文
* </p>
*
* <p> 一般情况下你不需要直接操作此类,因为框架的 starter 集成包里已经封装了完整的上下文操作 </p>
@@ -54,7 +54,7 @@ public interface SaTokenDao {
*
* @param key 键名称
* @param value 值
* @param timeout 数据有效期(值大于0时限时存储,值=-1时永久存储,值=0或小于-2时不存储)
* @param timeout 数据有效期(值大于0时限时存储,值=-1时永久存储,值=0或小于等于-2时不存储)
*/
void set(String key, String value, long timeout);
@@ -109,7 +109,7 @@ public interface SaTokenDao {
*
* @param key 键名称
* @param object 值
* @param timeout 存活时间(值大于0时限时存储,值=-1时永久存储,值=0或小于-2时不存储)
* @param timeout 存活时间(值大于0时限时存储,值=-1时永久存储,值=0或小于等于-2时不存储)
*/
void setObject(String key, Object object, long timeout);
@@ -55,7 +55,7 @@ public interface SaTokenDaoByObjectFollowString extends SaTokenDaoBySessionFollo
*
* @param key 键名称
* @param object 值
* @param timeout 存活时间(值大于0时限时存储,值=-1时永久存储,值=0或小于-2时不存储)
* @param timeout 存活时间(值大于0时限时存储,值=-1时永久存储,值=0或小于等于-2时不存储)
*/
@Override
default void setObject(String key, Object object, long timeout) {
@@ -80,6 +80,9 @@ public interface SaErrorCode {
/** 更改 Token 指向的 账号Id 时,账号Id值为空 */
int CODE_11003 = 11003;
/** 登录失败:当前账号已在其它客户端登录 */
int CODE_11004 = 11004;
/** 未能读取到有效Token */
int CODE_11011 = 11011;
@@ -16,9 +16,7 @@
package cn.dev33.satoken.httpauth.digest;
import cn.dev33.satoken.annotation.SaCheckHttpDigest;
import cn.dev33.satoken.context.SaHolder;
import java.util.LinkedHashMap;
import java.util.Map;
/**
@@ -36,6 +36,7 @@ import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.dev33.satoken.stp.parameter.SaLogoutParameter;
import cn.dev33.satoken.stp.parameter.enums.SaLogoutMode;
import cn.dev33.satoken.stp.parameter.enums.SaLogoutRange;
import cn.dev33.satoken.stp.parameter.enums.SaReplacedLoginExitMode;
import cn.dev33.satoken.stp.parameter.enums.SaReplacedRange;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil;
@@ -538,13 +539,25 @@ public class StpLogic {
protected String distUsableToken(Object id, SaLoginParameter loginParameter) {
// 1、获取全局配置的 isConcurrent 参数
// 如果配置为:不允许一个账号多地同时登录,则需要先将这个账号的历史登录会话标记为:被顶下线
if( ! loginParameter.getIsConcurrent()) {
if(loginParameter.getReplacedRange() == SaReplacedRange.CURR_DEVICE_TYPE) {
replaced(id, loginParameter.getDeviceType());
}
if(loginParameter.getReplacedRange() == SaReplacedRange.ALL_DEVICE_TYPE) {
replaced(id, createSaLogoutParameter());
// 如果配置为:不允许一个账号多地同时登录,则需要根据配置选择:
// 一.将这个账号的历史登录会话标记为:被顶下线
// 二.提示错误并拒绝本次登录
if (loginParameter.getReplacedLoginExitMode() == SaReplacedLoginExitMode.OLD_DEVICE){
if(loginParameter.getReplacedRange() == SaReplacedRange.CURR_DEVICE_TYPE) {
replaced(id, loginParameter.getDeviceType());
}
if(loginParameter.getReplacedRange() == SaReplacedRange.ALL_DEVICE_TYPE) {
replaced(id, createSaLogoutParameter());
}
} else if (loginParameter.getReplacedLoginExitMode() == SaReplacedLoginExitMode.NEW_DEVICE){
List<SaTerminalInfo> terminalListByLoginId = getTerminalListByLoginId(id);
// 只有当存在有效地会话时才拒绝登录
boolean hasActiveSession = terminalListByLoginId.stream()
.anyMatch(terminal -> isValidToken(terminal.getTokenValue()));
if (hasActiveSession) {
throw new SaTokenException("登录失败:当前账号已在其它客户端登录").setCode(SaErrorCode.CODE_11004);
}
}
}
@@ -548,7 +548,7 @@ public class StpUtil {
* @param tokenValue token
* @return 账号id
*/
public Object getLoginIdByTokenNotThinkFreeze(String tokenValue) {
public static Object getLoginIdByTokenNotThinkFreeze(String tokenValue) {
return stpLogic.getLoginIdByTokenNotThinkFreeze(tokenValue);
}
@@ -21,6 +21,7 @@ import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.fun.SaParamFunction;
import cn.dev33.satoken.stp.parameter.enums.SaLogoutMode;
import cn.dev33.satoken.stp.parameter.enums.SaReplacedLoginExitMode;
import cn.dev33.satoken.stp.parameter.enums.SaReplacedRange;
import cn.dev33.satoken.util.SaTokenConsts;
@@ -110,6 +111,11 @@ public class SaLoginParameter {
*/
private Boolean isWriteHeader;
/**
* 在 isConcurrent=false 时,决定新旧设备谁将放弃会话
*/
private SaReplacedLoginExitMode replacedLoginExitMode;
/**
* 当 isConcurrent=false 时,顶人下线的范围 (CURR_DEVICE_TYPE=当前指定的设备类型端, ALL_DEVICE_TYPE=所有设备类型端)
*/
@@ -158,6 +164,7 @@ public class SaLoginParameter {
this.replacedRange = config.getReplacedRange();
this.overflowLogoutMode = config.getOverflowLogoutMode();
this.rightNowCreateTokenSession = config.getRightNowCreateTokenSession();
this.replacedLoginExitMode = config.getReplacedLoginExitMode();
this.setupCookieConfig(cookie -> {
SaCookieConfig gCookie = config.getCookie();
@@ -568,6 +575,25 @@ public class SaLoginParameter {
return this;
}
/**
* 获取:在 isConcurrent=false 时,决定新旧设备谁将放弃会话 (OLD_DEVICE=旧设备下线,新设备登录成功, NEW_DEVICE=新设备登录失败,旧设备维持在线)
* @return /
*/
public SaReplacedLoginExitMode getReplacedLoginExitMode() {
return replacedLoginExitMode;
}
/**
* 设置:在 isConcurrent=false 时,决定新旧设备谁将放弃会话 (OLD_DEVICE=旧设备下线,新设备登录成功, NEW_DEVICE=新设备登录失败,旧设备维持在线)
* @param replacedLoginExitMode /
* @return 对象自身
*/
public SaLoginParameter setReplacedLoginExitMode(SaReplacedLoginExitMode replacedLoginExitMode) {
this.replacedLoginExitMode = replacedLoginExitMode;
return this;
}
/*
* toString
*/
@@ -577,6 +603,7 @@ public class SaLoginParameter {
+ "deviceType=" + deviceType
+ ", deviceId=" + deviceId
+ ", replacedRange=" + replacedRange
+ ", replacedLoginExitMode=" + replacedLoginExitMode
+ ", overflowLogoutMode=" + overflowLogoutMode
+ ", isLastingCookie=" + isLastingCookie
+ ", timeout=" + timeout
@@ -0,0 +1,35 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.stp.parameter.enums;
/**
* 在 isConcurrent=false 时,决定新旧设备谁将放弃会话
* @author 石泽旭
* @since 1.44.0
*/
public enum SaReplacedLoginExitMode {
/**
* 旧设备下线,新设备登录成功
*/
OLD_DEVICE,
/**
* 新设备登录失败,旧设备维持在线
*/
NEW_DEVICE
}
@@ -36,7 +36,7 @@ public class SaTokenConsts {
/**
* Sa-Token 当前版本号
*/
public static final String VERSION_NO = "v1.44.0";
public static final String VERSION_NO = "v1.45.0";
/**
* Sa-Token 开源地址 Gitee
@@ -28,12 +28,34 @@ package cn.dev33.satoken.util;
public class StrFormatter {
/**
* 占位符
* 占位符(保留原有 public 访问权限,避免破坏外部依赖)
* @deprecated 语义不明确,建议内部使用 {@link #DEFAULT_PLACEHOLDER} 替代
*/
@Deprecated
public static String EMPTY_JSON = "{}";
/**
* 反斜杠转义字符(保留原有 public 访问权限,避免破坏外部依赖)
* @deprecated 命名不规范,建议内部使用 {@link #BACKSLASH_CHAR} 替代
*/
@Deprecated
public static char C_BACKSLASH = '\\';
/**
* 新增内部规范常量(private,仅内部使用)
* 默认占位符 */
private static final String DEFAULT_PLACEHOLDER = "{}";
/**
* 反斜杠转义字符
* */
private static final char BACKSLASH_CHAR = '\\';
/**
* 字符串构建器初始扩容长度
* */
private static final int BUFFER_INIT_CAPACITY = 50;
/**
* 格式化字符串<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
@@ -45,10 +67,10 @@ public class StrFormatter {
*
* @param strPattern 字符串模板
* @param argArray 参数列表
* @return 结果
* @return 格式化后的结果
*/
public static String format(String strPattern, Object... argArray) {
return formatWith(strPattern, EMPTY_JSON, argArray);
return formatWith(strPattern, DEFAULT_PLACEHOLDER, argArray);
}
/**
@@ -63,7 +85,7 @@ public class StrFormatter {
* @param strPattern 字符串模板
* @param placeHolder 占位符,例如{}
* @param argArray 参数列表
* @return 结果
* @return 格式化后的结果
* @since 1.33.0
*/
public static String formatWith(String strPattern, String placeHolder, Object... argArray) {
@@ -74,7 +96,7 @@ public class StrFormatter {
final int placeHolderLength = placeHolder.length();
// 初始化定义好的长度以获得更好的性能
final StringBuilder sbu = new StringBuilder(strPatternLength + 50);
final StringBuilder sbu = new StringBuilder(strPatternLength + BUFFER_INIT_CAPACITY);
int handledPosition = 0;// 记录已经处理到的位置
int delimIndex;// 占位符所在位置
@@ -90,8 +112,8 @@ public class StrFormatter {
}
// 转义符
if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) {// 转义符
if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) {// 双转义符
if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == BACKSLASH_CHAR) {// 转义符
if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == BACKSLASH_CHAR) {// 双转义符
// 转义符之前还有一个转义符,占位符依旧有效
sbu.append(strPattern, handledPosition, delimIndex - 1);
sbu.append(argArray[argIndex]);
@@ -116,4 +138,4 @@ public class StrFormatter {
return sbu.toString();
}
}
}
+3
View File
@@ -11,6 +11,7 @@
<modules>
<module>sa-token-demo-alone-redis</module>
<module>sa-token-demo-alone-redis-cluster</module>
<module>sa-token-demo-alone-redis-sb4</module>
<module>sa-token-demo-apikey</module>
<module>sa-token-demo-async</module>
<module>sa-token-demo-beetl</module>
@@ -35,6 +36,7 @@
<module>sa-token-demo-solon-redisson</module>
<module>sa-token-demo-springboot</module>
<module>sa-token-demo-springboot3-redis</module>
<module>sa-token-demo-springboot4-redis</module>
<module>sa-token-demo-springboot-low-version</module>
<module>sa-token-demo-springboot-redis</module>
<module>sa-token-demo-springboot-redisson</module>
@@ -55,6 +57,7 @@
<module>sa-token-demo-thymeleaf</module>
<module>sa-token-demo-webflux</module>
<module>sa-token-demo-webflux-springboot3</module>
<module>sa-token-demo-webflux-springboot4</module>
<module>sa-token-demo-websocket</module>
<module>sa-token-demo-websocket-spring</module>
<module>sa-token-demo-loveqq-boot</module>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -0,0 +1,78 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-demo-alone-redis-sb4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- SpringBoot 4 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.3</version>
<relativePath/>
</parent>
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
<!-- SpringBoot 4 依赖:webmvc 替代 deprecated 的 starter-webaspectj 替代 starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aspectj</artifactId>
</dependency>
<!-- Sa-Token 权限认证, 在线文档:https://sa-token.cc/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot4-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token整合 Redis -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token整合 Redis -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template-jdk-serializer</artifactId>
<version>${sa-token.version}</version>
<scope>test</scope>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Sa-Token插件:权限缓存与业务缓存分离(Spring Boot 4 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-alone-redis-by-spring-boot4</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- @ConfigurationProperties -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,20 @@
package com.pj;
import cn.dev33.satoken.SaManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Sa-Token 整合 SpringBoot4 示例,整合 alone-redis 插件(权限缓存与业务缓存分离)
*
* @author click33
*/
@SpringBootApplication
public class SaTokenAloneRedisSb4Application {
public static void main(String[] args) {
SpringApplication.run(SaTokenAloneRedisSb4Application.class, args);
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
}
}
@@ -0,0 +1,40 @@
package com.pj.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
/**
* 测试专用Controller,演示 alone-redis 权限缓存与业务缓存分离
*
* @author click33
*/
@RestController
@RequestMapping("/test/")
public class TestController {
@Autowired
StringRedisTemplate stringRedisTemplate;
// 测试Sa-Token缓存,浏览器访问: http://localhost:8083/test/login
@RequestMapping("login")
public SaResult login(@RequestParam(defaultValue = "10001") String id) {
System.out.println("--------------- 测试Sa-Token缓存");
StpUtil.login(id);
return SaResult.ok();
}
// 测试业务缓存,浏览器访问: http://localhost:8083/test/test
@RequestMapping("test")
public SaResult test() {
System.out.println("--------------- 测试业务缓存");
stringRedisTemplate.opsForValue().set("hello", "Hello World");
return SaResult.ok();
}
}
@@ -0,0 +1,59 @@
# 端口
server:
port: 8083
# Sa-Token配置
sa-token:
# Token名称 (同时也是cookie名称)
token-name: satoken
# Token有效期,单位s 默认30天, -1代表永不过期
timeout: 2592000
# Token风格
token-style: uuid
# 配置Sa-Token单独使用的Redis连接
alone-redis:
# Redis数据库索引(默认为0
database: 2
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
# password:
# 连接超时时间(毫秒)
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
# 配置业务使用的Redis连接
spring:
data:
redis:
# Redis数据库索引(默认为0
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
+1 -1
View File
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
+1 -1
View File
@@ -18,7 +18,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
<java.run.main.class>com.pj.SaTokenAsyncApplication</java.run.main.class>
</properties>
+1 -1
View File
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -73,7 +73,7 @@
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-bom</artifactId>
<version>1.44.0</version>
<version>1.45.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
+1 -1
View File
@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
+1 -1
View File
@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -18,7 +18,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
<java.run.main.class>com.pj.SaTokenDeviceLockApplication</java.run.main.class>
</properties>
@@ -17,7 +17,7 @@
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
<dubbo.version>2.7.21</dubbo.version>
<nacos.version>1.4.2</nacos.version>
</properties>
@@ -17,7 +17,7 @@
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
<dubbo.version>2.7.21</dubbo.version>
<nacos.version>1.4.2</nacos.version>
</properties>
@@ -18,7 +18,7 @@
<properties>
<java.version>17</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
<dubbo.version>3.2.2</dubbo.version>
<nacos.version>2.2.2</nacos.version>
</properties>
@@ -18,7 +18,7 @@
<properties>
<java.version>17</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
<dubbo.version>3.2.2</dubbo.version>
<nacos.version>2.2.2</nacos.version>
</properties>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
+1 -1
View File
@@ -27,7 +27,7 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<lombok.version>1.18.10</lombok.version>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
+1 -1
View File
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -18,7 +18,7 @@
<java.version>17</java.version>
<maven.source.version>17</maven.source.version>
<maven.compile.version>17</maven.compile.version>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -156,7 +156,7 @@
<div style="height: 200px;"></div>
</div>
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
<script src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"></script>
<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
<script>window.jQuery || alert('当前页面CDN服务商已宕机,请将所有js包更换为本地依赖')</script>
<!-- 配置缓存读取 -->
@@ -17,7 +17,7 @@
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<!-- 定义 Sa-Token 版本号 -->
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -17,7 +17,7 @@
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<!-- 定义 Sa-Token 版本号 -->
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -17,7 +17,7 @@
</parent>
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
+1 -1
View File
@@ -19,7 +19,7 @@
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.traget>17</maven.compiler.traget>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
@@ -6,7 +6,7 @@ import cn.dev33.satoken.exception.SaTokenException;
import com.pj.satoken.custom_annotation.CheckAccount;
import org.noear.solon.annotation.Component;
import java.lang.reflect.Method;
import java.lang.reflect.AnnotatedElement;
/**
* 注解 CheckAccount 的处理器
@@ -25,7 +25,7 @@ public class CheckAccountHandler implements SaAnnotationHandlerInterface<CheckAc
// 每次请求校验注解时,会执行的方法
@Override
public void checkMethod(CheckAccount at, Method method) {
public void checkMethod(CheckAccount at, AnnotatedElement method) {
// 获取前端请求提交的参数
String name = SaHolder.getRequest().getParamNotNull("name");
String pwd = SaHolder.getRequest().getParamNotNull("pwd");
@@ -19,7 +19,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
<java.run.main.class>com.pj.SaTokenApplication</java.run.main.class>
<java.version>1.8</java.version>
</properties>
@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -17,7 +17,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -10,13 +10,13 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<version>3.5.11</version>
<relativePath/>
</parent>
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -48,7 +48,7 @@
<!-- Sa-Token整合 Redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>
@@ -0,0 +1,65 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-demo-springboot4-redis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- SpringBoot 4 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.3</version>
<relativePath/>
</parent>
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
<!-- SpringBoot 4 依赖:webmvc 替代 deprecated 的 starter-webaspectj 替代 starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aspectj</artifactId>
</dependency>
<!-- Sa-Token 权限认证, 在线文档:https://sa-token.cc/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot4-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token整合 Redis -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- @ConfigurationProperties -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,20 @@
package com.pj;
import cn.dev33.satoken.SaManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Sa-Token 整合 SpringBoot4 示例,整合 redis
* @author click33
*
*/
@SpringBootApplication
public class SaTokenSpringBoot4Application {
public static void main(String[] args) {
SpringApplication.run(SaTokenSpringBoot4Application.class, args);
System.out.println("\n🎉 启动成功:Sa-Token配置如下:" + SaManager.getConfig());
}
}
@@ -0,0 +1,56 @@
package com.pj.current;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.pj.util.AjaxJson;
import cn.dev33.satoken.exception.DisableServiceException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 全局异常处理
*/
@RestControllerAdvice
public class GlobalException {
// 全局异常拦截(拦截项目中的所有异常)
@ExceptionHandler
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 打印堆栈,以供调试
System.out.println("全局异常---------------");
e.printStackTrace();
// 不同异常返回不同状态码
AjaxJson aj = null;
if (e instanceof NotLoginException) { // 如果是未登录异常
NotLoginException ee = (NotLoginException) e;
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
}
else if(e instanceof NotRoleException) { // 如果是角色异常
NotRoleException ee = (NotRoleException) e;
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
}
else if(e instanceof NotPermissionException) { // 如果是权限异常
NotPermissionException ee = (NotPermissionException) e;
aj = AjaxJson.getNotJur("无此权限:" + ee.getPermission());
}
else if(e instanceof DisableServiceException) { // 如果是被封禁异常
DisableServiceException ee = (DisableServiceException) e;
aj = AjaxJson.getNotJur("当前账号 " + ee.getService() + " 服务已被封禁 (level=" + ee.getLevel() + ")" + ee.getDisableTime() + "秒后解封");
}
else { // 普通异常, 输出:500 + 异常信息
aj = AjaxJson.getError(e.getMessage());
}
// 返回给前端
return aj;
}
}
@@ -0,0 +1,26 @@
//package com.pj.current;
//
//import java.io.IOException;
//
//import org.springframework.boot.web.servlet.error.ErrorController;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//
//import cn.dev33.satoken.util.SaResult;
//import jakarta.servlet.http.HttpServletRequest;
//import jakarta.servlet.http.HttpServletResponse;
//
///**
// * 处理 404
// * @author click33
// */
//@RestController
//public class NotFoundHandle implements ErrorController {
//
// @RequestMapping("/error")
// public Object error(HttpServletRequest request, HttpServletResponse response) throws IOException {
// response.setStatus(200);
// return SaResult.get(404, "not found", null);
// }
//
//}
@@ -0,0 +1,71 @@
package com.pj.satoken;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.util.SaResult;
/**
* [Sa-Token 权限认证] 配置类
* @author click33
*
*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
/**
* 注册 Sa-Token 拦截器打开注解鉴权功能
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器打开注解鉴权功能
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
/**
* 注册 [Sa-Token 全局过滤器]
*/
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
// 指定 [拦截路由] 与 [放行路由]
.addInclude("/**")// .addExclude("/favicon.ico")
// 认证函数: 每次请求执行
.setAuth(obj -> {
// System.out.println("---------- sa全局认证 " + SaHolder.getRequest().getRequestPath());
})
// 异常处理函数:每次认证函数发生异常时执行此函数
.setError(e -> {
System.out.println("---------- sa全局异常 ");
e.printStackTrace();
return SaResult.error(e.getMessage());
})
// 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入)
.setBeforeAuth(r -> {
// ---------- 设置一些安全响应头 ----------
SaHolder.getResponse()
// 服务器名称
.setServer("sa-server")
// 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
.setHeader("X-Frame-Options", "SAMEORIGIN")
// 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面
.setHeader("X-XSS-Protection", "1; mode=block")
// 禁用浏览器内容嗅探
.setHeader("X-Content-Type-Options", "nosniff")
;
})
;
}
}
@@ -0,0 +1,44 @@
package com.pj.satoken;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.stp.StpInterface;
/**
* 自定义权限验证接口扩展
*/
@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {
/**
* 返回一个账号所拥有的权限码集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
List<String> list = new ArrayList<String>();
list.add("101");
list.add("user-add");
list.add("user-delete");
list.add("user-update");
list.add("user-get");
list.add("article-get");
return list;
}
/**
* 返回一个账号所拥有的角色标识集合
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
List<String> list = new ArrayList<String>();
list.add("admin");
list.add("super-admin");
return list;
}
}
@@ -0,0 +1,80 @@
package com.pj.test;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.annotation.SaCheckHttpBasic;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
/**
* 注解鉴权测试
* @author click33
*
*/
@RestController
@RequestMapping("/at/")
public class AtController {
// 登录认证,登录之后才可以进入方法 ---- http://localhost:8082/at/checkLogin
@SaCheckLogin
@RequestMapping("checkLogin")
public SaResult checkLogin() {
return SaResult.ok();
}
// 权限认证,具备user-add权限才可以进入方法 ---- http://localhost:8082/at/checkPermission
@SaCheckPermission("user-add")
@RequestMapping("checkPermission")
public SaResult checkPermission() {
return SaResult.ok();
}
// 权限认证,同时具备所有权限才可以进入 ---- http://localhost:8082/at/checkPermissionAnd
@SaCheckPermission({"user-add", "user-delete", "user-update"})
@RequestMapping("checkPermissionAnd")
public SaResult checkPermissionAnd() {
return SaResult.ok();
}
// 权限认证,只要具备其中一个就可以进入 ---- http://localhost:8082/at/checkPermissionOr
@SaCheckPermission(value = {"user-add", "user-delete", "user-update"}, mode = SaMode.OR)
@RequestMapping("checkPermissionOr")
public SaResult checkPermissionOr() {
return SaResult.ok();
}
// 角色认证,只有具备admin角色才可以进入 ---- http://localhost:8082/at/checkRole
@SaCheckRole("admin")
@RequestMapping("checkRole")
public SaResult checkRole() {
return SaResult.ok();
}
// 完成二级认证 ---- http://localhost:8082/at/openSafe
@RequestMapping("openSafe")
public SaResult openSafe() {
StpUtil.openSafe(200); // 打开二级认证,有效期为200秒
return SaResult.ok();
}
// 通过二级认证后才可以进入 ---- http://localhost:8082/at/checkSafe
@SaCheckSafe
@RequestMapping("checkSafe")
public SaResult checkSafe() {
return SaResult.ok();
}
// 通过Basic认证后才可以进入 ---- http://localhost:8082/at/checkBasic
@SaCheckHttpBasic(account = "sa:123456")
@RequestMapping("checkBasic")
public SaResult checkBasic() {
return SaResult.ok();
}
}
@@ -0,0 +1,15 @@
package com.pj.test;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FaviconController {
@RequestMapping("/favicon.ico")
public String favicon() {
return "";
}
}
@@ -0,0 +1,48 @@
package com.pj.test;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
/**
* 登录测试
* @author click33
*
*/
@RestController
@RequestMapping("/acc/")
public class LoginController {
// 测试登录 ---- http://localhost:8082/acc/doLogin?name=zhang&pwd=123456
@RequestMapping("doLogin")
public SaResult doLogin(String name, String pwd) {
// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
if("zhang".equals(name) && "123456".equals(pwd)) {
StpUtil.login(10001);
return SaResult.ok("登录成功");
}
return SaResult.error("登录失败");
}
// 查询登录状态 ---- http://localhost:8082/acc/isLogin
@RequestMapping("isLogin")
public SaResult isLogin() {
return SaResult.ok("是否登录:" + StpUtil.isLogin());
}
// 查询 Token 信息 ---- http://localhost:8082/acc/tokenInfo
@RequestMapping("tokenInfo")
public SaResult tokenInfo() {
return SaResult.data(StpUtil.getTokenInfo());
}
// 测试注销 ---- http://localhost:8082/acc/logout
@RequestMapping("logout")
public SaResult logout() {
StpUtil.logout();
return SaResult.ok();
}
}
@@ -0,0 +1,61 @@
package com.pj.test;
import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.pj.util.Ttime;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
/**
* 压力测试
* @author click33
*
*/
@RestController
@RequestMapping("/s-test/")
public class StressTestController {
// 测试 浏览器访问: http://localhost:8082/s-test/login
// 测试前,请先将 is-read-cookie 配置为 false
@RequestMapping("login")
public SaResult login() {
// StpUtil.getTokenSession().logout();
// StpUtil.logoutByLoginId(10001);
int count = 10; // 循环多少轮
int loginCount = 10000; // 每轮循环多少次
// 循环10次 取平均时间
List<Double> list = new ArrayList<>();
for (int i = 1; i <= count; i++) {
System.out.println("\n---------------------第" + i + "轮---------------------");
Ttime t = new Ttime().start();
// 每次登录的次数
for (int j = 1; j <= loginCount; j++) {
StpUtil.login("1000" + j, "PC-" + j);
if(j % 1000 == 0) {
System.out.println("已登录:" + j);
}
}
t.end();
list.add((t.returnMs() + 0.0) / 1000);
System.out.println("" + i + "" + "用时:" + t.toString());
}
// System.out.println(((SaTokenDaoDefaultImpl)SaTokenManager.getSaTokenDao()).dataMap.size());
System.out.println("\n---------------------测试结果---------------------");
System.out.println(list.size() + "次测试: " + list);
double ss = 0;
for (int i = 0; i < list.size(); i++) {
ss += list.get(i);
}
System.out.println("平均用时: " + ss / list.size());
return SaResult.ok();
}
}
@@ -0,0 +1,43 @@
package com.pj.test;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.servlet.util.SaTokenContextJakartaServletUtil;
import cn.dev33.satoken.spring.SpringMVCUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 测试专用Controller
* @author click33
*
*/
@RestController
@RequestMapping("/test/")
public class TestController {
// 测试 浏览器访问: http://localhost:8082/test/test
@RequestMapping("test")
public SaResult test() {
System.out.println("------------进来了");
System.out.println(SpringMVCUtil.getRequest());
System.out.println(SaTokenContextJakartaServletUtil.getRequest());
return SaResult.ok();
}
// 测试 浏览器访问: http://localhost:8082/test/test2
@RequestMapping("test2")
public SaResult test2() {
return SaResult.ok();
}
// 测试 浏览器访问: http://localhost:8082/test/getRequestPath
@RequestMapping("getRequestPath")
public SaResult getRequestPath() {
System.out.println("-------------- 测试请求 path 获取");
System.out.println("request.getRequestURI() " + SpringMVCUtil.getRequest().getRequestURI());
System.out.println("saRequest.getRequestPath() " + SaHolder.getRequest().getRequestPath());
return SaResult.ok();
}
}
@@ -0,0 +1,162 @@
package com.pj.util;
import java.io.Serializable;
import java.util.List;
/**
* ajax请求返回Json格式数据的封装
*/
public class AjaxJson implements Serializable{
private static final long serialVersionUID = 1L; // 序列化版本号
public static final int CODE_SUCCESS = 200; // 成功状态码
public static final int CODE_ERROR = 500; // 错误状态码
public static final int CODE_WARNING = 501; // 警告状态码
public static final int CODE_NOT_JUR = 403; // 无权限状态码
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
public int code; // 状态码
public String msg; // 描述信息
public Object data; // 携带对象
public Long dataCount; // 数据总数,用于分页
/**
* 返回code
* @return
*/
public int getCode() {
return this.code;
}
/**
* 给msg赋值,连缀风格
*/
public AjaxJson setMsg(String msg) {
this.msg = msg;
return this;
}
public String getMsg() {
return this.msg;
}
/**
* 给data赋值,连缀风格
*/
public AjaxJson setData(Object data) {
this.data = data;
return this;
}
/**
* 将data还原为指定类型并返回
*/
@SuppressWarnings("unchecked")
public <T> T getData(Class<T> cs) {
return (T) data;
}
// ============================ 构建 ==================================
public AjaxJson(int code, String msg, Object data, Long dataCount) {
this.code = code;
this.msg = msg;
this.data = data;
this.dataCount = dataCount;
}
// 返回成功
public static AjaxJson getSuccess() {
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
}
public static AjaxJson getSuccess(String msg) {
return new AjaxJson(CODE_SUCCESS, msg, null, null);
}
public static AjaxJson getSuccess(String msg, Object data) {
return new AjaxJson(CODE_SUCCESS, msg, data, null);
}
public static AjaxJson getSuccessData(Object data) {
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
}
public static AjaxJson getSuccessArray(Object... data) {
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
}
// 返回失败
public static AjaxJson getError() {
return new AjaxJson(CODE_ERROR, "error", null, null);
}
public static AjaxJson getError(String msg) {
return new AjaxJson(CODE_ERROR, msg, null, null);
}
// 返回警告
public static AjaxJson getWarning() {
return new AjaxJson(CODE_ERROR, "warning", null, null);
}
public static AjaxJson getWarning(String msg) {
return new AjaxJson(CODE_WARNING, msg, null, null);
}
// 返回未登录
public static AjaxJson getNotLogin() {
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
}
// 返回没有权限的
public static AjaxJson getNotJur(String msg) {
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
}
// 返回一个自定义状态码的
public static AjaxJson get(int code, String msg){
return new AjaxJson(code, msg, null, null);
}
// 返回分页和数据的
public static AjaxJson getPageData(Long dataCount, Object data){
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
}
// 返回,根据受影响行数的(大于0=ok,小于0=error)
public static AjaxJson getByLine(int line){
if(line > 0){
return getSuccess("ok", line);
}
return getError("error").setData(line);
}
// 返回,根据布尔值来确定最终结果的 (true=okfalse=error)
public static AjaxJson getByBoolean(boolean b){
return b ? getSuccess("ok") : getError("error");
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@SuppressWarnings("rawtypes")
@Override
public String toString() {
String data_string = null;
if(data == null){
} else if(data instanceof List){
data_string = "List(length=" + ((List)data).size() + ")";
} else {
data_string = data.toString();
}
return "{"
+ "\"code\": " + this.getCode()
+ ", \"msg\": \"" + this.getMsg() + "\""
+ ", \"data\": " + data_string
+ ", \"dataCount\": " + dataCount
+ "}";
}
}
@@ -0,0 +1,63 @@
package com.pj.util;
/**
* 用于测试用时
* @author click33
*
*/
public class Ttime {
private long start=0; //开始时间
private long end=0; //结束时间
public static Ttime t = new Ttime(); //static快捷使用
/**
* 开始计时
* @return
*/
public Ttime start() {
start=System.currentTimeMillis();
return this;
}
/**
* 结束计时
*/
public Ttime end() {
end=System.currentTimeMillis();
return this;
}
/**
* 返回所用毫秒数
*/
public long returnMs() {
return end-start;
}
/**
* 格式化输出结果
*/
public void outTime() {
System.out.println(this.toString());
}
/**
* 结束并格式化输出结果
*/
public void endOutTime() {
this.end().outTime();
}
@Override
public String toString() {
return (returnMs() + 0.0) / 1000 + "s"; // 格式化为:0.01s
}
}
@@ -0,0 +1,50 @@
# 端口
server:
port: 8082
# sa-token 配置
sa-token:
# token 名称 (同时也是 cookie 名称)
token-name: satoken
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
timeout: 2592000
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: -1
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
is-share: false
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik
token-style: uuid
# 是否输出操作日志
is-log: true
spring:
data:
# redis配置
redis:
# Redis数据库索引(默认为0
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
+1 -1
View File
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
<java.run.main.class>com.pj.SaTokenSseApplication</java.run.main.class>
</properties>
+1 -1
View File
@@ -27,7 +27,7 @@
<!--<spring.version>4.2.5.RELEASE</spring.version>-->
<spring.version>5.3.7</spring.version>
<jackson.version>2.16.1</jackson.version>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -0,0 +1,40 @@
// 服务端地址
var baseUrl = "http://sa-sso-server.com:9000";
// sa
var sa = {};
// 打开loading
sa.loading = function(msg) {
layer.closeAll(); // 开始前先把所有弹窗关了
return layer.msg(msg, {icon: 16, shade: 0.3, time: 1000 * 20, skin: 'ajax-layer-load'});
};
// 隐藏loading
sa.hideLoading = function() {
layer.closeAll();
};
// 封装一下Ajax
sa.ajax = function(url, data, successFn) {
$.ajax({
url: baseUrl + url,
type: "post",
data: data,
dataType: 'json',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'satoken': localStorage.getItem('satoken')
},
success: function(res){
console.log('返回数据:', res);
successFn(res);
},
error: function(xhr, type, errorThrown){
if(xhr.status == 0){
return alert('无法连接到服务器,请检查网络');
}
return alert("异常:" + JSON.stringify(xhr));
}
});
}
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SSO-Server 平台首页</title>
</head>
<body>
<h2>SSO-Server 平台首页 (前后端分离模式) (平台中心模式)</h2>
<p>
<a href='sso-auth.html?client=sso-client3&redirect=http://sa-sso-client1.com:9003/sso/login?back=http://sa-sso-client1.com:9003/'
target='_blank'> 进入Client1系统 </a>
</p>
<p>
<a href='sso-auth.html?client=sso-client3&redirect=http://sa-sso-client2.com:9003/sso/login?back=http://sa-sso-client2.com:9003/'
target='_blank'> 进入Client2系统 </a>
</p>
<p>
<a href='sso-auth.html?client=sso-client3&redirect=http://sa-sso-client3.com:9003/sso/login?back=http://sa-sso-client3.com:9003/'
target='_blank'> 进入Client3系统 </a>
</p>
<!-- scripts -->
<script src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"></script>
<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
<script src="./common.js"></script>
<script>
sa.ajax("/sso/isLogin", {}, function(res) {
if(res.data) {
// 已登录...
console.log('已登录,开始操作...');
} else {
layer.msg('未登录,请先登录...')
setTimeout(function(){
location.href = './sso-auth.html';
}, 1000)
}
})
</script>
</body>
</html>
@@ -4,7 +4,7 @@
<title>Sa-SSO-Server 认证中心-登录</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="./login.css">
<link rel="stylesheet" href="./sso-auth.css">
</head>
<body>
<div class="view-box">
@@ -35,9 +35,10 @@
</div>
<!-- scripts -->
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
<script src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"></script>
<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
<script src="./login.js"></script>
<script src="./common.js"></script>
<script src="./sso-auth.js"></script>
</body>
</html>
@@ -1,43 +1,3 @@
// 服务端地址
var baseUrl = "http://sa-sso-server.com:9000";
// sa
var sa = {};
// 打开loading
sa.loading = function(msg) {
layer.closeAll(); // 开始前先把所有弹窗关了
return layer.msg(msg, {icon: 16, shade: 0.3, time: 1000 * 20, skin: 'ajax-layer-load'});
};
// 隐藏loading
sa.hideLoading = function() {
layer.closeAll();
};
// 封装一下Ajax
sa.ajax = function(url, data, successFn) {
$.ajax({
url: baseUrl + url,
type: "post",
data: data,
dataType: 'json',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'satoken': localStorage.getItem('satoken')
},
success: function(res){
console.log('返回数据:', res);
successFn(res);
},
error: function(xhr, type, errorThrown){
if(xhr.status == 0){
return alert('无法连接到服务器,请检查网络');
}
return alert("异常:" + JSON.stringify(xhr));
}
});
}
// ----------------------------------- 相关事件 -----------------------------------
@@ -47,16 +7,29 @@ var pData = {
redirect: getParam('redirect', ''),
mode: getParam('mode', '')
};
sa.ajax("/sso/getRedirectUrl", pData, function(res) {
if(res.code == 200) {
// 已登录,并且redirect地址有效,开始跳转
location.href = res.data;
} else if(res.code == 401) {
console.log('未登录');
} else {
layer.alert(res.msg);
}
})
// 提供 redirect 参数时,登录后往 redirect 跳转
if(pData.redirect) {
sa.ajax("/sso/getRedirectUrl", pData, function(res) {
if(res.code == 200) {
// 已登录,并且redirect地址有效,开始跳转
location.href = res.data;
} else if(res.code == 401) {
console.log('未登录');
} else {
layer.alert(res.msg);
}
})
} else {
// 未提供 redirect 参数时,登录后往 home 跳转
sa.ajax("/sso/isLogin", {}, function(res) {
if(res.data) {
location.href = './home.html';
} else {
console.log('未登录,请先登录...');
}
})
}
// 登录
$('.login-btn').click(function(){
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -16,7 +16,15 @@ import org.springframework.web.bind.annotation.RestController;
*/
@RestController
public class H5Controller {
/**
* 返回当前是否已经登录
*/
@RequestMapping("/sso/isLogin")
public SaResult isLogin() {
return SaResult.data(StpUtil.isLogin());
}
/**
* 获取 redirectUrl
*/
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>
@@ -16,7 +16,7 @@
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
<sa-token.version>1.45.0</sa-token.version>
</properties>
<dependencies>

Some files were not shown because too many files have changed in this diff Show More