Merge branch 'dev' into xuxiaowei/Maven-Central-Portal
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# 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) |
|
||||
|
||||
### 详细说明
|
||||
|
||||
#### 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 脚本快速扫描。
|
||||
|
||||
## 快速使用
|
||||
|
||||
在 Cursor 对话中,直接描述你的需求即可自动触发相应 skill:
|
||||
|
||||
```
|
||||
用户:帮我生成 commit message
|
||||
→ 自动使用 commit-message skill 分析 git 变更并生成提交信息
|
||||
|
||||
用户:整理一下更新日志
|
||||
→ 自动使用 organize-update-log skill 生成更新日志
|
||||
|
||||
用户:清理一下冗余 import
|
||||
→ 自动使用 remove-redundancy-import skill 扫描并清理未使用的 import
|
||||
```
|
||||
|
||||
## 新增 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` 获取更多使用帮助
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
|
||||
若无 staged 变更,则用 `git diff` 查看工作区变更。
|
||||
|
||||
**注意新文件**:`git status` 中标记为 `Untracked files` 的新增文件也需要纳入分析,这些文件通常代表新增功能、新模块或新资源。
|
||||
|
||||
### 第二步:分析变更类型
|
||||
|
||||
根据变更内容(包括新增文件、修改文件、删除文件)选择 type 前缀:
|
||||
|
||||
| type | 适用场景 |
|
||||
|------|----------|
|
||||
| feat | 新增功能、新模块、新插件 |
|
||||
| fix | 修复 bug、修正错误 |
|
||||
| refactor | 重构、优化结构、重命名、移除冗余 |
|
||||
| perf | 性能优化(与 refactor 区分:侧重性能) |
|
||||
| docs | 文档更新、README、错别字、同步链接 |
|
||||
| style | 代码格式调整(缩进、空格等,不影响逻辑) |
|
||||
| chore | 构建配置、.gitignore、注释修复、依赖更新 |
|
||||
| test | 单元测试、测试用例 |
|
||||
| demo | 示例项目、demo 相关 |
|
||||
| memo | 备忘录、内部记录 |
|
||||
| revert | 回滚某次提交 |
|
||||
| AI | AI 创建的 skill、规则等 |
|
||||
|
||||
**新增文件的处理**:
|
||||
- 新创建的未跟踪文件(`git status` 中显示为 `Untracked files`)必须纳入 commit message 的描述范围
|
||||
- 新增文件通常对应 `feat` 类型(新功能、新模块、新插件、新资源)
|
||||
- 示例场景:新增插件模块、新增配置文件、新增资源文件、新增文档、新增 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 插件` |
|
||||
| 新增文件(未跟踪) | `feat: 新增 sign 模块配置类 SignConfig` |
|
||||
| 修复 bug | `fix: 修复 StpUtil.getLoginIdByTokenNotThinkFreeze 方法缺少 static 的问题` |
|
||||
| 性能优化 | `perf: 优化 StrFormatter 常量封装` |
|
||||
| 重构模块 | `refactor: 重构模块依赖层级` |
|
||||
| 移除冗余 | `refactor: 移除冗余导包` |
|
||||
| 文档更新 | `docs: 同步最新文章列表、赞助者名单` |
|
||||
| 注释修复 | `chore: 修复注释错别字` |
|
||||
| 新增 skill | `AI: 新增 skills/commit-message/SKILL.md,用于根据 git 变更生成符合项目风格的 commit message` |
|
||||
@@ -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**:回滚
|
||||
@@ -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()
|
||||
@@ -12,7 +12,9 @@ unpackage/
|
||||
/.factorypath
|
||||
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
sa-token-three-plugin/
|
||||
sa-token-doc/big-file/
|
||||
|
||||
.flattened-pom.xml
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -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,你给老子爬
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,28 +2,28 @@
|
||||
<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>
|
||||
<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、微服务鉴权。
|
||||
|
||||

|
||||

|
||||
|
||||
要在 SpringBoot 项目中使用 Sa-Token,你只需要在 pom.xml 中引入依赖:
|
||||
|
||||
@@ -36,7 +36,7 @@ Sa-Token 是一个轻量级 Java 权限认证框架,目前拥有五大核心
|
||||
</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 认证需求:
|
||||
|
||||

|
||||

|
||||
|
||||
| 系统架构 | 采用模式 | 简介 | 文档链接 |
|
||||
| :-------- | :-------- | :-------- | :-------- |
|
||||
| 前端同域 + 后端同 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 端下放 code,client 端获取 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ú) 🖐️🐟️。
|
||||
|
||||
|
||||
@@ -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 ..
|
||||
|
||||
@@ -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>
|
||||
@@ -71,7 +72,11 @@
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
|
||||
<!--
|
||||
导入 sa-token-dependencies 所有版本定义,并传导到每个子项目。
|
||||
需要注意的是:该 import 只会导入 <dependencyManagement> 部分,而不会导入 <dependencies> 部分和 <properties> 部分。
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dependencies</artifactId>
|
||||
@@ -89,7 +94,7 @@
|
||||
<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>
|
||||
@@ -100,10 +105,10 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.12.0</version>
|
||||
<configuration>
|
||||
<!-- 统一生成聚合文档,解决 mvn package 时控制台发出 javadoc 警告的问题 -->
|
||||
<aggregate>true</aggregate>
|
||||
<!-- <aggregate>true</aggregate>-->
|
||||
<!-- 忽略部分 error 和 warning -->
|
||||
<failOnError>false</failOnError>
|
||||
<failOnWarnings>false</failOnWarnings>
|
||||
@@ -123,7 +128,7 @@
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<additionalparam>-Xdoclint:none</additionalparam>
|
||||
<doclint>none</doclint>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
@@ -133,7 +138,7 @@
|
||||
<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>
|
||||
|
||||
+34
-10
@@ -4,6 +4,13 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>7</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-bom</artifactId>
|
||||
<version>${revision}</version>
|
||||
@@ -55,6 +62,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>
|
||||
@@ -72,7 +84,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>
|
||||
@@ -85,6 +107,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 -->
|
||||
@@ -123,6 +150,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>
|
||||
@@ -243,21 +275,13 @@
|
||||
</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>
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+35
@@ -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
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,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 +56,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>
|
||||
|
||||
@@ -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>
|
||||
<!-- 配置缓存读取 -->
|
||||
|
||||
+2
-2
@@ -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 element) {
|
||||
// 获取前端请求提交的参数
|
||||
String name = SaHolder.getRequest().getParamNotNull("name");
|
||||
String pwd = SaHolder.getRequest().getParamNotNull("pwd");
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<version>3.5.11</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@@ -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.44.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot 4 依赖:webmvc 替代 deprecated 的 starter-web,aspectj 替代 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>
|
||||
+20
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -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);
|
||||
// }
|
||||
//
|
||||
//}
|
||||
+71
@@ -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")
|
||||
;
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
/**
|
||||
* 自定义权限验证接口扩展
|
||||
*/
|
||||
@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的权限码集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("101");
|
||||
list.add("user-add");
|
||||
list.add("user-delete");
|
||||
list.add("user-update");
|
||||
list.add("user-get");
|
||||
list.add("article-get");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的角色标识集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("admin");
|
||||
list.add("super-admin");
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.annotation.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();
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 登录测试
|
||||
* @author 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();
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.util.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();
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
package com.pj.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* ajax请求返回Json格式数据的封装
|
||||
*/
|
||||
public class AjaxJson implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L; // 序列化版本号
|
||||
|
||||
public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||
public static final int CODE_ERROR = 500; // 错误状态码
|
||||
public static final int CODE_WARNING = 501; // 警告状态码
|
||||
public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||
|
||||
public int code; // 状态码
|
||||
public String msg; // 描述信息
|
||||
public Object data; // 携带对象
|
||||
public Long dataCount; // 数据总数,用于分页
|
||||
|
||||
/**
|
||||
* 返回code
|
||||
* @return
|
||||
*/
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给msg赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
public String getMsg() {
|
||||
return this.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给data赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setData(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将data还原为指定类型并返回
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(Class<T> cs) {
|
||||
return (T) data;
|
||||
}
|
||||
|
||||
// ============================ 构建 ==================================
|
||||
|
||||
public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.dataCount = dataCount;
|
||||
}
|
||||
|
||||
// 返回成功
|
||||
public static AjaxJson getSuccess() {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg, Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessData(Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessArray(Object... data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
|
||||
// 返回失败
|
||||
public static AjaxJson getError() {
|
||||
return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||
}
|
||||
public static AjaxJson getError(String msg) {
|
||||
return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回警告
|
||||
public static AjaxJson getWarning() {
|
||||
return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||
}
|
||||
public static AjaxJson getWarning(String msg) {
|
||||
return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回未登录
|
||||
public static AjaxJson getNotLogin() {
|
||||
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||
}
|
||||
|
||||
// 返回没有权限的
|
||||
public static AjaxJson getNotJur(String msg) {
|
||||
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回一个自定义状态码的
|
||||
public static AjaxJson get(int code, String msg){
|
||||
return new AjaxJson(code, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回分页和数据的
|
||||
public static AjaxJson getPageData(Long dataCount, Object data){
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||
}
|
||||
|
||||
// 返回,根据受影响行数的(大于0=ok,小于0=error)
|
||||
public static AjaxJson getByLine(int line){
|
||||
if(line > 0){
|
||||
return getSuccess("ok", line);
|
||||
}
|
||||
return getError("error").setData(line);
|
||||
}
|
||||
|
||||
// 返回,根据布尔值来确定最终结果的 (true=ok,false=error)
|
||||
public static AjaxJson getByBoolean(boolean b){
|
||||
return b ? getSuccess("ok") : getError("error");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public String toString() {
|
||||
String data_string = null;
|
||||
if(data == null){
|
||||
|
||||
} else if(data instanceof List){
|
||||
data_string = "List(length=" + ((List)data).size() + ")";
|
||||
} else {
|
||||
data_string = data.toString();
|
||||
}
|
||||
return "{"
|
||||
+ "\"code\": " + this.getCode()
|
||||
+ ", \"msg\": \"" + this.getMsg() + "\""
|
||||
+ ", \"data\": " + data_string
|
||||
+ ", \"dataCount\": " + dataCount
|
||||
+ "}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.pj.util;
|
||||
|
||||
|
||||
/**
|
||||
* 用于测试用时
|
||||
* @author 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
+23
-50
@@ -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(){
|
||||
+9
-1
@@ -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
|
||||
*/
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
+11
@@ -105,6 +105,17 @@ public class TestController {
|
||||
return SaResult.ok("登录人:" + StpUtil.getLoginIdByToken(satoken));
|
||||
}
|
||||
|
||||
// API测试:SaSession 写值 --- http://localhost:8081/test/sessionSet
|
||||
@RequestMapping("sessionSet")
|
||||
public Mono<SaResult> sessionSet() {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
|
||||
StpUtil.getSession().set("name", "zhangsan");
|
||||
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
|
||||
return SaResult.data(StpUtil.getSession().get("name"));
|
||||
});
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public SaResult test() {
|
||||
|
||||
+24
-24
@@ -20,27 +20,27 @@ sa-token:
|
||||
is-log: true
|
||||
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 10000ms
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
data:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 10000ms
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<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-webflux-springboot4</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.44.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- springboot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证(Reactor响应式集成), 在线文档:https://sa-token.cc/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-reactor-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>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.pj;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Sa-Token整合webflux 示例 (springboot4)
|
||||
*
|
||||
* @author click33
|
||||
* @since 2026年2月27日
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SaTokenWebfluxSpringboot4Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaTokenWebfluxSpringboot4Application.class, args);
|
||||
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
||||
+9
-21
@@ -13,42 +13,30 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.reactor.filter;
|
||||
package com.pj.satoken;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaTokenContextModelBox;
|
||||
import cn.dev33.satoken.exception.BackResultException;
|
||||
import cn.dev33.satoken.exception.StopMatchException;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.reactor.util.SaReactorOperateUtil;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* CORS 跨域策略过滤器 (基于 Reactor)
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.42.0
|
||||
* 自定义过滤器
|
||||
*/
|
||||
@Order(SaTokenConsts.CORS_FILTER_ORDER)
|
||||
public class SaTokenCorsFilterForReactor implements WebFilter {
|
||||
@Component
|
||||
public class MyFilter implements WebFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
System.out.println("进入自定义过滤器");
|
||||
|
||||
try {
|
||||
// 先 set 上下文,再调用 Sa-Token 同步 API,并在 finally 里清除上下文
|
||||
SaReactorSyncHolder.setContext(exchange);
|
||||
SaTokenContextModelBox box = SaHolder.getContext().getModelBox();
|
||||
SaStrategy.instance.corsHandle.execute(box.getRequest(), box.getResponse(), box.getStorage());
|
||||
}
|
||||
catch (StopMatchException ignored) {}
|
||||
catch (BackResultException e) {
|
||||
return SaReactorOperateUtil.writeResult(exchange, e.getMessage());
|
||||
System.out.println(StpUtil.isLogin());
|
||||
}
|
||||
finally {
|
||||
SaReactorSyncHolder.clearContext();
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* [Sa-Token 权限认证] 配置类
|
||||
* @author click33
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class SaTokenConfigure {
|
||||
|
||||
/**
|
||||
* 注册 [sa-token全局过滤器]
|
||||
*/
|
||||
@Bean
|
||||
public SaReactorFilter getSaReactorFilter() {
|
||||
return new SaReactorFilter()
|
||||
// 指定 [拦截路由]
|
||||
.addInclude("/**")
|
||||
// 指定 [放行路由]
|
||||
.addExclude("/favicon.ico")
|
||||
// 指定[认证函数]: 每次请求执行
|
||||
.setAuth(r -> {
|
||||
System.out.println("---------- sa全局认证");
|
||||
// SaRouter.match("/test/test", () -> StpUtil.checkLogin());
|
||||
})
|
||||
// 指定[异常处理函数]:每次[认证函数]发生异常时执行此函数
|
||||
.setError(e -> {
|
||||
System.out.println("---------- sa全局异常 ");
|
||||
return SaResult.error(e.getMessage());
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
|
||||
/**
|
||||
* 自定义权限验证接口扩展
|
||||
*/
|
||||
@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的权限码集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("101");
|
||||
list.add("user-add");
|
||||
list.add("user-delete");
|
||||
list.add("user-update");
|
||||
list.add("user-get");
|
||||
list.add("article-get");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的角色标识集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("admin");
|
||||
list.add("super-admin");
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
@Configuration
|
||||
public class DefineRoutes {
|
||||
|
||||
/**
|
||||
* 函数式编程,初始化路由表
|
||||
* @return 路由表
|
||||
*/
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> getRoutes() {
|
||||
return RouterFunctions.route(RequestPredicates.GET("/fun"), req -> {
|
||||
return SaReactorSyncHolder.setContext(req.exchange(), () -> {
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
SaResult res = SaResult.data(StpUtil.getTokenInfo());
|
||||
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalException {
|
||||
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 测试专用Controller
|
||||
* @author click33
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/test/")
|
||||
public class TestController {
|
||||
|
||||
@Autowired
|
||||
UserService userService;
|
||||
|
||||
// 登录测试:Controller 里调用 Sa-Token API --- http://localhost:8081/test/login
|
||||
@RequestMapping("login")
|
||||
public Mono<SaResult> login(@RequestParam(defaultValue="10001") String id) {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
StpUtil.login(id);
|
||||
return SaResult.ok("登录成功");
|
||||
});
|
||||
}
|
||||
|
||||
// API测试:手动设置上下文、try-finally 形式 --- http://localhost:8081/test/isLogin
|
||||
@RequestMapping("isLogin")
|
||||
public SaResult isLogin(ServerWebExchange exchange) {
|
||||
try {
|
||||
SaReactorSyncHolder.setContext(exchange);
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
} finally {
|
||||
SaReactorSyncHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
// API测试:手动设置上下文、lambda 表达式形式 --- http://localhost:8081/test/isLogin2
|
||||
@RequestMapping("isLogin2")
|
||||
public SaResult isLogin2(ServerWebExchange exchange) {
|
||||
SaResult res = SaReactorSyncHolder.setContext(exchange, ()->{
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
return SaResult.data(res);
|
||||
}
|
||||
|
||||
// API测试:自动设置上下文、lambda 表达式形式 --- http://localhost:8081/test/isLogin3
|
||||
@RequestMapping("isLogin3")
|
||||
public Mono<SaResult> isLogin3() {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
userService.isLogin();
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
}
|
||||
|
||||
// API测试:自动设置上下文、调用 userService Mono 方法 --- http://localhost:8081/test/isLogin4
|
||||
@RequestMapping("isLogin4")
|
||||
public Mono<SaResult> isLogin4() {
|
||||
return userService.findUserIdByNamePwd("ZhangSan", "123456").flatMap(userId -> {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
StpUtil.login(userId);
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// API测试:切换线程、复杂嵌套调用 --- http://localhost:8081/test/isLogin5
|
||||
@RequestMapping("isLogin5")
|
||||
public Mono<SaResult> isLogin5() {
|
||||
System.out.println("线程id-----" + Thread.currentThread().getId());
|
||||
// 要点:在流里调用 Sa-Token API 之前,必须用 SaReactorHolder.sync( () -> {} ) 进行包裹
|
||||
return Mono.delay(Duration.ofSeconds(1))
|
||||
.doOnNext(r-> System.out.println("线程id-----" + Thread.currentThread().getId()))
|
||||
.map(r-> SaReactorHolder.sync( () -> userService.isLogin() ))
|
||||
.map(r-> userService.findUserIdByNamePwd("ZhangSan", "123456"))
|
||||
.map(r-> SaReactorHolder.sync( () -> userService.isLogin() ))
|
||||
.flatMap(isLogin -> {
|
||||
System.out.println("是否登录 " + isLogin);
|
||||
return SaReactorHolder.sync(() -> {
|
||||
System.out.println("是否登录 " + StpUtil.isLogin());
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// API测试:使用上下文无关的API --- http://localhost:8081/test/isLogin6
|
||||
@RequestMapping("isLogin6")
|
||||
public SaResult isLogin6(@CookieValue("satoken") String satoken) {
|
||||
System.out.println("token 为:" + satoken);
|
||||
System.out.println("登录人:" + StpUtil.getLoginIdByToken(satoken));
|
||||
return SaResult.ok("登录人:" + StpUtil.getLoginIdByToken(satoken));
|
||||
}
|
||||
|
||||
// API测试:SaSession 写值 --- http://localhost:8081/test/sessionSet
|
||||
@RequestMapping("sessionSet")
|
||||
public Mono<SaResult> sessionSet() {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
|
||||
StpUtil.getSession().set("name", "zhangsan");
|
||||
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
|
||||
return SaResult.data(StpUtil.getSession().get("name"));
|
||||
});
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public SaResult test() {
|
||||
System.out.println("线程id------- " + Thread.currentThread().getId());
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 模拟 Service 方法
|
||||
* @author click33
|
||||
* @since 2025/4/6
|
||||
*/
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
public boolean isLogin() {
|
||||
System.out.println("UserService 里调用 API 测试,是否登录:" + StpUtil.isLogin());
|
||||
return StpUtil.isLogin();
|
||||
}
|
||||
|
||||
public Mono<Long> findUserIdByNamePwd(String name, String pwd) {
|
||||
// ...
|
||||
return Mono.just(10001L);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
# 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: 0
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
||||
port: 6379
|
||||
# Redis服务器连接密码(默认为空)
|
||||
password:
|
||||
# 连接超时时间(毫秒)
|
||||
timeout: 10000ms
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池最大连接数
|
||||
max-active: 200
|
||||
# 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 10
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -105,6 +105,17 @@ public class TestController {
|
||||
return SaResult.ok("登录人:" + StpUtil.getLoginIdByToken(satoken));
|
||||
}
|
||||
|
||||
// API测试:SaSession 写值 --- http://localhost:8081/test/sessionSet
|
||||
@RequestMapping("sessionSet")
|
||||
public Mono<SaResult> sessionSet() {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
|
||||
StpUtil.getSession().set("name", "zhangsan");
|
||||
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
|
||||
return SaResult.data(StpUtil.getSession().get("name"));
|
||||
});
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public SaResult test() {
|
||||
|
||||
@@ -4,33 +4,35 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>cn.dev33</groupId>
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-bom</artifactId>
|
||||
<version>${revision}</version>
|
||||
<!-- 寻址 -->
|
||||
<relativePath>../sa-token-bom/pom.xml</relativePath>
|
||||
</parent>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<artifactId>sa-token-dependencies</artifactId>
|
||||
<version>${revision}</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>sa-token-dependencies</name>
|
||||
<description>Sa-Token Dependencies</description>
|
||||
|
||||
<properties>
|
||||
<revision>1.44.0</revision>
|
||||
|
||||
<!-- 统一定义依赖版本号 -->
|
||||
<springboot.version>2.7.18</springboot.version>
|
||||
<springboot3.version>3.4.3</springboot3.version>
|
||||
<spring-web.low.version>5.3.39</spring-web.low.version>
|
||||
<reactor-core.version>3.7.4</reactor-core.version>
|
||||
<!-- 第三方依赖版本号 -->
|
||||
<jackson-databind.version>2.13.4.1</jackson-databind.version>
|
||||
<jackson-datatype-jsr310.version>2.11.2</jackson-datatype-jsr310.version>
|
||||
<jackson3-databind.version>3.1.0</jackson3-databind.version>
|
||||
<servlet-api.version>3.1.0</servlet-api.version>
|
||||
<jakarta-servlet-api.version>6.0.0</jakarta-servlet-api.version>
|
||||
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
|
||||
<freemarker.version>2.3.34</freemarker.version>
|
||||
<solon.version>3.2.1</solon.version>
|
||||
<noear-redisx.version>1.6.9</noear-redisx.version>
|
||||
<noear-snack3.version>3.2.133</noear-snack3.version>
|
||||
<noear-redisx.version>1.8.2</noear-redisx.version>
|
||||
<noear-snack3.version>3.2.139</noear-snack3.version>
|
||||
<noear-snack4.version>4.0.20</noear-snack4.version>
|
||||
<jfinal.version>4.9.17</jfinal.version>
|
||||
<jboot.version>3.14.4</jboot.version>
|
||||
<loveqq.version>1.1.2-java8</loveqq.version>
|
||||
<loveqq.version>1.1.5-java8</loveqq.version>
|
||||
<commons-pool2.version>2.5.0</commons-pool2.version>
|
||||
<dubbo.version>2.7.21</dubbo.version>
|
||||
<grpc-spring-boot-starter.version>2.10.1.RELEASE</grpc-spring-boot-starter.version>
|
||||
@@ -68,40 +70,19 @@
|
||||
<version>${jakarta-servlet-api.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- spring-boot-starter-web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- spring-boot-starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- spring-web -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>5.3.7</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- reactor-core -->
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>${reactor-core.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- jackson-databind -->
|
||||
<!-- jackson2 databind -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- jackson3 databind (tools.jackson) -->
|
||||
<dependency>
|
||||
<groupId>tools.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson3-databind.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- solon -->
|
||||
<dependency>
|
||||
@@ -116,6 +97,13 @@
|
||||
<artifactId>snack3</artifactId>
|
||||
<version>${noear-snack3.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- snack4 -->
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>snack4</artifactId>
|
||||
<version>${noear-snack4.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- jboot -->
|
||||
<dependency>
|
||||
@@ -152,25 +140,7 @@
|
||||
<version>${loveqq.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency> -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ****************** sa-token-plugin 相关依赖 ****************** -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Redisson 相关操作API -->
|
||||
<dependency>
|
||||
@@ -241,26 +211,6 @@
|
||||
<version>${freemarker.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- spring-boot-starter-thymeleaf -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- spring-boot-starter-aop -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- spring-boot-starter-actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool-jwt -->
|
||||
<dependency>
|
||||
@@ -317,15 +267,6 @@
|
||||
<version>${okhttps.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 自身依赖 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-bom</artifactId>
|
||||
<version>${revision}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
+49
-12
@@ -2,33 +2,35 @@
|
||||
<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>
|
||||
<h5 align="center">开源、免费、轻量级 Java 权限认证框架,让鉴权变得简单、优雅!</h5>
|
||||
<h5 align="center">✨ 开源、免费、一站式 java 权限认证框架,让鉴权变得简单、优雅!</h5>
|
||||
<p align="center" class="badge-box">
|
||||
<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>
|
||||
|
||||
---
|
||||
|
||||
## 前言:️️
|
||||
## 📝 前言:️️
|
||||
为了保证新同学不迷路,请允许我唠叨一下:无论您从何处看到本篇文章,最新开发文档永远在:[https://sa-token.cc](https://sa-token.cc),
|
||||
建议收藏在浏览器书签,如果您已经身处本网站下,则请忽略此条说明。
|
||||
|
||||
本文档将会尽力讲解每个功能的设计原因、应用场景,用心阅读文档,你学习到的将不止是 `Sa-Token` 框架本身,更是绝大多数场景下权限设计的最佳实践。
|
||||
|
||||
|
||||
## Sa-Token 介绍
|
||||
## 🛠️ Sa-Token 介绍
|
||||
|
||||
**Sa-Token** 是一个轻量级 Java 权限认证框架,主要解决:**登录认证**、**权限认证**、**单点登录**、**OAuth2.0**、**分布式Session会话**、**微服务网关鉴权**
|
||||
等一系列权限相关问题。
|
||||
|
||||

|
||||
<!--  -->
|
||||
|
||||
<object class="sa-token-jss-img" data="/big-file/index/intro/sa-token-jss--tran--onclick.svg"></object>
|
||||
|
||||
Sa-Token 旨在以简单、优雅的方式完成系统的权限认证部分,以登录认证为例,你只需要:
|
||||
|
||||
@@ -83,7 +85,7 @@ registry.addInterceptor(new SaInterceptor(handler -> {
|
||||
当你受够 Shiro、SpringSecurity 等框架的三拜九叩之后,你就会明白,相对于这些传统老牌框架,Sa-Token 的 API 设计是多么的简单、优雅!
|
||||
|
||||
|
||||
## Sa-Token 功能一览
|
||||
## 🎉 Sa-Token 功能一览
|
||||
|
||||
Sa-Token 目前主要五大功能模块:登录认证、权限认证、单点登录、OAuth2.0、微服务鉴权。
|
||||
|
||||
@@ -127,10 +129,45 @@ Sa-Token 目前主要五大功能模块:登录认证、权限认证、单点
|
||||
|
||||
功能结构图:
|
||||
|
||||

|
||||
<img class="s-w" src="/big-file/index/intro/sa-token-js4.png" />
|
||||
|
||||
|
||||
## 开源仓库 Star 趋势
|
||||
## 📖❓ 疑问解答
|
||||
|
||||
**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)。
|
||||
我们将定期同步赞助者名单到在线文档展示。(您需要注意的一点是:该赞助仅为友情赞助,不提供任何商业交换)
|
||||
|
||||
|
||||
|
||||
## 📈 开源仓库 Star 趋势
|
||||
|
||||
<p class="un-dec-a-pre"></p>
|
||||
|
||||
@@ -138,15 +175,15 @@ Sa-Token 目前主要五大功能模块:登录认证、权限认证、单点
|
||||
|
||||
如果 Sa-Token 帮助到了您,希望您可以为其点上一个 `star`:
|
||||
[码云](https://gitee.com/dromara/sa-token)、
|
||||
[AtomGit](https://atomgit.com/dromara/sa-token)、
|
||||
[GitHub](https://github.com/dromara/sa-token)
|
||||
|
||||
|
||||
## 使用Sa-Token的开源项目
|
||||
## 🚀 使用 Sa-Token 的开源项目
|
||||
参考:[Sa-Token 生态](/more/link)
|
||||
|
||||
|
||||
|
||||
## 交流群
|
||||
## 💬 交流群
|
||||
加入 Sa-Token 框架 QQ、微信讨论群:[点击加入](/more/join-group.md)
|
||||
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
- [NoSdk、ReSdk 模式与非 java 项目](/sso/sso-nosdk)
|
||||
- [SSO 代码 API 参考](/sso/sso-dev)
|
||||
- [常见问题总结](/sso/sso-questions)
|
||||
- [Sa-Sso-Pro:单点登录商业版](/sso/sso-pro)
|
||||
- [Sa-Pro:单点登录商业版](/pro/st_sso)
|
||||
|
||||
- **OAuth2.0**
|
||||
- [OAuth2.0简述](/oauth2/readme)
|
||||
@@ -75,6 +75,7 @@
|
||||
- [OAuth2-与登录会话实现数据互通](/oauth2/oauth2-interworking)
|
||||
- [OAuth2 代码 API 参考](/oauth2/oauth2-dev)
|
||||
- [常见问题总结](/oauth2/oauth2-questions)
|
||||
- [Sa-Max:统一认证商业版](/pro/st_oauth2)
|
||||
<!-- - [前后端分离模式整合方案](/oauth2/4) -->
|
||||
<!-- - [平台中心模式开发](/oauth2/5) -->
|
||||
<!-- - [jwt 风格 token](/oauth2/6) -->
|
||||
@@ -103,6 +104,7 @@
|
||||
- [Sa-Token 插件开发指南](/fun/plugin-dev)
|
||||
- [自定义 SaTokenContext 指南](/fun/sa-token-context)
|
||||
|
||||
|
||||
- **API手册**
|
||||
- [StpUtil-鉴权工具类](/api/stp-util)
|
||||
- [SaSession-会话对象](/api/sa-session)
|
||||
@@ -111,12 +113,18 @@
|
||||
- [全局类、方法](/more/common-action)
|
||||
|
||||
|
||||
- **框架设计**
|
||||
- [仓库目录](/arch/dir-intro)
|
||||
- [数据结构](/arch/data-structure)
|
||||
|
||||
|
||||
- **其它**
|
||||
- [更新日志](/more/update-log)
|
||||
- [框架生态](/more/link)
|
||||
- [框架博客](/more/blog)
|
||||
- [推荐公众号](/more/tj-gzh)
|
||||
- [加入讨论群](/more/join-group)
|
||||
- [Sa-Token 内容合作群](/more/content-cooperation)
|
||||
- [赞助 Sa-Token](/more/sa-token-donate)
|
||||
- [需求提交](/more/demand-commit)
|
||||
- [问卷调查](/more/wenjuan)
|
||||
@@ -133,7 +141,6 @@
|
||||
- [数据读写三大作用域](/fun/three-scope)
|
||||
- [TokenInfo参数详解](/fun/token-info)
|
||||
- [异常细分状态码](/fun/exception-code)
|
||||
- [数据结构](/fun/data-structure)
|
||||
- [自定义注解](/fun/custom-annotations)
|
||||
- [防火墙](/fun/firewall)
|
||||
- [参考:把权限放在缓存里](/fun/jur-cache)
|
||||
|
||||
+106
-30
@@ -4,7 +4,7 @@ SaStrategy-全局策略,核心逻辑的代理封装。
|
||||
|
||||
---
|
||||
|
||||
### 所有策略
|
||||
### 核心策略
|
||||
|
||||
``` java
|
||||
/**
|
||||
@@ -24,6 +24,11 @@ public Function<String, SaSession> createSession = (sessionId) -> {
|
||||
return new SaSession(sessionId);
|
||||
};
|
||||
|
||||
/**
|
||||
* 反序列化 SaSession 时默认指定的类型
|
||||
*/
|
||||
public Class<? extends SaSession> sessionClassType = SaSession.class;
|
||||
|
||||
/**
|
||||
* 判断:集合中是否包含指定元素(模糊匹配)
|
||||
* <p> 参数 [集合, 元素]
|
||||
@@ -32,38 +37,14 @@ public BiFunction<List<String>, String, Boolean> hasElement = (list, element) ->
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* 对一个 [Method] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数 [Method句柄]
|
||||
*/
|
||||
public Consumer<Method> checkMethodAnnotation = (method) -> {
|
||||
// ...
|
||||
};
|
||||
|
||||
/**
|
||||
* 对一个 [元素] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数 [element元素]
|
||||
* 生成唯一式 token 的算法
|
||||
* <p> 参数:元素名称, 最大尝试次数, 创建 token 函数, 检查 token 函数 </p>
|
||||
*/
|
||||
public Consumer<AnnotatedElement> checkElementAnnotation = (target) -> {
|
||||
// ...
|
||||
};
|
||||
|
||||
/**
|
||||
* 从元素上获取注解(注解鉴权内部实现)
|
||||
* <p> 参数 [element元素,要获取的注解类型]
|
||||
*/
|
||||
public BiFunction<AnnotatedElement, Class<? extends Annotation> , Annotation> getAnnotation = (element, annotationClass)->{
|
||||
// 默认使用jdk的注解处理器
|
||||
return element.getAnnotation(annotationClass);
|
||||
};
|
||||
|
||||
/**
|
||||
* 拼接两个url
|
||||
* <p> 例如:url1=http://domain.cn,url2=/sso/auth,则返回:http://domain.cn/sso/auth
|
||||
* <p> 参数 [第一个url, 第二个url]
|
||||
*/
|
||||
public BiFunction<String, String, String> spliceTwoUrl = (url1, url2) -> {
|
||||
return xxx;
|
||||
public SaGenerateUniqueTokenFunction generateUniqueToken = (elementName, maxTryTimes, createTokenFunction, checkTokenFunction) -> {
|
||||
// ...
|
||||
return "xxxxxx";
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -74,10 +55,105 @@ public BiFunction<String, String, String> spliceTwoUrl = (url1, url2) -> {
|
||||
public Function<StpLogic, Boolean> autoRenew = (stpLogic) -> {
|
||||
return stpLogic.getConfigOrGlobal().getAutoRenew();
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建 StpLogic 的算法
|
||||
* <p> 参数:账号体系标识 </p>
|
||||
* <p> 返回:创建好的 StpLogic 对象 </p>
|
||||
*/
|
||||
public SaCreateStpLogicFunction createStpLogic = (loginType) -> {
|
||||
return new StpLogic(loginType);
|
||||
};
|
||||
|
||||
/**
|
||||
* 路由匹配策略
|
||||
* <p> 参数:pattern, path </p>
|
||||
* <p> 返回:是否匹配 </p>
|
||||
*/
|
||||
public SaRouteMatchFunction routeMatcher = (pattern, path) -> {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* CORS 策略处理函数
|
||||
* <p> 参数:请求包装对象, 响应包装对象, 数据读写对象 </p>
|
||||
*/
|
||||
public SaCorsHandleFunction corsHandle = (req, res, sto) -> {
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### 注解操作相关策略
|
||||
|
||||
``` java
|
||||
/**
|
||||
* 对一个 [Method] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数:Method句柄 </p>
|
||||
* <p> 返回:无 </p>
|
||||
*/
|
||||
public SaCheckMethodAnnotationFunction checkMethodAnnotation = (method) -> {
|
||||
// ...
|
||||
};
|
||||
|
||||
/**
|
||||
* 对一个 [Element] 对象进行注解校验 (注解鉴权内部实现)
|
||||
* <p> 参数:element元素 </p>
|
||||
* <p> 返回:无 </p>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public SaCheckElementAnnotationFunction checkElementAnnotation = (element) -> {
|
||||
// ...
|
||||
};
|
||||
|
||||
/**
|
||||
* 从元素上获取注解
|
||||
* <p> 参数:element元素,要获取的注解类型 </p>
|
||||
* <p> 返回:注解对象 </p>
|
||||
*/
|
||||
public SaGetAnnotationFunction getAnnotation = (element, annotationClass)->{
|
||||
return element.getAnnotation(annotationClass);
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断一个 Method 或其所属 Class 是否包含指定注解
|
||||
* <p> 参数:Method、注解 </p>
|
||||
* <p> 返回:是否包含 </p>
|
||||
*/
|
||||
public SaIsAnnotationPresentFunction isAnnotationPresent = (method, annotationClass) -> {
|
||||
// ...
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* SaCheckELRootMap 扩展函数
|
||||
* <p> 参数:SaCheckELRootMap 对象 </p>
|
||||
*/
|
||||
public SaCheckELRootMapExtendFunction checkELRootMapExtendFunction = rootMap -> {
|
||||
// 默认不做任何处理
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 防火墙相关策略
|
||||
|
||||
``` java
|
||||
/**
|
||||
* 防火墙校验函数
|
||||
* <p> 参数:请求对象、响应对象、预留扩展参数 </p>
|
||||
*/
|
||||
public SaFirewallCheckFunction check = (req, res, extArg) -> {
|
||||
// ...
|
||||
};
|
||||
|
||||
/**
|
||||
* 自定义当请求 path 校验不通过时地处理方案
|
||||
* <p> 参数:防火墙校验异常、请求对象、响应对象、预留扩展参数 </p>
|
||||
*/
|
||||
SaFirewallStrategy.instance.checkFailHandle = (e, req, res, extArg) -> {
|
||||
// 自定义处理逻辑 ...
|
||||
};
|
||||
```
|
||||
|
||||
参考:[防火墙](/fun/firewall)
|
||||
|
||||
@@ -6,24 +6,20 @@
|
||||
### 1.1、token -> loginId 映射
|
||||
|
||||
``` js
|
||||
{tokenName}:{loginType}:token:{tokenValue}
|
||||
// ttl = 此 token 的 timeout 有效期
|
||||
{tokenName}:{loginType}:token:{tokenValue} --> {loginId}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>详细</summary>
|
||||
|
||||
key 示例 (ttl 为 timeout 有效期值 )
|
||||
示例:
|
||||
``` js
|
||||
satoken:login:token:47ab0105-2be1-400c-b517-82f81a0cfcf8
|
||||
```
|
||||
|
||||
|
||||
正常 value 格式
|
||||
``` js
|
||||
10001 loginId,登录id,一般为账号id
|
||||
satoken:login:token:47ab0105-2be1-400c-b517-82f81a0cfcf8 --> 10001
|
||||
```
|
||||
|
||||
异常 value 格式
|
||||
|
||||
``` js
|
||||
-1 未能从请求中读取到有效 token
|
||||
-2 已读取到 token,但是 token 无效
|
||||
@@ -40,28 +36,27 @@ satoken:login:token:47ab0105-2be1-400c-b517-82f81a0cfcf8
|
||||
### 1.2、active-timeout
|
||||
|
||||
``` js
|
||||
{tokenName}:{loginType}:last-active:{tokenValue}
|
||||
// ttl = 对应 token 的 timeout 有效期值
|
||||
{tokenName}:{loginType}:last-active:{tokenValue} --> {13位时间戳}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>详细</summary>
|
||||
|
||||
key 示例 (key 的 ttl 为 timeout 有效期值 )
|
||||
示例:
|
||||
``` js
|
||||
satoken:login:last-active:06d1f12b-614e-4c00-8d8e-c07fef5f4aa9
|
||||
satoken:login:last-active:06d1f12b-614e-4c00-8d8e-c07fef5f4aa9 --> 1722334954193
|
||||
```
|
||||
|
||||
value 格式
|
||||
value 格式分两种:
|
||||
```
|
||||
1722334954193 // 单值时:此 token 最后访问日期
|
||||
1722334954193, 1200 // 双值时:此 token 最后访问日期,此 token 指定的动态 active-timeout 值
|
||||
```
|
||||
|
||||
active-timeout 判断方式:
|
||||
注意:判断一个 token 是否 active-timeout 过期,与 ttl 无关,而是利 value 值计算:
|
||||
``` js
|
||||
当前时间 - token 最后访问时间 > active-timeout
|
||||
返回 true: 此 token 已冻结
|
||||
返回 false:此 token 未冻结
|
||||
当前时间 - token 最后访问时间 > active-timeout (true=token 已冻结,false=token 未冻结 )
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -71,9 +66,9 @@ active-timeout 判断方式:
|
||||
### 1.3、SaSession
|
||||
|
||||
``` js
|
||||
{tokenName}:{loginType}:session:{loginId} // Account-Session
|
||||
{tokenName}:{loginType}:token-session:{loginId} // Token-Session
|
||||
{tokenName}:custom:session:{sessionId} // Custom-Session
|
||||
{tokenName}:{loginType}:session:{loginId} --> {SaSession 对象} // Account-Session
|
||||
{tokenName}:{loginType}:token-session:{loginId} --> {SaSession 对象} // Token-Session
|
||||
{tokenName}:custom:session:{sessionId} --> {SaSession 对象} // Custom-Session
|
||||
```
|
||||
|
||||
<details>
|
||||
@@ -139,14 +134,14 @@ value 格式
|
||||
|
||||
### 1.4、二级认证
|
||||
``` js
|
||||
{tokenName}:{loginType}:safe:{service}:{tokenValue}
|
||||
{tokenName}:{loginType}:safe:{service}:{tokenValue} --> SAFE_AUTH_SAVE_VALUE
|
||||
```
|
||||
value 为常亮值:`SAFE_AUTH_SAVE_VALUE`
|
||||
|
||||
|
||||
### 1.5、账号服务封禁
|
||||
``` js
|
||||
{tokenName}:{loginType}:disable:{service}:{loginId}
|
||||
{tokenName}:{loginType}:disable:{service}:{loginId} --> {level}
|
||||
```
|
||||
value 为封禁等级,int类型
|
||||
|
||||
@@ -157,19 +152,19 @@ SaApplication 全局变量
|
||||
{tokenName}:var:{变量名}
|
||||
```
|
||||
|
||||
本次请求新创建 token,存储 key
|
||||
本次请求新创建 token,在 SaStorage 存储 key
|
||||
``` js
|
||||
JUST_CREATED_
|
||||
JUST_CREATED_ --> {token}
|
||||
```
|
||||
|
||||
本次请求新创建 token,存储 key (无前缀方式)
|
||||
本次请求新创建 token,在 SaStorage 存储 key (无前缀方式)
|
||||
``` js
|
||||
JUST_CREATED_NOT_PREFIX_
|
||||
JUST_CREATED_NOT_PREFIX_ --> {token}
|
||||
```
|
||||
|
||||
临时身份切换,使用的key
|
||||
``` js
|
||||
SWITCH_TO_SAVE_KEY_{loginType}
|
||||
SWITCH_TO_SAVE_KEY_{loginType} --> {loginId}
|
||||
```
|
||||
|
||||
|
||||
@@ -177,23 +172,19 @@ SWITCH_TO_SAVE_KEY_{loginType}
|
||||
|
||||
### 2.1、ticket -> loginId 映射
|
||||
``` js
|
||||
{tokenName}:ticket:{ticket}
|
||||
// ttl = 此 ticket 有效期,下同理
|
||||
{tokenName}:ticket:{ticket} --> {loginId}
|
||||
```
|
||||
值为 loginId
|
||||
|
||||
|
||||
### 2.2、ticket -> client 映射
|
||||
``` js
|
||||
{tokenName}:ticket-client:{ticket}
|
||||
{tokenName}:ticket-client:{ticket} --> {client}
|
||||
```
|
||||
值为 client
|
||||
|
||||
|
||||
### 2.3、loginId -> ticket 映射(loginId 反查 ticket)
|
||||
### 2.3、loginId -> ticket 映射(client + loginId 反查 ticket)
|
||||
``` js
|
||||
{tokenName}:id-ticket:{id}
|
||||
{tokenName}:ticket-index:{client}:{loginId} --> {ticket}
|
||||
```
|
||||
值为 ticket
|
||||
|
||||
|
||||
|
||||
@@ -201,21 +192,21 @@ SWITCH_TO_SAVE_KEY_{loginType}
|
||||
|
||||
### 3.1、Code 授权码
|
||||
``` js
|
||||
{tokenName}:oauth2:code:{code}
|
||||
{tokenName}:oauth2:code:{code} --> {CodeModel 对象}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>详细</summary>
|
||||
|
||||
值为 CodeModel
|
||||
value 示例:
|
||||
|
||||
``` js
|
||||
{
|
||||
"@class": "cn.dev33.satoken.oauth2.model.CodeModel", // java class 信息
|
||||
"code": "AbRVp2HrgyklE0BXYWszskGJWAGY7xhGu6Zaco4zJECzGYagCCFWj0jOlHza", // code值
|
||||
"scope": "", // 所申请权限列表,多个用逗号隔开
|
||||
"loginId": "10001", // 对应的loginId
|
||||
"redirectUri": "", // 重定向地址
|
||||
"@class": "cn.dev33.satoken.oauth2.model.CodeModel", // java class 信息
|
||||
"code": "AbRVp2HrgyklE0BXYWszskGJWAGY7xhGu6Zaco4zJECzGYagCCFWj0jOlHza", // code值
|
||||
"scope": "", // 所申请权限列表,多个用逗号隔开
|
||||
"loginId": "10001", // 对应的loginId
|
||||
"redirectUri": "", // 重定向地址
|
||||
}
|
||||
```
|
||||
|
||||
@@ -223,20 +214,20 @@ SWITCH_TO_SAVE_KEY_{loginType}
|
||||
|
||||
clientId + loginId 反查 code
|
||||
``` js
|
||||
{tokenName}:oauth2:code-index:{clientId}:{loginId}
|
||||
{tokenName}:oauth2:code-index:{clientId}:{loginId} --> {code 值}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 3.2、Access-Token 资源令牌
|
||||
``` js
|
||||
{tokenName}:oauth2:access-token:{accessToken}
|
||||
{tokenName}:oauth2:access-token:{accessToken} --> {AccessTokenModel 对象}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>详细</summary>
|
||||
|
||||
值为 AccessTokenModel
|
||||
value 示例:
|
||||
|
||||
``` js
|
||||
{
|
||||
@@ -277,19 +268,19 @@ clientId + loginId 反查 code
|
||||
|
||||
clientId + loginId 反查 Access-Token
|
||||
``` js
|
||||
{tokenName}:oauth2:access-token-index:{clientId}:{loginId}
|
||||
{tokenName}:oauth2:access-token-index:{clientId}:{loginId} --> {access_token 值}
|
||||
```
|
||||
|
||||
|
||||
### 3.3、Refresh-Token 资源令牌
|
||||
``` js
|
||||
{tokenName}:oauth2:refresh-token:{refreshToken}
|
||||
{tokenName}:oauth2:refresh-token:{refreshToken} --> {RefreshTokenModel 对象}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>详细</summary>
|
||||
|
||||
值为 RefreshTokenModel
|
||||
value 示例:
|
||||
|
||||
``` js
|
||||
{
|
||||
@@ -324,19 +315,19 @@ clientId + loginId 反查 Access-Token
|
||||
|
||||
clientId + loginId 反查 Refresh-Token
|
||||
``` js
|
||||
{tokenName}:oauth2:refresh-token-index:{clientId}:{loginId}
|
||||
{tokenName}:oauth2:refresh-token-index:{clientId}:{loginId} --> {refresh_token 值}
|
||||
```
|
||||
|
||||
|
||||
### 3.4、Client-Token 应用令牌
|
||||
``` js
|
||||
{tokenName}:oauth2:client-token:{clientToken}
|
||||
{tokenName}:oauth2:client-token:{clientToken} --> {ClientTokenModel 对象}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>详细</summary>
|
||||
|
||||
值为 ClientTokenModel
|
||||
value 示例:
|
||||
|
||||
``` js
|
||||
{
|
||||
@@ -368,19 +359,19 @@ clientId + loginId 反查 Refresh-Token
|
||||
|
||||
clientId 反查 Client-Token
|
||||
``` js
|
||||
{tokenName}:oauth2:client-token-index:{clientId}
|
||||
{tokenName}:oauth2:client-token-index:{clientId} --> {client_token 值}
|
||||
```
|
||||
|
||||
Lower-Client-Token 次级应用令牌索引
|
||||
``` js
|
||||
{tokenName}:oauth2:lower-client-token-index:{clientId}
|
||||
{tokenName}:oauth2:lower-client-token-index:{clientId} --> {client_token 值}
|
||||
```
|
||||
|
||||
### 3.5、用户授权记录
|
||||
``` js
|
||||
{tokenName}:oauth2:grant-scope:{clientId}:{loginId}
|
||||
{tokenName}:oauth2:grant-scope:{clientId}:{loginId} --> {scope列表}
|
||||
```
|
||||
值为 scope 列表,多个用逗号隔开
|
||||
值为 scope 列表,多个用逗号隔开,例如:`userinfo,openid,userid`。
|
||||
|
||||
|
||||
|
||||
@@ -388,21 +379,35 @@ Lower-Client-Token 次级应用令牌索引
|
||||
## 4、插件
|
||||
|
||||
### 4.1、临时 token 会话
|
||||
|
||||
temp-token -> value
|
||||
|
||||
``` js
|
||||
{tokenName}:temp-token:{service}:{token}
|
||||
// namespace 默认值为 "temp-token"
|
||||
{tokenName}:{namespace}:{temp-token} --> {value}
|
||||
```
|
||||
|
||||
value 反查 temp-token
|
||||
|
||||
``` js
|
||||
{tokenName}:raw-session:{namespace}:{value} --> {Raw#SaSession 对象}
|
||||
```
|
||||
|
||||
- 在 SaSession 以 `__HD_TEMP_TOKEN_MAP` 为 key 存储 temp-token 索引列表。值类型为 Map。
|
||||
- 其中:Map 的 key = temp-token 值,Map 的 value = 此 temp-token 到期时间戳。
|
||||
|
||||
|
||||
|
||||
### 4.2、 Same-Token
|
||||
|
||||
Same-Token
|
||||
``` js
|
||||
{tokenName}:var:same-token
|
||||
{tokenName}:var:same-token --> {same-token 值}
|
||||
```
|
||||
|
||||
Past-Same-Token
|
||||
``` js
|
||||
{tokenName}:var:past-same-token
|
||||
{tokenName}:var:past-same-token --> {same-token 值}
|
||||
```
|
||||
|
||||
|
||||
@@ -410,9 +415,59 @@ Past-Same-Token
|
||||
|
||||
随机字符串
|
||||
``` js
|
||||
{tokenName}:sign:nonce:{32位随机字符}
|
||||
// nonce 值 默认为 32位随机字符
|
||||
{tokenName}:sign:nonce:{nonce} --> {nonce 值}
|
||||
```
|
||||
|
||||
### 4.4、API Key
|
||||
|
||||
``` js
|
||||
// namespace 默认值为 "apikey" (全小写), ttl = 此 API Key 剩余有效期
|
||||
{tokenName}:{namespace}:{apikey} --> {ApiKeyModel 对象}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>详细</summary>
|
||||
|
||||
key 示例:
|
||||
|
||||
``` js
|
||||
satoken:apikey:AK-XCoJLP2E7Q9GXyPiiZWMM8Sqi6Fm0JoFC41R
|
||||
```
|
||||
|
||||
value 示例:
|
||||
``` js
|
||||
{
|
||||
"@class": "cn.dev33.satoken.apikey.model.ApiKeyModel", // java class 信息
|
||||
"title": "test", // API Key 名称
|
||||
"intro": null, // 用途介绍
|
||||
"apiKey": "AK-XCoJLP2E7Q9GXyPiiZWMM8Sqi6Fm0JoFC41R", // API Key 值
|
||||
"loginId": "10001", // 所属用户 id
|
||||
"createTime": 1766509019137, // 创建时间戳
|
||||
"expiresTime": 1769101019136, // 到期时间戳
|
||||
"isValid": true, // 是否有效
|
||||
"scopes": [ // 含有权限
|
||||
"java.util.ArrayList",
|
||||
[
|
||||
"userinfo",
|
||||
"user-update"
|
||||
]
|
||||
],
|
||||
"extraData": null // 扩展数据:Map<String, Object> 类型
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
value 反查 API Key
|
||||
|
||||
``` js
|
||||
{tokenName}:raw-session:{namespace}:{value} --> {Raw#SaSession 对象}
|
||||
```
|
||||
|
||||
- 在 SaSession 以 `__HD_API_KEY_LIST` 为 key 存储 API Key 索引列表。值类型为 List (API Key 列表)。
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
# 仓库目录介绍
|
||||
|
||||
---
|
||||
|
||||
### 1、仓库根目录介绍:
|
||||
|
||||
``` js
|
||||
── sa-token
|
||||
├── sa-token-core // [核心] Sa-Token 核心模块
|
||||
├── sa-token-dependencies // [依赖] Sa-Token 依赖版本信息
|
||||
├── sa-token-bom // [核心] Sa-Token bom 包
|
||||
├── sa-token-starter // [整合] Sa-Token 与其它框架整合
|
||||
├── sa-token-plugin // [插件] Sa-Token 插件合集
|
||||
├── sa-token-demo // [示例] Sa-Token 示例合集
|
||||
├── sa-token-test // [测试] Sa-Token 单元测试合集
|
||||
├── sa-token-doc // [文档] Sa-Token 开发文档
|
||||
├── pom.xml // [依赖] 顶级pom文件
|
||||
├── LICENSE // 开源协议
|
||||
├── mvn clean.bat // 一键 mvn clean 核心包+所有示例包
|
||||
├── mvn test.bat // 一键单元测试
|
||||
├── preview-doc.bat // 一键预览开发文档
|
||||
├── README.md // 仓库自述文件
|
||||
```
|
||||
|
||||
|
||||
### 2、所有目录详细介绍:
|
||||
|
||||
``` js
|
||||
── sa-token
|
||||
├── sa-token-core // [核心] Sa-Token 核心模块
|
||||
├── sa-token-dependencies // [依赖] Sa-Token 依赖版本信息
|
||||
├── sa-token-bom // [核心] Sa-Token bom 包
|
||||
├── sa-token-starter // [整合] Sa-Token 与其它框架整合
|
||||
├── sa-token-servlet // [整合] Sa-Token 整合 Servlet 容器实现类包
|
||||
├── sa-token-spring-boot-starter // [整合] Sa-Token 整合 SpringBoot2 快速集成
|
||||
├── sa-token-reactor-spring-boot-starter // [整合] Sa-Token 整合 SpringBoot2 Reactor 响应式编程 快速集成
|
||||
├── sa-token-jakarta-servlet // [整合] Sa-Token 整合 Jakarta-Servlet 容器实现类包
|
||||
├── sa-token-spring-boot3-starter // [整合] Sa-Token 整合 SpringBoot3 快速集成
|
||||
├── sa-token-reactor-spring-boot3-starter // [整合] Sa-Token 整合 SpringBoot3 Reactor 响应式编程 快速集成
|
||||
├── sa-token-spring-boot-autoconfig // [整合] Sa-Token 整合 SpringBoot 自动配置包
|
||||
├── sa-token-solon-plugin // [整合] Sa-Token 整合 Solon 快速集成
|
||||
├── sa-token-jfinal-plugin // [整合] Sa-Token 整合 JFinal 快速集成
|
||||
├── sa-token-jboot-plugin // [整合] Sa-Token 整合 jboot 快速集成
|
||||
├── sa-token-plugin // [插件] Sa-Token 插件合集
|
||||
├── sa-token-jackson // [插件] Sa-Token 整合 Jackson (json序列化插件)
|
||||
├── sa-token-fastjson // [插件] Sa-Token 整合 Fastjson (json序列化插件)
|
||||
├── sa-token-fastjson2 // [插件] Sa-Token 整合 Fastjson (json序列化插件)
|
||||
├── sa-token-snack3 // [插件] Sa-Token 整合 Snack3 (json序列化插件)
|
||||
├── sa-token-hutool-timed-cache // [插件] Sa-Token 整合 Hutool 缓存组件 Timed-Cache(基于内存) (数据缓存插件)
|
||||
├── sa-token-caffeine // [插件] Sa-Token 整合 Caffeine 缓存组件(基于内存) (数据缓存插件)
|
||||
├── sa-token-thymeleaf // [插件] Sa-Token 整合 Thymeleaf (自定义标签方言)
|
||||
├── sa-token-freemarker // [插件] Sa-Token 整合 Freemarker (自定义标签方言)
|
||||
├── sa-token-dubbo // [插件] Sa-Token 整合 Dubbo (RPC 调用鉴权、状态传递)
|
||||
├── sa-token-dubbo3 // [插件] Sa-Token 整合 Dubbo3 (RPC 调用鉴权、状态传递)
|
||||
├── sa-token-temp-jwt // [插件] Sa-Token 整合 jjwt (临时 Token)
|
||||
├── sa-token-sso // [插件] Sa-Token 实现 SSO 单点登录
|
||||
├── sa-token-oauth2 // [插件] Sa-Token 实现 OAuth2.0 认证
|
||||
├── sa-token-apikey // [插件] Sa-Token 实现 API Key 认证
|
||||
├── sa-token-sign // [插件] Sa-Token 实现 API 参数签名
|
||||
├── sa-token-redisson // [插件] Sa-Token 整合 Redisson (数据缓存插件)
|
||||
├── sa-token-redisx // [插件] Sa-Token 整合 Redisx (数据缓存插件)
|
||||
├── sa-token-serializer-features // [插件] Sa-Token 序列化实现扩展
|
||||
├── sa-token-redis-template // [插件] Sa-Token 整合 RedisTemplate (数据缓存插件)
|
||||
├── sa-token-redis-template-jdk-serializer // [插件] Sa-Token 整合 RedisTemplate - 使用 jdk 序列化算法 (数据缓存插件)
|
||||
├── sa-token-redis-jackson // [插件] Sa-Token 整合 RedisTemplate - 使用 Jackson 序列化算法 (数据缓存插件)
|
||||
├── sa-token-alone-redis // [插件] Sa-Token 独立 Redis 插件,实现 [ 权限缓存与业务缓存分离 ]
|
||||
├── sa-token-spring-aop // [插件] Sa-Token 整合 SpringAOP 注解鉴权
|
||||
├── sa-token-spring-el // [插件] Sa-Token 实现 SpringEL 表达式注解鉴权
|
||||
├── sa-token-grpc // [插件] Sa-Token 整合 gRPC (RPC 调用鉴权、状态传递)
|
||||
├── sa-token-quick-login // [插件] Sa-Token 快速注入登录页插件
|
||||
├── sa-token-redisson-spring-boot-starter // [插件] Sa-Token 整合 Redisson - SpringBoot 自动配置包 (数据缓存插件)
|
||||
├── sa-token-forest // [插件] Sa-Token 整合 Forest,http 请求处理器
|
||||
├── sa-token-okhttps // [插件] Sa-Token 整合 OkHttps,http 请求处理器
|
||||
├── sa-token-demo // [示例] Sa-Token 示例合集
|
||||
├── sa-token-demo-alone-redis // [示例] Sa-Token 集成 alone-redis 模块
|
||||
├── sa-token-demo-alone-redis-cluster // [示例] Sa-Token 集成 alone-redis 模块、集群模式
|
||||
├── sa-token-demo-apikey // [示例] Sa-Token API Key 模块示例
|
||||
├── sa-token-demo-async // [示例] Sa-Token 异步场景示例
|
||||
├── sa-token-demo-beetl // [示例] Sa-Token 集成 beetl 示例
|
||||
├── sa-token-demo-bom-import // [示例] Sa-Token bom 包导入示例
|
||||
├── sa-token-demo-case // [示例] Sa-Token 各模块示例
|
||||
├── sa-token-demo-device-lock // [示例] Sa-Token 设备锁登录示例 - 后端
|
||||
├── sa-token-demo-device-lock-h5 // [示例] Sa-Token 设备锁登录示例 - 前端
|
||||
├── sa-token-demo-dubbo // [示例] Sa-Token 集成 dubbo
|
||||
├── sa-token-demo-dubbo-consumer // [示例] Sa-Token 集成 dubbo 鉴权,消费端(调用端)
|
||||
├── sa-token-demo-dubbo-provider // [示例] Sa-Token 集成 dubbo 鉴权,生产端(被调用端)
|
||||
├── sa-token-demo-dubbo3-consumer // [示例] Sa-Token 集成 dubbo3 鉴权,消费端(调用端)
|
||||
├── sa-token-demo-dubbo3-provider // [示例] Sa-Token 集成 dubbo3 鉴权,生产端(被调用端)
|
||||
├── sa-token-demo-freemarker // [示例] Sa-Token 集成 Freemarker 标签方言
|
||||
├── sa-token-demo-grpc // [示例] Sa-Token 集成 grpc 鉴权
|
||||
├── client // [示例] Sa-Token 集成 grpc 鉴权,client 端
|
||||
├── server // [示例] Sa-Token 集成 grpc 鉴权,server 端
|
||||
├── sa-token-demo-hutool-timed-cache // [示例] Sa-Token 集成 hutool timed-cache
|
||||
├── sa-token-demo-caffeine // [示例] Sa-Token 集成 Caffeine
|
||||
├── sa-token-demo-jwt // [示例] Sa-Token 集成 jwt 登录认证
|
||||
├── sa-token-demo-oauth2 // [示例] Sa-Token 集成 OAuth2.0
|
||||
├── sa-token-demo-oauth2-client // [示例] Sa-Token 集成 OAuth2.0 (客户端)
|
||||
├── sa-token-demo-oauth2-client-h5 // [示例] Sa-Token OAuth2 前端测试页
|
||||
├── sa-token-demo-oauth2-server // [示例] Sa-Token 集成 OAuth2.0 (服务端)
|
||||
├── sa-token-demo-oauth2-server-h5 // [示例] Sa-Token 集成 OAuth2.0 (服务端 - 前后台分离示例)
|
||||
├── sa-token-demo-quick-login // [示例] Sa-Token 集成 quick-login 模块
|
||||
├── sa-token-demo-quick-login-sb3 // [示例] Sa-Token 集成 quick-login 模块 (SpringBoot3)
|
||||
├── sa-token-demo-remember-me // [示例] Sa-Token 实现 [ 记住我 ] 模式
|
||||
├── page_project // [示例] Sa-Token 实现 [ 记住我 ] 模式、前端页面
|
||||
├── sa-token-demo-remember-me-server // [示例] Sa-Token 实现 [ 记住我 ] 模式、后端接口
|
||||
├── sa-token-demo-solon // [示例] Sa-Token 集成 Solon
|
||||
├── sa-token-demo-solon-reisson // [示例] Sa-Token 集成 Solon、Reisson
|
||||
├── sa-token-demo-springboot // [示例] Sa-Token 整合 SpringBoot
|
||||
├── sa-token-demo-springboot3-redis // [示例] Sa-Token 整合 SpringBoot3 整合 Redis
|
||||
├── sa-token-demo-springboot-low-version // [示例] Sa-Token 整合 SpringBoot2 低版本
|
||||
├── sa-token-demo-springboot-redis // [示例] Sa-Token 整合 SpringBoot 整合 Redis
|
||||
├── sa-token-demo-springboot-redisson // [示例] Sa-Token 整合 SpringBoot 整合 redisson
|
||||
├── sa-token-demo-sse // [示例] 在 SSE 中使用 Sa-Token
|
||||
├── sa-token-demo-ssm // [示例] 在 SSM 中使用 Sa-Token
|
||||
├── sa-token-demo-sso // [示例] Sa-Token 集成 SSO 单点登录
|
||||
├── sa-token-demo-sso-server // [示例] Sa-Token 集成 SSO单点登录-Server认证中心
|
||||
├── sa-token-demo-sso1-client // [示例] Sa-Token 集成 SSO单点登录-模式一 应用端 (同域、同Redis)
|
||||
├── sa-token-demo-sso2-client // [示例] Sa-Token 集成 SSO单点登录-模式二 应用端 (跨域、同Redis)
|
||||
├── sa-token-demo-sso3-client // [示例] Sa-Token 集成 SSO单点登录-模式三 应用端 (跨域、跨Redis)
|
||||
├── sa-token-demo-sso3-client-nosdk // [示例] Sa-Token 集成 SSO单点登录-模式三 应用端 (不使用sdk,纯手动对接)
|
||||
├── sa-token-demo-sso3-client-resdk // [示例] Sa-Token 集成 SSO单点登录-模式三 应用端 (ReSdk 模式,重写部分方法对接任意技术栈)
|
||||
├── sa-token-demo-sso3-client-anon // [示例] Sa-Token 集成 SSO单点登录-模式三 应用端 (匿名应用接入示例)
|
||||
├── sa-token-demo-sso-server-h5 // [示例] Sa-Token 集成 SSO单点登录-Server认证中心 (前后端分离)
|
||||
├── sa-token-demo-sso-client-h5 // [示例] Sa-Token 集成 SSO单点登录-client应用端 (前后端分离-原生h5 版本)
|
||||
├── sa-token-demo-sso-server-vue2 // [示例] Sa-Token 集成 SSO单点登录-client应用端 (前后端分离-Vue2 版本)
|
||||
├── sa-token-demo-sso-client-vue3 // [示例] Sa-Token 集成 SSO单点登录-client应用端 (前后端分离-Vue3 版本)
|
||||
├── sa-token-demo-sso-for-solon // [示例] Sa-Token 集成 SSO 单点登录(Solon 版)
|
||||
├── sa-token-demo-sso-server-solon // [示例] Sa-Token 集成 SSO单点登录-Server认证中心
|
||||
├── sa-token-demo-sso1-client-solon // [示例] Sa-Token 集成 SSO单点登录-模式一 应用端 (同域、同Redis)
|
||||
├── sa-token-demo-sso2-client-solon // [示例] Sa-Token 集成 SSO单点登录-模式二 应用端 (跨域、同Redis)
|
||||
├── sa-token-demo-sso3-client-solon // [示例] Sa-Token 集成 SSO单点登录-模式三 应用端 (跨域、跨Redis)
|
||||
├── sa-token-demo-test // [示例] Sa-Token 整合测试项目
|
||||
├── sa-token-demo-thymeleaf // [示例] Sa-Token 集成 Thymeleaf 标签方言
|
||||
├── sa-token-demo-webflux // [示例] Sa-Token 整合 WebFlux
|
||||
├── sa-token-demo-webflux-springboot3 // [示例] Sa-Token 整合 WebFlux (SpringBoot3)
|
||||
├── sa-token-demo-websocket // [示例] Sa-Token 集成 Web-Socket 鉴权示例
|
||||
├── sa-token-demo-websocket-spring // [示例] Sa-Token 集成 Web-Socket(Spring封装版) 鉴权示例
|
||||
├── pom.xml // 示例 pom 文件,用于帮助在 idea 中一键导入所有 demo
|
||||
├── sa-token-test // [测试] Sa-Token 单元测试合集
|
||||
├── sa-token-springboot-test // [测试] Sa-Token SpringBoot 整合测试
|
||||
├── sa-token-jwt-test // [测试] Sa-Token jwt 整合测试
|
||||
├── sa-token-doc // [文档] Sa-Token 开发文档
|
||||
├── pom.xml // [依赖] 顶级pom文件
|
||||
├── LICENSE // 开源协议
|
||||
├── mvn clean.bat // 一键 mvn clean 核心包+所有示例包
|
||||
├── mvn test.bat // 一键单元测试
|
||||
├── preview-doc.bat // 一键预览开发文档
|
||||
├── README.md // 仓库自述文件
|
||||
```
|
||||
|
||||
|
||||
|
||||
其它:
|
||||
|
||||
- [sa-token-demo-cross](https://gitee.com/sa-tokens/sa-token-demo-cross):Sa-Token 处理跨域示例。
|
||||
- [sa-token-three-plugin](https://gitee.com/sa-tokens/sa-token-three-plugin):Sa-Token 第三方插件合集。
|
||||
- [sa-token-study](https://gitee.com/sa-tokens/sa-token-study):Sa-Token 涉及知识点学习。
|
||||
+134
-191
@@ -4,9 +4,11 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Sa-Token</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Sa-Token是一个java权限认证框架,功能全面,上手简单,登录认证、权限认证、Session会话、踢人下线、账号封禁、集成Redis、前后端分离、分布式会话、微服务网关鉴权、单点登录、OAuth2.0、临时Token验证、记住我模式、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、密码加密、jwt集成、Spring集成、WebFlux集成...,有了sa-token,你所有的权限认证问题,都不再是问题">
|
||||
<meta name="description"
|
||||
content="Sa-Token是一个java权限认证框架,功能全面,上手简单,登录认证、权限认证、Session会话、踢人下线、账号封禁、集成Redis、前后端分离、分布式会话、微服务网关鉴权、单点登录、OAuth2.0、临时Token验证、记住我模式、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、密码加密、jwt集成、Spring集成、WebFlux集成...,有了sa-token,你所有的权限认证问题,都不再是问题">
|
||||
<meta name="keywords" content="sa-token,sa-token框架,sa-token文档,java权限认证">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="logo.png">
|
||||
<link rel="stylesheet" href="./static/doc.css">
|
||||
<link rel="stylesheet" href="./static/vue.css">
|
||||
@@ -23,7 +25,7 @@
|
||||
</a>
|
||||
</div>
|
||||
<nav class="nav-right">
|
||||
<div class="sear-box p-none" tabindex="-1" >
|
||||
<div class="sear-box p-none" tabindex="-1">
|
||||
<!-- 加载中…… -->
|
||||
</div>
|
||||
<select class="select-version p-none" onchange="location.href=this.value">
|
||||
@@ -86,16 +88,16 @@
|
||||
<div style="height: 5px;"></div>
|
||||
<span style="background-color: #FFFFFF;"></span>
|
||||
<span style="background-color: #f5f5f5;"></span>
|
||||
<span style="background-color: #f5e5f5;"></span>
|
||||
<span style="background-color: #F1FAFA;"></span>
|
||||
<span style="background-color: #f5f5d5;"></span>
|
||||
<span style="background-color: #d5f5f5;"></span>
|
||||
|
||||
<span style="background-color: #f5e5f5;"></span>
|
||||
|
||||
<span style="background-color: #E8E8FF;"></span>
|
||||
<span style="background-color: #f0f9eb;"></span>
|
||||
<span style="background-color: #d5f5f5;"></span>
|
||||
<span style="background-color: #ebe5dd;"></span>
|
||||
<span style="background-color: #e8f4ff;"></span>
|
||||
|
||||
|
||||
<!-- <span style="background-color: #F0DAD2;"></span> -->
|
||||
<!-- <span style="background-color: #f5d5d5;"></span> -->
|
||||
<!-- <span style="background-color: #FFFFE0;"></span> -->
|
||||
@@ -106,7 +108,6 @@
|
||||
</div>
|
||||
<a class="wzi" href="index.html">首页</a>
|
||||
<a class="wzi" href="doc.html">文档</a>
|
||||
<a class="p-none wzi" href="#/more/link">案例</a>
|
||||
<div class="zk-box">
|
||||
<a class="wzi" href="javascript:;">
|
||||
<span>视频 </span>
|
||||
@@ -114,24 +115,28 @@
|
||||
</a>
|
||||
<div class="zk-context">
|
||||
<div>
|
||||
<a href="https://www.bilibili.com/video/BV1fsUVBWEyH/" target="_blank">朱老师的小课堂(7集)</a>
|
||||
<a href="https://www.bilibili.com/video/BV1NF1FBpEe6/" target="_blank">王清江唷 SSO篇(29集)</a>
|
||||
<a href="https://www.bilibili.com/video/BV1uZUpYVEst/" target="_blank">fox说技术(7集)</a>
|
||||
<a href="https://www.bilibili.com/video/BV1eFtRezERp?p=87" target="_blank">架构驿站(11集)</a>
|
||||
<a href="https://www.bilibili.com/video/BV1Zt421u7gk/" target="_blank">王清江唷(99集)</a>
|
||||
<a href="https://www.bilibili.com/video/BV1kG411o7Ms/" target="_blank">筑梦信仰-joy(20集)</a>
|
||||
<a href="https://www.bilibili.com/video/BV11u4y197JL/" target="_blank">达达-Java(26集)</a>
|
||||
<a href="https://space.bilibili.com/473679148/video" target="_blank">晒太阳的盐(22集)</a>
|
||||
<a href="https://space.bilibili.com/473679148/video" target="_blank">晒太阳的盐(22集)</a>
|
||||
<div class="zk-fengexian"></div>
|
||||
<a href="javascript: layer.alert('如您有 Sa-Token 相关课程录制,请联系官网文档右侧 < sa-token 小助手 > 进行提交');">
|
||||
<a href="doc.html#/more/content-cooperation">[ + 课程提交 ]</a>
|
||||
<!-- <a href="javascript: layer.alert('如您有 Sa-Token 相关课程录制,请联系官网文档右侧 < sa-token 小助手 > 进行提交');">
|
||||
[ + 课程提交 ]
|
||||
</a>
|
||||
</a> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="p-none wzi" href="#/more/demand-commit">需求提交</a>
|
||||
<!-- <a class="p-none wzi" href="#/more/blog">博客</a> -->
|
||||
<a class="p-none wzi" href="#/more/link">案例</a>
|
||||
<a class="p-none wzi" href="#/more/join-group">加入讨论群</a>
|
||||
<a class="p-none wzi" href="#/more/demand-commit">需求提交</a>
|
||||
<a class="p-none wzi" href="#/more/blog">博客</a>
|
||||
<a class="p-none wzi" href="#/more/sa-token-donate">赞助</a>
|
||||
<a class="p-none wzi" href="#/sso/sso-pro">🔥 SSO商业版</a>
|
||||
<a class="p-none wzi" href="#/pro/st_doc_top">🔥 SSO/OAuth2 商业版</a>
|
||||
<div class="zk-box">
|
||||
<a class="wzi" href="javascript:;">
|
||||
<span>相关资源 </span>
|
||||
@@ -161,58 +166,46 @@
|
||||
<h1 class="logo-text">Sa-Token</h1>
|
||||
</div>
|
||||
</a> -->
|
||||
|
||||
|
||||
<div class="main-box">
|
||||
<!-- 内容区 -->
|
||||
<div id="app">加载中...</div>
|
||||
|
||||
|
||||
<!-- 右边盒子 -->
|
||||
<div class="doc-right-bj-box">
|
||||
<div class="doc-right-bj-box-title">目录</div>
|
||||
<div class="doc-right-more-item">
|
||||
|
||||
|
||||
<!-- ad盒子 -->
|
||||
<div class="ad-box">
|
||||
|
||||
|
||||
<div class="ad-title">
|
||||
<span class="ad-tips">推广信息:</span>
|
||||
<span class="ad-tips ad-close">关闭</span>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ssp -->
|
||||
<div class="top-ad-box" style="margin-bottom: 12px;">
|
||||
<a href="http://sa-pro.dev33.cn?way=st_r" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/ad/sa-sso-pro-s3.png" />
|
||||
<a href="https://sa-pro.yun94.cn?way=st_r" target="_blank">
|
||||
<div class="mad-bg-box">
|
||||
<div class="mad-context-box">
|
||||
<img class="mad-img" src="/big-file/contact/sspx-ad-11.png" />
|
||||
<span class="mad-text">
|
||||
<b>轻松搭建:SSO 单点登录、OAuth2.0 统一认证、API Key、用户数据同步。全源码交付、可二开。</b>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 万维广告div -->
|
||||
<div class="wwads-cn wwads-horizontal" data-id="88" style="min-height: 0px; border: 1px #eee solid; margin-bottom: 12px;"></div>
|
||||
|
||||
<!-- ssp -->
|
||||
<div class="top-ad-box top-ad-box2" style="margin-bottom: 12px;">
|
||||
<a href="https://www.codefather.cn/vip?shareCode=qin883" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/ad/bianchengdaohang.png" style="height: 26px;" />
|
||||
</a>
|
||||
<a href="https://www.mianshiya.com/?shareCode=skmky7" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/ad/mianshiya.png" style="height: 26px;" />
|
||||
</a>
|
||||
<!-- <a href="http://sa-pro.dev33.cn?from=satop" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/ad/sa-sso-pro-s3.png" />
|
||||
</a> -->
|
||||
<!--
|
||||
<a href="http://sa-pro.dev33.cn?from=satop" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/ad/sa-sso-pro-s3.png" />
|
||||
</a>
|
||||
<a href="http://sa-pro.dev33.cn?from=satop" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/ad/sa-sso-pro-s3.png" />
|
||||
</a> -->
|
||||
</div>
|
||||
|
||||
<div class="wwads-cn wwads-horizontal" data-id="88"
|
||||
style="min-height: 0px; border: 1px #eee solid; margin-bottom: 12px;"></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- help 按钮 -->
|
||||
<div class="help-btn">❤️ 技术求助</div>
|
||||
<!-- <div class="help-btn">❤️ 技术求助</div> -->
|
||||
<!-- ew-wa -->
|
||||
<div class="ew-wa">
|
||||
<p>
|
||||
@@ -225,16 +218,16 @@
|
||||
<p>如果 Sa-Token 帮助到了你,希望你可以向同事、朋友推荐了解本框架,这对我们非常重要,感谢支持! <!-- 🤗 --> </p>
|
||||
<p>加油,工程师!</p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 万维广告div -->
|
||||
<!-- <div style="position: fixed; right: 0; bottom: 0; z-index: 10000; border: 0px #aaa solid;">
|
||||
<div class="wwads-cn wwads-vertical" data-id="88" style="max-width:150px"></div>
|
||||
</div> -->
|
||||
|
||||
|
||||
<!-- 小助手div -->
|
||||
<!-- <div class="p-none help-btn-box" style="position: fixed; right: 40px; bottom: 330px; z-index: 10000; border: 0px #aaa solid;">
|
||||
<div class="help-tips" style="position: relative; left: -30px; top: -10px;"></div>
|
||||
@@ -242,28 +235,34 @@
|
||||
<span style="font-size: 18px; color: #FFF; line-height: 60px;">Help</span>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
|
||||
|
||||
<!-- UI逐渐显现 -->
|
||||
<style type="text/css">
|
||||
body{opacity: 0.01; transition: opacity 0.5s; background-color: #FFF;}
|
||||
body {
|
||||
opacity: 0.01;
|
||||
transition: opacity 0.5s;
|
||||
background-color: #FFF;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
setTimeout(function() {
|
||||
document.body.style.opacity = 1;
|
||||
}, 1);
|
||||
</script>
|
||||
|
||||
|
||||
<!-- jqeury -->
|
||||
<script src="static/jquery.min.js"></script>
|
||||
<script src="static/layer-v3.1.1/layer.js"></script>
|
||||
|
||||
|
||||
<!-- -->
|
||||
<script src="./static/docsify-plugin.js?v=7"></script>
|
||||
<script src="./static/is-star-plugin.js?v=7"></script>
|
||||
<script src="./static/is-fill-in-wj-plugin.js?v=7"></script>
|
||||
<!-- <script src="./static/is-fill-in-wj-plugin.js?v=7"></script> -->
|
||||
<link rel="stylesheet" href="./static/custom-docsify-plugins/doc-lock-plugin.css">
|
||||
<!-- <script src="./static/custom-docsify-plugins/doc-lock-plugin.js"></script> -->
|
||||
<!-- <script src="./static/custom-docsify-plugins/doc-lock-by-gzh-plugin.js"></script> -->
|
||||
<script>
|
||||
var saTokenTopVersion = '1.44.0'; // Sa-Token最新版本
|
||||
var saTokenTopVersion = '1.44.0'; // Sa-Token最新版本
|
||||
var name = '<img style="width: 60px; height: 60px; vertical-align: middle;" src="logo.png" alt="logo" /> ';
|
||||
name += '<b style="font-size: 28px; vertical-align: middle;">Sa-Token</b> <sub>v' + saTokenTopVersion + '</sub>';
|
||||
window.$docsify = {
|
||||
@@ -290,11 +289,11 @@
|
||||
},
|
||||
// tab选项卡
|
||||
tabs: {
|
||||
persist : true, // 是否在刷新页面时重置选项卡
|
||||
sync : false, // 页面上的多个tab是否同步切换
|
||||
theme : 'classic', // 主题:'classic', 'material', false
|
||||
tabComments: true, // 用注释来标注选项卡标题,例如:<!-- tab:SpringBoot -->
|
||||
tabHeadings: true // 用标题+粗体来定制选项卡
|
||||
persist: true, // 是否在刷新页面时重置选项卡
|
||||
sync: false, // 页面上的多个tab是否同步切换
|
||||
theme: 'classic', // 主题:'classic', 'material', false
|
||||
tabComments: true, // 用注释来标注选项卡标题,例如:<!-- tab:SpringBoot -->
|
||||
tabHeadings: true // 用标题+粗体来定制选项卡
|
||||
},
|
||||
// 阅读进度
|
||||
progress: {
|
||||
@@ -304,7 +303,7 @@
|
||||
},
|
||||
// 信息提示框
|
||||
'flexible-alerts': {
|
||||
style: 'flat', // 默认风格 callout=浅色,flat=深色
|
||||
style: 'flat', // 默认风格 callout=浅色,flat=深色
|
||||
note: {
|
||||
label: {}
|
||||
},
|
||||
@@ -319,24 +318,29 @@
|
||||
},
|
||||
},
|
||||
// 自定义插件
|
||||
plugins: [myDocsifyPlugin, window.isStarPlugin, window.isFillInWjPlugin],
|
||||
plugins: [
|
||||
myDocsifyPlugin, // 基础插件
|
||||
// window.isStarPlugin, // 是否 star
|
||||
// window.isFillInWjPlugin // 问卷填写
|
||||
// window.docLockPlugin, // 章节锁
|
||||
],
|
||||
}
|
||||
</script>
|
||||
<script src="static/docsify.min.js"></script>
|
||||
<script src="static/docsify-copy-code.min.js"></script>
|
||||
|
||||
|
||||
<!-- 语言合集:https://cdn.jsdelivr.net/npm/prismjs@1/components/ -->
|
||||
<script src="static/prism/prism-java.min.js"></script>
|
||||
<script src="static/prism/prism-gradle.min.js"></script>
|
||||
<script src="static/prism/prism-yaml.min.js"></script>
|
||||
<script src="static/prism/prism-properties.min.js"></script>
|
||||
|
||||
|
||||
<!-- 文档阅读进度条 -->
|
||||
<!-- <script src="static/docsify-plugins/progress.update.js"></script> -->
|
||||
|
||||
|
||||
<!-- 右上角次级导航栏 -->
|
||||
<script src="static/docsify-plugins/sub-nav-draw.js"></script>
|
||||
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<script src="static/search.min.js"></script>
|
||||
<!-- 多 tab 切换 -->
|
||||
@@ -345,7 +349,7 @@
|
||||
<script src="static/zoom-image.min.js"></script>
|
||||
<!-- 好看的提示框 -->
|
||||
<script src="static/docsify-plugins/docsify-plugin-flexible-alerts.min-1.1.1.js"></script>
|
||||
|
||||
|
||||
<!-- docsify 里一个 md 引入另一个 md-->
|
||||
<script src="static/docsify-plugins/docsify-betterembed-1.1.1.js"></script>
|
||||
|
||||
@@ -353,41 +357,41 @@
|
||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-sidebar-collapse/dist/sidebar.min.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-sidebar-collapse/dist/docsify-sidebar-collapse.min.js"></script> -->
|
||||
|
||||
|
||||
|
||||
<!-- 渲染赞助数据 -->
|
||||
<script src="static/donate/donate-list.js"></script>
|
||||
<script src="static/donate/donate-fun.js"></script>
|
||||
|
||||
|
||||
<!-- 广告盒子 -->
|
||||
<script>
|
||||
(function(){
|
||||
(function() {
|
||||
// 功能6:标题下面的广告
|
||||
if($(window).width() >= 800) {
|
||||
if ($(window).width() >= 800) {
|
||||
// 如果一天内用户点击过关闭广告,则不再展现
|
||||
let allowJg = 1000 * 60 * 60 * 24 * 1;
|
||||
// allowJg = 1000 * 10;
|
||||
try{
|
||||
try {
|
||||
const closeAdTime = localStorage.closeAdTime;
|
||||
if(closeAdTime) {
|
||||
if (closeAdTime) {
|
||||
// 点击广告关闭的时间,和当前时间的差距
|
||||
const closeAdJg = new Date().getTime() - parseInt(closeAdTime);
|
||||
|
||||
|
||||
// 差距小于1天,不再展示
|
||||
if(closeAdJg < allowJg) {
|
||||
if (closeAdJg < allowJg) {
|
||||
console.log('not show ad ...');
|
||||
$('.ad-box').remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
|
||||
// 添加关闭事件
|
||||
$('.ad-close').click(function(){
|
||||
$('.ad-close').click(function() {
|
||||
console.log('关闭广告');
|
||||
// $('.top-ad-box').slideUp(); // 折叠收起
|
||||
layer.confirm('关闭后,一天内不再展现此信息', function(){
|
||||
layer.confirm('关闭后,一天内不再展现此信息', function() {
|
||||
$(".ad-box").fadeOut(1000); // 淡出效果
|
||||
layer.msg('关闭成功');
|
||||
localStorage.closeAdTime = new Date().getTime();
|
||||
@@ -396,7 +400,7 @@
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<!-- 搜索引擎自动提交 -->
|
||||
<script>
|
||||
(function() {
|
||||
@@ -411,32 +415,33 @@
|
||||
s.parentNode.insertBefore(bp, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<!-- 万维广告 -->
|
||||
<script data-mode="hash" type="text/javascript" src="https://cdn.wwads.cn/js/makemoney.js" async></script>
|
||||
|
||||
|
||||
<!-- 百度统计 -->
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?35ad501304eae758ac6139a22a9830f5";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
// 预览版提示
|
||||
if(location.host === 'rc.sa-token.cc') {
|
||||
const newTips = '<b>当前文档为RC预览版文档,仅做学习测试使用,正式项目请使用正式版:<a href="https://sa-token.cc/" target="_blank">https://sa-token.cc/</a></b>';
|
||||
if (location.host === 'rc.sa-token.cc') {
|
||||
const newTips =
|
||||
'<b>当前文档为RC预览版文档,仅做学习测试使用,正式项目请使用正式版:<a href="https://sa-token.cc/" target="_blank">https://sa-token.cc/</a></b>';
|
||||
layer.alert(newTips);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<!-- 小助手提示 -->
|
||||
<script>
|
||||
$('.help-btn').click(function(){
|
||||
$('.help-btn').click(function() {
|
||||
var str = `
|
||||
<div class="xiaozhushou-intro">
|
||||
<h2>报错了?搞不懂?别急、莫慌</h2>
|
||||
@@ -455,13 +460,13 @@
|
||||
<p style="margin-top: 30px;">打开方式:</p>
|
||||
<p>1、如果你是使用 PC 端微信,请点此链接:<a href="https://work.weixin.qq.com/kfid/kfcdd45c432fee9655f" target="_blank">https://work.weixin.qq.com/kfid/kfcdd45c432fee9655f</a></p>
|
||||
<p>2、如果你是使用手机端微信,请扫码:</p>
|
||||
<p><img src="https://oss.dev33.cn/sa-token/sa-token-xiaozhushou.jpg" width="200px"></p>
|
||||
<p><img src="/big-file/contact/sa-token-xiaozhushou.jpg" width="200px"></p>
|
||||
<p>如果您的问题已解决,我们希望您能够花费一点时间将解决方案发布在:<a href="https://gitee.com/dromara/sa-token/issues/I9I9CY" target="_blank">踩坑记录征集</a>,帮助以后遇到同样问题的开发者快速排查,感激不尽!🌹🌹🌹</p>
|
||||
</div>
|
||||
`;
|
||||
layer.alert(str, {
|
||||
title: '技术求助',
|
||||
area: '680px',
|
||||
title: '技术求助',
|
||||
area: '680px',
|
||||
offset: '7%',
|
||||
})
|
||||
})
|
||||
@@ -478,120 +483,58 @@
|
||||
// console.error(e);
|
||||
// }
|
||||
// }, 500)
|
||||
|
||||
</script>
|
||||
|
||||
<!-- 修改背景颜色 -->
|
||||
<script>
|
||||
|
||||
// 绑定修改背景色的按钮事件
|
||||
$('.theme-box span').click(function() {
|
||||
let bgColor = this.style.backgroundColor;
|
||||
setBg(bgColor);
|
||||
localStorage.setItem('bg-color-value', bgColor)
|
||||
})
|
||||
// 读取上次记录
|
||||
let bgColor = localStorage.getItem('bg-color-value');
|
||||
if(bgColor) {
|
||||
setBg(bgColor);
|
||||
}
|
||||
|
||||
// 设置背景颜色
|
||||
function setBg(bgColor) {
|
||||
console.log('---- 背景颜色设定为:', bgColor);
|
||||
|
||||
// -------- 设置 body 背景
|
||||
document.body.style.backgroundColor = bgColor;
|
||||
|
||||
// -------- 设置 header 头背景
|
||||
// 如果是 16 进制,转 rgba
|
||||
if(bgColor.indexOf('#') == 0) {
|
||||
bgColor = hexToRgba(bgColor, 0.97);
|
||||
}
|
||||
// 如果是 rgb,转 rgba
|
||||
else if(bgColor.match(/\,/g).length == 2) {
|
||||
bgColor = bgColor.replace(')', ' ,0.97)');
|
||||
}
|
||||
|
||||
document.querySelector('.doc-header').style.backgroundColor = bgColor;
|
||||
}
|
||||
|
||||
// 16进制 转 rgba
|
||||
function hexToRgba(str, a){
|
||||
a = a || 1;
|
||||
|
||||
var reg = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/
|
||||
if(!reg.test(str)){return;}
|
||||
let newStr = (str.toLowerCase()).replace(/\#/g,'')
|
||||
let len = newStr.length;
|
||||
if(len == 3){
|
||||
let t = ''
|
||||
for(var i=0;i<len;i++){
|
||||
t += newStr.slice(i,i+1).concat(newStr.slice(i,i+1))
|
||||
}
|
||||
newStr = t
|
||||
}
|
||||
let arr = []; //将字符串分隔,两个两个的分隔
|
||||
for(var i =0;i<6;i=i+2){
|
||||
let s = newStr.slice(i,i+2)
|
||||
arr.push(parseInt("0x" + s))
|
||||
}
|
||||
return 'rgb(' + arr.join(",") + ', ' + a + ')';
|
||||
}
|
||||
|
||||
</script>
|
||||
<!-- 修改背景颜色 水滴波纹特效 -->
|
||||
<link rel="stylesheet" href="static/water-change-theme/water-change-theme.css" />
|
||||
<script src="static/water-change-theme/gsap-3.12.2.min.js"></script>
|
||||
<script src="static/water-change-theme/water-change-theme.js"></script>
|
||||
|
||||
|
||||
<!-- 赞助页的展开和收缩 -->
|
||||
<script>
|
||||
// 展开
|
||||
function expandZanZhu(){
|
||||
function expandZanZhu() {
|
||||
$('.zk-btn--1').hide();
|
||||
$('.zk-btn--2').show();
|
||||
$('.zanzhu-box').height($('.zanzhu-box table').height());
|
||||
}
|
||||
// 折叠
|
||||
function foldZanZhu(){
|
||||
function foldZanZhu() {
|
||||
$('.zanzhu-box').height(500);
|
||||
$('.zk-btn--2').hide();
|
||||
$('.zk-btn--1').show();
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 赞助效果图展示 -->
|
||||
|
||||
<!-- 自定义滚动条颜色 -->
|
||||
<!-- <style>
|
||||
/* 自定义body滚动条样式 */
|
||||
body::-webkit-scrollbar { width: 8px; }
|
||||
/* 滚动条颜色 */
|
||||
body::-webkit-scrollbar-thumb { background-color: green; border-radius: 3px; }
|
||||
/* 滚动条上面的和下面的颜色 */
|
||||
body::-webkit-scrollbar-track {
|
||||
background: linear-gradient(to bottom,
|
||||
#42B983 0%,
|
||||
#42B983 var(--scroll-progress, 0%),
|
||||
#FCFCFC var(--scroll-progress, 0%),
|
||||
#FCFCFC 100%);
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function showSyzz(){
|
||||
layer.photos({
|
||||
photos: {
|
||||
title: '',
|
||||
id: new Date().getTime(),
|
||||
start: 0,
|
||||
data: [
|
||||
{
|
||||
pid: 1,
|
||||
alt: 'gitee 开源项目推广,日增 star 74',
|
||||
src: 'https://oss.dev33.cn/sa-token/more/syzz-xg-1.png',
|
||||
},
|
||||
{
|
||||
pid: 2,
|
||||
alt: 'gitee 开源项目推广,日增 star 247',
|
||||
src: 'https://oss.dev33.cn/sa-token/more/syzz-xg-2.png',
|
||||
},
|
||||
{
|
||||
pid: 3,
|
||||
alt: '某互联网公司线下活动,报名邀请',
|
||||
src: 'https://oss.dev33.cn/sa-token/more/syzz-xg-3.png',
|
||||
},
|
||||
{
|
||||
pid: 4,
|
||||
alt: 'gitee 开源项目推广,日增 star 70',
|
||||
src: 'https://oss.dev33.cn/sa-token/more/syzz-xg-4.png',
|
||||
}
|
||||
]
|
||||
}
|
||||
,anim: 5 //0-6的选择,指定弹出图片动画类型,默认随机(请注意,3.0之前的版本用shift参数)
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
// 动态更新滚动条颜色
|
||||
window.addEventListener('scroll', function() {
|
||||
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
const scrollHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
|
||||
const scrollProgress = (scrollTop / scrollHeight) * 100;
|
||||
|
||||
document.body.style.setProperty('--scroll-progress', scrollProgress + '%');
|
||||
});
|
||||
// 初始化滚动条状态
|
||||
window.dispatchEvent(new Event('scroll'));
|
||||
</script> -->
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
### Sa-Token 功能结构图:
|
||||
|
||||

|
||||
<img class="s-w" src="/big-file/doc/fun/sa-token-js4--2.png" alt="sa-token-rz" />
|
||||
|
||||
### Sa-Token 认证流程图:
|
||||
|
||||

|
||||
<img class="s-w" src="/big-file/doc/fun/sa-token-rz2.png" alt="sa-token-rz" />
|
||||
|
||||
<!--  -->
|
||||
|
||||
PS:鼠标右键选择 **`[在新窗口打开图片]`** 即可高清模式查看图片。(本流程图使用 [ProcessOn](https://www.processon.com) 绘制完成)
|
||||
PS:鼠标右键选择 **`[在新窗口打开图片]`** 即可高清模式查看图片。
|
||||
|
||||
@@ -713,13 +713,13 @@ public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
// 加载角色信息
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
return Arrays.asList("admin", "super-admin", "ceo");
|
||||
}
|
||||
|
||||
// 加载权限信息
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
return Arrays.asList("user:add", "user:delete", "user:update");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,4 +2,8 @@
|
||||
|
||||
<!-- 参考:[https://blog.csdn.net/shengzhang_/article/details/119928794](https://blog.csdn.net/shengzhang_/article/details/119928794) -->
|
||||
|
||||
参考:[https://juejin.cn/post/7491603065944129590](https://juejin.cn/post/7491603065944129590)
|
||||
参考1: [https://juejin.cn/post/7491603065944129590](https://juejin.cn/post/7491603065944129590)
|
||||
|
||||
参考2: [https://mp.weixin.qq.com/s/tbqjCKrTMj-l1lZbeyu81g](https://mp.weixin.qq.com/s/tbqjCKrTMj-l1lZbeyu81g)
|
||||
|
||||
参考3: [https://mp.weixin.qq.com/s/8aziIhqGCb_qsr8kLiqzmg](https://mp.weixin.qq.com/s/8aziIhqGCb_qsr8kLiqzmg)
|
||||
@@ -16,11 +16,11 @@ public String test() {
|
||||
|
||||
从浏览器访问此接口,我们可以看到:
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/fun/test-curr-domain.png" alt="test-curr-domain.png" />
|
||||
|
||||
此 API 在本地开发时一般可以正常工作,然而如果我们在部署时使用 Nginx 做了一层反向代理后,其最终结果可能会和我们预想的有一点偏差:
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/fun/test-curr-domain-fxdl.png" alt="test-curr-domain-fxdl.png" />
|
||||
|
||||
不仅是 Nginx,所有包含路由转发的地方都有可能导致上述丢失 uri 的现象,解决方案也很简单,既然程序无法自动识别,我们改成手动获取即可,Sa-Token 提供两个方案:
|
||||
|
||||
@@ -29,7 +29,7 @@ public String test() {
|
||||
|
||||
##### 1、首先在 Nginx 代理转发的地方增加参数
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/fun/nginx-add-header.png" alt="nginx-add-header.png" />
|
||||
|
||||
重点是这一句:`proxy_set_header Public-Network-URL http://$http_host$request_uri;`
|
||||
|
||||
|
||||
+12
-12
@@ -3,12 +3,12 @@
|
||||
2. 滑动右侧页面滑块, 查看页面内容最下方, 评论区上方
|
||||
3. 找到这一行文字
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/online_1.png" alt="在线编辑提示" />
|
||||
|
||||
4. 点击Gitee或GitHub按钮中的任意一个, 国内用户推荐使用 [Gitee](https://gitee.com) (请先注册登录后再往下浏览)
|
||||
5. 此时会进入当前页面源码预览页面,找到下方按钮组
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/online_2.png" alt="按钮组" />
|
||||
|
||||
6. 点击编辑按钮
|
||||
7. 此时进入待修改页面的源码页面, 按照markdown格式编辑为需要的结果(Ctrl+P可查看最终效果,再次按下可恢复源码界面)
|
||||
@@ -36,16 +36,16 @@ git config --list
|
||||
1. 点击[Gitee](https://gitee.com/dromara/sa-token)或[Github](https://github.com/dromara/sa-token)进入Sa-Token项目主页, 以下以Gitee为例,Github类似(请先注册登录后再往下浏览)
|
||||
2. 找到页面右上角的按钮组, 点击Forked按钮
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/code_1.png" alt="按钮组" />
|
||||
|
||||
3. 选择个人仓库并点击确认
|
||||
4. 此时在你的个人仓库中会多了一个Sa-Token项目
|
||||
5. 在新的Sa-Token项目中, 点击  按钮, 点击弹出框里面的复制按钮
|
||||
5. 在新的Sa-Token项目中, 点击 <img src="/big-file/doc/fun/code_2.png" alt="克隆/下载" /> 按钮, 点击弹出框里面的复制按钮
|
||||
6. 在本地某空文件夹下右键选择: git bash here
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/code_4.png" alt="git bash" />
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/code_3.png" alt="git bash 打开后的图" />
|
||||
|
||||
14. 在里面输入如下命令, 按换行后自动下载整个项目
|
||||
|
||||
@@ -61,18 +61,18 @@ git clone 这里替换为复制后的链接
|
||||
### 方式一
|
||||
1. 在idea中打开项目进入Commit选项
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/code_5.png" alt="本地暂存" />
|
||||
|
||||
2. 勾选需要本地暂存的文件
|
||||
3. 在同一页面的下方输入提示信息
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/code_6.png" alt="提示信息" />
|
||||
|
||||
4. 点击Commit按钮暂存到本地, 点击Commit and Push按钮暂存之后提交到远程
|
||||
### 方式二
|
||||
1. 除了点击Commit and Push按钮外,还有一个地方可以提交git
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/code_7.png" alt="git按钮" />
|
||||
|
||||
2. 位置在idea右上方的工具栏里面
|
||||
3. 指向左下箭头为拉取项目,可以随时更新
|
||||
@@ -82,11 +82,11 @@ git clone 这里替换为复制后的链接
|
||||
1. 提交后进入Gitee个人仓库中克隆的Sa-Token项目
|
||||
2. 找到下图的Pull Request按钮
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/code_8.png" alt="工具栏" />
|
||||
|
||||
3. 点击提交, 进入如下页面
|
||||
|
||||

|
||||
<img class="s-width" src="/big-file/doc/fun/code_9.png" alt="提交信息填写页面" />
|
||||
|
||||
4. 在这里,你可以选择要提交的分支,一般都是dev开发分支.可以填写合并信息,其他测试审查之类的可以不填写, 最后点击创建即可完成一次提交.
|
||||
|
||||
@@ -94,7 +94,7 @@ git clone 这里替换为复制后的链接
|
||||
1. 有时候主项目更新了,之前克隆的项目代码陈旧,如何处理?
|
||||
2. 在个人仓库的Sa-Token项目主页面中, 找到下图的圆圈
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/code_10.png" alt="更新按钮" />
|
||||
|
||||
3. 点击右侧圆圈按钮后Gitee会自动同步主项目, 这样就不用像我之前一样,删除项目又重新fork了.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
解决这个问题的关键就在于 `SaTokenContext` 接口,此接口的作用是屏蔽掉不同 Web 框架之间的差异,提供统一的调用API:
|
||||
|
||||

|
||||
<img class="s-w" src="/big-file/doc/fun/sa-token-context.svg" alt="sa-token-context" />
|
||||
|
||||
|
||||
SaTokenContext只是一个接口,没有工作能力,这也就意味着 SaTokenContext 接口的实现是必须的。
|
||||
|
||||
@@ -60,7 +60,7 @@ public static void setContext(HttpServletRequest request, HttpServletResponse re
|
||||
|
||||
Sa-Token 为了一套代码对接所有的 Web 框架,就在原生请求对象的基础上又封装了一层 `SaTokenContext` 相关接口,用于屏蔽掉不同 Web 框架之间的差异,提供统一的调用API:
|
||||
|
||||

|
||||
<img src="/big-file/doc/fun/sa-token-context-2.svg" alt="sa-token-context" />
|
||||
|
||||
因此,要对接不同的 Web 框架,就要针对不同的 Web 框架封装不同版本的 `SaRequest`、`SaResponse`、`SaStorage` 包装类对象。
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ session.updateTimeout(1000); // 参数说明和全局有效期保持一致
|
||||
|
||||
**假设三个客户端登录同一账号,且配置了不共享token,那么此时的Session模型是:**
|
||||
|
||||

|
||||
<img class="s-w" src="/big-file/doc/fun/session-model3.png" alt="session-model" />
|
||||
|
||||
简而言之:
|
||||
- `Account-Session` 以账号 id 为主,只要 token 指向的账号 id 一致,那么对应的Session对象就一致
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
---
|
||||
|
||||
QQ群库经常有小伙伴提问:项目需要搭建统一认证中心,是用 SSO 方便还是 OAuth2.0 方便呢?针对这个问题,我们列出两者的主要区别以供大家参考:
|
||||
QQ群里经常有小伙伴提问:项目需要搭建统一认证中心,是用 SSO 方便还是 OAuth2.0 方便呢?针对这个问题,我们列出两者的主要区别以供大家参考:
|
||||
|
||||
|
||||
| 功能点 | SSO单点登录 | OAuth2.0 |
|
||||
|
||||
+11
-11
@@ -12,17 +12,17 @@
|
||||
<th>个人主页</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-xiaofengzheng.jpg" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-xiaofengzheng.jpg" /></td>
|
||||
<td>小风筝(作者)</td>
|
||||
<td><a href="https://gitee.com/click33" target="_blank">https://gitee.com/click33</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-AppleOfGray.png" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-AppleOfGray.png" /></td>
|
||||
<td>AppleOfGray</td>
|
||||
<td><a href="https://gitee.com/appleOfGray" target="_blank">https://gitee.com/appleOfGray</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-ly-chn.png" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-ly-chn.png" /></td>
|
||||
<td>ly-chn</td>
|
||||
<td><a href="https://gitee.com/ly-chn" target="_blank">https://gitee.com/ly-chn</a></td>
|
||||
</tr>
|
||||
@@ -41,42 +41,42 @@
|
||||
<th>个人主页</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-xiaofengzheng.jpg" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-xiaofengzheng.jpg" /></td>
|
||||
<td>刘潇</td>
|
||||
<td><a href="https://gitee.com/click33" target="_blank">https://gitee.com/click33</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-AppleOfGray.png" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-AppleOfGray.png" /></td>
|
||||
<td>AppleOfGray</td>
|
||||
<td><a href="https://gitee.com/appleOfGray" target="_blank">https://gitee.com/appleOfGray</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-ly-chn.png" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-ly-chn.png" /></td>
|
||||
<td>ly-chn</td>
|
||||
<td><a href="https://gitee.com/ly-chn" target="_blank">https://gitee.com/ly-chn</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-moli.jpg" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-moli.jpg" /></td>
|
||||
<td>茉莉</td>
|
||||
<td><a href="https://gitee.com/kidoldman" target="_blank">https://gitee.com/kidoldman</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-yaoshui.jpg" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-yaoshui.jpg" /></td>
|
||||
<td>药水</td>
|
||||
<td><a href="https://gitee.com/java_pioneer" target="_blank">https://gitee.com/java_pioneer</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-daimouren.png" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-daimouren.png" /></td>
|
||||
<td>呆某人</td>
|
||||
<td><a href="https://gitee.com/zhubj0510" target="_blank">https://gitee.com/zhubj0510</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-chunkunqiufa.jpg" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-chunkunqiufa.jpg" /></td>
|
||||
<td>春困夏倦秋乏</td>
|
||||
<td><a href="https://gitee.com/uncarbon97" target="_blank">https://gitee.com/uncarbon97</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oss.dev33.cn/sa-token/team/avatar-danmo.jpg" /></td>
|
||||
<td><img src="/big-file/doc/team/avatar-danmo.jpg" /></td>
|
||||
<td>淡墨</td>
|
||||
<td><a href="https://gitee.com/jinan-jimeng-network_0" target="_blank">https://gitee.com/jinan-jimeng-network_0</a></td>
|
||||
</tr>
|
||||
|
||||
+365
-271
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@
|
||||
由于`jwt`模式不在服务端存储数据,对于比较复杂的业务可能会功能受限,因此更加推荐使用方案三
|
||||
|
||||
|
||||
<button class="show-img" img-src="https://oss.dev33.cn/sa-token/doc/g/g3--dcs-session.gif">加载动态演示图</button>
|
||||
<button class="show-img" img-src="/big-file/doc/micro/g3--dcs-session.gif">加载动态演示图</button>
|
||||
|
||||
|
||||
集成依赖示例:
|
||||
|
||||
@@ -18,6 +18,9 @@ Sa-Token提供两种解决方案:
|
||||
|
||||
本篇主要讲解方案二 `Same-Token` 模块的整合步骤,其鉴权流程与 OAuth2.0 类似,不过使用方式上更加简洁(希望使用方案一的同学可参考Sa-OAuth2模块,此处不再赘述)
|
||||
|
||||
<img class="w-100" src="/big-file/doc/micro/micro-network-isolation.svg" alt="Same-Token_同源系统认证.svg" />
|
||||
|
||||
|
||||
### 二、网关转发鉴权
|
||||
|
||||
##### 1、引入依赖
|
||||
@@ -201,7 +204,12 @@ public interface SpCfgInterface {
|
||||
|
||||
Same-Token —— 专门解决同源系统互相调用时的身份认证校验,它的作用不仅局限于微服务调用场景
|
||||
|
||||
基本使用流程为:服务调用方获取Token,提交到请求中,被调用方取出Token进行校验:Token一致则校验通过,否则拒绝服务
|
||||
基本使用流程为:服务调用方获取 Same-Token,提交到请求中,被调用方取出 Same-Token 进行校验:如果一致则校验通过,否则拒绝服务。
|
||||
|
||||
<img class="w-100" src="/big-file/doc/micro/micro-same-token.svg" alt="Same-Token_同源系统认证.svg" />
|
||||
|
||||
|
||||
|
||||
|
||||
首先我们预览一下此模块的相关API:
|
||||
``` java
|
||||
|
||||
+143
-3
@@ -1,14 +1,154 @@
|
||||
# 框架博客
|
||||
|
||||
> 此页面收集 Sa-Token 相关技术文章,欢迎大家投稿(不限平台,按照发表日期倒序),
|
||||
> [投稿链接](https://wj.qq.com/s2/10596458/aa96/)
|
||||
> 此页面收集 Sa-Token 相关技术文章(不限平台,按照发表日期倒序),
|
||||
> 如果你也想投稿,请考虑加入:[Sa-Token 内容合作群 ](/more/content-cooperation)
|
||||
|
||||
---
|
||||
|
||||
- [[ CSDN ] JAVA:Spring Boot3 集成 Sa-Token 轻量级权限认证](https://shdxhl.blog.csdn.net/article/details/157695326) (2026-2-27)
|
||||
|
||||
- [[ 公众号 ] 苦 Spring Security 久矣?这款霸榜 Gitee 的权限框架,把优雅做到了极致](https://mp.weixin.qq.com/s/CzBPkeV6jWZ7mpA_6JH09g) (2026-2-27)
|
||||
|
||||
- [[ 公众号 ] 架构师推荐开源项目:轻量级Java权限认证框架!](https://mp.weixin.qq.com/s/OZtTqYIZNU2l_yyKFvbd9A) (2026-2-24)
|
||||
|
||||
- [[ 公众号 ] Sa-Token(一)之简介及入门:告别鉴权内耗,让每一位Java开发者都能轻松上手](https://mp.weixin.qq.com/s/HLG1PHnbfOTpC3e-tGutew) (2026-2-24)
|
||||
|
||||
- [[ CSDN ] Sa-Token SSO 前后端分离实战:SpringBoot + Vue2 单点登录全流程解析](https://blog.csdn.net/weixin_29291863/article/details/158324193) (2026-2-24)
|
||||
|
||||
- [[ CSDN ] RefreshToken反查踩坑记:Sa-Token 1.42.0临时令牌管理新姿势](https://blog.csdn.net/weixin_28327051/article/details/158301601) (2026-2-23)
|
||||
|
||||
- [[ 公众号 ] SpringBoot3 + Sa-Token 单点登录|30分钟上手,代码可复制,新手零踩坑](https://mp.weixin.qq.com/s/2jN9HotfYttLFMzHE54XiA) (2026-2-21)
|
||||
|
||||
- [[ 公众号 ] Sa-Token Session会话:三种模型彻底搞懂,不再傻傻分不清](https://mp.weixin.qq.com/s/gpqogF0QyahuqJIqhs6DUg) (2026-2-21)
|
||||
|
||||
- [[ 公众号 ] SpringBoot3 + Sa-Token 双Token登录认证实战(避坑版)](https://mp.weixin.qq.com/s/LDSCSZYuUIkQA91MYXhnGQ) (2026-2-20)
|
||||
|
||||
- [[ CSDN ] SaToken实战:5分钟搞定微信小程序登录功能(附完整代码)](https://blog.csdn.net/weixin_29218509/article/details/158226233) (2026-2-20)
|
||||
|
||||
- [[ 公众号 ] 告别SpringSecurity!Sa-Token+Gateway+Nacos极简鉴权实战](https://mp.weixin.qq.com/s/lFcH7XyLRtaNH6q4-TzHbQ) (2026-2-19)
|
||||
|
||||
- [[ 公众号 ] SpringSecurity、Shiro和Sa-Token,哪个更好?](https://mp.weixin.qq.com/s/BEvk1ohFntL7iorDEvqIpg) (2026-2-18)
|
||||
|
||||
- [[ CSDN ] Sa-Token 1.42.0实战:5分钟搞定API Key权限隔离与TOTP双因子认证](https://blog.csdn.net/weixin_29271053/article/details/158175327) (2026-2-18)
|
||||
|
||||
- [[ CSDN ] SaToken权限注解全解析:@SaCheckPermission和@SaCheckRole的20种实战用法](https://blog.csdn.net/weixin_28454475/article/details/158163447) (2026-2-18)
|
||||
|
||||
- [[ 公众号 ] 5 分钟上手 Sa-Token:Spring Boot 权限认证从未如此简单](https://mp.weixin.qq.com/s/2iRMPQdfBEgqSgeld3crXw) (2026-2-17)
|
||||
|
||||
- [[ 公众号 ] SpringSecurity、Shiro和Sa-Token,哪个更好?](https://mp.weixin.qq.com/s/QdR1tyIXN8GvWbhN48XTjA) (2026-2-13)
|
||||
|
||||
- [[ 公众号 ] sa-token前后端分离集成redis与jwt基础案例](https://mp.weixin.qq.com/s/c1UYdxuRjWudGnpQa_A72w) (2026-2-11)
|
||||
|
||||
- [[ 公众号 ] Sa-Token(一)之简介及入门:告别鉴权内耗,让每一位Java开发者都能轻松上手](https://mp.weixin.qq.com/s/JLQSMAgqK1U0vtrdtRsKlA) (2026-2-11)
|
||||
|
||||
- [[ 公众号 ] Sa-Token 的极简设计哲学](https://mp.weixin.qq.com/s/Yr48InNXxaVkNfVRbllO7w) (2026-2-10)
|
||||
|
||||
- [[ 公众号 ] 还在用 @PreAuthorize?聊聊我切换到 Sa-Token 路由拦截后的真实体感](https://mp.weixin.qq.com/s/VMtSZDC1AFquCKRakTxytw) (2026-2-10)
|
||||
|
||||
- [[ 公众号 ] Sa-Token 实战进阶:从“能用”到“好用”的企业级鉴权方案](https://mp.weixin.qq.com/s/hOY37lIxw01aPvjmdTf6VQ) (2026-2-9)
|
||||
|
||||
- [[ 公众号 ] Sa-Token:把“登录/鉴权/踢人/SSO/OAuth2”做成一套顺手的 Java 权限方案(附 Spring Boot 快速上手)](https://mp.weixin.qq.com/s/zT8iRNuFfOEqDZfhVFy15w) (2026-2-9)
|
||||
|
||||
- [[ 公众号 ] 开源、免费、一站式 java 权限认证框架,让鉴权变得简单、优雅!](https://mp.weixin.qq.com/s/FLDwIXHQoa6V2nKPs1r4cw) (2026-2-9)
|
||||
|
||||
- [[ 公众号 ] Sa-Token 注解鉴权](https://mp.weixin.qq.com/s/72oLlgj-x8oetUpIJhR02A) (2026-2-9)
|
||||
|
||||
- [[ 公众号 ] 用户投诉账号异常登录,CTO 让我 5 分钟内解决](https://mp.weixin.qq.com/s/pfMSZLxmDKIVYoq6UGUj1Q) (2026-2-4)
|
||||
|
||||
- [[ 公众号 ] Sa-Token实战:SpringBoot与微服务权限认证极简方案](https://mp.weixin.qq.com/s/yNma6FhHvPLNHUqYFkdPCg) (2026-1-25)
|
||||
|
||||
- [[ 公众号 ] Sa-Token过期机制](https://mp.weixin.qq.com/s/gFQ8YJT1yg5pTm8Z3uDZHw) (2026-1-22)
|
||||
|
||||
- [[ 公众号 ] 别再写死权限了!SpringBoot + Sa-Token 实现 RBAC 的最佳姿势](https://mp.weixin.qq.com/s/ZwzAInOoqiQ2h0ogWaOoQg) (2026-1-14)
|
||||
|
||||
- [[ 公众号 ] 集成sa-token跨域正确姿势](https://mp.weixin.qq.com/s/tbqjCKrTMj-l1lZbeyu81g) (2026-1-9)
|
||||
|
||||
- [[ 公众号 ] 后端开发必看:最简单的 Java 登录认证框架 Sa-Token 上手指南](https://mp.weixin.qq.com/s/Kk9HEVAG43-FPikiMZKWJw) (2026-1-8)
|
||||
|
||||
- [[ 公众号 ] Sa-Token:一站式权限认证解决方案的实战指南](https://mp.weixin.qq.com/s/FVkn-3CqWT8dNM6a5kD2oA) (2025-12-30)
|
||||
|
||||
- [[ 公众号 ] SpringSecurity、Shiro和Sa-Token,哪个更好?](https://mp.weixin.qq.com/s/gtQ7_n9cPJd2-i_Qm-jk1A) (2025-12-28)
|
||||
|
||||
- [[ 公众号 ] SpringBoot + JWT + Sa-Token:认证鉴权双框架对比,安全登录与权限控制最佳实践](https://mp.weixin.qq.com/s/SDPKdmxtwb4MbOHfg-bF8Q) (2025-12-27)
|
||||
|
||||
- [[ 公众号 ] 一行代码搞定认证](https://mp.weixin.qq.com/s/UaAw1WdVtumA44SxjFW3yg) (2025-12-24)
|
||||
|
||||
- [[ 掘金 ] Netty + Sa-Token 实现 WebSocket 握手认证](https://juejin.cn/post/7585490245006950406) (2025-12-21)
|
||||
|
||||
- [[ 公众号 ] 《第27节》SpringBoot+SaToken实现鉴权功能](https://mp.weixin.qq.com/s/mY_jrQL1dG2yis51rX2i9w) (2025-12-16)
|
||||
|
||||
- [[ 公众号 ] 告别 Spring Security!Sa-Token + Gateway + Nacos 极简鉴权实战](https://mp.weixin.qq.com/s/s36bdkhi5ACGN2j7hLiD7w) (2025-12-15)
|
||||
|
||||
- [[ 公众号 ] 《第26节》SpringBoot3+SaToken实现用户注册登录功能](https://mp.weixin.qq.com/s/u7nVa0PJcFWx-9xKq1RNhA) (2025-12-13)
|
||||
|
||||
- [[ 公众号 ] 《第25节》SpringBoot3之集成sa-token权限认证框架](https://mp.weixin.qq.com/s/sxgzLqiKCf4_fqWAxN8Ozg) (2025-12-12)
|
||||
|
||||
- [[ 掘金 ] sa-token前后端分离集成redis与jwt基础案例](https://juejin.cn/post/7576843726011645978) (2025-11-26)
|
||||
|
||||
- [[ 公众号 ] Sa-Token 1.44.0:Java权限认证的“轻量级王者”,让鉴权优雅如诗](https://mp.weixin.qq.com/s/UprusTkp9LZOH9TJDTKJRw) (2025-11-20)
|
||||
|
||||
- [[ 公众号 ] sa-token-rust 项目:高性能的 Rust 认证授权框架](https://mp.weixin.qq.com/s/jlQAX1K1M64DtUgrHrDX1A) (2025-11-17)
|
||||
|
||||
- [[ 公众号 ] 不会吧,居然还有人没有用过?全网爆火的权限校验框架 Sa-Token 超详细教程它来了](https://mp.weixin.qq.com/s/kNYq0MmlYB_0tRWI-HvU1g) (2025-11-6)
|
||||
|
||||
- [[ 公众号 ] 若依框架集成 Sa-Token 实现权限认证与会话管理](https://mp.weixin.qq.com/s/JAgL0hxcPeP0E4OW4oy8Yg) (2025-10-21)
|
||||
|
||||
- [[ 公众号 ] 太强了!Sa-Token 的 Go 版本!](https://mp.weixin.qq.com/s/idfrMeAMY2CeGAZGY9csmw) (2025-10-20)
|
||||
|
||||
- [[ 公众号 ] 太强了!Sa-Token 的 rust 版本!](https://mp.weixin.qq.com/s/CveVq368Dz5Xw-a2nT3YDw) (2025-10-12)
|
||||
|
||||
- [[ 公众号 ] 功能最全的Java权限认证框架](https://mp.weixin.qq.com/s/fO5Mm1UIN8oDOwvbq-sQTw) (2025-10-10)
|
||||
|
||||
- [[ 公众号 ] 从 0 到 1!Sa-Token 与 SpringBoot 整合教程,让鉴权优雅到飞起](https://mp.weixin.qq.com/s/PudodYBsIQODdfeweo39fQ) (2025-10-16)
|
||||
|
||||
- [[ 公众号 ] 一篇搞定!SpringBoot 搭建超安全 Sa-Token 登录鉴权系统](https://mp.weixin.qq.com/s/5fbrNS6jMpuViPPO8z_kMw) (2025-10-3)
|
||||
|
||||
- [[ 公众号 ] 《Spring Cloud Gateway 从入门到实战》第4篇:安全与认证 —— 基于 Sa-Token 的网关统一鉴权方案](https://mp.weixin.qq.com/s/qeWQufIDNtGPyJ0b7yF4vQ) (2025-9-29)
|
||||
|
||||
- [[ 公众号 ] SpringBoot整合Sa-Token实现认证与鉴权](https://mp.weixin.qq.com/s/l4OjqdeNpXjMyFCSr3PuWw) (2025-9-25)
|
||||
|
||||
- [[ 公众号 ] Spring Gateway、Sa-Token、Nacos 认证/鉴权方案](https://mp.weixin.qq.com/s/JpXtI75eANwkRAppQZJG9Q) (2025-9-17)
|
||||
|
||||
- [[ 公众号 ] 告别 Spring Security!Sa-Token + Gateway + Nacos 极简鉴权实战](https://mp.weixin.qq.com/s/hlBH1H6vX-KIlQGmeY8bIg) (2025-9-15)
|
||||
|
||||
- [[ 公众号 ] Spring Gateway、Sa-Token、Nacos 认证/鉴权方案,yyds!](https://mp.weixin.qq.com/s/OYsxjEmLfkxH0NqZidb4fA) (2025-9-10)
|
||||
|
||||
- [[ 公众号 ] Ruoyi-vue-plus-5.x第一篇Sa-Token权限认证体系深度解析:1.4 Sa-Token高级特性实现](https://mp.weixin.qq.com/s/0Fex83DyngC-mxIu346X1Q) (2025-8-30)
|
||||
|
||||
- [[ 公众号 ] Ruoyi-vue-plus-5.x第一篇Sa-Token权限认证体系深度解析:1.3 权限控制与注解使用](https://mp.weixin.qq.com/s/f4iVDeIAZ-nixqR6BNtUfg) (2025-8-30)
|
||||
|
||||
- [[ 公众号 ] Ruoyi-vue-plus-5.x第一篇Sa-Token权限认证体系深度解析:1.2 登录认证机制详解](https://mp.weixin.qq.com/s/gu5kT93WjEauut7xoXmuag) (2025-8-29)
|
||||
|
||||
- [[ 公众号 ] Ruoyi-vue-plus-5.x第一篇Sa-Token权限认证体系深度解析:1.1 Sa-Token框架基础](https://mp.weixin.qq.com/s/w8c5fvaap7ipMu2OdXNZng) (2025-8-29)
|
||||
|
||||
- [[ 公众号 ] 告别 Spring Security!Sa-Token + Gateway + Nacos 极简鉴权实战](https://mp.weixin.qq.com/s/5nmEDAsFgEWk-Ymn_pE74w) (2025-8-22)
|
||||
|
||||
- [[ 公众号 ] 搭建基于sa-token 的网关权限管理系统](https://mp.weixin.qq.com/s/LM6g3QaklSHpaVJXUz5Gvg) (2025-7-19)
|
||||
|
||||
- [[ 公众号 ] 别再被 Spring Security 和 Shiro 劝退了!这款国产 Java 权限框架真香!](https://mp.weixin.qq.com/s/2C0WSlM8zpjqQDtgv59Kaw) (2025-7-1)
|
||||
|
||||
- [[ 公众号 ] 一文精通Java集成Sa-Token实现SSO单点登录](https://mp.weixin.qq.com/s/-fex5XFm4wmTmuzZCtUiRw) (2025-5-22)
|
||||
|
||||
- [[ 公众号 ] 47.8k star,一款接私活神器,10分钟搞定企业级鉴权!](https://mp.weixin.qq.com/s/KRz3-h6etPaKOwHN4Xz7qg) (2025-5-15)
|
||||
|
||||
- [[ 公众号 ] Sa-Token:17.5k Star!轻量级Java权限认证框架,登录鉴权超简单](https://mp.weixin.qq.com/s/akPTgU8sQkwWmXm4GNSyyQ) (2025-5-11)
|
||||
|
||||
- [[ 公众号 ] SaToken-微服务认证与授权](https://mp.weixin.qq.com/s/9WYg6iLDST7YDSSsCt1NxQ) (2025-4-3)
|
||||
|
||||
- [[ 公众号 ] SpringBoot 整合 Sa-Token 快速实现 API 接口签名安全校验](https://mp.weixin.qq.com/s/2NS3axN1CbRYULHrv5Du2w) (2025-3-20)
|
||||
|
||||
- [[ 公众号 ] SaToken 简化开发的身份认证与权限管理框架](https://mp.weixin.qq.com/s/Yxsswl4Zn8244j4XeV8_VA) (2025-1-24)
|
||||
|
||||
- [[ 公众号 ] 使用 Sa-Token 平替 Spring Security,告别繁琐的认证与鉴权!](https://mp.weixin.qq.com/s/whKQPm09ApzkVBjlSwO2Tg) (2025-1-15)
|
||||
|
||||
- [[ 公众号 ] sa-token之@SaIgnore注解失效的真正原因及正确姿势](https://mp.weixin.qq.com/s/c6eckHp2M4oz2x3Hea6pGg) (2025-1-14)
|
||||
|
||||
- [[ 公众号 ] SpringBoot3.x+Vue3+Sa-Token实现登录认证](https://mp.weixin.qq.com/s/0GkDoOYW8KKxTfzV83J6UA) (2024-12-27)
|
||||
|
||||
- [[ 公众号 ] 万字雄文:一次说清基于Sa-Token和MaxKey的统一认证中心实现](https://mp.weixin.qq.com/s/Gl2K47F9I6-Il-AieWLplw) (2024-11-15)
|
||||
|
||||
<!-- 2026-2-3 搜集至 2024-11-15 公众号平台 -->
|
||||
|
||||
- [[ 公众号 ] sa-token之@SaIgnore注解失效的真正原因及正确姿势](https://mp.weixin.qq.com/s/c6eckHp2M4oz2x3Hea6pGg) (2025-1-15)
|
||||
|
||||
- [[ 公众号 ] 集成sa-token前后端分离部署配置corsFliter解决跨域失效的真正原因](https://mp.weixin.qq.com/s/bSS4vmKlKM7ov_CUkjxkBg) (2024-07-08)
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# Sa-Token 内容合作群
|
||||
|
||||
**好内容值得被看见!**
|
||||
|
||||
为感谢 Sa-Token 的内容创作者们,我们特别创建了「Sa-Token 内容合作群」,帮助大家的内容触达更多 Sa-Token 的使用者 (加群方式在最下方)。
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 📖 1、一些碎碎念,想和写公众号/录视频的朋友们聊聊
|
||||
|
||||
前几天,我在公众号上搜索 “Sa-Token”,想看看有没有人写过相关的教程或者踩坑心得。
|
||||
|
||||
说实话,当时没抱太大期望。毕竟我们开发团队这几年来几乎将所有精力都放在了代码开发,而一直疏于内容运营建设。
|
||||
|
||||
但结果让我挺意外的 —— **我发现了不少公众号都在写 Sa-Token 的文章,而且其中不少都写得很用心**。
|
||||
|
||||
有从零开始的入门教程,有深入源码的解析文章,有对框架各个功能的介绍,还有一些结合真实业务场景的实战案例 …… 解决的都是实实在在的问题。
|
||||
|
||||
但这些文章的阅读量… 很多都只有几百,有些甚至只有几十。
|
||||
|
||||
我知道这很正常。**技术公众号起步非常不容易,粉丝少的时候,再好的内容也很难被看到**。我自己也经历过这个阶段,知道那种 [ 写了一整天,发出来没人看 ] 的感觉。
|
||||
|
||||
为了不让这些有价值的内容埋没,我连夜将这些文章整理到了 Sa-Token 官网:[框架博客](/more/blog)。
|
||||
|
||||
在整理这些博客的过程中,我突然有了一个想法。
|
||||
|
||||
|
||||
### ✨ 2、一个可能 [三赢] 的想法
|
||||
|
||||
我在想,我为什么不拉一个群聊,把这些为 Sa-Token 写文章的博主们,给聚集起来呢?
|
||||
|
||||
只要有朋友写了 Sa-Token 相关文章,都可以转发到群里,我们团队会把这个文章转发到 Sa-Token 所有粉丝群里:
|
||||
|
||||
这可能是一个三赢的合作:
|
||||
|
||||
- **对于 Sa-Token 来说**:能获得更多的优质内容,帮助新用户更快上手,生态也能更丰富。
|
||||
- **对写文章的朋友来说**:你的好文章能被更多人看到,公众号能涨涨粉,付出的时间更有价值。
|
||||
- **对 Sa-Token 的用户来说**:能看到更多的技术干货,学到更多知识,找到各种场景的解决方案,不用重复踩坑。
|
||||
|
||||
听起来好像…还不错?
|
||||
|
||||
所以我打算建个群,名字就叫 「Sa-Token 内容合作群」。不是什么正式的组织,就是一群对技术内容感兴趣的朋友,凑在一起互相帮帮忙。
|
||||
|
||||
|
||||
### 🤝 3、这个群主要用来做什么?
|
||||
|
||||
1、如果你写了 Sa-Token 相关的文章(或录制了视频课程),可以分享到群里,我们团队会把文章:
|
||||
|
||||
- **转发到 Sa-Token 所有粉丝群里**:Sa-Token 目前拥有 30+ 微信粉丝群 (500人),10+ QQ粉丝群 (1000人 or 2000人)。
|
||||
- **挂载到 Sa-Token 在线文档博客栏目**:Sa-Token 目前在线文档访问量月PV 20万+。
|
||||
|
||||
相信这一定可以大大提高文章的曝光量。
|
||||
|
||||
2、我们团队偶尔也会为 Sa-Token 撰写技术文章,发到群里:
|
||||
|
||||
- 如果你觉得内容不错,想转载到自己的公众号,**直接转就行**。
|
||||
- 不用专门申请授权,Sa-Token 官方订阅号所有内容均开放版权,任何人都可以自由转载。
|
||||
|
||||
|
||||
### ❤️ 4、几个你可能关心的小问题
|
||||
|
||||
#### Q:我没写过 Sa-Token 的文章,可以加入吗?
|
||||
可以。完全没问题。哪怕你之前从来没写过 Sa-Token 的文章,但只要你有公众号,想试试写相关的内容,都欢迎。
|
||||
|
||||
如果你没有公众号,但是在别的平台,比如掘金、CSDN有写过文章,也可以加入。如果你在 B站/抖音录制过视频,也可以加入。
|
||||
|
||||
总之:只要你有意向在任意平台创作 Sa-Token 相关内容,就可以加入。
|
||||
|
||||
|
||||
#### Q:有 KPI 吗?是不是进了群就要为 Sa-Token 写文章?
|
||||
没有。 想写就写,不想写就不写。哪怕加群后一篇都不写,也没关系。**这就是个「互助群」,不是「任务群」**。
|
||||
|
||||
#### Q:文章有什么要求吗?
|
||||
就一点:认真写。
|
||||
可以是入门教程、源码解析、实战心得、bug排查、常见踩坑、对比测评…什么形式都行。不需要多长的篇幅,能把一个知识点讲清楚就好。唯一的要求是:不要是纯 AI 生成的粗制滥造文,不要写明显错误的技术观点。
|
||||
|
||||
#### Q:对粉丝量有要求吗?
|
||||
没有要求。 我自己也是从小号做起来的,完全理解起步的难处。群里不分大号小号,只看内容用不用心。
|
||||
|
||||
#### Q:除了转发,还有别的吗?
|
||||
有。 我偶尔会分享一些 Sa-Token 的更新动态、设计思路,或者我发现的其他好的技术文章。大家也可以互相**聊聊技术写作、视频制作的心得,分享好用的工具**。就是个普通的交流群,只不过主题稍微聚焦一点。
|
||||
|
||||
说到底,我希望这个群不只是一个内容分发渠道,更是一个 「Sa-Token内容共创伙伴」的聚集地。我们一起,让好的技术方案被更多人看见和使用。
|
||||
|
||||
|
||||
### 👋 5、怎么加群?
|
||||
|
||||
你可以在 [加入讨论群](/more/join-group) 处,添加我们的微信账号, **请在添加时备注或者加好友成功后发送以下信息:[申请加入 Sa-Token 内容合作群]** 。
|
||||
|
||||
一定要备注以上信息,否则我们团队人员只会把你拉入到普通粉丝交流群。
|
||||
|
||||
<img class="s-w" src="/big-file/contact/show/wx-sthzq-show.png" style="max-width: 50%;" alt="Sa-Token 生态合作群" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,29 +6,32 @@
|
||||
|
||||
### 1、加入QQ交流群
|
||||
|
||||
<!--  -->
|
||||
|
||||
<!-- 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交流群:936523917 [点击加入](https://qm.qq.com/q/xfoMJA5Az0) -->
|
||||
QQ交流群:823181187 [点击加入](https://qm.qq.com/q/EBIJVZBVGE)
|
||||
QQ交流群:1081649142 [点击加入](https://qm.qq.com/q/SCAaZ6Ros2)
|
||||
|
||||
### 2、加入微信交流群:
|
||||
|
||||
<!--  -->
|
||||
<!-- <img class="s-w" src="/big-file/contact/wx-qr-300.png" style="width: 180px;" alt="微信群" /> -->
|
||||
|
||||

|
||||
<img class="s-w" src="/big-file/contact/i-wx-qr2.jpg" style="width: 180px;" alt="微信群" />
|
||||
|
||||
(扫码添加微信,备注:sa,邀您加入群聊)
|
||||
PS:扫码添加微信 (备注:sa),邀您加入群聊。
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
<img class="s-w" src="/big-file/contact/show/wx-group-show3.png" style="max-width: 50%;" alt="微信群" />
|
||||
|
||||
|
||||
加入群聊的好处:
|
||||
- 第一时间收到框架更新通知。
|
||||
- 第一时间收到框架 bug 通知。
|
||||
- 第一时间收到新增开源案例通知。
|
||||
- 和众多大佬一起互相 (huá shuǐ) 交流 (mō yú)。
|
||||
- 和众多大佬一起互相 (huá shuǐ) 交流 (mō yú) 🖐️🐟️。
|
||||
|
||||
|
||||
|
||||
### 3、群规(碎碎念):
|
||||
@@ -67,5 +70,6 @@ QQ群聊号码:939849926
|
||||
(例如:xx开源项目作者集成了 sa-token,申请加入群聊),如果字数太多无法写完,也可在开源交流群里@管理员协助交流。
|
||||
|
||||
|
||||
|
||||
### 5、Sa-Token 内容合作群
|
||||
专门为 Sa-Token 内容创作者们准备的交流群:[Sa-Token 内容合作群](/more/content-cooperation)
|
||||
|
||||
|
||||
+87
-11
@@ -1,12 +1,31 @@
|
||||
# 使用 Sa-Token 的开源项目
|
||||
|
||||
|
||||
> 集成 Sa-Token 的开源案例收集,取自 [Awesome-Sa-Token](https://gitee.com/sa-tokens/awesome-sa-token),定期同步。
|
||||
> 集成 Sa-Token 的开源案例收集,取自 Awesome-Sa-Token,定期同步:
|
||||
> [Gitee](https://gitee.com/sa-tokens/awesome-sa-token)、
|
||||
> [GitHub](https://github.com/sa-tokens/awesome-sa-token)、
|
||||
> [AtomGit](https://atomgit.com/sa-tokens/awesome-sa-token)
|
||||
|
||||
---
|
||||
|
||||
|
||||
### 后台管理
|
||||
### 📊 后台管理
|
||||
|
||||
- [[ art-design-pro-java ]](https://github.com/anganing/art-design-pro-java):SpringBoot17+Sa-token+Art-Design-Pro+Unibest 技术栈的企业级后台开发管理系统。
|
||||
|
||||
- [[ wemirr-platform ]](https://gitee.com/battcn/wemirr-platform):JDK17、SCA2023、SC2024、Sa-Token、VBen5.x 全网最炫酷,功能最多,最优雅地真开源 多租户、SAAS 微服务项目。
|
||||
|
||||
- [[ Lucky-Admin-Vue ]](https://gitee.com/xiaodu6/lucky-admin-vue):一个基于vue-admin-template的后台管理框架,集成了动态角色权限,动态路由,角色权限动态配置,日志框架,代码生成,Sa-Token权限校验,快速构建一个后台的开发框架。
|
||||
|
||||
- [[ 灯灯]](https://github.com/dromara/lamp-cloud):基于java + SpringCloudAlibaba +SpringBoot 开发的微服务中后台快速开发平台,专注于多租户 (SaaS架构) 解决方案,亦可作为普通项目(非SaaS架构)的基础开发框架使用,目前已实现 数据源隔离、字段隔离、无租户隔离 等几种模式。
|
||||
|
||||
- [[ 橙单 ]](https://gitee.com/orangeform/orange-admin):技术栈Boot3 + Flowable7 + Sa-Token + Mybatis-Flex/Mybatis-Plus + Vue3,支持开箱即用且功能完成的工作流和在线表单功能,提供高颜值的流程和表单编辑器全部前后端源码。
|
||||
|
||||
- [[ Sz-Admin ]](https://github.com/feiyuchuixue):一个开源RBAC中后台框架,专为现代应用设计。它结合了最新的技术栈,包括后端的Spring Boot 3、JDK 21、Mybatis Flex、Sa-Token、Knife4j和Flyway,以及前端的Vue 3、Vite5、TypeScript和Element Plus,致力于为您提供一个直观、流畅且功能强大的开发体验。
|
||||
|
||||
- [[ newbie-boot3 ]](https://github.com/zhangyuge7/newbie-boot3):企业级中大型项目快速开发平台,后端使用JDK21+SpringBoot3+SaToken+MybatisPlus等,前端基于FiveAdminV2后台管理系统模板开发,使用js+vue3+vite5+ElementPlus等最新技术栈。
|
||||
|
||||
- [[ EuBackend ]](https://gitee.com/zhaoeryu/eu-backend):EuBackend 是一套全部开源的前后端分离 Java EE 企业级快速开发平台,基于最新技术栈SpringBoot、Sa-Token、MyBatisPlus等作为后端框架,使用RBAC作为权限控制模型,并且毫无保留给个人及企业免费使用。
|
||||
|
||||
- [[ srppms ]](https://gitee.com/cai-bin00/srppms):基于SpringBoot+Vue+sa-token前后端分离的科研项目管理平台。
|
||||
|
||||
@@ -28,8 +47,6 @@
|
||||
|
||||
- [[ vue-satoken-admin ]](https://gitee.com/niluni/vue-satoken-admin):基于Vue2和Sa-Token1.18.0的后台权限系统。
|
||||
|
||||
- [[ 人事管理系统后端 ]](https://gitee.com/sdones_1512/personnel-management-system-back-end):人事管理系统后端,框架:springboot,持久层:mybatis,缓存:redis,权限:sa-token
|
||||
|
||||
- [[ bootx-platform ]](https://gitee.com/bootx/bootx-platform):包含支付收单(支付宝、微信、聚合、组合支付)、工作流(Flowable)、三方对接(微信、钉钉、企微、短信)等模块,前端基于Vue2和Vue3分别打造,可应用在不同业务场景中,目标是致力实现媲美商业版应用脚手架。
|
||||
|
||||
- [[ spba-admin ]](https://gitee.com/qkdja/spring-boot-admin):基于SpringBoot、Vue开发的通用后台管理系统,做到开箱即用,为新项目开发省去了基础功能开发的步骤。主要使用Sa-Token权限认证、MyBatis-Plus、MySQL、Redis、validation、七牛云等技术。
|
||||
@@ -38,7 +55,7 @@
|
||||
|
||||
- [[ ExciteCMS-Layui ]](https://gitee.com/ExciteTeam/ExciteCMS-SpringBoot-Layui):ExciteCMS 快速开发脚手架:一款后端基于 SpringBoot2 + Sa-Token + Mybatis-Plus,前端基于 Layuimini 的内容管理系统,具备RBAC、日志管理、代码生成等功能,并集成常用的支付、OSS等第三方服务,拥有详细的开发文档
|
||||
|
||||
- [[ sss-rbac-admin ]](https://gitee.com/momoljw/sss-rbac-admin):基于springboot,整合satoken、sqltoy的权限管理系统
|
||||
- [[ sra-admin ]](https://github.com/CoCoTeaNet/sra-admin):快速开发脚手架,核心依赖:springboot3+sqltoy+satoken+hutool | 轻量级 | 只实现了用户、字典、角色、权限等常见功能,能够快速搭建一个web项目。
|
||||
|
||||
- [[ QuickBuild ]](https://gitee.com/CodeLiQing/custom-quick-build-platform): 快速构建 | 基于springboot+sa-token+neety+代码生产器(生成vue页面和增删改查代码)| 以及前端vue3和字节arco.design框架整合
|
||||
|
||||
@@ -58,7 +75,7 @@
|
||||
|
||||
- [[ RuoYi-Vue-CMS ]](https://gitee.com/liweiyi/RuoYi-Vue-CMS):RuoYi-Vue-CMS是前后端分离的内容管理系统,支持站群管理、多平台静态化、元数据模型扩展、多语言、全文检索,能轻松组织各种复杂内容形态。技术栈:SpringBoot3 + VUE2 + MybatisPlus + Sa-Token + xxl-job + Freemarker + ES + Redis + MySQL。
|
||||
|
||||
- [[ springboot-multi-tenant-sa-token ]](https://gitee.com/willf/springboot-multi-tenant-sa-token):轻量的多租户权限管理系统脚手架(SpringBoot,Sa-Token,mybatis-plus,Vue & Element)。
|
||||
- [[ springboot-multi-tenant-sa-token ]](https://gitee.com/willf/springboot-multi-tenant-sa-token):轻量的多租户后台管理系统脚手架(SpringBoot,Sa-Token,mybatis-plus,Vue & Element)。
|
||||
|
||||
- [[ solon_angis_beetlsql ]](https://gitee.com/smartcity/solon_angis_beetlsql):并元国产开发平台 solon、sa-token、beetlsql、smart-http
|
||||
|
||||
@@ -66,8 +83,29 @@
|
||||
|
||||
- [[ nebula-swagger-demo ]](https://gitee.com/flgitee/nebula-swagger-demo):springboot+nebula 集成knife4j案例
|
||||
|
||||
- [[ warm-sun]](https://gitee.com/min290/warm-sun):基于solon+vue3开发,jdk17+satoken+redisx/redisson+mybaits-flex+hutool+jackson+mapstruct+poi
|
||||
|
||||
### 微服务相关
|
||||
- [[ContiNew Admin]](https://gitee.com/Charles7c/continew-admin):ContiNew Admin 中后台管理框架/脚手架,Continue New Admin,持续以最新流行技术栈构建,拥抱变化,迭代优化。当前采用的技术栈:Vue3、TypeScript、Arco Design Vue、Spring Boot3(JDK17)、Undertow、Sa-Token、JWT、MariaDB、MyBatis Plus、Redis、Redisson、Easy Excel、Hutool 等。
|
||||
|
||||
- [[laymini-admin]](https://gitee.com/wlf213/laymini-admin):基于layuimini前端框架开发的一个简单的后台管理前后端不分离框架,主体技术mybatisplus+sa_token+springboot+freemarker,主要功能:RABC认证授权,后台管理功能,集成Quartz动态定时任务。
|
||||
|
||||
- [[ Smart-Admin ]](https://gitee.com/lab1024/smart-admin):SmartAdmin国内首个以「高质量代码」为核心,「简洁、高效、安全」中后台快速开发平台;基于SpringBoot + Sa-Token + Mybatis-Plus 和 Vue3 + Vite5 + Ant Design Vue 4.x (同时支持JavaScript和TypeScript双版本);满足国家三级等保要求、支持登录限制、接口数据国产加解密、高防SQL注入等一系列安全体系。
|
||||
|
||||
- [[ Halcyon-Admin ]](https://github.com/hhfb8848/halcyon-springboot):基于 Spring Boot 3 和 Vue 3 的通用后台管理系统,专注于提供基本的管理功能,而非特定的部门管理或业务功能。
|
||||
|
||||
- [ breeze-boot-satoken-xxx系统 ]:breeze-boot-satoken-xxx 是一个开源免费(前后端分离)中后台管理系统基础解决方案,前端技术栈:( Vue3、 TypeScript、Element Plus、Pinia 、Vite)后端技术栈:(jdk17、 springboot3、SaToken、MybatisPlus等)
|
||||
- SSO 版本,后端:https://gitee.com/breeze-boot/breeze-boot-satoken-sso
|
||||
- SSO 版本,前端:https://gitee.com/breeze-boot/breeze-vite-ui-satoken-sso
|
||||
- OAUTH 版本,后端:https://gitee.com/breeze-boot/breeze-boot-satoken-oauth
|
||||
- OAUTH 版本,前端: https://gitee.com/breeze-boot/breeze-vite-ui-satoken-oauth
|
||||
- [[ Summer-Flowers · 夏花 ]](https://gitee.com/Luv404/summer-flowers):基于 **Spring Boot 3 + JPA + QueryDSL + Sa-Token** 的企业级后台开发框架,前端采用 **SoybeanAdmin**。不同于常见 MyBatis 体系,Summer-Flowers 以 **Entity 作为业务第一表达**,通过 QueryDSL 实现类型安全的复杂查询,配合代码生成器与模块化架构,显著降低中长期项目的维护成本。
|
||||
|
||||
|
||||
|
||||
|
||||
### 🚀 微服务相关
|
||||
|
||||
- [[ XHan Admin ]](https://gitee.com/sun-xiaohan/xh-admin-frontend):XHan Admin 是一个开源免费(前后端分离)中后台管理系统基础解决方案, 无专业版收费,所有功能毫无保留的贡献给开源社区,使用最新技术栈全新开发,无任何历史代码包袱。
|
||||
|
||||
- [[ RuoYi-Cloud-Plus ]](https://gitee.com/dromara/RuoYi-Cloud-Plus):重写RuoYi-Cloud所有功能 整合 SpringCloudAlibaba + Sa-Token + Dubbo + Mybatis-Plus + Xxl-Job 全方位升级 定期同步
|
||||
|
||||
@@ -93,8 +131,12 @@
|
||||
|
||||
- [[ quick-boot ]](https://github.com/csx-bill/quick-boot):一款基于 Spring Cloud 2022 、Spring Boot 3、AMIS 和 APIJSON 的低代码系统。
|
||||
|
||||
- [[ linkin-platform ]](https://gitee.com/paohaizi/linkin-platform):Springboot + Springcloud + nacos + Mybatis Plus + Sa-Token + Vue3 + ElementPlus
|
||||
微服务下使用Sa-Token的样例,是一套比较简洁的后台系统。
|
||||
|
||||
### 商城
|
||||
- [[ LangChat ]](https://github.com/TyCoding/langchat):( OpenAI / Gemini / Ollama / Azure / 智谱 / 阿里通义大模型 / 百度千帆大模型), Java生态下AI大模型产品解决方案,快速构建企业级AI知识库、AI机器人应用
|
||||
|
||||
### 🛒 商城
|
||||
|
||||
- [[ litemall-plus ]](https://gitee.com/ysling-org/litemall-plus):微信小程序SaaS商城系统,可支持多小程序同时运行。
|
||||
|
||||
@@ -103,7 +145,7 @@
|
||||
- [[ Huanxing-mall ]](https://gitee.com/lijiaxing_boy/huanxing-mall):HuanXing 商城基于SpringCloud 2021 & Alibaba + Sa-token,前端基于 Vue3 +Element plus 的微服务商城
|
||||
|
||||
|
||||
### 博客
|
||||
### 📝 博客
|
||||
|
||||
- [[ jthink ]](https://gitee.com/wtsoftware/jthink): 一个基于 SpringBoot + Sa-Token + Thymeleaf 的博客系统
|
||||
|
||||
@@ -111,17 +153,34 @@
|
||||
|
||||
- [[ June 12 ]](https://gitee.com/hanshaung/ants):June 12 是一个纯开源免费的资讯/博客类网站,基于Spring Boot + Sa-Token + Vue开发。
|
||||
|
||||
- [[ YuanBlog ]](https://gitee.com/wlf213/yuan-blog):一款代码简单,功能丰富的多人社交博客平台。前后端分离,Vue+SpringBoot3,博客前端使用Quasar,后台管理前端使用NaiveUI,博客后端,后台管理后端分为两个系统,均使用Sa-Token进行认证授权。支持邮箱验证码登录。
|
||||
|
||||
- [[ 鸢尾博客 ]](https://gitee.com/lxwise/iris-blog_parent):鸢尾博客是一个基于Spring Boot+Vue3 + TypeScript + Vite+JavaFx的客户端和服务器端的博客系统。项目采用前端与后端分离,支持移动端自适应,配有完备的前台和后台管理功能。后端使用Sa-Token进行权限管理,支持动态菜单权限,服务健康监控,数据流量统计,支持QQ、微博、码云、GitHub等三方登录。
|
||||
|
||||
|
||||
### 插件
|
||||
|
||||
### 🔌 插件
|
||||
|
||||
- [[ Sa-Token-Plugin ]](https://gitee.com/bootx/sa-token-plugin):Sa-Token第三方插件实现,基于Sa-Token-Core,提供一些与官方不同实现机制的的插件集合,作为Sa-Token开源生态的补充
|
||||
|
||||
- [[ quarkus-sa-token ]](https://github.com/quarkiverse/quarkus-sa-token): quarkus 整合 Sa-Token。
|
||||
|
||||
|
||||
### 🌐 多语言
|
||||
|
||||
### 其它
|
||||
- Rust:[[ sa-token-rust ]](https://github.com/sa-tokens/sa-token-rust): 一个轻量级、高性能的 Rust 认证授权框架。
|
||||
|
||||
- Go:[[ sa-token-go ]](https://github.com/sa-tokens/sa-token-go): 一个轻量级、高性能的 Go 权限认证框架。
|
||||
|
||||
- PHP:[[ real-token ]](https://gitee.com/jinan-jimeng-network_0/real-token): 一个轻量级 thinkphp6 权限认证框架,让鉴权变得简单、优雅!
|
||||
|
||||
|
||||
|
||||
### 📦 其它
|
||||
|
||||
- [[ Glowxq-OJ ]](https://github.com/glowxq/glowxq-oj):Glowxq-OJ 专业开源在线编程测评系统 | 基于Spring Boot 3.x + Java 21 + Vue 3构建 | 支持ACM/ICPC竞赛、信奥赛训练、编程教育 | 多语言判题、实时竞赛、在线IDE | Docker一键部署 | Modern Online Judge Platform for Competitive Programming & Coding Education。
|
||||
|
||||
- [[ FlyFlow ]](https://gitee.com/junyue/flyflow):基于SaToken开发的开源工作流系统:FlyFlow借鉴了钉钉与飞书的界面设计理念,致力于打造一款用户友好、快速上手的工作流程工具。
|
||||
|
||||
- [[ Sa-Token-Study ]](https://gitee.com/sa-tokens/sa-token-study):以demo示例的方式讲解 Sa-Token 源码涉及到的技术点,连载中……
|
||||
|
||||
@@ -130,3 +189,20 @@
|
||||
- [[ iot-kit ]](https://gitee.com/iotkit-open-source/iotkit-parent):一个轻量级低门槛的物联网平台,包含了多协议设备接入、规则引擎、第三方平台接入、智能家居小程序等模块的项目,基于SpringBoot架构并集成了Sa-Token的OAuth2认证。
|
||||
|
||||
- [[ cubic ]](https://gitee.com/dromara/cubic):一站式问题定位平台,实时线程栈监控、线程池监控、动态arthas命令集、依赖分析等等等,助你快速定位问题。
|
||||
|
||||
- [[ ChatGPT-WEB ]](https://github.com/dulaiduwang003/ChatGPT-WEB):基于JDK17+SpringBoot3+UniApp 绘图 聊天 充值应用。(Web版本)
|
||||
|
||||
- [[ SuperBot-ChatGPTApp ]](https://github.com/dulaiduwang003/SuperBot-ChatGPTApp):基于JDK17+SpringBoot3+UniApp 绘图 聊天 充值应用。(小程序版本)
|
||||
|
||||
- [[ ScribbleHub ]](https://github.com/dulaiduwang003/ScribbleHub):基于SpringBoot+satoken+wxss开发的博客小程序
|
||||
|
||||
- [[ TIME-SEA-chatgpt ]](https://github.com/dulaiduwang003/TIME-SEA-chatgpt):基于SpringBoot+satoken+vue3+uniapp开发的多端Ai平台应用
|
||||
|
||||
- [[ SUPERBOT-GPT]](https://github.com/dulaiduwang003/SUPERBOT-GPT):基于SpringBoot3+satoken+uniapp开发的流量主小程序
|
||||
|
||||
- [[ DaxPay ]](https://gitee.com/dromara/dax-pay):一款免费开源的支付网关系统,支持支付宝、微信、云闪付等通道,提供收单、退款、聚合支付、对账、分账等功能。
|
||||
|
||||
- [[ Dinky ]](https://github.com/DataLinkDC/dinky):基于Apache Flink的实时数据开发平台,实现敏捷的数据开发、部署和运维
|
||||
|
||||
- [[ mldong ]](https://gitee.com/mldong/mldong):SpringBoot + Vue3 快速开发平台、自研工作流引擎
|
||||
|
||||
|
||||
@@ -41,16 +41,22 @@ Sa-Token 无意发明任何晦涩概念提升逼格,但在处理 issue 、Q群
|
||||
- 单地登录:指同一时间只能在一个地方登录,新登录会挤掉旧登录,也可以叫:单端登录。
|
||||
- 多地登录:指同一时间可以在不同地方登录,新登录会和旧登录共存,也可以叫:多端登录。
|
||||
- 同端互斥登录:在同一类型设备上只允许单地点登录,在不同类型设备上允许同时在线,参考腾讯QQ的登录模式:手机和电脑可以同时在线,但不能两个手机同时在线。
|
||||
- 限量登录:限定账号登录设备总数量,低于此数量时可以正常登录,高于此数量后每次登录自动清退一个之前的登录。
|
||||
- 记住我模式:指在一个设备终端登录成功,该设备重启之后依然保持登录状态。
|
||||
- 单点登录:在进入多个系统时,只需要登录一次即可。解决用户在不同系统间频繁登录的问题。
|
||||
- 同端多登录:指在一个终端可以同时登录多个账号。
|
||||
- 记住我模式:指在一个设备终端登录成功,该设备重启之后依然保持登录状态。
|
||||
|
||||
|
||||
#### 几种注销策略:
|
||||
- 单端注销:只在调用登录的一端注销。
|
||||
- 全端注销:一端注销,全端下线。
|
||||
- 同端注销:发起注销后,同类型设备端一起下线,不同设备类型不受影响。
|
||||
- 单点注销:与单点登录对应,一个系统注销,所有系统一起下线。
|
||||
|
||||
<p><a class="case-btn case-btn-video" href="https://www.bilibili.com/video/BV1XrzABbEa1/" target="_blank">
|
||||
视频讲解:如何设计出最优的登录会话策略?单端登录、强制下线、多端互踢、记住我登录
|
||||
</a></p>
|
||||
|
||||
|
||||
#### 几种鉴权方式:
|
||||
- 代码鉴权:在代码里直接调用 `StpUtil.checkXxx` 相关 API 进行鉴权。
|
||||
|
||||
@@ -10,7 +10,7 @@ Sa-Token 采用 Apache-2.0 开源协议,**承诺框架本身与官网文档永
|
||||
|
||||
您可以在项目 [Gitee](https://gitee.com/dromara/sa-token) 主页进行捐赠
|
||||
|
||||

|
||||
<!--  -->
|
||||
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ Sa-Token 目前总计14+微信交流群(每个群人数大约400以上,总
|
||||
- 拒绝接受违反法律法规、以及灰色相关的产品推广,为避免不必要的麻烦,目前也拒绝推广IP代理、上网工具等等。
|
||||
- 为避免过多打扰群友,目前一天内至多在群里推广两次,超过次数的可顺延到第二天。
|
||||
|
||||
有意见合作者可直接在 gitee 发起赞赏后,将您的产品信息发至 [ sa-pro 小助手 ] ,加好友链接点 [这里](http://sa-pro.dev33.cn/),点击后往下拉有二维码,添加时请备注:Sa-Token 商业赞助。
|
||||
有意见合作者可直接在 gitee 发起赞赏后,将您的产品信息发至 [ sa-pro 小助手 ] ,加好友链接点 [这里](https://sa-pro.yun94.cn/),点击后往下拉有二维码,添加时请备注:Sa-Token 商业赞助。
|
||||
|
||||
注:在群发消息时,我们会明确对群友声明,此条消息为赞助商的产品,属于硬广信息。如您介意这一点,我们将暂时无法与您合作。
|
||||
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
|
||||
---
|
||||
|
||||
Sa-Token 采用 Apache-2.0 开源协议,**承诺框架本身与官网文档永久免费开放**,
|
||||
Sa-Token 采用 Apache-2.0 开源协议,<green>**承诺框架本身与在线文档永久免费开放**</green>,
|
||||
但是框架的日常更新与社区运营需要付出大量的精力,靠爱发电难以长久,如果 Sa-Token 帮助到了您,您可以友情支持一下 Sa-Token。
|
||||
|
||||
|
||||
### 友情赞助
|
||||
|
||||
您可以在项目 [Gitee](https://gitee.com/dromara/sa-token) 主页进行捐赠
|
||||
<!-- 您可以在项目 [Gitee](https://gitee.com/dromara/sa-token) 主页进行捐赠
|
||||
|
||||

|
||||
<img src="/big-file/doc/more/gitee-zanzhu2.png" alt="gitee-zanzhu2.png" /> -->
|
||||
|
||||
<img src="/big-file/contact/wx-zsm.png" alt="微信赞赏码" style="width: 280px;" />
|
||||
|
||||
|
||||
**已捐赠列表:**
|
||||
@@ -58,7 +59,7 @@ Sa-Token 目前总计18+微信交流群(每个群人数大约400以上,总
|
||||
- 拒绝接受违反法律法规、以及灰色相关的产品推广,为避免不必要的麻烦,目前也拒绝推广IP代理、上网工具等等。
|
||||
- 为避免过多打扰群友,目前一天内至多在群里推广一次,超过次数的可顺延到第二天。
|
||||
|
||||
有意见合作者可直接在 gitee 发起赞赏后,将您的产品信息发至 [ sa-pro 小助手 ] ,加好友链接点 [这里](http://sa-pro.dev33.cn/),点击后往下拉有二维码,添加时请备注:Sa-Token 商业赞助。
|
||||
有意见合作者可直接在 gitee 发起赞赏后,将您的产品信息发至 [ sa-pro 小助手 ] ,加好友链接点 [这里](https://sa-pro.yun94.cn/),点击后往下拉有二维码,添加时请备注:Sa-Token 商业赞助。
|
||||
|
||||
注:在群发消息时,我们会明确对群友声明,此条消息为赞助商的产品,属于硬广信息。如您介意这一点,我们将暂时无法与您合作。
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader {
|
||||
|
||||
当不知情的小红被诱导访问了这个 URL 时,它将被重定向至百度首页。
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/oauth2-new/oauth2-ticket-jc.png" alt="oauth2-ticket-jc" />
|
||||
|
||||
可以看到,代表着用户身份的 code 授权码也显现到了URL之中,借此漏洞,攻击者完全可以构建一个 URL 将小红的 code 授权码自动提交到攻击者自己的服务器,伪造小红身份登录网站。
|
||||
|
||||
@@ -64,7 +64,7 @@ public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader {
|
||||
|
||||
再次访问上述链接:
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/oauth2-new/oauth2-feifa-rf.png" alt="oauth2-feifa-rf" />
|
||||
|
||||
URL 没有通过校验,拒绝授权!
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
### 1、如何自定义 OAuth-Server 端的登录视图?
|
||||
|
||||
重写 `cfg.notLoginView` 策略:
|
||||
重写 `SaOAuth2Strategy.instance.notLoginView` 策略:
|
||||
|
||||
``` java
|
||||
@Autowired
|
||||
@@ -60,7 +60,7 @@ public SaResult ss(String name, String pwd) {
|
||||
|
||||
### 3、如何自定义 OAuth-Server 端的确认授权视图?
|
||||
|
||||
重写 `cfg.confirmView` 策略:
|
||||
重写 `SaOAuth2Strategy.instance.confirmView` 策略:
|
||||
|
||||
``` java
|
||||
@Autowired
|
||||
|
||||
@@ -117,11 +117,11 @@ public class SaOAuth2ServerH5Controller {
|
||||
|
||||
在前端 ide 中导入 demo 案例的 `sa-token-demo-oauth2-server-h5` 项目,然后直接预览 `oauth2-authorize.html` 页面,如图所示:
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/oauth2-new/sa-oauth2-server-authorize-h5.png" alt="sa-oauth2-server-authorize-h5.png" />
|
||||
|
||||
复制上述地址,然后将其配置到 “OAuth2前端测试页” 的 “OAuth2 Server 授权页地址” 选项中,其它选项保持默认不变:
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/oauth2-new/sa-oauth2-client-test-h5-page-setting.png" alt="sa-oauth2-client-test-h5-page-setting.png" />
|
||||
|
||||
然后根据 “OAuth2前端测试页” 的页面提示进行测试即可,此处不再赘述。
|
||||
|
||||
|
||||
@@ -227,14 +227,14 @@ http://sa-oauth-server.com:8000/oauth2/authorize?response_type=code&client_id=10
|
||||
|
||||
2、由于首次访问,我们在OAuth-Server端暂未登录,会被转发到登录视图
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/oauth2-new/sa-oauth2-server-login-view--v43.png" alt="sa-oauth2-server-login-view" />
|
||||
|
||||
3、输入 `sa/123456` 进行登录之后,会提示我们确认授权
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/oauth2-new/sa-oauth2-server-scope.png" alt="sa-oauth2-server-scope" />
|
||||
|
||||
4、点击同意授权之后,我们会被重定向至 redirect_uri 页面,并携带了code参数
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/oauth2-new/sa-oauth2-server-code.png" alt="sa-oauth2-server-code" />
|
||||
|
||||
4、我们拿着code参数,访问以下地址:
|
||||
``` url
|
||||
@@ -259,7 +259,6 @@ http://sa-oauth-server.com:8000/oauth2/token?grant_type=authorization_code&clien
|
||||
}
|
||||
```
|
||||
|
||||
<!--  -->
|
||||
|
||||
测试完毕
|
||||
|
||||
@@ -272,7 +271,7 @@ http://sa-oauth-server.com:8000/oauth2/token?grant_type=authorization_code&clien
|
||||
|
||||
依次启动`OAuth2-Server` 与 `OAuth2-Client`,然后从浏览器访问:[http://sa-oauth-client.com:8002](http://sa-oauth-client.com:8002)
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/oauth2-new/sa-oauth2-client-index.png" alt="sa-oauth2-client-index" />
|
||||
|
||||
如图,可以针对OAuth2.0四种模式进行详细测试
|
||||
|
||||
@@ -286,7 +285,7 @@ OAuth2 前端测试页:
|
||||
|
||||
此示例允许你在前端自由配置 OAuth-Client 端所需的各个参数,方便对 OAuth2 四种模式的测试。
|
||||
|
||||

|
||||
<img class="s-w-sh" src="/big-file/doc/oauth2-new/sa-oauth2-client-test-h5-page.png" alt="sa-oauth2-client-index" />
|
||||
|
||||
<p><a class="case-btn case-btn-video" href="https://www.bilibili.com/video/BV13LSMYzEmE/" target="_blank">
|
||||
参考视频:OAuth2 四种模式 前端测试页
|
||||
|
||||
@@ -23,7 +23,7 @@ OAuth2.0 与 SSO 相比,增加了对应用授权范围的控制,减弱了应
|
||||
3. 密码式(Password):Client 端直接拿着用户的账号密码换取授权 `Access-Token`。
|
||||
4. 客户端凭证(Client Credentials):Server 端针对 Client 级别的 Token,代表应用自身的资源授权。
|
||||
|
||||

|
||||
<img src="/big-file/doc/oauth2-new/sa-oauth2-setup.png" alt="sa-oauth2-setup.png" />
|
||||
|
||||
接下来我们将通过简单示例演示如何在 Sa-Token-OAuth2 中完成这四种模式的对接: [搭建OAuth2-Server](/oauth2/oauth2-server)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user