新建 json 转换器模块

This commit is contained in:
click33 2022-04-26 18:59:55 +08:00
parent 30758464ac
commit 26393d17dc
14 changed files with 306 additions and 113 deletions

View File

@ -11,6 +11,8 @@ import cn.dev33.satoken.context.second.SaTokenSecondContext;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.json.SaJsonTemplate;
import cn.dev33.satoken.json.SaJsonTemplateDefaultImpl;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.listener.SaTokenListenerDefaultImpl;
import cn.dev33.satoken.stp.StpInterface;
@ -171,6 +173,24 @@ public class SaManager {
}
return saTemp;
}
/**
* JSON 转换器 Bean
*/
private volatile static SaJsonTemplate saJsonTemplate;
public static void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) {
SaManager.saJsonTemplate = saJsonTemplate;
}
public static SaJsonTemplate getSaJsonTemplate() {
if (saJsonTemplate == null) {
synchronized (SaManager.class) {
if (saJsonTemplate == null) {
setSaJsonTemplate(new SaJsonTemplateDefaultImpl());
}
}
}
return saJsonTemplate;
}
/**
* StpLogic集合, 记录框架所有成功初始化的StpLogic

View File

@ -0,0 +1,21 @@
package cn.dev33.satoken.json;
import java.util.Map;
/**
* JSON 转换器
*
* @author kong
*
*/
public interface SaJsonTemplate {
/**
* json 字符串解析为 Map
*
* @param jsonStr json 字符串
* @return 转换后的 Map 对象
*/
public Map<String, Object> parseJsonToMap(String jsonStr);
}

View File

@ -0,0 +1,22 @@
package cn.dev33.satoken.json;
import java.util.Map;
import cn.dev33.satoken.exception.ApiDisabledException;
/**
* JSON 相关操作接口
*
* @author kong
*
*/
public class SaJsonTemplateDefaultImpl implements SaJsonTemplate {
/**
* json 字符串解析为 Map
*/
public Map<String, Object> parseJsonToMap(String jsonStr) {
throw new ApiDisabledException("未实现具体的 json 转换器");
};
}

View File

@ -35,6 +35,7 @@
- **单点登录**
- [单点登录简述](/sso/readme)
- [搭建统一认证中心SSO-Server](/sso/sso-server)
- [SSO-Server 认证中心开放接口](/sso/sso-apidoc)
- [SSO模式一 共享Cookie同步会话](/sso/sso-type1)
- [SSO模式二 URL重定向传播会话](/sso/sso-type2)
- [SSO模式三 Http请求获取会话](/sso/sso-type3)

View File

@ -15,7 +15,7 @@
### 架构选型
对于单点登录网上教程大多以CAS模式为主其实对于不同的系统架构实现单点登录的步骤也大为不同Sa-Token 由简入难将其划分为三种模式:
Sa-Token-SSO 由简入难划分为三种模式,解决不同架构下的 SSO 接入问题
| 系统架构 | 采用模式 | 简介 | 文档链接 |
| :-------- | :-------- | :-------- | :-------- |
@ -27,16 +27,15 @@
1. 前端同域:就是指多个系统可以部署在同一个主域名之下,比如:`c1.domain.com`、`c2.domain.com`、`c3.domain.com`。
2. 后端同Redis就是指多个系统可以连接同一个Redis。PS这里并不需要把所有项目的数据都放在同一个Redis中Sa-Token提供了 **`[权限缓存与业务缓存分离]`** 的解决方案,详情戳:[Alone独立Redis插件](http://sa-token.dev33.cn/doc/index.html#/plugin/alone-redis)。
3. 如果既无法做到前端同域也无法做到后端同Redis那么只能走模式三Http请求获取会话Sa-Token对SSO提供了完整的封装你只需要按照示例从文档上复制几段代码便可以轻松集成
4. 技术选型一定要根据系统架构对症下药,切不可胡乱选择。
### Sa-Token-SSO 特性
1. API简单易用文档介绍详细且提供直接可用的集成示例
2. 支持三种模式不论是否跨域、是否共享Redis、是否前后端分离都可以完美解决
3. 安全性高内置域名校验、Ticket校验、秘钥校验等杜绝`Ticket劫持`、`Token窃取`等常见攻击手段(文档讲述攻击原理和防御手段)
4. 不丢参数:笔者曾试验多个单点登录框架,均有参数丢失的情况,比如重定向之前是:`http://a.com?id=1&name=2`,登录成功之后就变成了:`http://a.com?id=1`Sa-Token-SSO内有专门的算法保证了参数不丢失登录成功之后原路返回页面
1. API 简单易用,文档介绍详细,且提供直接可用的集成示例
2. 支持三种模式不论是否跨域、是否共享Redis、是否前后端分离都可以完美解决
3. 安全性高内置域名校验、Ticket校验、秘钥校验等杜绝`Ticket劫持`、`Token窃取`等常见攻击手段(文档讲述攻击原理和防御手段)
4. 不丢参数:笔者曾试验多个单点登录框架,均有参数丢失的情况,比如重定向之前是:`http://a.com?id=1&name=2`,登录成功之后就变成了:`http://a.com?id=1`Sa-Token-SSO内有专门的算法保证了参数不丢失登录成功之后原路返回页面
5. 无缝集成由于Sa-Token本身就是一个权限认证框架因此你可以只用一个框架同时解决`权限认证` + `单点登录`问题让你不再到处搜索xxx单点登录与xxx权限认证如何整合……
6. 高可定制Sa-Token-SSO模块对代码架构侵入性极低结合Sa-Token本身的路由拦截特性你可以非常轻松的定制化开发
6. 高可定制Sa-Token-SSO模块对代码架构侵入性极低结合Sa-Token本身的路由拦截特性你可以非常轻松的定制化开发

View File

@ -0,0 +1,106 @@
# SSO-Server 认证中心开放接口
如果你的 SSO-Server 端和 SSO-Client 端都使用 Sa-Token-SSO 搭建,那么你可以直接跳过本章,开始 [SSO模式一 共享Cookie同步会话](/sso/sso-type1) 的学习。
如果你仅在 SSO-Server 端使用 Sa-Token-SSO 搭建,而 SSO-Client 端使用其它框架的话,那么你就需要手动调用 http 请求来对接 SSO-Server 认证中心,
下面的 API 列表将给你的对接步骤做一份参考。
---
### 1、单点登录授权地址
``` url
http://{host}:{port}/sso/auth
```
接收参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| redirect | 是 | 登录成功后的重定向地址,一般填写 location.href从哪来回哪去 |
| mode | 否 | 授权模式,取值 [simple, ticket]simple=登录后直接重定向ticket=带着ticket参数重定向默认值为ticket |
访问接口后有两种情况:
- 情况一:当前会话在 SSO 认证中心未登录,会进入登录页开始登录。
- 情况二:当前会话在 SSO 认证中心已登录,会被重定向至 `redirect` 地址,并携带 `ticket` 参数。
### 2、RestAPI 登录接口
``` url
http://{host}:{port}/sso/doLogin
```
接收参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| name | 是 | 用户名 |
| pwd | 是 | 密码 |
此接口属于 RestAPI (使用ajax访问),会进入后端配置的 `setDoLoginHandle` 函数中,另外需要注意:
此接口并非只能携带 name、pwd 参数,因为你可以在 setDoLoginHandle 函数里通过 `SaHolder.getRequest().getParam("xxx")` 来获取其它参数。
### 3、Ticket 校验接口
此接口仅配置模式三 `(isHttp=true)` 时打开
``` url
http://{host}:{port}/sso/checkTicket
```
接收参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| ticket | 是 | 在步骤 5.1 中授权重定向时的 ticket 参数 |
| ssoLogoutCall | 否 | 单点注销时的回调通知地址只在SSO模式三单点注销时需要携带此参数|
返回值场景:
- 返回空,代表校验失败。
- 返回具体的 loginId例如10001代表校验成功值为此 ticket 码代表的用户id。
### 4、单点注销接口
``` url
http://{host}:{port}/sso/logout
```
接受参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| loginId | 否 | 要注销的账号id |
| secretkey | 否 | 接口通信秘钥 |
| back | 否 | 注销成功后的重定向地址 |
此接口有两种调用方式
##### 方式一:在 Client 的前端页面引导用户直接跳转,并带有 back 参数
例如:`http://{host}:{port}/sso/logout?back=xxx`代表用户注销成功后返回back地址
##### 方式二:在 Client 的后端通过 http 工具来调用
例如:`http://{host}:{port}/sso/logout?loginId={value}&secretkey={value}`,代表注销 账号=loginId 的账号返回json数据结果形如
``` js
{
"code": 200, // 200表示请求成功非200标识请求失败
"msg": "单点注销成功",
"data": null
}
```
<br>
SSO 认证中心只有这四个接口,接下来让我一起来看一下 Client 端的对接流程:[SSO模式一 共享Cookie同步会话](/sso/sso-type1)

View File

@ -190,104 +190,10 @@ public class SaSsoServerApplication {
默认账号密码为:`sa / 123456`,先别着急点击登录,因为我们还没有搭建对应的 Client 端项目,
真实项目中我们是不会直接从浏览器访问 `/sso/auth` 授权地址的,我们需要在 Client 端点击登录按钮重定向而来。
现在我们先来看看除了 `/sso/auth` 统一授权地址,这个 SSO-Server 认证中心还开放了哪些API。
### 5、API 列表
如果你仅仅使用 Sa-Token 搭建 SSO-Server 端,而 Client 端使用其它框架的话,那么下面的 API 列表将给你的对接步骤做一份参考。
如果你在 Client 端也用到了 Sa-Token 框架那么你可以选择跳过本小节Sa-Token 对 Client 端也提供了相应的封装,你可以直接开始学习:[SSO模式一 共享Cookie同步会话](/sso/sso-type1)
#### 5.1、单点登录授权地址
``` url
http://{host}:{port}/sso/auth
```
接收参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| redirect | 是 | 登录成功后的重定向地址,一般填写 location.href从哪来回哪去 |
| mode | 否 | 授权模式,取值 [simple, ticket]simple=登录后直接重定向ticket=带着ticket参数重定向默认值为ticket |
访问接口后有两种情况:
- 情况一:当前会话在 SSO 认证中心未登录,会进入登录页开始登录。
- 情况二:当前会话在 SSO 认证中心已登录,会被重定向至 `redirect` 地址,并携带 `ticket` 参数。
#### 5.2、RestAPI 登录接口
``` url
http://{host}:{port}/sso/doLogin
```
接收参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| name | 是 | 用户名 |
| pwd | 是 | 密码 |
此接口属于 RestAPI (使用ajax访问),会进入后端配置的 `setDoLoginHandle` 函数中,另外需要注意:
此接口并非只能携带 name、pwd 参数,因为你可以在 setDoLoginHandle 函数里通过 `SaHolder.getRequest().getParam("xxx")` 来获取其它参数。
#### 5.3、Ticket 校验接口
此接口仅配置模式三 `(isHttp=true)` 时打开
``` url
http://{host}:{port}/sso/checkTicket
```
接收参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| ticket | 是 | 在步骤 5.1 中授权重定向时的 ticket 参数 |
| ssoLogoutCall | 否 | 单点注销时的回调通知地址只在SSO模式三单点注销时需要携带此参数|
返回值场景:
- 返回空,代表校验失败。
- 返回具体的 loginId例如10001代表校验成功值为此 ticket 码代表的用户id。
#### 5.4、单点注销接口
``` url
http://{host}:{port}/sso/logout
```
接受参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| loginId | 否 | 要注销的账号id |
| secretkey | 否 | 接口通信秘钥 |
| back | 否 | 注销成功后的重定向地址 |
此接口有两种调用方式
##### 方式一:在 Client 的前端页面引导用户直接跳转,并带有 back 参数
例如:`http://{host}:{port}/sso/logout?back=xxx`代表用户注销成功后返回back地址
##### 方式二:在 Client 的后端通过 http 工具来调用
例如:`http://{host}:{port}/sso/logout?loginId={value}&secretkey={value}`,代表注销 账号=loginId 的账号返回json数据结果形如
``` js
{
"code": 200, // 200表示请求成功非200标识请求失败
"msg": "单点注销成功",
"data": null
}
```
<br>
SSO 认证中心只有这四个接口,接下来让我一起来看一下 Client 端的对接流程:[SSO模式一 共享Cookie同步会话](/sso/sso-type1)
---
现在我们先来看看除了 `/sso/auth` 统一授权地址,这个 SSO-Server 认证中心还开放了哪些API[SSO-Server 认证中心开放接口](/sso/sso-apidoc)。

View File

@ -59,6 +59,14 @@
<optional>true</optional>
</dependency>
<!-- jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.6.1</version>
<optional>true</optional>
</dependency>
<!-- config -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -13,6 +13,7 @@ import cn.dev33.satoken.context.second.SaTokenSecondContextCreator;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.id.SaIdTemplate;
import cn.dev33.satoken.id.SaIdUtil;
import cn.dev33.satoken.json.SaJsonTemplate;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic;
@ -113,10 +114,20 @@ public class SaBeanInject {
* @param saBasicTemplate saBasicTemplate对象
*/
@Autowired(required = false)
public void setSaSsoTemplate(SaBasicTemplate saBasicTemplate) {
public void setSaBasicTemplate(SaBasicTemplate saBasicTemplate) {
SaBasicUtil.saBasicTemplate = saBasicTemplate;
}
/**
* 注入自定义的 JSON 转换器 Bean
*
* @param saJsonTemplate JSON 转换器
*/
@Autowired(required = false)
public void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) {
SaManager.setSaJsonTemplate(saJsonTemplate);
}
/**
* 注入自定义的 StpLogic
* @param stpLogic /

View File

@ -6,6 +6,8 @@ import org.springframework.context.annotation.Bean;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.context.SaTokenContextForThreadLocal;
import cn.dev33.satoken.json.SaJsonTemplate;
import cn.dev33.satoken.reactor.spring.json.SaJsonTemplateForJackson;
/**
* 注册Sa-Token所需要的Bean
@ -44,4 +46,14 @@ public class SaBeanRegister {
};
}
/**
* 获取 json 转换器 Bean (Jackson版)
*
* @return json 转换器 Bean (Jackson版)
*/
@Bean
public SaJsonTemplate getSaJsonTemplateForJackson() {
return new SaJsonTemplateForJackson();
}
}

View File

@ -0,0 +1,38 @@
package cn.dev33.satoken.reactor.spring.json;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.json.SaJsonTemplate;
/**
* JSON 转换器 Jackson 版实现
*
* @author kong
* @date: 2022-4-26
*/
public class SaJsonTemplateForJackson implements SaJsonTemplate {
/**
* 底层 Mapper 对象
*/
public ObjectMapper objectMapper = new ObjectMapper();
/**
* json 字符串解析为 Map
*/
@Override
public Map<String, Object> parseJsonToMap(String jsonStr) {
try {
@SuppressWarnings("unchecked")
Map<String, Object> map = objectMapper.readValue(jsonStr, Map.class);
return map;
} catch (JsonProcessingException e) {
throw new SaTokenException(e);
}
}
}

View File

@ -13,9 +13,8 @@ import cn.dev33.satoken.context.second.SaTokenSecondContextCreator;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.id.SaIdTemplate;
import cn.dev33.satoken.id.SaIdUtil;
import cn.dev33.satoken.json.SaJsonTemplate;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.sso.SaSsoTemplate;
import cn.dev33.satoken.sso.SaSsoUtil;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
@ -115,20 +114,20 @@ public class SaBeanInject {
* @param saBasicTemplate saBasicTemplate对象
*/
@Autowired(required = false)
public void setSaSsoTemplate(SaBasicTemplate saBasicTemplate) {
public void setSaBasicTemplate(SaBasicTemplate saBasicTemplate) {
SaBasicUtil.saBasicTemplate = saBasicTemplate;
}
/**
* 注入 Sa-Token-SSO 单点登录模块 Bean
* 注入自定义的 JSON 转换器 Bean
*
* @param saSsoTemplate saSsoTemplate对象
* @param saJsonTemplate JSON 转换器
*/
@Autowired(required = false)
public void setSaSsoTemplate(SaSsoTemplate saSsoTemplate) {
SaSsoUtil.saSsoTemplate = saSsoTemplate;
public void setSaJsonTemplate(SaJsonTemplate saJsonTemplate) {
SaManager.setSaJsonTemplate(saJsonTemplate);
}
/**
* 注入自定义的 StpLogic
* @param stpLogic /

View File

@ -5,6 +5,8 @@ import org.springframework.context.annotation.Bean;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.json.SaJsonTemplate;
import cn.dev33.satoken.spring.json.SaJsonTemplateForJackson;
/**
* 注册Sa-Token所需要的Bean
@ -26,7 +28,7 @@ public class SaBeanRegister {
}
/**
* 获取容器交互Bean (Spring版)
* 获取上下文Bean (Spring版)
*
* @return 容器交互Bean (Spring版)
*/
@ -35,4 +37,14 @@ public class SaBeanRegister {
return new SaTokenContextForSpring();
}
/**
* 获取 json 转换器 Bean (Jackson版)
*
* @return json 转换器 Bean (Jackson版)
*/
@Bean
public SaJsonTemplate getSaJsonTemplateForJackson() {
return new SaJsonTemplateForJackson();
}
}

View File

@ -0,0 +1,38 @@
package cn.dev33.satoken.spring.json;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.json.SaJsonTemplate;
/**
* JSON 转换器 Jackson 版实现
*
* @author kong
* @date: 2022-4-26
*/
public class SaJsonTemplateForJackson implements SaJsonTemplate {
/**
* 底层 Mapper 对象
*/
public ObjectMapper objectMapper = new ObjectMapper();
/**
* json 字符串解析为 Map
*/
@Override
public Map<String, Object> parseJsonToMap(String jsonStr) {
try {
@SuppressWarnings("unchecked")
Map<String, Object> map = objectMapper.readValue(jsonStr, Map.class);
return map;
} catch (JsonProcessingException e) {
throw new SaTokenException(e);
}
}
}