初始代码
This commit is contained in:
71
yunzhupaas-boot-common/yunzhupaas-common-auth/pom.xml
Normal file
71
yunzhupaas-boot-common/yunzhupaas-common-auth/pom.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?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/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>yunzhupaas-boot-common</artifactId>
|
||||
<groupId>com.yunzhupaas</groupId>
|
||||
<version>5.2.0-RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>yunzhupaas-common-auth</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.yunzhupaas</groupId>
|
||||
<artifactId>yunzhupaas-common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-jwt</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-jwt</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
<!--<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
</exclusion>
|
||||
</exclusions>-->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>boot3</id>
|
||||
<activation>
|
||||
<jdk>[17,)</jdk>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>boot2</id>
|
||||
<activation>
|
||||
<jdk>(,17)</jdk>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.yunzhupaas.config;
|
||||
|
||||
import com.yunzhupaas.base.UserInfo;
|
||||
import com.yunzhupaas.model.tenant.TenantVO;
|
||||
import com.yunzhupaas.util.TenantHolder;
|
||||
import com.yunzhupaas.util.UserProvider;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* 提供一个全局的Spring线程池对象
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @version V3.1.0
|
||||
* @copyright 深圳市乐程软件有限公司(http://www.szlecheng.cn)
|
||||
* @date 2024-12-10
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
@AllArgsConstructor
|
||||
public class AsyncConfig implements AsyncConfigurer {
|
||||
|
||||
|
||||
// private final Map<Object, Integer> asyncTaskCount = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
@Primary
|
||||
@Bean("threadPoolTaskExecutor")
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
// 设置线程池核心容量
|
||||
executor.setCorePoolSize(10);
|
||||
// 设置线程池最大容量
|
||||
executor.setMaxPoolSize(50);
|
||||
// 设置任务队列长度
|
||||
executor.setQueueCapacity(2000);
|
||||
// 设置线程超时时间
|
||||
executor.setKeepAliveSeconds(30);
|
||||
// 设置线程名称前缀
|
||||
executor.setThreadNamePrefix("sysTaskExecutor");
|
||||
// 设置任务丢弃后的处理策略
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
executor.setTaskDecorator( r ->{
|
||||
//实现线程上下文穿透, 异步线程内无法获取之前的Request,租户信息等, 如有新的上下文对象在此处添加
|
||||
//此方法在请求结束后在无法获取request, 下方完整异步Servlet请求
|
||||
// RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
TenantVO tenantVO = TenantHolder.getLocalTenantCache();
|
||||
UserInfo userInfo = UserProvider.getUser();
|
||||
return () -> {
|
||||
try {
|
||||
// if(attributes!= null) {
|
||||
// RequestContextHolder.setRequestAttributes(attributes);
|
||||
// }
|
||||
if(tenantVO != null){
|
||||
TenantHolder.setLocalTenantCache(tenantVO);
|
||||
}
|
||||
UserProvider.setLocalLoginUser(userInfo);
|
||||
r.run();
|
||||
} finally {
|
||||
UserProvider.clearLocalUser();
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
TenantHolder.clearLocalTenantCache();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/*
|
||||
//Tomcat异步Servlet只能增加吞吐量, 不能减少响应时间
|
||||
executor.setTaskDecorator( r ->{
|
||||
//实现线程上下文穿透, 异步线程内无法获取之前的Request,租户信息等, 如有新的上下文对象在此处添加
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
String dataSourceId = DataSourceContextHolder.getDatasourceId();
|
||||
String dataSourceName = DataSourceContextHolder.getDatasourceName();
|
||||
final AsyncContext asyncContext = (AsyncContext) Optional.ofNullable(null).orElseGet(()->{
|
||||
if(attributes!= null) {
|
||||
HttpServletRequest request = ((ServletRequestAttributes) attributes).getRequest();
|
||||
HttpServletResponse response = ((ServletRequestAttributes) attributes).getResponse();
|
||||
synchronized (AsyncConfig.class) {
|
||||
//开启多个异步
|
||||
AsyncContext tmpAsyncContext = request.isAsyncStarted() ? request.getAsyncContext() : request.startAsync(request, response);
|
||||
asyncTaskCount.put(tmpAsyncContext, asyncTaskCount.getOrDefault(tmpAsyncContext, 0) +1);
|
||||
return tmpAsyncContext;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return () -> {
|
||||
if (asyncContext != null) {
|
||||
asyncContext.start(() -> {
|
||||
if (dataSourceId != null || dataSourceName != null) {
|
||||
DataSourceContextHolder.setDatasource(dataSourceId, dataSourceName);
|
||||
}
|
||||
RequestContextHolder.setRequestAttributes(attributes);
|
||||
try {
|
||||
r.run();
|
||||
}finally{
|
||||
synchronized (AsyncConfig.class){
|
||||
//多个异步 最后一个执行关闭
|
||||
int count = asyncTaskCount.get(asyncContext)-1;
|
||||
if(count > 0){
|
||||
asyncTaskCount.put(asyncContext, count);
|
||||
}else{
|
||||
asyncTaskCount.remove(asyncContext);
|
||||
asyncContext.complete();
|
||||
}
|
||||
}
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
DataSourceContextHolder.clearDatasourceType();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (dataSourceId != null || dataSourceName != null) {
|
||||
DataSourceContextHolder.setDatasource(dataSourceId, dataSourceName);
|
||||
}
|
||||
try {
|
||||
r.run();
|
||||
}finally{
|
||||
DataSourceContextHolder.clearDatasourceType();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
*/
|
||||
return executor;
|
||||
}
|
||||
|
||||
|
||||
@Bean("defaultExecutor")
|
||||
public ThreadPoolTaskExecutor getAsyncExecutorDef(@Qualifier("threadPoolTaskExecutor") Executor executor) {
|
||||
return (ThreadPoolTaskExecutor) executor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.yunzhupaas.config;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import com.yunzhupaas.consts.AuthConsts;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
@Configuration
|
||||
public class AuthAutoConfigration {
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "oauth.login")
|
||||
public SaTokenConfig getYunzhupaasTokenConfig() {
|
||||
return new YunzhupaasTokenConfig();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConfigurationProperties(prefix = YunzhupaasOauthConfig.PREFIX)
|
||||
public YunzhupaasOauthConfig getYunzhupaasOauthConfig() {
|
||||
return new YunzhupaasOauthConfig();
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean(AuthConsts.ACCOUNT_LOGIC_BEAN_DEFAULT)
|
||||
public StpLogic getYunzhupaasTokenJwtLogic() {
|
||||
return new StpLogicJwtForSimple(AuthConsts.ACCOUNT_TYPE_DEFAULT);
|
||||
}
|
||||
|
||||
@Bean(AuthConsts.ACCOUNT_LOGIC_BEAN_TENANT)
|
||||
public StpLogic getYunzhupaasTenantTokenJwtLogic() {
|
||||
return new StpLogicJwtForSimple(AuthConsts.ACCOUNT_TYPE_TENANT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.yunzhupaas.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
@Data
|
||||
public class YunzhupaasOauthConfig {
|
||||
|
||||
public static final String PREFIX = "oauth";
|
||||
|
||||
/**
|
||||
* 服务器域名
|
||||
*
|
||||
* @see ConfigValueUtil#getApiDomain()
|
||||
*/
|
||||
@Deprecated
|
||||
@Value("${config.ApiDomain:}")
|
||||
private String yunzhupaasDomain;
|
||||
|
||||
/**
|
||||
* 开启单点登录, 需额外代码支持
|
||||
*/
|
||||
private Boolean ssoEnabled = false;
|
||||
|
||||
/**
|
||||
* 后端登录完整路径路径
|
||||
*/
|
||||
private String loginPath;
|
||||
|
||||
/**
|
||||
* 默认发起的登录协议
|
||||
*/
|
||||
private String defaultSSO = "cas";
|
||||
|
||||
/**
|
||||
* 轮询Ticket有效期, 秒
|
||||
*/
|
||||
private long ticketTimeout = 60;
|
||||
|
||||
/**
|
||||
* pc端服务器域名
|
||||
*
|
||||
* @see ConfigValueUtil#getFrontDomain()
|
||||
*/
|
||||
@Deprecated
|
||||
@Value("${config.FrontDomain:}")
|
||||
private String yunzhupaasFrontDomain;
|
||||
|
||||
/**
|
||||
* app端服务器域名
|
||||
*
|
||||
* @see ConfigValueUtil#getAppDomain()
|
||||
*/
|
||||
@Deprecated
|
||||
@Value("${config.AppDomain:}")
|
||||
private String yunzhupaasAppDomain;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.yunzhupaas.config;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import com.yunzhupaas.consts.AuthConsts;
|
||||
import com.yunzhupaas.model.BaseSystemInfo;
|
||||
import com.yunzhupaas.util.Constants;
|
||||
import com.yunzhupaas.util.TenantProvider;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
public class YunzhupaasTokenConfig extends SaTokenConfig {
|
||||
|
||||
@Override
|
||||
public long getTimeout() {
|
||||
BaseSystemInfo baseSystemInfo = getSycConfig();
|
||||
if (baseSystemInfo == null) {
|
||||
return super.getTimeout();
|
||||
} else {
|
||||
return Long.parseLong(getSycConfig().getTokenTimeout()) * 60L;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getIsConcurrent() {
|
||||
BaseSystemInfo baseSystemInfo = getSycConfig();
|
||||
if (baseSystemInfo == null) {
|
||||
return super.getIsConcurrent();
|
||||
} else {
|
||||
return Optional.ofNullable(getSycConfig().getSingleLogin()).orElse(1) == 2;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJwtSecretKey() {
|
||||
String secrekey = super.getJwtSecretKey();
|
||||
if (secrekey == null) {
|
||||
return AuthConsts.JWT_SECRET;
|
||||
}
|
||||
return secrekey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrDomain() {
|
||||
return super.getCurrDomain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTokenPrefix() {
|
||||
return AuthConsts.TOKEN_PREFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getTokenSessionCheckLogin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getIsPrint() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getIsShare() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTokenName() {
|
||||
return Constants.AUTHORIZATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getIsReadCookie() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getIsReadBody() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getIsReadHeader() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLoginCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private BaseSystemInfo getSycConfig() {
|
||||
return TenantProvider.getBaseSystemInfo();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.yunzhupaas.consts;
|
||||
|
||||
import cn.dev33.satoken.same.SaSameUtil;
|
||||
import com.yunzhupaas.service.UserDetailService;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
public class AuthConsts {
|
||||
|
||||
public static final String DEF_TENANT_ID = "";
|
||||
public static final String DEF_TENANT_DB = "";
|
||||
|
||||
public static final String ACCOUNT_TYPE_DEFAULT = "login";
|
||||
public static final String ACCOUNT_TYPE_TENANT = "tenant";
|
||||
public static final String ACCOUNT_LOGIC_BEAN_DEFAULT = "defaultStpLogic";
|
||||
public static final String ACCOUNT_LOGIC_BEAN_TENANT = "tenantStpLogic";
|
||||
|
||||
public static final String PAR_GRANT_TYPE = "grant_type";
|
||||
|
||||
public static final String SYSTEM_INFO = "system_info";
|
||||
|
||||
/**
|
||||
* 跨服务调用验证KEY
|
||||
*/
|
||||
public static final String INNER_TOKEN_KEY = SaSameUtil.SAME_TOKEN;
|
||||
|
||||
/**
|
||||
* 网关调用验证KEY
|
||||
*/
|
||||
public static final String INNER_GATEWAY_TOKEN_KEY = INNER_TOKEN_KEY + "_GATEWAY";
|
||||
|
||||
public static final String TENANT_SESSION = "tenant:";
|
||||
|
||||
public static final String TOKEN_PREFIX = "bearer";
|
||||
public static final String TOKEN_PREFIX_SP = TOKEN_PREFIX + " ";
|
||||
|
||||
public static final String PARAMS_YUNZHUPAAS_TICKET = "yunzhupaas_ticket";
|
||||
public static final String PARAMS_SSO_LOGOUT_TICKET = "ticket";
|
||||
|
||||
public static final Integer REDIRECT_PAGETYPE_LOGIN = 1;
|
||||
public static final Integer REDIRECT_PAGETYPE_LOGOUT = 2;
|
||||
|
||||
public static final Integer TMP_TOKEN_UNLOGIN = -1;
|
||||
public static final Integer TMP_TOKEN_ERRLOGIN = -2;
|
||||
|
||||
public static final String ONLINE_TICKET_KEY = "online_ticket:";
|
||||
public static final String ONLINE_TICKET_TOKEN = "online_token";
|
||||
|
||||
public static final String JWT_SECRET = "WviMjFNC72VKwGqm5LPoheQo5XN9iN4d";
|
||||
|
||||
/**
|
||||
* clientId
|
||||
*/
|
||||
public static final String Client_Id = "Client_Id";
|
||||
|
||||
/**
|
||||
* 用户信息获取方式 account
|
||||
*/
|
||||
public static final String USERDETAIL_ACCOUNT = UserDetailService.USER_DETAIL_PREFIX + "UserAccount";
|
||||
/**
|
||||
* 用户信息获取方式 user_id
|
||||
*/
|
||||
public static final String USERDETAIL_USER_ID = UserDetailService.USER_DETAIL_PREFIX + "UserId";
|
||||
|
||||
/**
|
||||
* 认证方式 常规账号密码
|
||||
*/
|
||||
public static final String GRANT_TYPE_PASSWORD = "password";
|
||||
/**
|
||||
* 认证方式 单点 CAS
|
||||
*/
|
||||
public static final String GRANT_TYPE_CAS = "cas";
|
||||
/**
|
||||
* 认证方式 单点 OAUTH
|
||||
*/
|
||||
public static final String GRANT_TYPE_OAUTH = "auth2";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.yunzhupaas.consts;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DeviceType {
|
||||
|
||||
/**
|
||||
* pc端
|
||||
*/
|
||||
PC("PC"),
|
||||
|
||||
/**
|
||||
* app端 手机都归为移动 自行扩展
|
||||
*/
|
||||
APP("APP"),
|
||||
|
||||
/**
|
||||
* 程序运行中使用的无限制临时用户
|
||||
*/
|
||||
TEMPUSER("TEMPUSER"),
|
||||
|
||||
|
||||
/**
|
||||
* 程序运行中使用的限制临时用户, 不可访问主系统, CurrentUser接口报错
|
||||
*/
|
||||
TEMPUSERLIMITED("TEMPUSERLIMITED");
|
||||
|
||||
|
||||
private final String device;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.yunzhupaas.consts;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LoginTicketStatus {
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
*/
|
||||
Success(1),
|
||||
/**
|
||||
* 未登录
|
||||
*/
|
||||
UnLogin(2),
|
||||
/**
|
||||
* 登录失败
|
||||
*/
|
||||
ErrLogin(3),
|
||||
/**
|
||||
* 未绑定
|
||||
*/
|
||||
UnBind(4),
|
||||
/**
|
||||
* 失效
|
||||
*/
|
||||
Invalid(5),
|
||||
/**
|
||||
* 多租户
|
||||
*/
|
||||
Multitenancy(6),
|
||||
/**
|
||||
* 第三方账号未绑定账号,请绑定后重试
|
||||
*/
|
||||
UnBindMes(7),;
|
||||
|
||||
private int status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.yunzhupaas.consts;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ScanCodeTicketStatus {
|
||||
|
||||
/**
|
||||
* 已失效
|
||||
*/
|
||||
Invalid(-1),
|
||||
/**
|
||||
* 未扫码
|
||||
*/
|
||||
UnScanCode(0),
|
||||
/**
|
||||
* 已扫码
|
||||
*/
|
||||
ScanCode(1),
|
||||
/**
|
||||
* 登录成功
|
||||
*/
|
||||
Success(2);
|
||||
|
||||
|
||||
private int status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
package com.yunzhupaas.granter;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.yunzhupaas.base.ActionResult;
|
||||
import com.yunzhupaas.base.UserInfo;
|
||||
import com.yunzhupaas.config.ConfigValueUtil;
|
||||
import com.yunzhupaas.constant.MsgCode;
|
||||
import com.yunzhupaas.consts.AuthConsts;
|
||||
import com.yunzhupaas.consts.DeviceType;
|
||||
import com.yunzhupaas.consts.LoginTicketStatus;
|
||||
import com.yunzhupaas.exception.LoginException;
|
||||
import com.yunzhupaas.exception.TenantDatabaseException;
|
||||
import com.yunzhupaas.model.BaseSystemInfo;
|
||||
import com.yunzhupaas.model.LoginTicketModel;
|
||||
import com.yunzhupaas.model.logout.LogoutResultModel;
|
||||
import com.yunzhupaas.model.tenant.TenantVO;
|
||||
import com.yunzhupaas.service.LoginService;
|
||||
import com.yunzhupaas.util.RedisUtil;
|
||||
import com.yunzhupaas.util.TenantProvider;
|
||||
import com.yunzhupaas.util.TicketUtil;
|
||||
import com.yunzhupaas.util.UserProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.yunzhupaas.consts.AuthConsts.DEF_TENANT_DB;
|
||||
import static com.yunzhupaas.consts.AuthConsts.DEF_TENANT_ID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
public abstract class AbstractTokenGranter implements TokenGranter, Ordered {
|
||||
|
||||
@Autowired(required = false)
|
||||
protected LoginService loginService;
|
||||
@Autowired
|
||||
protected UserProvider userProvider;
|
||||
@Autowired
|
||||
protected ConfigValueUtil configValueUtil;
|
||||
@Autowired
|
||||
protected RedisUtil redisUtil;
|
||||
|
||||
protected static PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
private String authenticationUrl;
|
||||
|
||||
public AbstractTokenGranter(String authenticationUrl) {
|
||||
this.authenticationUrl = authenticationUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 最终登录用户
|
||||
*
|
||||
* @param userInfo 包含账户名, 登录方式
|
||||
* @return
|
||||
*/
|
||||
protected String loginAccount(UserInfo userInfo, BaseSystemInfo baseSystemInfo) throws LoginException {
|
||||
try {
|
||||
// 获取用户实现类接口名称
|
||||
userInfo.setUserDetailKey(getUserDetailKey());
|
||||
// 获取登录信息
|
||||
userInfo = getUserInfo(userInfo, baseSystemInfo);
|
||||
// 预登陆
|
||||
preLogin(userInfo, baseSystemInfo);
|
||||
// 登录
|
||||
login(userInfo, baseSystemInfo);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
loginFailure(userInfo, baseSystemInfo, e);
|
||||
} catch (Exception e1) {
|
||||
throw e1;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
loginSuccess(userInfo, baseSystemInfo);
|
||||
// 返回token信息
|
||||
return userInfo.getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换多租户
|
||||
*
|
||||
* @param userInfo
|
||||
* @return userAccount, tenantId, tenandDb
|
||||
* @throws LoginException
|
||||
*/
|
||||
protected UserInfo switchTenant(UserInfo userInfo) throws LoginException {
|
||||
if (configValueUtil.isMultiTenancy()) {
|
||||
userInfo = loginService.getTenantAccount(userInfo);
|
||||
return userInfo;
|
||||
|
||||
}
|
||||
userInfo.setTenantId(DEF_TENANT_ID);
|
||||
userInfo.setTenantDbConnectionString(DEF_TENANT_DB);
|
||||
userInfo.setTenantDbType(TenantVO.NONE);
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统配置
|
||||
*
|
||||
* @param userInfo
|
||||
* @return
|
||||
*/
|
||||
protected BaseSystemInfo getSysconfig(UserInfo userInfo) throws LoginException {
|
||||
BaseSystemInfo baseSystemInfo = loginService.getBaseSystemConfig(userInfo.getTenantId());
|
||||
if (baseSystemInfo != null && baseSystemInfo.getSingleLogin() != null) {
|
||||
TenantProvider.setBaseSystemInfo(baseSystemInfo);
|
||||
} else {
|
||||
throw new TenantDatabaseException().setLogMsg(MsgCode.LOG110.get());
|
||||
}
|
||||
return baseSystemInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录设备
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected DeviceType getDeviceType() {
|
||||
return UserProvider.getDeviceForAgent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成登录用户信息
|
||||
*
|
||||
* @param userInfo
|
||||
* @return
|
||||
*/
|
||||
protected UserInfo getUserInfo(UserInfo userInfo, BaseSystemInfo sysConfigInfo) throws LoginException {
|
||||
userInfo.setGrantType(getGrantType());
|
||||
userInfo = loginService.userInfo(userInfo, sysConfigInfo);
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录前执行
|
||||
*
|
||||
* @param userInfo
|
||||
* @param baseSystemInfo
|
||||
*/
|
||||
protected void preLogin(UserInfo userInfo, BaseSystemInfo baseSystemInfo) throws LoginException {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录操作
|
||||
*
|
||||
* @param userInfo
|
||||
* @param baseSystemInfo
|
||||
*/
|
||||
protected void login(UserInfo userInfo, BaseSystemInfo baseSystemInfo) throws LoginException {
|
||||
UserProvider.login(userInfo, getLoginModel(userInfo, baseSystemInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功触发
|
||||
*
|
||||
* @param userInfo
|
||||
* @param baseSystemInfo
|
||||
*/
|
||||
protected void loginSuccess(UserInfo userInfo, BaseSystemInfo baseSystemInfo) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录失败触发
|
||||
*
|
||||
* @param baseSystemInfo
|
||||
*/
|
||||
protected void loginFailure(UserInfo userInfo, BaseSystemInfo baseSystemInfo, Exception e) {
|
||||
|
||||
}
|
||||
|
||||
protected abstract String getUserDetailKey();
|
||||
|
||||
protected String createToken(UserInfo userInfo, BaseSystemInfo baseSystemInfo) {
|
||||
// 登录
|
||||
UserProvider.login(userInfo, getLoginModel(userInfo, baseSystemInfo));
|
||||
return StpUtil.getTokenValueNotCut();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新轮询结果为成功
|
||||
*/
|
||||
protected void updateTicketSuccess(UserInfo userInfo) {
|
||||
String ticket = getYunzhupaasTicket();
|
||||
if (!ticket.isEmpty()) {
|
||||
LoginTicketModel loginTicketModel = new LoginTicketModel()
|
||||
.setStatus(LoginTicketStatus.Success.getStatus())
|
||||
.setValue(StpUtil.getTokenValueNotCut())
|
||||
.setTheme(userInfo.getTheme());
|
||||
TicketUtil.updateTicket(ticket, loginTicketModel, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新轮询结果为失败
|
||||
*/
|
||||
protected void updateTicketError(String msg) {
|
||||
String ticket = getYunzhupaasTicket();
|
||||
if (!ticket.isEmpty()) {
|
||||
LoginTicketModel loginTicketModel = new LoginTicketModel()
|
||||
.setStatus(LoginTicketStatus.ErrLogin.getStatus())
|
||||
.setValue(msg);
|
||||
TicketUtil.updateTicket(ticket, loginTicketModel, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取轮询ticket
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getYunzhupaasTicket() {
|
||||
return SaHolder.getRequest().getParam(AuthConsts.PARAMS_YUNZHUPAAS_TICKET, "");
|
||||
}
|
||||
|
||||
protected boolean isValidYunzhupaasTicket() {
|
||||
String yunzhupaasTicket = getYunzhupaasTicket();
|
||||
if (!yunzhupaasTicket.isEmpty()) {
|
||||
LoginTicketModel loginTicketModel = TicketUtil.parseTicket(yunzhupaasTicket);
|
||||
if (loginTicketModel == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录参数
|
||||
*
|
||||
* @param userInfo
|
||||
* @param baseSystemInfo
|
||||
* @return
|
||||
*/
|
||||
protected SaLoginModel getLoginModel(UserInfo userInfo, BaseSystemInfo baseSystemInfo) {
|
||||
SaLoginModel loginModel = new SaLoginModel();
|
||||
loginModel.setTimeout(userInfo.getTokenTimeout() * 60L);
|
||||
loginModel.setExtraData(getTokenExtraData(userInfo, baseSystemInfo));
|
||||
if (userInfo.getLoginDevice() == null) {
|
||||
loginModel.setDevice(getDeviceType().getDevice());
|
||||
userInfo.setLoginDevice(loginModel.device);
|
||||
} else {
|
||||
loginModel.setDevice(userInfo.getLoginDevice());
|
||||
}
|
||||
return loginModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取额外的JWT内容
|
||||
*
|
||||
* @param userInfo
|
||||
* @param baseSystemInfo
|
||||
* @return
|
||||
*/
|
||||
protected Map<String, Object> getTokenExtraData(UserInfo userInfo, BaseSystemInfo baseSystemInfo) {
|
||||
Map<String, Object> tokenInfo = new HashMap<>();
|
||||
// tokenInfo.put("token", StpUtil.getTokenValue());
|
||||
tokenInfo.put("singleLogin", baseSystemInfo == null ? null : baseSystemInfo.getSingleLogin());
|
||||
tokenInfo.put("user_name", userInfo.getUserAccount());
|
||||
tokenInfo.put("user_id", userInfo.getUserId());
|
||||
tokenInfo.put("exp", userInfo.getOverdueTime().getTime());
|
||||
tokenInfo.put("token", userInfo.getId());
|
||||
return tokenInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult<LogoutResultModel> logout() {
|
||||
UserProvider.logout();
|
||||
return ActionResult.success();
|
||||
}
|
||||
|
||||
protected abstract String getGrantType();
|
||||
|
||||
@Override
|
||||
public boolean requiresAuthentication() {
|
||||
String path = SaHolder.getRequest().getRequestPath();
|
||||
if (path != null && path.startsWith("/api/oauth")) {
|
||||
path = path.replace("/api/oauth", "");
|
||||
}
|
||||
return pathMatcher.match(authenticationUrl, path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.yunzhupaas.granter;
|
||||
|
||||
import com.yunzhupaas.base.ActionResult;
|
||||
import com.yunzhupaas.exception.LoginException;
|
||||
import com.yunzhupaas.model.logout.LogoutResultModel;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
public interface TokenGranter {
|
||||
|
||||
ActionResult granter(Map<String, String> loginParameters) throws LoginException;
|
||||
|
||||
|
||||
ActionResult<LogoutResultModel> logout();
|
||||
|
||||
boolean requiresAuthentication();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.yunzhupaas.granter;
|
||||
|
||||
import com.yunzhupaas.base.UserInfo;
|
||||
import com.yunzhupaas.config.YunzhupaasOauthConfig;
|
||||
import com.yunzhupaas.constant.MsgCode;
|
||||
import com.yunzhupaas.consts.AuthConsts;
|
||||
import com.yunzhupaas.exception.LoginException;
|
||||
import com.yunzhupaas.util.UserProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
@Component
|
||||
public class TokenGranterBuilder {
|
||||
|
||||
@Autowired
|
||||
private UserProvider userProvider;
|
||||
@Autowired
|
||||
private YunzhupaasOauthConfig oauthConfig;
|
||||
|
||||
private final Map<String, TokenGranter> granterPool = new ConcurrentHashMap<>();
|
||||
|
||||
public TokenGranterBuilder(Map<String, TokenGranter> granterPool) {
|
||||
granterPool.forEach(this.granterPool::put);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取TokenGranter
|
||||
*
|
||||
* @param grantType 授权类型
|
||||
* @return ITokenGranter
|
||||
*/
|
||||
public TokenGranter getGranter(String grantType) throws LoginException {
|
||||
TokenGranter tokenGranter = null;
|
||||
if (!oauthConfig.getSsoEnabled()) {
|
||||
tokenGranter = granterPool.get(grantType);
|
||||
}
|
||||
if (tokenGranter == null) {
|
||||
// URL匹配
|
||||
for (TokenGranter value : granterPool.values()) {
|
||||
if (value.requiresAuthentication()) {
|
||||
tokenGranter = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tokenGranter == null) {
|
||||
if (oauthConfig.getSsoEnabled()) {
|
||||
throw new LoginException(MsgCode.LOG111.get());
|
||||
} else {
|
||||
throw new LoginException(MsgCode.LOG112.get());
|
||||
}
|
||||
}
|
||||
return tokenGranter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户的TokenGranter
|
||||
*
|
||||
* @return
|
||||
* @throws LoginException
|
||||
*/
|
||||
public TokenGranter getGranterByLogin(String grandType) {
|
||||
if (grandType == null || grandType.isEmpty()) {
|
||||
UserInfo userInfo = userProvider.get();
|
||||
if (userInfo.getGrantType() != null) {
|
||||
grandType = userInfo.getGrantType();
|
||||
} else {
|
||||
grandType = AuthConsts.GRANT_TYPE_PASSWORD;
|
||||
}
|
||||
}
|
||||
return granterPool.get(grandType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.yunzhupaas.granter;
|
||||
|
||||
import com.yunzhupaas.consts.AuthConsts;
|
||||
import com.yunzhupaas.service.UserDetailService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
@Component
|
||||
public class UserDetailsServiceBuilder {
|
||||
|
||||
|
||||
private final Map<String, UserDetailService> userDetailServices = new ConcurrentHashMap<>();
|
||||
|
||||
public UserDetailsServiceBuilder(Map<String, UserDetailService> userDetailServices) {
|
||||
userDetailServices.forEach(this.userDetailServices::put);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据类型获取合适的UserDetailService
|
||||
* @param detailType
|
||||
* @return
|
||||
*/
|
||||
public UserDetailService getUserDetailService(String detailType){
|
||||
if(detailType == null){
|
||||
detailType = AuthConsts.USERDETAIL_ACCOUNT;
|
||||
}
|
||||
return userDetailServices.get(detailType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.yunzhupaas.model;
|
||||
|
||||
import com.yunzhupaas.consts.LoginTicketStatus;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 轮询登录模型
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class LoginTicketModel {
|
||||
|
||||
/**
|
||||
* 状态
|
||||
* @see LoginTicketStatus
|
||||
*/
|
||||
@NonNull
|
||||
private int status = LoginTicketStatus.UnLogin.getStatus();
|
||||
|
||||
/**
|
||||
* 额外的值, 登录Token、第三方登录的ID
|
||||
*/
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 前端主题
|
||||
*/
|
||||
private String theme;
|
||||
/**
|
||||
* 票据有效期, 时间戳
|
||||
*/
|
||||
private Long ticketTimeout;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.yunzhupaas.model;
|
||||
|
||||
import com.yunzhupaas.consts.ScanCodeTicketStatus;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
@Data
|
||||
public class ScanCodeLoginConfigModel {
|
||||
|
||||
/**
|
||||
* 状态
|
||||
* @see ScanCodeTicketStatus
|
||||
*/
|
||||
@NonNull
|
||||
private int status = ScanCodeTicketStatus.UnScanCode.getStatus();
|
||||
|
||||
/**
|
||||
* 额外的值, 登录Token、第三方登录的ID
|
||||
*/
|
||||
private String value;
|
||||
/**
|
||||
* 票据有效期, 时间戳
|
||||
*/
|
||||
private Long ticketTimeout;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.yunzhupaas.service;
|
||||
|
||||
import com.yunzhupaas.base.UserInfo;
|
||||
import com.yunzhupaas.exception.LoginException;
|
||||
import com.yunzhupaas.model.BaseSystemInfo;
|
||||
import com.yunzhupaas.model.login.PcUserVO;
|
||||
|
||||
/**
|
||||
* 登陆业务层
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @version V3.1.0
|
||||
* @copyright 深圳市乐程软件有限公司(http://www.szlecheng.cn)
|
||||
* @date 2024-03-23
|
||||
*/
|
||||
public interface LoginService {
|
||||
|
||||
/**
|
||||
* 租戶登录验证
|
||||
*
|
||||
* @param userInfo
|
||||
* @return userAccount, tenantId, tenandDb
|
||||
* @throws LoginException
|
||||
*/
|
||||
UserInfo getTenantAccount(UserInfo userInfo) throws LoginException;
|
||||
|
||||
/**
|
||||
* 生成用户登录信息
|
||||
* @param userInfo 账户信息
|
||||
* @param sysConfigInfo 系统配置
|
||||
* @return
|
||||
* @throws LoginException
|
||||
*/
|
||||
UserInfo userInfo(UserInfo userInfo, BaseSystemInfo sysConfigInfo) throws LoginException;
|
||||
|
||||
/**
|
||||
* 获取用户登陆信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
PcUserVO getCurrentUser(String type, String systemCode);
|
||||
|
||||
/**
|
||||
* 修改密码信息发送
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
void updatePasswordMessage();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param tenantId
|
||||
* @param tenantDb
|
||||
* @param isAssignDataSource 是否租户指定数据源
|
||||
* @return
|
||||
*/
|
||||
BaseSystemInfo getBaseSystemConfig(String tenantId);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.yunzhupaas.service;
|
||||
|
||||
import com.yunzhupaas.base.UserInfo;
|
||||
import com.yunzhupaas.exception.LoginException;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
public interface UserDetailService extends Ordered {
|
||||
|
||||
static final String USER_DETAIL_PREFIX = "USERDETAIL_";
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param userInfo
|
||||
* @return UserEntity
|
||||
* @param <T>
|
||||
*/
|
||||
<T> T loadUserEntity(UserInfo userInfo) throws LoginException;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.yunzhupaas.util;
|
||||
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.session.SaSessionCustomUtil;
|
||||
import com.yunzhupaas.model.BaseSystemInfo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.yunzhupaas.consts.AuthConsts.DEF_TENANT_ID;
|
||||
import static com.yunzhupaas.consts.AuthConsts.TENANT_SESSION;
|
||||
|
||||
|
||||
/**
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
@Slf4j
|
||||
public class TenantProvider {
|
||||
|
||||
|
||||
private static final long tenantTimeout = 60 * 60 * 24 * 30L;
|
||||
|
||||
/**
|
||||
* 获取租户Redis存储对象
|
||||
*
|
||||
* @param tenantId
|
||||
* @return
|
||||
*/
|
||||
public static SaSession getTenantSession(String tenantId) {
|
||||
if (tenantId == null) {
|
||||
// tenantId = TenantHolder.getDatasourceId();
|
||||
// if (tenantId == null) {
|
||||
tenantId = DEF_TENANT_ID;
|
||||
// }
|
||||
}
|
||||
SaSession saSession = SaSessionCustomUtil.getSessionById(TENANT_SESSION + tenantId);
|
||||
if (saSession != null && !saSession.get("init", false)) {
|
||||
saSession.set("init", true);
|
||||
saSession.updateTimeout(tenantTimeout);
|
||||
}
|
||||
return saSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* 存入租户缓存空间
|
||||
*
|
||||
* @param tenantId
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public static void putTenantCache(String tenantId, String key, Object value) {
|
||||
SaSession saSession = getTenantSession(tenantId);
|
||||
if (saSession != null) {
|
||||
saSession.set(key, value).updateTimeout(tenantTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户缓存数据
|
||||
*
|
||||
* @param tenantId
|
||||
* @param key
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> T getTenantCache(String tenantId, String key) {
|
||||
SaSession saSession = getTenantSession(tenantId);
|
||||
if (saSession != null) {
|
||||
return (T) saSession.get(key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除租户缓存数据
|
||||
*
|
||||
* @param tenantId
|
||||
* @param key
|
||||
*/
|
||||
public static void delTenantCache(String tenantId, String key) {
|
||||
SaSession saSession = getTenantSession(tenantId);
|
||||
if (saSession != null) {
|
||||
saSession.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static void renewTimeout(String tenantId, long timeout) {
|
||||
if (tenantId == null) {
|
||||
tenantId = DEF_TENANT_ID;
|
||||
}
|
||||
SaSession saSession = getTenantSession(tenantId);
|
||||
if (saSession != null) {
|
||||
saSession.updateTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static ThreadLocal<BaseSystemInfo> systemInfoThreadLocal = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 获取系统设置信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static BaseSystemInfo getBaseSystemInfo() {
|
||||
BaseSystemInfo systemInfo = systemInfoThreadLocal.get();
|
||||
return systemInfo;
|
||||
}
|
||||
|
||||
|
||||
public static void setBaseSystemInfo(BaseSystemInfo baseSystemInfo) {
|
||||
systemInfoThreadLocal.set(baseSystemInfo);
|
||||
}
|
||||
|
||||
|
||||
public static void clearBaseSystemIfo() {
|
||||
systemInfoThreadLocal.remove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.yunzhupaas.util;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.temp.SaTempUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 云筑产品开发平台组
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
*/
|
||||
@Component
|
||||
public class TicketUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 创建临时TOKEN
|
||||
* @param value 值
|
||||
* @param timeout 有效时间, 秒
|
||||
* @return
|
||||
*/
|
||||
public static String createTicket(Object value, long timeout){
|
||||
return SaTempUtil.createToken(value, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取临时TOKEN内的数据
|
||||
* @see #createTicket(Object, long)
|
||||
* @param ticket 票据
|
||||
* @return
|
||||
* @param <T>
|
||||
*/
|
||||
public static <T> T parseTicket(String ticket){
|
||||
return (T) SaTempUtil.parseToken(ticket);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除临时Token
|
||||
* @param ticket
|
||||
*/
|
||||
public static void deleteTicket(String ticket){
|
||||
SaTempUtil.deleteToken(ticket);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Ticket内的内容
|
||||
* @see #createTicket(Object, long)
|
||||
* @param ticket 票据
|
||||
* @param value 新值
|
||||
* @param timeout 超时时间, 秒, 可空为不更新
|
||||
*/
|
||||
public static void updateTicket(String ticket, Object value, Long timeout){
|
||||
Object obj = parseTicket(ticket);
|
||||
if(obj == null) return;
|
||||
String key = getTicketKey(ticket);
|
||||
if(timeout != null){
|
||||
SaManager.getSaTokenDao().setObject(key, value, timeout);
|
||||
}else{
|
||||
SaManager.getSaTokenDao().updateObject(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getTicketKey(String ticket){
|
||||
return SaManager.getSaTemp().splicingKeyTempToken(SaTokenConsts.DEFAULT_TEMP_TOKEN_SERVICE, ticket);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,546 @@
|
||||
package com.yunzhupaas.util;
|
||||
|
||||
import cn.dev33.satoken.same.SaSameUtil;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.session.TokenSign;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.text.StrPool;
|
||||
import com.yunzhupaas.base.UserInfo;
|
||||
import com.yunzhupaas.consts.DeviceType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.yunzhupaas.consts.AuthConsts.TOKEN_PREFIX_SP;
|
||||
|
||||
|
||||
/**
|
||||
* @author 云筑产品开发平台组
|
||||
* @version V3.1.0
|
||||
* @copyright 深圳市乐程软件有限公司
|
||||
* @date 2024/3/16 10:57
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UserProvider {
|
||||
|
||||
private static RedisUtil redisUtil;
|
||||
private static CacheKeyUtil cacheKeyUtil;
|
||||
|
||||
public static final String USER_INFO_KEY = "userInfo";
|
||||
|
||||
private static final ThreadLocal<UserInfo> USER_CACHE = new ThreadLocal<>();
|
||||
|
||||
public UserProvider(RedisUtil redisUtil, CacheKeyUtil cacheKeyUtil) {
|
||||
UserProvider.redisUtil = redisUtil;
|
||||
UserProvider.cacheKeyUtil = cacheKeyUtil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== 登录相关操作 ===================
|
||||
|
||||
/**
|
||||
* 登录系统 适用于Request环境
|
||||
*
|
||||
* @param userInfo 登录用户信息
|
||||
*/
|
||||
public static void login(UserInfo userInfo) {
|
||||
setLocalLoginUser(userInfo);
|
||||
StpUtil.login(splicingLoginId(userInfo.getUserId()));
|
||||
userInfo.setToken(StpUtil.getTokenValueNotCut());
|
||||
setLoginUser(userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录系统 适用于Request环境
|
||||
*
|
||||
* @param userInfo 用户信息
|
||||
* @param loginModel 登录参数
|
||||
*/
|
||||
public static void login(UserInfo userInfo, SaLoginModel loginModel) {
|
||||
setLocalLoginUser(userInfo);
|
||||
StpUtil.login(splicingLoginId(userInfo.getUserId()), loginModel);
|
||||
userInfo.setToken(StpUtil.getTokenValueNotCut());
|
||||
setLoginUser(userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 适用于非Request环境
|
||||
* @param userInfo
|
||||
* @param loginModel
|
||||
*/
|
||||
public static void loginNoRequest(UserInfo userInfo, SaLoginModel loginModel) {
|
||||
setLocalLoginUser(userInfo);
|
||||
String token = StpUtil.createLoginSession(splicingLoginId(userInfo.getUserId()), loginModel);
|
||||
userInfo.setToken(TOKEN_PREFIX_SP + token);
|
||||
setLoginUser(userInfo);
|
||||
}
|
||||
|
||||
|
||||
// =================== 登录用户ID相关操作 ===================
|
||||
|
||||
/**
|
||||
* 获取指定TOKEN用户ID
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static String getLoginUserId(String token) {
|
||||
String loginId = (String) StpUtil.getLoginIdByToken(token);
|
||||
return parseLoginId(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户ID, 包含临时切换用户ID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getLoginUserId() {
|
||||
String loginId = getUser().getUserId();
|
||||
return parseLoginId(loginId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== 用户ID拼接相关操作 ===================
|
||||
|
||||
|
||||
/**
|
||||
* 拼接租户下的用户ID
|
||||
*
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
public static String splicingLoginId(String userId) {
|
||||
return splicingLoginId(userId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接租户下的用户ID
|
||||
* @param userId
|
||||
* @param tenantId
|
||||
* @return
|
||||
*/
|
||||
private static String splicingLoginId(String userId, String tenantId) {
|
||||
if(StringUtil.isEmpty(tenantId)){
|
||||
tenantId = TenantHolder.getDatasourceId();
|
||||
}
|
||||
if (!StringUtil.isEmpty(tenantId)) {
|
||||
return tenantId + StrPool.COLON + userId;
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析租户下的登录ID
|
||||
* @param loginId
|
||||
* @return
|
||||
*/
|
||||
private static String parseLoginId(String loginId) {
|
||||
if (loginId != null && loginId.contains(StrPool.COLON)) {
|
||||
loginId = loginId.substring(loginId.indexOf(StrPool.COLON) + 1);
|
||||
}
|
||||
return loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Token是否有效
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static Boolean isValidToken(String token) {
|
||||
UserInfo userInfo = getUser(token);
|
||||
return userInfo.getUserId() != null;
|
||||
}
|
||||
|
||||
|
||||
// =================== UserInfo缓存相关操作 ===================
|
||||
|
||||
|
||||
/**
|
||||
* 设置Redis用户数据
|
||||
*/
|
||||
public static void setLoginUser(UserInfo userInfo) {
|
||||
StpUtil.getTokenSessionByToken(cutToken(userInfo.getToken())).set(USER_INFO_KEY, userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置本地用户数据
|
||||
*/
|
||||
public static void setLocalLoginUser(UserInfo userInfo) {
|
||||
USER_CACHE.set(userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地用户数据
|
||||
*/
|
||||
public static UserInfo getLocalLoginUser() {
|
||||
return USER_CACHE.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空本地用户数据
|
||||
*/
|
||||
public static void clearLocalUser() {
|
||||
USER_CACHE.remove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户缓存
|
||||
* 保留旧方法
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public UserInfo get(String token) {
|
||||
return UserProvider.getUser(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户缓存
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public UserInfo get() {
|
||||
return UserProvider.getUser();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据用户ID, 租户ID获取随机获取一个UserInfo
|
||||
* @param userId
|
||||
* @param tenantId
|
||||
* @return
|
||||
*/
|
||||
public static UserInfo getUser(String userId, String tenantId){
|
||||
return getUser(userId, tenantId, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID, 租户ID, 设备类型获取随机获取一个UserInfo
|
||||
* @param userId
|
||||
* @param tenantId
|
||||
* @param includeDevice 指定的设备类型中查找
|
||||
* @param excludeDevice 排除指定设备类型
|
||||
* @return
|
||||
*/
|
||||
public static UserInfo getUser(String userId, String tenantId, List<String> includeDevice, List<String> excludeDevice){
|
||||
SaSession session = StpUtil.getSessionByLoginId(splicingLoginId(userId, tenantId), false);
|
||||
if (session != null) {
|
||||
List<TokenSign> tokenSignList = session.tokenSignListCopy();
|
||||
if (!tokenSignList.isEmpty()) {
|
||||
tokenSignList = tokenSignList.stream().filter(tokenSign -> {
|
||||
if(!ObjectUtils.isEmpty(excludeDevice)){
|
||||
if(excludeDevice.contains(tokenSign.getDevice())){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(!ObjectUtils.isEmpty(includeDevice)){
|
||||
if(!includeDevice.contains(tokenSign.getDevice())){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).collect(Collectors.toList());
|
||||
if(!tokenSignList.isEmpty()){
|
||||
return getUser(tokenSignList.get(0).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new UserInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户缓存
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static UserInfo getUser(String token) {
|
||||
UserInfo userInfo = null;
|
||||
String tokens = null;
|
||||
if (token != null) {
|
||||
tokens = cutToken(token);
|
||||
} else {
|
||||
try {
|
||||
//处理非Web环境报错
|
||||
tokens = StpUtil.getTokenValue();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
if (tokens != null) {
|
||||
if (StpUtil.getLoginIdByToken(tokens) != null) {
|
||||
userInfo = (UserInfo) StpUtil.getTokenSessionByToken(tokens).get(USER_INFO_KEY);
|
||||
}
|
||||
}
|
||||
if (userInfo == null) {
|
||||
userInfo = new UserInfo();
|
||||
}
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户缓存
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static UserInfo getUser() {
|
||||
// if(StpUtil.getTokenValue() == null){
|
||||
// return new UserInfo();
|
||||
// }
|
||||
UserInfo userInfo = USER_CACHE.get();
|
||||
if (userInfo != null) {
|
||||
return userInfo;
|
||||
}
|
||||
userInfo = UserProvider.getUser(null);
|
||||
if (userInfo.getUserId() != null) {
|
||||
USER_CACHE.set(userInfo);
|
||||
}
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
// =================== Token相关操作 ===================
|
||||
|
||||
/**
|
||||
* 去除Token前缀
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static String cutToken(String token) {
|
||||
if (token != null && token.startsWith(TOKEN_PREFIX_SP)) {
|
||||
token = token.substring(TOKEN_PREFIX_SP.length());
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token
|
||||
*/
|
||||
public static String getToken() {
|
||||
String toke = getAuthorize();
|
||||
return toke;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取Authorize
|
||||
*/
|
||||
public static String getAuthorize() {
|
||||
String authorize = ServletUtil.getHeader(Constants.AUTHORIZATION);
|
||||
return authorize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TOKEN续期
|
||||
*/
|
||||
public static void renewTimeout() {
|
||||
if (StpUtil.getTokenValue() != null) {
|
||||
UserInfo userInfo = UserProvider.getUser();
|
||||
if(userInfo.getUserId() == null || userInfo.getTokenTimeout() == null) {
|
||||
//避免请求过网关之后TOKEN失效(携带TOKEN调用登录接口之后账号被顶替)
|
||||
return;
|
||||
}
|
||||
StpUtil.renewTimeout(userInfo.getTokenTimeout() * 60L);
|
||||
SaSession saSession = StpUtil.getSessionByLoginId(splicingLoginId(userInfo.getUserId()), false);
|
||||
if (saSession != null) {
|
||||
saSession.updateTimeout(userInfo.getTokenTimeout() * 60L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有Token记录
|
||||
* 包含无效状态的用户、临时用户
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getLoginUserListToken() {
|
||||
return StpUtil.searchTokenValue("", -1, -1, true).stream().map(token -> token.replace(StpUtil.stpLogic.splicingKeyTokenValue(""), "")).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
// =================== 临时Token相关操作 ===================
|
||||
|
||||
|
||||
/**
|
||||
* 获取内部服务传递验证TOKEN
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getInnerAuthToken() {
|
||||
return SaSameUtil.getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证内部传递Token是否有效 抛出异常
|
||||
* @param token
|
||||
*/
|
||||
public static void checkInnerToken(String token){
|
||||
SaSameUtil.checkToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证内部传递Token是否有效
|
||||
* @param token
|
||||
*/
|
||||
public static boolean isValidInnerToken(String token){
|
||||
return SaSameUtil.isValid(token);
|
||||
}
|
||||
|
||||
|
||||
// =================== 退出相关操作 ===================
|
||||
|
||||
|
||||
/**
|
||||
* 根据用户ID踢出全部用户
|
||||
* @param userId
|
||||
*/
|
||||
public static void kickoutByUserId(String userId, String tenantId) {
|
||||
StpUtil.kickout(splicingLoginId(userId, tenantId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Token踢出指定会话
|
||||
* @param tokens
|
||||
*/
|
||||
public static void kickoutByToken(String... tokens) {
|
||||
for (String token : tokens) {
|
||||
StpUtil.kickoutByTokenValue(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出当前Token, 不清除用户其他系统缓存
|
||||
*/
|
||||
public static void logout() {
|
||||
StpUtil.logout();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出指定Token, 不清除用户其他系统缓存
|
||||
*
|
||||
* @param token
|
||||
*/
|
||||
public static void logoutByToken(String token) {
|
||||
if (token == null) {
|
||||
logout();
|
||||
} else {
|
||||
StpUtil.logoutByTokenValue(cutToken(token));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出指定设备类型的用户的全部登录信息, 不清除用户其他系统缓存
|
||||
*
|
||||
* @param userId
|
||||
* @param deviceType
|
||||
*/
|
||||
public static void logoutByUserId(String userId, DeviceType deviceType) {
|
||||
StpUtil.logout(splicingLoginId(userId), deviceType.getDevice());
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出指定用户的全部登录信息, 清除相关缓存
|
||||
*
|
||||
* @param userId
|
||||
*/
|
||||
public static void logoutByUserId(String userId) {
|
||||
StpUtil.logout(splicingLoginId(userId));
|
||||
removeOtherCache(userId);
|
||||
|
||||
}
|
||||
|
||||
// =================== 用户权限 ===================
|
||||
|
||||
/**
|
||||
* 获取当前用户拥有的权限列表(菜单编码列表、功能ID列表)
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getPermissionList(){
|
||||
return StpUtil.getPermissionList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户拥有的角色列表
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getRoleList(){
|
||||
return StpUtil.getRoleList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// =================== 其他缓存相关操作 ===================
|
||||
|
||||
/**
|
||||
* 移除
|
||||
*/
|
||||
public static void removeOtherCache(String userId) {
|
||||
redisUtil.remove(cacheKeyUtil.getUserAuthorize() + userId);
|
||||
redisUtil.remove(cacheKeyUtil.getSystemInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在线
|
||||
*/
|
||||
public boolean isOnLine(String userId) {
|
||||
return StpUtil.getTokenValueByLoginId(splicingLoginId(userId), getDeviceForAgent().getDevice()) != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否登陆
|
||||
*/
|
||||
public static boolean isLogined() {
|
||||
return StpUtil.isLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定Token是否有效
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static boolean isValid(String token) {
|
||||
return StpUtil.getLoginIdByToken(token) != null;
|
||||
}
|
||||
|
||||
|
||||
public static DeviceType getDeviceForAgent() {
|
||||
if (ServletUtil.getIsMobileDevice()) {
|
||||
return DeviceType.APP;
|
||||
} else {
|
||||
return DeviceType.PC;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户是否是临时用户
|
||||
* @param userInfo
|
||||
* @return
|
||||
*/
|
||||
public static boolean isTempUser(UserInfo userInfo){
|
||||
if(userInfo == null){
|
||||
userInfo = getUser();
|
||||
}
|
||||
return DeviceType.TEMPUSER.getDevice().equals(userInfo.getLoginDevice())
|
||||
|| DeviceType.TEMPUSERLIMITED.getDevice().equals(userInfo.getLoginDevice());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user