初始代码

This commit is contained in:
wangmingwei
2026-04-21 17:41:09 +08:00
parent 186ec6683a
commit b686ecac5f
493 changed files with 52349 additions and 0 deletions

View 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>

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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";
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}