新增 Dubbo 集成插件

This commit is contained in:
click33 2021-11-01 09:52:57 +08:00
parent a1bab9e747
commit 652e6172af
36 changed files with 1218 additions and 71 deletions

View File

@ -127,9 +127,9 @@ StpUtil.switchTo(10044); // 将当前会话身份临时切换
| 系统架构 | 采用模式 | 简介 | 文档链接 |
| :-------- | :-------- | :-------- | :-------- |
| 前端同域 + 后端同 Redis | 模式一 | 共享Cookie同步会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type1)、[示例](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-sso1-client) |
| 前端不同域 + 后端同 Redis | 模式二 | URL重定向传播会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type2)、[示例](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-sso2-client) |
| 前端不同域 + 后端 不同Redis | 模式三 | Http请求获取会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type3)、[示例](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-sso3-client) |
| 前端同域 + 后端同 Redis | 模式一 | 共享Cookie同步会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type1)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso1-client) |
| 前端不同域 + 后端同 Redis | 模式二 | URL重定向传播会话 | [文档](http://sa-token.dev33.cn/doc/index.html#/sso/sso-type2)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso2-client) |
| 前端不同域 + 后端 不同Redis | 模式三 | Http请求获取会话 | [文档](http://sa-token.dev33.cn/doc/index.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`

View File

@ -80,6 +80,8 @@ public class SaTokenConfig implements Serializable {
/** 配置当前项目的网络访问地址 */
private String currDomain;
/** 是否校验Id-Token部分rpc插件有效 */
private Boolean checkIdToken = false;
/**
* Cookie配置对象
@ -399,6 +401,22 @@ public class SaTokenConfig implements Serializable {
this.currDomain = currDomain;
return this;
}
/**
* @return 是否校验Id-Token部分rpc插件有效
*/
public Boolean getCheckIdToken() {
return checkIdToken;
}
/**
* @param checkIdToken 是否校验Id-Token部分rpc插件有效
* @return 对象自身
*/
public SaTokenConfig setCheckIdToken(Boolean checkIdToken) {
this.checkIdToken = checkIdToken;
return this;
}
/**
* @return SSO单点登录配置对象
@ -454,6 +472,7 @@ public class SaTokenConfig implements Serializable {
+ ", idTokenTimeout=" + idTokenTimeout
+ ", basic=" + basic
+ ", currDomain=" + currDomain
+ ", checkIdToken=" + checkIdToken
+ ", sso=" + sso
+ ", cookie=" + cookie
+ "]";

View File

@ -94,29 +94,52 @@ public class StpLogic {
return SaStrategy.me.createToken.apply(loginId, loginType);
}
/**
* 在当前会话写入当前TokenValue
* @param tokenValue token值
*/
public void setTokenValue(String tokenValue){
setTokenValue(tokenValue, (int)SaManager.getConfig().getTimeout());
}
/**
* 在当前会话写入当前TokenValue
* @param tokenValue token值
* @param cookieTimeout Cookie存活时间()
*/
public void setTokenValue(String tokenValue, int cookieTimeout){
SaTokenConfig config = getConfig();
if(SaFoxUtil.isEmpty(tokenValue)) {
return;
}
// 1. 将token保存到[存储器]
setTokenValueToStorage(tokenValue);
// 2. Token 保存到 [Cookie]
if (getConfig().getIsReadCookie()) {
setTokenValueToCookie(tokenValue, cookieTimeout);
}
}
/**
* Token 保存到 [Storage]
* @param tokenValue token值
*/
public void setTokenValueToStorage(String tokenValue){
// 1. 将token保存到[存储器]
SaStorage storage = SaHolder.getStorage();
// 如果打开了token前缀模式则拼接上前缀一起写入
String tokenPrefix = config.getTokenPrefix();
// 2. 如果打开了 Token 前缀模式则拼接上前缀
String tokenPrefix = getConfig().getTokenPrefix();
if(SaFoxUtil.isEmpty(tokenPrefix) == false) {
storage.set(splicingKeyJustCreatedSave(), tokenPrefix + SaTokenConsts.TOKEN_CONNECTOR_CHAT + tokenValue);
} else {
storage.set(splicingKeyJustCreatedSave(), tokenValue);
}
// 2. Token 保存到 [Cookie]
if (config.getIsReadCookie()) {
setTokenValueToCookie(tokenValue, cookieTimeout);
}
// 3. 写入 (无前缀)
storage.set(SaTokenConsts.JUST_CREATED_NOT_PREFIX, tokenValue);
}
/**
@ -144,6 +167,30 @@ public class StpLogic {
* @return 当前tokenValue
*/
public String getTokenValue(){
// 1. 获取
String tokenValue = getTokenValueNotCut();
// 2. 如果打开了前缀模式则裁剪掉
String tokenPrefix = getConfig().getTokenPrefix();
if(SaFoxUtil.isEmpty(tokenPrefix) == false) {
// 如果token并没有按照指定的前缀开头则视为未提供token
if(SaFoxUtil.isEmpty(tokenValue) || tokenValue.startsWith(tokenPrefix + SaTokenConsts.TOKEN_CONNECTOR_CHAT) == false) {
tokenValue = null;
} else {
// 则裁剪掉前缀
tokenValue = tokenValue.substring(tokenPrefix.length() + SaTokenConsts.TOKEN_CONNECTOR_CHAT.length());
}
}
// 3. 返回
return tokenValue;
}
/**
* 获取当前TokenValue (不裁剪前缀)
* @return /
*/
public String getTokenValueNotCut(){
// 0. 获取相应对象
SaStorage storage = SaHolder.getStorage();
SaRequest request = SaHolder.getRequest();
@ -168,19 +215,7 @@ public class StpLogic {
tokenValue = request.getCookieValue(keyTokenName);
}
// 5. 如果打开了前缀模式
String tokenPrefix = getConfig().getTokenPrefix();
if(SaFoxUtil.isEmpty(tokenPrefix) == false) {
// 如果token并没有按照指定的前缀开头则视为未提供token
if(SaFoxUtil.isEmpty(tokenValue) || tokenValue.startsWith(tokenPrefix + SaTokenConsts.TOKEN_CONNECTOR_CHAT) == false) {
tokenValue = null;
} else {
// 则裁剪掉前缀
tokenValue = tokenValue.substring(tokenPrefix.length() + SaTokenConsts.TOKEN_CONNECTOR_CHAT.length());
}
}
// 6. 返回
// 5. 返回
return tokenValue;
}
@ -1599,7 +1634,8 @@ public class StpLogic {
* @return key
*/
public String splicingKeyJustCreatedSave() {
return SaTokenConsts.JUST_CREATED_SAVE_KEY + loginType;
// return SaTokenConsts.JUST_CREATED_SAVE_KEY + loginType;
return SaTokenConsts.JUST_CREATED;
}
/**

View File

@ -51,6 +51,14 @@ public class StpUtil {
return stpLogic.getTokenName();
}
/**
* 在当前会话写入当前TokenValue
* @param tokenValue token值
*/
public static void setTokenValue(String tokenValue){
stpLogic.setTokenValue(tokenValue);
}
/**
* 在当前会话写入当前TokenValue
* @param tokenValue token值
@ -68,6 +76,14 @@ public class StpUtil {
return stpLogic.getTokenValue();
}
/**
* 获取当前TokenValue (不裁剪前缀)
* @return /
*/
public static String getTokenValueNotCut(){
return stpLogic.getTokenValueNotCut();
}
/**
* 获取当前会话的Token信息
* @return token信息

View File

@ -30,7 +30,12 @@ public class SaTokenConsts {
/**
* 常量key标记: 如果token为本次请求新创建的则以此字符串为key存储在当前request中
*/
public static final String JUST_CREATED_SAVE_KEY = "JUST_CREATED_SAVE_KEY_";
public static final String JUST_CREATED = "JUST_CREATED_";
/**
* 常量key标记: 如果token为本次请求新创建的则以此字符串为key存储在当前request中不拼接前缀纯Token
*/
public static final String JUST_CREATED_NOT_PREFIX = "JUST_CREATED_NOT_PREFIX_";
/**
* 常量key标记: 如果本次请求已经验证过[无操作过期], 则以此值存储在当前request中
@ -97,5 +102,14 @@ public class SaTokenConsts {
* 切面拦截器过滤器等各种组件的注册优先级顺序
*/
public static final int ASSEMBLY_ORDER = -100;
// =================== 废弃 ===================
/**
* 请更换为 JUST_CREATED
*/
@Deprecated
public static final String JUST_CREATED_SAVE_KEY = JUST_CREATED;
}

View File

@ -0,0 +1,13 @@
target/
.project
.classpath
.settings
/.idea/
node_modules/
bin/
.settings/
unpackage/
/.apt_generated/
/.apt_generated_tests/

View File

@ -0,0 +1,75 @@
<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>com.pj</groupId>
<artifactId>sa-token-demo-dubbo-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- SpringBoot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
</parent>
<!-- 指定一些属性 -->
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
</properties>
<dependencies>
<!-- SpringBoot Web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Sa-Token -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.27.0</version>
</dependency>
<!-- Sa-Token整合 Redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.27.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.11</version>
</dependency>
<!-- Dubbo 注册到 Nacos -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>2.7.11</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.4.1</version>
</dependency>
<!-- Sa-Token 整合 Dubbo -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-context-dubbo</artifactId>
<version>1.27.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,22 @@
package com.pj;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Dubbo 服务消费端
*
* @author kong
*
*/
@EnableDubbo
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
System.out.println("ConsumerApplication 启动成功");
}
}

View File

@ -0,0 +1,16 @@
package com.pj.more;
public interface DemoService {
/**
* 登录
* @param loginId 账号id
*/
void doLogin(Object loginId);
/**
* 判断是否登录打印状态
*/
void isLogin(String str);
}

View File

@ -0,0 +1,60 @@
package com.pj.more;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.stp.StpUtil;
@RestController
public class TestController {
@DubboReference
private DemoService demoService;
// Consumer端登录状态传播到Provider端
@RequestMapping("test")
public String test() {
demoService.isLogin("----------- 登录前 ");
StpUtil.login(10001);
demoService.isLogin("----------- 登录后 ");
return "ok";
}
// Provider端登录状态回传到Consumer端
@RequestMapping("test2")
public String test2() {
System.out.println("----------- 登录前 ");
System.out.println("Token值" + StpUtil.getTokenValue());
System.out.println("是否登录:" + StpUtil.isLogin());
demoService.doLogin(10002);
System.out.println("----------- 登录后 ");
System.out.println("Token值" + StpUtil.getTokenValue());
System.out.println("是否登录:" + StpUtil.isLogin());
return "ok";
}
// Consumer端登录状态在Consumer端保持
@RequestMapping("test3")
public String test3() {
System.out.println("----------- 登录前 ");
System.out.println("Token值" + StpUtil.getTokenValue());
System.out.println("是否登录:" + StpUtil.isLogin());
StpUtil.login(10003);
demoService.isLogin("----------- Provider状态");
System.out.println("----------- 登录后 ");
System.out.println("Token值" + StpUtil.getTokenValue());
System.out.println("是否登录:" + StpUtil.isLogin());
return "ok";
}
}

View File

@ -0,0 +1,24 @@
server:
# 端口号
port: 8081
spring:
# redis配置
redis:
# Redis数据库索引默认为0
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
password:
# 连接超时时间
dubbo:
application:
# 服务名称
name: dubbo-consumer-demo
registry:
# 注册中心地址
address: nacos://127.0.0.1:8001

View File

@ -0,0 +1,13 @@
target/
.project
.classpath
.settings
/.idea/
node_modules/
bin/
.settings/
unpackage/
/.apt_generated/
/.apt_generated_tests/

View File

@ -0,0 +1,75 @@
<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>com.pj</groupId>
<artifactId>sa-token-demo-dubbo-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- SpringBoot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
</parent>
<!-- 指定一些属性 -->
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
</properties>
<dependencies>
<!-- SpringBoot Web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Sa-Token -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.27.0</version>
</dependency>
<!-- Sa-Token整合 Redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.27.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.11</version>
</dependency>
<!-- Dubbo 注册到 Nacos -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>2.7.11</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.4.1</version>
</dependency>
<!-- Sa-Token 整合 Dubbo -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-context-dubbo</artifactId>
<version>1.27.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,22 @@
package com.pj;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Dubbo 服务提供端
*
* @author kong
*
*/
@EnableDubbo
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
System.out.println("ProviderApplication 启动成功");
}
}

View File

@ -0,0 +1,16 @@
package com.pj.more;
public interface DemoService {
/**
* 登录
* @param loginId 账号id
*/
void doLogin(Object loginId);
/**
* 判断是否登录打印状态
*/
void isLogin(String str);
}

View File

@ -0,0 +1,23 @@
package com.pj.more;
import org.apache.dubbo.config.annotation.DubboService;
import cn.dev33.satoken.stp.StpUtil;
@DubboService()
public class DemoServiceImpl implements DemoService {
@Override
public void doLogin(Object loginId) {
StpUtil.login(loginId);
}
@Override
public void isLogin(String str) {
System.out.println(str);
System.out.println("Token值" + StpUtil.getTokenValue());
System.out.println("是否登录:" + StpUtil.isLogin());
}
}

View File

@ -0,0 +1,38 @@
server:
# 端口号
port: 8080
spring:
# redis配置
redis:
# Redis数据库索引默认为0
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
password:
# 连接超时时间
timeout: 10s
# Dubbo
dubbo:
# 服务名
application:
name: dubbo-provider-demo
# 扫描包
scan:
base-packages: com.pj
# 注册中心地址
registry:
address: nacos://127.0.0.1:8001
# 协议
protocol:
name: dubbo
port: 12345

View File

@ -2,8 +2,6 @@ package com.pj.satoken.at;
import java.util.List;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.fun.SaFunction;
import cn.dev33.satoken.session.SaSession;
@ -13,10 +11,9 @@ import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
/**
* Sa-Token 权限认证工具类
* Sa-Token 权限认证工具类 (User版)
* @author kong
*/
@Component
public class StpUserUtil {
/**
@ -58,6 +55,14 @@ public class StpUserUtil {
return stpLogic.getTokenName();
}
/**
* 在当前会话写入当前TokenValue
* @param tokenValue token值
*/
public static void setTokenValue(String tokenValue){
stpLogic.setTokenValue(tokenValue);
}
/**
* 在当前会话写入当前TokenValue
* @param tokenValue token值
@ -75,6 +80,14 @@ public class StpUserUtil {
return stpLogic.getTokenValue();
}
/**
* 获取当前TokenValue (不裁剪前缀)
* @return /
*/
public static String getTokenValueNotCut(){
return stpLogic.getTokenValueNotCut();
}
/**
* 获取当前会话的Token信息
* @return token信息
@ -347,7 +360,7 @@ public class StpUserUtil {
}
// =================== [临时] 验证相关 ===================
// =================== [临时有效] 验证相关 ===================
/**
* 检查当前token 是否已经[临时过期]如果已经过期则抛出异常
@ -404,8 +417,34 @@ public class StpUserUtil {
// =================== 角色验证操作 ===================
/**
* 获取当前账号的角色集合
* @return /
*/
public static List<String> getRoleList() {
return stpLogic.getRoleList();
}
/**
* 获取指定账号的角色集合
* @param loginId 指定账号id
* @return /
*/
public static List<String> getRoleList(Object loginId) {
return stpLogic.getRoleList(loginId);
}
/**
* 判断指定账号id是否含有角色标识, 返回true或false
* 判断当前账号是否拥有指定角色, 返回true或false
* @param role 角色标识
* @return 是否含有指定角色标识
*/
public static boolean hasRole(String role) {
return stpLogic.hasRole(role);
}
/**
* 判断指定账号是否含有指定角色标识, 返回true或false
* @param loginId 账号id
* @param role 角色标识
* @return 是否含有指定角色标识
@ -414,15 +453,6 @@ public class StpUserUtil {
return stpLogic.hasRole(loginId, role);
}
/**
* 判断当前账号是否含有指定角色标识, 返回true或false
* @param role 角色标识
* @return 是否含有指定角色标识
*/
public static boolean hasRole(String role) {
return stpLogic.hasRole(role);
}
/**
* 判断当前账号是否含有指定角色标识 [指定多个必须全部验证通过]
* @param roleArray 角色标识数组
@ -465,26 +495,24 @@ public class StpUserUtil {
stpLogic.checkRoleOr(roleArray);
}
// --
/**
* 返回当前账号所拥有的角色标识集合
* @return /
*/
public static List<String> getRoleList() {
return stpLogic.getRoleList();
}
// =================== 权限验证操作 ===================
/**
* 判断指定账号id是否含有指定权限, 返回true或false
* @param loginId 账号id
* @param permission 权限码
* @return 是否含有指定权限
*/
public static boolean hasPermission(Object loginId, String permission) {
return stpLogic.hasPermission(loginId, permission);
/**
* 获取当前账号的权限码集合
* @return /
*/
public static List<String> getPermissionList() {
return stpLogic.getPermissionList();
}
/**
* 获取指定账号的权限码集合
* @param loginId 指定账号id
* @return /
*/
public static List<String> getPermissionList(Object loginId) {
return stpLogic.getPermissionList(loginId);
}
/**
@ -496,6 +524,16 @@ public class StpUserUtil {
return stpLogic.hasPermission(permission);
}
/**
* 判断指定账号id是否含有指定权限, 返回true或false
* @param loginId 账号id
* @param permission 权限码
* @return 是否含有指定权限
*/
public static boolean hasPermission(Object loginId, String permission) {
return stpLogic.hasPermission(loginId, permission);
}
/**
* 判断当前账号是否含有指定权限, [指定多个必须全部具有]
* @param permissionArray 权限码数组
@ -538,15 +576,6 @@ public class StpUserUtil {
stpLogic.checkPermissionOr(permissionArray);
}
// --
/**
* 返回当前账号所拥有的权限码集合
* @return /
*/
public static List<String> getPermissionList() {
return stpLogic.getPermissionList();
}
// =================== id 反查token 相关操作 ===================
@ -819,6 +848,7 @@ public class StpUserUtil {
* <p> 当对方再次访问系统时会抛出NotLoginException异常场景值=-2
* @param loginId 账号id
*/
@Deprecated
public static void logoutByLoginId(Object loginId) {
stpLogic.kickout(loginId);
}
@ -831,6 +861,7 @@ public class StpUserUtil {
* @param loginId 账号id
* @param device 设备标识 (填null代表所有注销设备)
*/
@Deprecated
public static void logoutByLoginId(Object loginId, String device) {
stpLogic.kickout(loginId, device);
}

View File

@ -65,6 +65,7 @@
- [持久层扩展](/plugin/dao-extend)
- [和 Thymeleaf 集成](/plugin/thymeleaf-extend)
- [和 jwt 集成](/plugin/jwt-extend)
- [和 Dubbo 集成](/plugin/dubbo-extend)
- **其它**
- [更新日志](/more/update-log)

View File

@ -19,6 +19,9 @@ Sa-Token 采用 Apache-2.0 开源协议,**承诺框架本身与官网文档永
| 赞助人 | 赞助金额 | 留言 | 时间 |
| :-------- | :-------- | :-------- | :-------- |
| songfazhun | ¥ 10 | 感谢您的开源项目! | 2021-10-28 |
| ithorns | ¥ 10 | 感谢您的开源项目! | 2021-10-25 |
| xiaoyan | ¥ 200 | 节日快乐 | 2021-10-24 |
| apifox001 | ¥ 200 | 开源不易Apifoxapifox.cn和你们一起加油 | 2021-10-15 |
| 永夜 | ¥ 20 | 感谢您的开源项目! | 2021-09-18 |
| 苏永晓 | ¥ 10 | 感谢您的开源项目! | 2021-09-01 |

View File

@ -0,0 +1,148 @@
# 和 Dubbo 集成
本插件的作用是让 Sa-Token 和 Dubbo 做一个整合。
---
### 先说说要解决的问题
在 Dubbo 的整个调用链中,代码被分为 Consumer 端和 Provider 端,为方便理解我们可以称其为 `[调用端]``[被调用端]`
RPC 模式的调用,可以让我们像调用本地方法一样完成服务通信,然而这种便利下却隐藏着两个问题:
- 上下文环境的丢失。
- 上下文参数的丢失。
这种问题作用在 Sa-Token 框架上就是,在 [ 被调用端 ] 调用 Sa-Token 相关API会抛出异常**`无效上下文`**。
所以本插件的目的也就是解决上述两个问题:
- 在 [ 被调用端 ] 提供以 Dubbo 为基础的上下文环境
- 在 RPC 调用时将 Token 传递至 [ 被调用端 ],同时在调用结束时将 Token 回传至 [ 调用端 ]。
### 引入插件
在项目已经引入 Dubbo 的基础上继续添加依赖Consumer 端和 Provider 端都需要引入):
``` xml
<!-- Sa-Token 整合 Dubbo -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-context-dubbo</artifactId>
<version>${sa.top.version}</version>
</dependency>
```
然后我们就可以愉快的做到以下事情:
1. 在 [ 被调用端 ] 安全的调用 Sa-Token 相关 API。
2. 在 [ 调用端 ] 登录的会话,其登录状态可以自动传递到 [ 被调用端 ] 。
3. 在 [ 被调用端 ] 登录的会话,其登录状态也会自动回传到 [ 调用端 ] 。
但是我们仍具有以下限制:
1. [ 调用端 ] 与 [ 被调用端 ] 的 `SaStorage` 数据无法互通。
2. [ 被调用端 ] 执行的 `SaResponse.setHeader()`、`setStatus()` 等代码无效。
应该合理避开以上 API 的使用。
### RPC调用鉴权
在之前的 [Id-Token](/micro/id-token) 章节,我们演示了基于 Feign 的 RPC 调用鉴权,下面我们演示一下在 Dubbo 中如何集成 Id-Token 模块。
其实思路和 Feign 模式一致,在 [ 调用端 ] 追加 Id-Token 参数,在 [ 被调用端 ] 校验这个 Id-Token 参数:
- 校验通过:调用成功。
- 校验不通过:通过失败,抛出异常。
我们有两种方式完成整合。
##### 方式一、使用配置
直接在 `application.yml` 配置即可:
``` yml
sa-token:
# 打开 RPC 调用鉴权
check-id-token: true
```
##### 方式二、自建 Dubbo 过滤器校验
1、在 [ 调用端 ] 的 `\resources\META-INF\dubbo\` 目录新建 `org.apache.dubbo.rpc.Filter` 文件
``` html
dubboConsumerFilter=com.pj.DubboConsumerFilter
```
新建 `DubboConsumerFilter.java` 过滤器
``` java
package com.pj;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import cn.dev33.satoken.id.SaIdUtil;
/**
* Sa-Token 整合 Dubbo Consumer端过滤器
*/
@Activate(group = {CommonConstants.CONSUMER}, order = -10000)
public class DubboConsumerFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// 追加 Id-Token 参数
RpcContext.getContext().setAttachment(SaIdUtil.ID_TOKEN, SaIdUtil.getToken());
// 开始调用
return invoker.invoke(invocation);
}
}
```
2、在 [ 被调用端 ] 的 `\resources\META-INF\dubbo\` 目录新建 `org.apache.dubbo.rpc.Filter` 文件
``` html
dubboProviderFilter=com.pj.DubboProviderFilter
```
新建 `DubboProviderFilter.java` 过滤器
``` java
package com.pj;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import cn.dev33.satoken.id.SaIdUtil;
/**
* Sa-Token 整合 Dubbo Provider端过滤器
*/
@Activate(group = {CommonConstants.PROVIDER}, order = -10000)
public class DubboProviderFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// 取出 Id-Token 进行校验
String idToken = invocation.getAttachment(SaIdUtil.ID_TOKEN);
SaIdUtil.checkToken(idToken);
// 开始调用
return invoker.invoke(invocation);
}
}
```
然后我们就可以进行安全的 RPC 调用了,不带有 Id-Token 参数的调用都会抛出异常,无法调用成功。

View File

@ -19,9 +19,9 @@
| 系统架构 | 采用模式 | 简介 | 文档链接 |
| :-------- | :-------- | :-------- | :-------- |
| 前端同域 + 后端同 Redis | 模式一 | 共享 Cookie 同步会话 | [文档](/sso/sso-type1)、[示例](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-sso1-client) |
| 前端不同域 + 后端同 Redis | 模式二 | URL重定向传播会话 | [文档](/sso/sso-type2)、[示例](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-sso2-client) |
| 前端不同域 + 后端不同 Redis | 模式三 | Http请求获取会话 | [文档](/sso/sso-type3)、[示例](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-sso3-client) |
| 前端同域 + 后端同 Redis | 模式一 | 共享 Cookie 同步会话 | [文档](/sso/sso-type1)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso1-client) |
| 前端不同域 + 后端同 Redis | 模式二 | URL重定向传播会话 | [文档](/sso/sso-type2)、[示例](https://gitee.com/dromara/sa-token/blob/master/sa-token-demo/sa-token-demo-sso2-client) |
| 前端不同域 + 后端不同 Redis | 模式三 | Http请求获取会话 | [文档](/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`。

View File

@ -94,7 +94,8 @@ PS两者的区别在于**`模式1会覆盖yml中的配置模式2会与y
| jwtSecretKey | String | null | jwt秘钥 (只有集成 `sa-token-temp-jwt` 模块时此参数才会生效) |
| idTokenTimeout | long | 86400 | Id-Token的有效期 (单位: 秒) |
| basic | String | "" | Http Basic 认证的账号和密码 [参考Http Basic 认证](/up/basic-auth) |
| currDomain | null | "" | 配置当前项目的网络访问地址 |
| currDomain | Boolean | false | 是否校验Id-Token部分rpc插件有效 |
| checkIdToken | false | false | 配置当前项目的网络访问地址 |
| sso | Object | new SaSsoConfig() | SSO 单点登录相关配置 |
| cookie | Object | new SaCookieConfig() | Cookie配置对象 |

View File

@ -311,7 +311,7 @@
<img src="https://oss.dev33.cn/sa-token/link/maxkey.png" msg="业界领先的身份管理和认证产品">
</a>
<a href="http://forest.dtflyx.com/" target="_blank">
<img src="https://oss.dev33.cn/sa-token/link/forest-logo.png" msg="Forest能够帮助您使用更简单的方式编写Java的HTTP客户端">
<img src="https://oss.dev33.cn/sa-token/link/forest-logo.png" msg="Forest能够帮助您使用更简单的方式编写Java的HTTP客户端" nf>
</a>
<a href="https://jpom.io/" target="_blank">
<img src="https://oss.dev33.cn/sa-token/link/jpom.png" msg="一款简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件">
@ -442,6 +442,42 @@
content: content
});
})
// set 一下 img 的宽度
function setImgWidth(img) {
// 如果已经有了宽度参数,则不追加
if(img.src.indexOf('?') > -1) {
return;
}
// console.log(img.getAttribute('nf'));
if(img.getAttribute('nf') != null) {
return;
}
img.src = img.src + "?x-oss-process=image/resize,m_lfit,w_" + (img.width) + ",limit_0/auto-orient,0";
}
// set 一遍 img 的尺寸,防止失真
function f5ImgSize() {
$('.com-box-f .com-box a img').each(function() {
// console.log(this.src, this.width);
// 未加载完毕时则等待其load之后再设置
if(this.complete == false) {
this.onload = function() {
setImgWidth(this);
}
return;
}
// 追加宽度参数
setImgWidth(this);
})
}
if(window.innerWidth > 1800) {
if(navigator.userAgent.indexOf('WebKit') > -1) {
f5ImgSize();
}
}
</script>
</body>

View File

@ -26,6 +26,7 @@
<module>sa-token-spring-aop</module>
<module>sa-token-temp-jwt</module>
<module>sa-token-jwt</module>
<module>sa-token-context-dubbo</module>
</modules>
<dependencies>

View File

@ -0,0 +1,12 @@
target/
node_modules/
bin/
.settings/
unpackage/
.classpath
.project
.factorypath
.idea/

View File

@ -0,0 +1,41 @@
<?xml version='1.0' encoding='utf-8'?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-plugin</artifactId>
<version>1.27.0</version>
</parent>
<packaging>jar</packaging>
<name>sa-token-context-dubbo</name>
<artifactId>sa-token-context-dubbo</artifactId>
<description>sa-token-context-dubbo</description>
<dependencies>
<!-- sa-token-core -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<version>${sa-token-version}</version>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.11</version>
</dependency>
<!-- <dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.11</version>
</dependency> -->
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package cn.dev33.satoken.context.dubbo;
import cn.dev33.satoken.context.second.SaTokenSecondContext;
import cn.dev33.satoken.context.second.SaTokenSecondContextCreator;
/**
* Sa-Token 二级Context - 创建器 [Dubbo版]
*
* @author kong
*
*/
public class SaTokenSecondContextCreatorForDubbo implements SaTokenSecondContextCreator {
@Override
public SaTokenSecondContext create() {
return new SaTokenSecondContextForDubbo();
}
}

View File

@ -0,0 +1,47 @@
package cn.dev33.satoken.context.dubbo;
import org.apache.dubbo.rpc.RpcContext;
import cn.dev33.satoken.context.dubbo.model.SaRequestForDubbo;
import cn.dev33.satoken.context.dubbo.model.SaResponseForDubbo;
import cn.dev33.satoken.context.dubbo.model.SaStorageForDubbo;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.context.second.SaTokenSecondContext;
import cn.dev33.satoken.exception.ApiDisabledException;
/**
* Sa-Token 上下文 [Dubbo版本]
*
* @author kong
*
*/
public class SaTokenSecondContextForDubbo implements SaTokenSecondContext {
@Override
public SaRequest getRequest() {
return new SaRequestForDubbo(RpcContext.getContext());
}
@Override
public SaResponse getResponse() {
return new SaResponseForDubbo(RpcContext.getContext());
}
@Override
public SaStorage getStorage() {
return new SaStorageForDubbo(RpcContext.getContext());
}
@Override
public boolean matchPath(String pattern, String path) {
throw new ApiDisabledException();
}
@Override
public boolean isValid() {
return RpcContext.getContext() != null;
}
}

View File

@ -0,0 +1,48 @@
package cn.dev33.satoken.context.dubbo.filter;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.id.SaIdUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaTokenConsts;
/**
*
* Sa-Token 整合 Dubbo Consumer端过滤器
*
* @author kong
*
*/
@Activate(group = {CommonConstants.CONSUMER}, order = -10000)
public class SaTokenDubboConsumerFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// 追加 Id-Token 参数
if(SaManager.getConfig().getCheckIdToken()) {
RpcContext.getContext().setAttachment(SaIdUtil.ID_TOKEN, SaIdUtil.getToken());
}
// 1. 调用前向下传递会话Token
RpcContext.getContext().setAttachment(SaTokenConsts.JUST_CREATED, StpUtil.getTokenValueNotCut());
// 2. 开始调用
Result invoke = invoker.invoke(invocation);
// 3. 调用后解析回传的Token值
StpUtil.setTokenValue(invoke.getAttachment(SaTokenConsts.JUST_CREATED_NOT_PREFIX));
// note
return invoke;
}
}

View File

@ -0,0 +1,37 @@
package cn.dev33.satoken.context.dubbo.filter;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.id.SaIdUtil;
/**
*
* Sa-Token 整合 Dubbo Provider端过滤器
*
* @author kong
*
*/
@Activate(group = {CommonConstants.PROVIDER}, order = -10000)
public class SaTokenDubboProviderFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// RPC 调用鉴权
if(SaManager.getConfig().getCheckIdToken()) {
String idToken = invocation.getAttachment(SaIdUtil.ID_TOKEN);
SaIdUtil.checkToken(idToken);
}
// 开始调用
return invoker.invoke(invocation);
}
}

View File

@ -0,0 +1,99 @@
package cn.dev33.satoken.context.dubbo.model;
import org.apache.dubbo.rpc.RpcContext;
import cn.dev33.satoken.context.model.SaRequest;
/**
* Request for Dubbo
*
* @author kong
*
*/
public class SaRequestForDubbo implements SaRequest {
/**
* 底层对象
*/
protected RpcContext rpcContext;
/**
* 实例化
* @param rpcContext rpcContext对象
*/
public SaRequestForDubbo(RpcContext rpcContext) {
this.rpcContext = rpcContext;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return rpcContext;
}
/**
* [请求体] 里获取一个值
*/
@Override
public String getParam(String name) {
// 不传播 url 参数
return null;
}
/**
* [请求头] 里获取一个值
*/
@Override
public String getHeader(String name) {
// 不传播 header 参数
return null;
}
/**
* [Cookie作用域] 里获取一个值
*/
@Override
public String getCookieValue(String name) {
// 不传播 cookie 参数
return null;
}
/**
* 返回当前请求path (不包括上下文名称)
*/
@Override
public String getRequestPath() {
// 不传播 requestPath
return null;
}
/**
* 返回当前请求的urlhttp://xxx.com/test
* @return see note
*/
public String getUrl() {
// 不传播 url
return null;
}
/**
* 返回当前请求的类型
*/
@Override
public String getMethod() {
// 不传播 method
return null;
}
/**
* 转发请求
*/
@Override
public Object forward(String path) {
// 不传播 forward 动作
return null;
}
}

View File

@ -0,0 +1,74 @@
package cn.dev33.satoken.context.dubbo.model;
import org.apache.dubbo.rpc.RpcContext;
import cn.dev33.satoken.context.model.SaResponse;
/**
* Response for Servlet
* @author kong
*
*/
public class SaResponseForDubbo implements SaResponse {
/**
* 底层Request对象
*/
protected RpcContext rpcContext;
/**
* 实例化
* @param rpcContext rpcContext对象
*/
public SaResponseForDubbo(RpcContext rpcContext) {
this.rpcContext = rpcContext;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return rpcContext;
}
/**
* 设置响应状态码
*/
@Override
public SaResponse setStatus(int sc) {
// 不回传 status 状态
return this;
}
/**
* 在响应头里写入一个值
*/
@Override
public SaResponse setHeader(String name, String value) {
// 不回传 header响应头
return this;
}
/**
* 在响应头里添加一个值
* @param name 名字
* @param value
* @return 对象自身
*/
public SaResponse addHeader(String name, String value) {
// 不回传 header响应头
return this;
}
/**
* 重定向
*/
@Override
public Object redirect(String url) {
// 不回传 重定向 动作
return null;
}
}

View File

@ -0,0 +1,64 @@
package cn.dev33.satoken.context.dubbo.model;
import org.apache.dubbo.rpc.RpcContext;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.util.SaTokenConsts;
/**
* Storage for Servlet
* @author kong
*
*/
public class SaStorageForDubbo implements SaStorage {
/**
* 底层对象
*/
protected RpcContext rpcContext;
/**
* 实例化
* @param rpcContext rpcContext对象
*/
public SaStorageForDubbo(RpcContext rpcContext) {
this.rpcContext = rpcContext;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return rpcContext;
}
/**
* [Request作用域] 里写入一个值
*/
@Override
public void set(String key, Object value) {
rpcContext.setObjectAttachment(key, value);
// 如果是token写入则回传到Consumer端
if(key.equals(SaTokenConsts.JUST_CREATED_NOT_PREFIX)) {
RpcContext.getServerContext().setAttachment(key, value);
}
}
/**
* [Request作用域] 里获取一个值
*/
@Override
public Object get(String key) {
return rpcContext.getObjectAttachment(key);
}
/**
* [Request作用域] 里删除一个值
*/
@Override
public void delete(String key) {
rpcContext.removeAttachment(key);
}
}

View File

@ -0,0 +1,2 @@
saTokenDubboConsumerFilter=cn.dev33.satoken.context.dubbo.filter.SaTokenDubboConsumerFilter
saTokenDubboProviderFilter=cn.dev33.satoken.context.dubbo.filter.SaTokenDubboProviderFilter

View File

@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.context.dubbo.SaTokenSecondContextCreatorForDubbo