mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-18 17:38:48 +08:00
sso-system dubbo服务实现
This commit is contained in:
parent
b789426363
commit
8bb82902c0
8
pom.xml
8
pom.xml
@ -62,7 +62,7 @@
|
|||||||
<maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
|
<maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
|
||||||
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
|
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
|
||||||
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
|
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
|
||||||
<dubbo.version>3.3.0</dubbo.version>
|
<dubbo.version>3.2.14</dubbo.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
@ -422,6 +422,12 @@
|
|||||||
<version>${dubbo.version}</version>
|
<version>${dubbo.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.dubbo</groupId>
|
||||||
|
<artifactId>dubbo-metadata-report-redis</artifactId>
|
||||||
|
<version>${dubbo.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- <dependency>
|
<!-- <dependency>
|
||||||
<groupId>org.apache.dubbo</groupId>
|
<groupId>org.apache.dubbo</groupId>
|
||||||
<artifactId>dubbo</artifactId>
|
<artifactId>dubbo</artifactId>
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
<artifactId>ruoyi-api-system</artifactId>
|
<artifactId>ruoyi-api-system</artifactId>
|
||||||
|
|
||||||
<description>
|
<description>
|
||||||
ruoyi-api-system系统接口模块
|
ruoyi-api-system系统接口模块 TODO 记得改名 ruoyi-api-system-saas
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|||||||
@ -0,0 +1,71 @@
|
|||||||
|
package org.dromara.system.api.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前在线会话
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class SysUserOnline implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会话编号
|
||||||
|
*/
|
||||||
|
private String tokenId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门名称
|
||||||
|
*/
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名称
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端
|
||||||
|
*/
|
||||||
|
private String clientKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备类型
|
||||||
|
*/
|
||||||
|
private String deviceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录IP地址
|
||||||
|
*/
|
||||||
|
private String ipaddr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录地址
|
||||||
|
*/
|
||||||
|
private String loginLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浏览器类型
|
||||||
|
*/
|
||||||
|
private String browser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作系统
|
||||||
|
*/
|
||||||
|
private String os;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录时间
|
||||||
|
*/
|
||||||
|
private Long loginTime;
|
||||||
|
|
||||||
|
}
|
||||||
@ -17,11 +17,6 @@
|
|||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.dromara</groupId>
|
|
||||||
<artifactId>ruoyi-common-satoken</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara</groupId>
|
<groupId>org.dromara</groupId>
|
||||||
<artifactId>ruoyi-common-json</artifactId>
|
<artifactId>ruoyi-common-json</artifactId>
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
package org.dromara.common.log.aspect;
|
||||||
|
|
||||||
|
import org.dromara.common.core.domain.model.LoginUser;
|
||||||
|
|
||||||
|
public interface CommonLogTenantIntercept {
|
||||||
|
|
||||||
|
Object getTenantId();
|
||||||
|
String getUsername();
|
||||||
|
|
||||||
|
String getDeptName();
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import cn.hutool.core.lang.Dict;
|
|||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -13,7 +14,6 @@ import org.aspectj.lang.annotation.AfterReturning;
|
|||||||
import org.aspectj.lang.annotation.AfterThrowing;
|
import org.aspectj.lang.annotation.AfterThrowing;
|
||||||
import org.aspectj.lang.annotation.Aspect;
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
import org.aspectj.lang.annotation.Before;
|
import org.aspectj.lang.annotation.Before;
|
||||||
import org.dromara.common.core.domain.model.LoginUser;
|
|
||||||
import org.dromara.common.core.utils.ServletUtils;
|
import org.dromara.common.core.utils.ServletUtils;
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
@ -21,7 +21,7 @@ import org.dromara.common.json.utils.JsonUtils;
|
|||||||
import org.dromara.common.log.annotation.Log;
|
import org.dromara.common.log.annotation.Log;
|
||||||
import org.dromara.common.log.enums.BusinessStatus;
|
import org.dromara.common.log.enums.BusinessStatus;
|
||||||
import org.dromara.common.log.event.OperLogEvent;
|
import org.dromara.common.log.event.OperLogEvent;
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
@ -41,10 +41,23 @@ import java.util.StringJoiner;
|
|||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
public class LogAspect {
|
public class LogAspect {
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private CommonLogTenantIntercept commonLogTenantIntercept;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
if (commonLogTenantIntercept != null) {
|
||||||
|
log.info("CommonLogTenantIntercept 有实现对象: " + commonLogTenantIntercept.getClass().getName());
|
||||||
|
} else {
|
||||||
|
System.exit(0);
|
||||||
|
throw new RuntimeException("CommonLogTenantIntercept 未实现, 请实现 CommonLogTenantIntercept 接口");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 排除敏感属性字段
|
* 排除敏感属性字段
|
||||||
*/
|
*/
|
||||||
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
|
public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,15 +101,18 @@ public class LogAspect {
|
|||||||
|
|
||||||
// *========数据库日志=========*//
|
// *========数据库日志=========*//
|
||||||
OperLogEvent operLog = new OperLogEvent();
|
OperLogEvent operLog = new OperLogEvent();
|
||||||
operLog.setTenantId(LoginHelper.getTenantId());
|
if (commonLogTenantIntercept != null) {
|
||||||
|
operLog.setTenantId(commonLogTenantIntercept.getTenantId().toString());
|
||||||
|
}
|
||||||
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
|
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
|
||||||
// 请求的地址
|
// 请求的地址
|
||||||
String ip = ServletUtils.getClientIP();
|
String ip = ServletUtils.getClientIP();
|
||||||
operLog.setOperIp(ip);
|
operLog.setOperIp(ip);
|
||||||
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
|
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
|
||||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
if (commonLogTenantIntercept != null) {
|
||||||
operLog.setOperName(loginUser.getUsername());
|
operLog.setOperName(commonLogTenantIntercept.getUsername());
|
||||||
operLog.setDeptName(loginUser.getDeptName());
|
operLog.setDeptName(commonLogTenantIntercept.getDeptName());
|
||||||
|
}
|
||||||
|
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
operLog.setStatus(BusinessStatus.FAIL.ordinal());
|
operLog.setStatus(BusinessStatus.FAIL.ordinal());
|
||||||
|
|||||||
@ -59,20 +59,59 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- satoken -->
|
<!-- RuoYi Common Redis-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara</groupId>
|
<groupId>org.dromara</groupId>
|
||||||
<artifactId>ruoyi-common-satoken</artifactId>
|
<artifactId>ruoyi-common-redis</artifactId>
|
||||||
<version>${revision}</version>
|
</dependency>
|
||||||
|
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 安全模块 -->
|
<!-- Sa-Token 整合 jwt -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara</groupId>
|
<groupId>cn.dev33</groupId>
|
||||||
<artifactId>ruoyi-common-security</artifactId>
|
<artifactId>sa-token-jwt</artifactId>
|
||||||
<version>${revision}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>com.github.ben-manes.caffeine</groupId>-->
|
||||||
|
<!-- <artifactId>caffeine</artifactId>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-redis-jackson</artifactId>
|
||||||
|
<version>1.38.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-pool2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Sa-Token 插件:整合SSO -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-sso</artifactId>
|
||||||
|
<version>1.38.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Sa-Token 插件:整合SSO -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-sso</artifactId>
|
||||||
|
<version>1.38.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- <!– 安全模块 –>-->
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>org.dromara</groupId>-->
|
||||||
|
<!-- <artifactId>ruoyi-common-security</artifactId>-->
|
||||||
|
<!-- <version>${revision}</version>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
<!-- 租户模块 -->
|
<!-- 租户模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara</groupId>
|
<groupId>org.dromara</groupId>
|
||||||
@ -80,26 +119,12 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 远程调用模块 -->
|
<!-- 远程调用模块 TODO rename -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara</groupId>
|
<groupId>org.dromara</groupId>
|
||||||
<artifactId>ruoyi-api-system</artifactId>
|
<artifactId>ruoyi-api-system</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Sa-Token 插件:整合SSO -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.dev33</groupId>
|
|
||||||
<artifactId>sa-token-sso</artifactId>
|
|
||||||
<version>1.38.0</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Sa-Token 插件:整合SSO -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.dev33</groupId>
|
|
||||||
<artifactId>sa-token-sso</artifactId>
|
|
||||||
<version>1.38.0</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Apache Dubbo 配置 -->
|
<!-- Apache Dubbo 配置 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.dubbo</groupId>
|
<groupId>org.apache.dubbo</groupId>
|
||||||
@ -111,6 +136,46 @@
|
|||||||
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
|
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- JustAuth 的依赖配置-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.zhyd.oauth</groupId>
|
||||||
|
<artifactId>JustAuth</artifactId>
|
||||||
|
<version>1.16.6</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara</groupId>
|
||||||
|
<artifactId>ruoyi-common-log</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara</groupId>
|
||||||
|
<artifactId>ruoyi-common-ratelimiter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-captcha</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara</groupId>
|
||||||
|
<artifactId>ruoyi-common-social</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara</groupId>
|
||||||
|
<artifactId>ruoyi-common-encrypt</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>org.apache.dubbo</groupId>-->
|
||||||
|
<!-- <artifactId>dubbo-metadata-report-redis</artifactId>-->
|
||||||
|
<!-- <exclusions>-->
|
||||||
|
<!-- <exclusion>-->
|
||||||
|
<!-- <groupId>redis.clients</groupId>-->
|
||||||
|
<!-- <artifactId>jedis</artifactId>-->
|
||||||
|
<!-- </exclusion>-->
|
||||||
|
<!-- </exclusions>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,88 @@
|
|||||||
|
package com.pj.auth.captcha;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.generator.CodeGenerator;
|
||||||
|
import cn.hutool.core.math.Calculator;
|
||||||
|
import cn.hutool.core.util.CharUtil;
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无符号计算生成器
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public class UnsignedMathGenerator implements CodeGenerator {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = -5514819971774091076L;
|
||||||
|
|
||||||
|
private static final String OPERATORS = "+-*";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参与计算数字最大长度
|
||||||
|
*/
|
||||||
|
private final int numberLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
|
public UnsignedMathGenerator() {
|
||||||
|
this(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param numberLength 参与计算最大数字位数
|
||||||
|
*/
|
||||||
|
public UnsignedMathGenerator(int numberLength) {
|
||||||
|
this.numberLength = numberLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generate() {
|
||||||
|
final int limit = getLimit();
|
||||||
|
int a = RandomUtil.randomInt(limit);
|
||||||
|
int b = RandomUtil.randomInt(limit);
|
||||||
|
String max = Integer.toString(Math.max(a, b));
|
||||||
|
String min = Integer.toString(Math.min(a, b));
|
||||||
|
max = StringUtils.rightPad(max, this.numberLength, CharUtil.SPACE);
|
||||||
|
min = StringUtils.rightPad(min, this.numberLength, CharUtil.SPACE);
|
||||||
|
|
||||||
|
return max + RandomUtil.randomChar(OPERATORS) + min + '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(String code, String userInputCode) {
|
||||||
|
int result;
|
||||||
|
try {
|
||||||
|
result = Integer.parseInt(userInputCode);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 用户输入非数字
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int calculateResult = (int) Calculator.conversion(code);
|
||||||
|
return result == calculateResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取验证码长度
|
||||||
|
*
|
||||||
|
* @return 验证码长度
|
||||||
|
*/
|
||||||
|
public int getLength() {
|
||||||
|
return this.numberLength * 2 + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据长度获取参与计算数字最大值
|
||||||
|
*
|
||||||
|
* @return 最大值
|
||||||
|
*/
|
||||||
|
private int getLimit() {
|
||||||
|
return Integer.parseInt("1" + StringUtils.repeat('0', this.numberLength));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
package com.pj.auth.config;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.CaptchaUtil;
|
||||||
|
import cn.hutool.captcha.CircleCaptcha;
|
||||||
|
import cn.hutool.captcha.LineCaptcha;
|
||||||
|
import cn.hutool.captcha.ShearCaptcha;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码配置
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CaptchaConfig {
|
||||||
|
|
||||||
|
private static final int WIDTH = 160;
|
||||||
|
private static final int HEIGHT = 60;
|
||||||
|
private static final Color BACKGROUND = Color.LIGHT_GRAY;
|
||||||
|
private static final Font FONT = new Font("Arial", Font.BOLD, 48);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 圆圈干扰验证码
|
||||||
|
*/
|
||||||
|
@Lazy
|
||||||
|
@Bean
|
||||||
|
public CircleCaptcha circleCaptcha() {
|
||||||
|
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(WIDTH, HEIGHT);
|
||||||
|
captcha.setBackground(BACKGROUND);
|
||||||
|
captcha.setFont(FONT);
|
||||||
|
return captcha;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线段干扰的验证码
|
||||||
|
*/
|
||||||
|
@Lazy
|
||||||
|
@Bean
|
||||||
|
public LineCaptcha lineCaptcha() {
|
||||||
|
LineCaptcha captcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT);
|
||||||
|
captcha.setBackground(BACKGROUND);
|
||||||
|
captcha.setFont(FONT);
|
||||||
|
return captcha;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扭曲干扰验证码
|
||||||
|
*/
|
||||||
|
@Lazy
|
||||||
|
@Bean
|
||||||
|
public ShearCaptcha shearCaptcha() {
|
||||||
|
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(WIDTH, HEIGHT);
|
||||||
|
captcha.setBackground(BACKGROUND);
|
||||||
|
captcha.setFont(FONT);
|
||||||
|
return captcha;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
package com.pj.auth.controller;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.AbstractCaptcha;
|
||||||
|
import cn.hutool.captcha.generator.CodeGenerator;
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import com.pj.auth.domain.vo.CaptchaVo;
|
||||||
|
import com.pj.auth.enums.CaptchaType;
|
||||||
|
import com.pj.auth.properties.CaptchaProperties;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.common.core.constant.Constants;
|
||||||
|
import org.dromara.common.core.constant.GlobalConstants;
|
||||||
|
import org.dromara.common.core.domain.R;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.core.utils.reflect.ReflectUtils;
|
||||||
|
import org.dromara.common.ratelimiter.annotation.RateLimiter;
|
||||||
|
import org.dromara.common.ratelimiter.enums.LimitType;
|
||||||
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码操作处理
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
public class CaptchaController {
|
||||||
|
|
||||||
|
private final CaptchaProperties captchaProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成验证码
|
||||||
|
*/
|
||||||
|
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
|
||||||
|
@GetMapping("/code")
|
||||||
|
public R<CaptchaVo> getCode() {
|
||||||
|
CaptchaVo captchaVo = new CaptchaVo();
|
||||||
|
boolean captchaEnabled = captchaProperties.getEnabled();
|
||||||
|
if (!captchaEnabled) {
|
||||||
|
captchaVo.setCaptchaEnabled(false);
|
||||||
|
return R.ok(captchaVo);
|
||||||
|
}
|
||||||
|
// 保存验证码信息
|
||||||
|
String uuid = IdUtil.simpleUUID();
|
||||||
|
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
|
||||||
|
// 生成验证码
|
||||||
|
CaptchaType captchaType = captchaProperties.getType();
|
||||||
|
boolean isMath = CaptchaType.MATH == captchaType;
|
||||||
|
Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
|
||||||
|
CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
|
||||||
|
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
|
||||||
|
captcha.setGenerator(codeGenerator);
|
||||||
|
captcha.createCode();
|
||||||
|
// 如果是数学验证码,使用SpEL表达式处理验证码结果
|
||||||
|
String code = captcha.getCode();
|
||||||
|
if (isMath) {
|
||||||
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
|
||||||
|
code = exp.getValue(String.class);
|
||||||
|
}
|
||||||
|
RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||||
|
captchaVo.setUuid(uuid);
|
||||||
|
captchaVo.setImg(captcha.getImageBase64());
|
||||||
|
return R.ok(captchaVo);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,225 @@
|
|||||||
|
package com.pj.auth.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.pj.auth.domain.vo.LoginTenantVo;
|
||||||
|
import com.pj.auth.domain.vo.LoginVo;
|
||||||
|
import com.pj.auth.domain.vo.TenantListVo;
|
||||||
|
import com.pj.auth.form.RegisterBody;
|
||||||
|
import com.pj.auth.form.SocialLoginBody;
|
||||||
|
import com.pj.auth.service.IAuthStrategy;
|
||||||
|
import com.pj.auth.service.SysLoginService;
|
||||||
|
import com.pj.helper.LoginSaasHelper;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import me.zhyd.oauth.model.AuthResponse;
|
||||||
|
import me.zhyd.oauth.model.AuthUser;
|
||||||
|
import me.zhyd.oauth.request.AuthRequest;
|
||||||
|
import me.zhyd.oauth.utils.AuthStateUtils;
|
||||||
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
|
import org.dromara.common.core.constant.UserConstants;
|
||||||
|
import org.dromara.common.core.domain.R;
|
||||||
|
import org.dromara.common.core.domain.model.LoginBody;
|
||||||
|
import org.dromara.common.core.utils.*;
|
||||||
|
import org.dromara.common.encrypt.annotation.ApiEncrypt;
|
||||||
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
|
||||||
|
import org.dromara.common.social.config.properties.SocialProperties;
|
||||||
|
import org.dromara.common.social.utils.SocialUtils;
|
||||||
|
import org.dromara.common.tenant.helper.TenantHelper;
|
||||||
|
import org.dromara.system.api.RemoteClientService;
|
||||||
|
import org.dromara.system.api.RemoteConfigService;
|
||||||
|
import org.dromara.system.api.RemoteSocialService;
|
||||||
|
import org.dromara.system.api.RemoteTenantService;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteClientVo;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteTenantVo;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* token 控制
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
public class TokenController {
|
||||||
|
|
||||||
|
private final SocialProperties socialProperties;
|
||||||
|
private final SysLoginService sysLoginService;
|
||||||
|
private final ScheduledExecutorService scheduledExecutorService;
|
||||||
|
|
||||||
|
@DubboReference
|
||||||
|
private RemoteConfigService remoteConfigService;
|
||||||
|
@DubboReference
|
||||||
|
private RemoteTenantService remoteTenantService;
|
||||||
|
@DubboReference
|
||||||
|
private RemoteClientService remoteClientService;
|
||||||
|
@DubboReference
|
||||||
|
private RemoteSocialService remoteSocialService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录方法
|
||||||
|
*
|
||||||
|
* @param body 登录信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@ApiEncrypt
|
||||||
|
@PostMapping("/login")
|
||||||
|
public R<LoginVo> login(@RequestBody String body) {
|
||||||
|
LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
|
||||||
|
ValidatorUtils.validate(loginBody);
|
||||||
|
// 授权类型和客户端id
|
||||||
|
String clientId = loginBody.getClientId();
|
||||||
|
String grantType = loginBody.getGrantType();
|
||||||
|
RemoteClientVo clientVo = remoteClientService.queryByClientId(clientId);
|
||||||
|
|
||||||
|
// 查询不到 client 或 client 内不包含 grantType
|
||||||
|
if (ObjectUtil.isNull(clientVo) || !StringUtils.contains(clientVo.getGrantType(), grantType)) {
|
||||||
|
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
|
||||||
|
return R.fail(MessageUtils.message("auth.grant.type.error"));
|
||||||
|
} else if (!UserConstants.NORMAL.equals(clientVo.getStatus())) {
|
||||||
|
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
|
||||||
|
}
|
||||||
|
// 校验租户
|
||||||
|
sysLoginService.checkTenant(loginBody.getTenantId());
|
||||||
|
// 登录
|
||||||
|
LoginVo loginVo = IAuthStrategy.login(body, clientVo, grantType);
|
||||||
|
|
||||||
|
Long userId = LoginSaasHelper.getUserId();
|
||||||
|
log.info(userId + "欢迎登录RuoYi-Cloud-Plus微服务管理系统");
|
||||||
|
return R.ok(loginVo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方登录请求
|
||||||
|
*
|
||||||
|
* @param source 登录来源
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/binding/{source}")
|
||||||
|
public R<String> authBinding(@PathVariable("source") String source,
|
||||||
|
@RequestParam String tenantId, @RequestParam String domain) {
|
||||||
|
SocialLoginConfigProperties obj = socialProperties.getType().get(source);
|
||||||
|
if (ObjectUtil.isNull(obj)) {
|
||||||
|
return R.fail(source + "平台账号暂不支持");
|
||||||
|
}
|
||||||
|
AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("tenantId", tenantId);
|
||||||
|
map.put("domain", domain);
|
||||||
|
map.put("state", AuthStateUtils.createState());
|
||||||
|
String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8));
|
||||||
|
return R.ok("操作成功", authorizeUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方登录回调业务处理 绑定授权
|
||||||
|
*
|
||||||
|
* @param loginBody 请求体
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/social/callback")
|
||||||
|
public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
|
||||||
|
// 获取第三方登录信息
|
||||||
|
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
|
||||||
|
loginBody.getSource(), loginBody.getSocialCode(),
|
||||||
|
loginBody.getSocialState(), socialProperties);
|
||||||
|
AuthUser authUserData = response.getData();
|
||||||
|
// 判断授权响应是否成功
|
||||||
|
if (!response.ok()) {
|
||||||
|
return R.fail(response.getMsg());
|
||||||
|
}
|
||||||
|
sysLoginService.socialRegister(authUserData);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消授权
|
||||||
|
*
|
||||||
|
* @param socialId socialId
|
||||||
|
*/
|
||||||
|
@DeleteMapping(value = "/unlock/{socialId}")
|
||||||
|
public R<Void> unlockSocial(@PathVariable Long socialId) {
|
||||||
|
Boolean rows = remoteSocialService.deleteWithValidById(socialId);
|
||||||
|
return rows ? R.ok() : R.fail("取消授权失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登出方法
|
||||||
|
*/
|
||||||
|
@PostMapping("logout")
|
||||||
|
public R<Void> logout() {
|
||||||
|
sysLoginService.logout();
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
*/
|
||||||
|
@ApiEncrypt
|
||||||
|
@PostMapping("register")
|
||||||
|
public R<Void> register(@RequestBody RegisterBody registerBody) {
|
||||||
|
if (!remoteConfigService.selectRegisterEnabled(registerBody.getTenantId())) {
|
||||||
|
return R.fail("当前系统没有开启注册功能!");
|
||||||
|
}
|
||||||
|
// 用户注册
|
||||||
|
sysLoginService.register(registerBody);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录页面租户下拉框
|
||||||
|
*
|
||||||
|
* @return 租户列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/tenant/list")
|
||||||
|
public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
|
||||||
|
// 返回对象
|
||||||
|
LoginTenantVo result = new LoginTenantVo();
|
||||||
|
boolean enable = TenantHelper.isEnable();
|
||||||
|
result.setTenantEnabled(enable);
|
||||||
|
// 如果未开启租户这直接返回
|
||||||
|
if (!enable) {
|
||||||
|
return R.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RemoteTenantVo> tenantList = remoteTenantService.queryList();
|
||||||
|
List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
|
||||||
|
try {
|
||||||
|
// 如果只超管返回所有租户
|
||||||
|
if (LoginSaasHelper.isSuperAdmin()) {
|
||||||
|
result.setVoList(voList);
|
||||||
|
return R.ok(result);
|
||||||
|
}
|
||||||
|
} catch (NotLoginException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取域名
|
||||||
|
String host;
|
||||||
|
String referer = request.getHeader("referer");
|
||||||
|
if (StringUtils.isNotBlank(referer)) {
|
||||||
|
// 这里从referer中取值是为了本地使用hosts添加虚拟域名,方便本地环境调试
|
||||||
|
host = referer.split("//")[1].split("/")[0];
|
||||||
|
} else {
|
||||||
|
host = new URL(request.getRequestURL().toString()).getHost();
|
||||||
|
}
|
||||||
|
// 根据域名进行筛选
|
||||||
|
List<TenantListVo> list = StreamUtils.filter(voList, vo ->
|
||||||
|
StringUtils.equals(vo.getDomain(), host));
|
||||||
|
result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
|
||||||
|
return R.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.pj.auth.domain.convert;
|
||||||
|
|
||||||
|
import io.github.linpeilie.BaseMapper;
|
||||||
|
import com.pj.auth.domain.vo.TenantListVo;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteTenantVo;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户vo转换器
|
||||||
|
* @author zhujie
|
||||||
|
*/
|
||||||
|
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
|
||||||
|
public interface TenantVoConvert extends BaseMapper<RemoteTenantVo, TenantListVo> {
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.pj.model.vo;
|
package com.pj.auth.domain.vo;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.pj.auth.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录租户对象
|
||||||
|
*
|
||||||
|
* @author Michelle.Chung
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class LoginTenantVo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户开关
|
||||||
|
*/
|
||||||
|
private Boolean tenantEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户对象列表
|
||||||
|
*/
|
||||||
|
private List<TenantListVo> voList;
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.pj.model.vo;
|
package com.pj.auth.domain.vo;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.pj.auth.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户列表
|
||||||
|
*
|
||||||
|
* @author zhujie
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TenantListVo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户编号
|
||||||
|
*/
|
||||||
|
private String tenantId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 企业名称
|
||||||
|
*/
|
||||||
|
private String companyName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 域名
|
||||||
|
*/
|
||||||
|
private String domain;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.pj.auth.enums;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.AbstractCaptcha;
|
||||||
|
import cn.hutool.captcha.CircleCaptcha;
|
||||||
|
import cn.hutool.captcha.LineCaptcha;
|
||||||
|
import cn.hutool.captcha.ShearCaptcha;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码类别
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum CaptchaCategory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线段干扰
|
||||||
|
*/
|
||||||
|
LINE(LineCaptcha.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 圆圈干扰
|
||||||
|
*/
|
||||||
|
CIRCLE(CircleCaptcha.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扭曲干扰
|
||||||
|
*/
|
||||||
|
SHEAR(ShearCaptcha.class);
|
||||||
|
|
||||||
|
private final Class<? extends AbstractCaptcha> clazz;
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.pj.auth.enums;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.generator.CodeGenerator;
|
||||||
|
import cn.hutool.captcha.generator.RandomGenerator;
|
||||||
|
import com.pj.auth.captcha.UnsignedMathGenerator;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码类型
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum CaptchaType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数字
|
||||||
|
*/
|
||||||
|
MATH(UnsignedMathGenerator.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符
|
||||||
|
*/
|
||||||
|
CHAR(RandomGenerator.class);
|
||||||
|
|
||||||
|
private final Class<? extends CodeGenerator> clazz;
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.pj.auth.form;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Email;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.dromara.common.core.domain.model.LoginBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮件登录对象
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class EmailLoginBody extends LoginBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{user.email.not.blank}")
|
||||||
|
@Email(message = "{user.email.not.valid}")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱code
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{email.code.not.blank}")
|
||||||
|
private String emailCode;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.pj.auth.form;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.dromara.common.core.domain.model.LoginBody;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
import static org.dromara.common.core.constant.UserConstants.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码登录对象
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class PasswordLoginBody extends LoginBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{user.username.not.blank}")
|
||||||
|
@Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户密码
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{user.password.not.blank}")
|
||||||
|
@Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package com.pj.auth.form;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.dromara.common.core.domain.model.LoginBody;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
import static org.dromara.common.core.constant.UserConstants.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册对象
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class RegisterBody extends LoginBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{user.username.not.blank}")
|
||||||
|
@Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户密码
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{user.password.not.blank}")
|
||||||
|
@Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户类型
|
||||||
|
*/
|
||||||
|
private String userType;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.pj.auth.form;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.dromara.common.core.domain.model.LoginBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信登录对象
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class SmsLoginBody extends LoginBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手机号
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{user.phonenumber.not.blank}")
|
||||||
|
private String phonenumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信code
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{sms.code.not.blank}")
|
||||||
|
private String smsCode;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.pj.auth.form;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.dromara.common.core.domain.model.LoginBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 三方登录对象
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class SocialLoginBody extends LoginBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方登录平台
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{social.source.not.blank}")
|
||||||
|
private String source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方登录code
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{social.code.not.blank}")
|
||||||
|
private String socialCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方登录socialState
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{social.state.not.blank}")
|
||||||
|
private String socialState;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.pj.auth.form;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.dromara.common.core.domain.model.LoginBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 三方登录对象
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class XcxLoginBody extends LoginBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小程序id(多个小程序时使用)
|
||||||
|
*/
|
||||||
|
private String appid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小程序code
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{xcx.code.not.blank}")
|
||||||
|
private String xcxCode;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,169 @@
|
|||||||
|
package com.pj.auth.listener;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.config.SaTokenConfig;
|
||||||
|
import cn.dev33.satoken.listener.SaTokenListener;
|
||||||
|
import cn.dev33.satoken.stp.SaLoginModel;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.http.useragent.UserAgent;
|
||||||
|
import cn.hutool.http.useragent.UserAgentUtil;
|
||||||
|
import com.pj.helper.LoginSaasHelper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
|
import org.dromara.common.core.constant.CacheConstants;
|
||||||
|
import org.dromara.common.core.constant.Constants;
|
||||||
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
|
import org.dromara.common.core.utils.ServletUtils;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.common.core.utils.ip.AddressUtils;
|
||||||
|
import org.dromara.common.log.event.LogininforEvent;
|
||||||
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
|
import org.dromara.common.tenant.helper.TenantHelper;
|
||||||
|
import org.dromara.system.api.RemoteUserService;
|
||||||
|
import org.dromara.system.api.domain.SysUserOnline;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户行为 侦听器的实现
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class UserActionListener implements SaTokenListener {
|
||||||
|
|
||||||
|
private final SaTokenConfig tokenConfig;
|
||||||
|
@DubboReference
|
||||||
|
private RemoteUserService remoteUserService;
|
||||||
|
// @DubboReference
|
||||||
|
// private RemoteMessageService remoteMessageService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次登录时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
|
||||||
|
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||||
|
String ip = ServletUtils.getClientIP();
|
||||||
|
SysUserOnline userOnline = new SysUserOnline();
|
||||||
|
userOnline.setIpaddr(ip);
|
||||||
|
userOnline.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||||
|
userOnline.setBrowser(userAgent.getBrowser().getName());
|
||||||
|
userOnline.setOs(userAgent.getOs().getName());
|
||||||
|
userOnline.setLoginTime(System.currentTimeMillis());
|
||||||
|
userOnline.setTokenId(tokenValue);
|
||||||
|
String username = (String) loginModel.getExtra(LoginSaasHelper.USER_NAME_KEY);
|
||||||
|
String tenantId = (String) loginModel.getExtra(LoginSaasHelper.TENANT_KEY);
|
||||||
|
userOnline.setUserName(username);
|
||||||
|
userOnline.setClientKey((String) loginModel.getExtra(LoginSaasHelper.CLIENT_KEY));
|
||||||
|
userOnline.setDeviceType(loginModel.getDevice());
|
||||||
|
userOnline.setDeptName((String) loginModel.getExtra(LoginSaasHelper.DEPT_NAME_KEY));
|
||||||
|
TenantHelper.dynamic(tenantId, () -> {
|
||||||
|
if (tokenConfig.getTimeout() == -1) {
|
||||||
|
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline);
|
||||||
|
} else {
|
||||||
|
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline, Duration.ofSeconds(tokenConfig.getTimeout()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 记录登录日志
|
||||||
|
LogininforEvent logininforEvent = new LogininforEvent();
|
||||||
|
logininforEvent.setTenantId(tenantId);
|
||||||
|
logininforEvent.setUsername(username);
|
||||||
|
logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
|
||||||
|
logininforEvent.setMessage(MessageUtils.message("user.login.success"));
|
||||||
|
SpringUtils.context().publishEvent(logininforEvent);
|
||||||
|
// 更新登录信息
|
||||||
|
remoteUserService.recordLoginInfo((Long) loginModel.getExtra(LoginSaasHelper.USER_KEY), ip);
|
||||||
|
log.info("user doLogin, useId:{}, token:{}", loginId, tokenValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次注销时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doLogout(String loginType, Object loginId, String tokenValue) {
|
||||||
|
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginSaasHelper.TENANT_KEY));
|
||||||
|
TenantHelper.dynamic(tenantId, () -> {
|
||||||
|
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||||
|
});
|
||||||
|
log.info("user doLogout, useId:{}, token:{}", loginId, tokenValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次被踢下线时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doKickout(String loginType, Object loginId, String tokenValue) {
|
||||||
|
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginSaasHelper.TENANT_KEY));
|
||||||
|
TenantHelper.dynamic(tenantId, () -> {
|
||||||
|
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||||
|
});
|
||||||
|
log.info("user doLogoutByLoginId, useId:{}, token:{}", loginId, tokenValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次被顶下线时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doReplaced(String loginType, Object loginId, String tokenValue) {
|
||||||
|
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginSaasHelper.TENANT_KEY));
|
||||||
|
TenantHelper.dynamic(tenantId, () -> {
|
||||||
|
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||||
|
});
|
||||||
|
log.info("user doReplaced, useId:{}, token:{}", loginId, tokenValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次被封禁时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次被解封时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doUntieDisable(String loginType, Object loginId, String service) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次打开二级认证时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次创建Session时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doCloseSafe(String loginType, String tokenValue, String service) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次创建Session时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doCreateSession(String id) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次注销Session时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doLogoutSession(String id) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次Token续期时触发
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package com.pj.auth.properties;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import com.pj.auth.enums.CaptchaCategory;
|
||||||
|
import com.pj.auth.enums.CaptchaType;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码配置
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@RefreshScope
|
||||||
|
@ConfigurationProperties(prefix = "security.captcha")
|
||||||
|
public class CaptchaProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码类型
|
||||||
|
*/
|
||||||
|
private CaptchaType type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码类别
|
||||||
|
*/
|
||||||
|
private CaptchaCategory category;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数字验证码位数
|
||||||
|
*/
|
||||||
|
private Integer numberLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符验证码长度
|
||||||
|
*/
|
||||||
|
private Integer charLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码开关
|
||||||
|
*/
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.pj.auth.properties;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户密码配置
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@RefreshScope
|
||||||
|
@ConfigurationProperties(prefix = "user.password")
|
||||||
|
public class UserPasswordProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码最大错误次数
|
||||||
|
*/
|
||||||
|
private Integer maxRetryCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码锁定时间(默认10分钟)
|
||||||
|
*/
|
||||||
|
private Integer lockTime;
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,10 +1,9 @@
|
|||||||
package com.pj.service;
|
package com.pj.auth.service;
|
||||||
|
|
||||||
|
import com.pj.auth.domain.vo.LoginVo;
|
||||||
import com.pj.model.vo.LoginVo;
|
|
||||||
import com.pj.model.vo.SysClientVo;
|
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteClientVo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 授权策略
|
* 授权策略
|
||||||
@ -23,9 +22,9 @@ public interface IAuthStrategy {
|
|||||||
* @param grantType 授权类型
|
* @param grantType 授权类型
|
||||||
* @return 登录验证信息
|
* @return 登录验证信息
|
||||||
*/
|
*/
|
||||||
static LoginVo login(String body, SysClientVo client, String grantType) {
|
static LoginVo login(String body, RemoteClientVo client, String grantType) {
|
||||||
// 授权类型和客户端id
|
// 授权类型和客户端id
|
||||||
String beanName = grantType + IAuthStrategy.BASE_NAME;
|
String beanName = grantType + BASE_NAME;
|
||||||
if (!SpringUtils.containsBean(beanName)) {
|
if (!SpringUtils.containsBean(beanName)) {
|
||||||
throw new ServiceException("授权类型不正确!");
|
throw new ServiceException("授权类型不正确!");
|
||||||
}
|
}
|
||||||
@ -40,6 +39,6 @@ public interface IAuthStrategy {
|
|||||||
* @param client 授权管理视图对象
|
* @param client 授权管理视图对象
|
||||||
* @return 登录验证信息
|
* @return 登录验证信息
|
||||||
*/
|
*/
|
||||||
LoginVo login(String body, SysClientVo client);
|
LoginVo login(String body, RemoteClientVo client);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,269 @@
|
|||||||
|
package com.pj.auth.service;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.secure.BCrypt;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.baomidou.lock.annotation.Lock4j;
|
||||||
|
import com.pj.auth.form.RegisterBody;
|
||||||
|
import com.pj.auth.properties.CaptchaProperties;
|
||||||
|
import com.pj.auth.properties.UserPasswordProperties;
|
||||||
|
import com.pj.helper.LoginSaasHelper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import me.zhyd.oauth.model.AuthUser;
|
||||||
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
|
import org.dromara.common.core.constant.CacheConstants;
|
||||||
|
import org.dromara.common.core.constant.Constants;
|
||||||
|
import org.dromara.common.core.constant.GlobalConstants;
|
||||||
|
import org.dromara.common.core.constant.TenantConstants;
|
||||||
|
import org.dromara.common.core.enums.LoginType;
|
||||||
|
import org.dromara.common.core.enums.TenantStatus;
|
||||||
|
import org.dromara.common.core.enums.UserType;
|
||||||
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
|
import org.dromara.common.core.exception.user.CaptchaException;
|
||||||
|
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
||||||
|
import org.dromara.common.core.exception.user.UserException;
|
||||||
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.log.event.LogininforEvent;
|
||||||
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
|
import org.dromara.common.tenant.exception.TenantException;
|
||||||
|
import org.dromara.common.tenant.helper.TenantHelper;
|
||||||
|
import org.dromara.system.api.RemoteSocialService;
|
||||||
|
import org.dromara.system.api.RemoteTenantService;
|
||||||
|
import org.dromara.system.api.RemoteUserService;
|
||||||
|
import org.dromara.system.api.domain.bo.RemoteSocialBo;
|
||||||
|
import org.dromara.system.api.domain.bo.RemoteUserBo;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteSocialVo;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteTenantVo;
|
||||||
|
import org.dromara.system.api.model.LoginUser;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录校验方法
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class SysLoginService {
|
||||||
|
|
||||||
|
@DubboReference
|
||||||
|
private RemoteUserService remoteUserService;
|
||||||
|
@DubboReference
|
||||||
|
private RemoteTenantService remoteTenantService;
|
||||||
|
@DubboReference
|
||||||
|
private RemoteSocialService remoteSocialService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserPasswordProperties userPasswordProperties;
|
||||||
|
@Autowired
|
||||||
|
private final CaptchaProperties captchaProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定第三方用户
|
||||||
|
*
|
||||||
|
* @param authUserData 授权响应实体
|
||||||
|
*/
|
||||||
|
@Lock4j
|
||||||
|
public void socialRegister(AuthUser authUserData) {
|
||||||
|
String authId = authUserData.getSource() + authUserData.getUuid();
|
||||||
|
// 第三方用户信息
|
||||||
|
RemoteSocialBo bo = BeanUtil.toBean(authUserData, RemoteSocialBo.class);
|
||||||
|
BeanUtil.copyProperties(authUserData.getToken(), bo);
|
||||||
|
Long userId = LoginSaasHelper.getUserId();
|
||||||
|
bo.setUserId(userId);
|
||||||
|
bo.setAuthId(authId);
|
||||||
|
bo.setOpenId(authUserData.getUuid());
|
||||||
|
bo.setUserName(authUserData.getUsername());
|
||||||
|
bo.setNickName(authUserData.getNickname());
|
||||||
|
List<RemoteSocialVo> checkList = remoteSocialService.selectByAuthId(authId);
|
||||||
|
if (CollUtil.isNotEmpty(checkList)) {
|
||||||
|
throw new ServiceException("此三方账号已经被绑定!");
|
||||||
|
}
|
||||||
|
// 查询是否已经绑定用户
|
||||||
|
RemoteSocialBo params = new RemoteSocialBo();
|
||||||
|
params.setUserId(userId);
|
||||||
|
params.setSource(bo.getSource());
|
||||||
|
List<RemoteSocialVo> list = remoteSocialService.queryList(params);
|
||||||
|
if (CollUtil.isEmpty(list)) {
|
||||||
|
// 没有绑定用户, 新增用户信息
|
||||||
|
remoteSocialService.insertByBo(bo);
|
||||||
|
} else {
|
||||||
|
// 更新用户信息
|
||||||
|
bo.setId(list.get(0).getId());
|
||||||
|
remoteSocialService.updateByBo(bo);
|
||||||
|
// 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断
|
||||||
|
// throw new ServiceException("此平台账号已经被绑定!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录
|
||||||
|
*/
|
||||||
|
public void logout() {
|
||||||
|
try {
|
||||||
|
LoginUser loginUser = LoginSaasHelper.getLoginUser();
|
||||||
|
if (ObjectUtil.isNull(loginUser)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (TenantHelper.isEnable() && LoginSaasHelper.isSuperAdmin()) {
|
||||||
|
// 超级管理员 登出清除动态租户
|
||||||
|
TenantHelper.clearDynamic();
|
||||||
|
}
|
||||||
|
recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
|
||||||
|
} catch (NotLoginException ignored) {
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
StpUtil.logout();
|
||||||
|
} catch (NotLoginException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册
|
||||||
|
*/
|
||||||
|
public void register(RegisterBody registerBody) {
|
||||||
|
String tenantId = registerBody.getTenantId();
|
||||||
|
String username = registerBody.getUsername();
|
||||||
|
String password = registerBody.getPassword();
|
||||||
|
// 校验用户类型是否存在
|
||||||
|
String userType = UserType.getUserType(registerBody.getUserType()).getUserType();
|
||||||
|
|
||||||
|
boolean captchaEnabled = captchaProperties.getEnabled();
|
||||||
|
// 验证码开关
|
||||||
|
if (captchaEnabled) {
|
||||||
|
validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册用户信息
|
||||||
|
RemoteUserBo remoteUserBo = new RemoteUserBo();
|
||||||
|
remoteUserBo.setTenantId(tenantId);
|
||||||
|
remoteUserBo.setUserName(username);
|
||||||
|
remoteUserBo.setNickName(username);
|
||||||
|
remoteUserBo.setPassword(BCrypt.hashpw(password));
|
||||||
|
remoteUserBo.setUserType(userType);
|
||||||
|
|
||||||
|
boolean regFlag = remoteUserService.registerUserInfo(remoteUserBo);
|
||||||
|
if (!regFlag) {
|
||||||
|
throw new UserException("user.register.error");
|
||||||
|
}
|
||||||
|
recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验验证码
|
||||||
|
*
|
||||||
|
* @param username 用户名
|
||||||
|
* @param code 验证码
|
||||||
|
* @param uuid 唯一标识
|
||||||
|
*/
|
||||||
|
public void validateCaptcha(String tenantId, String username, String code, String uuid) {
|
||||||
|
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
|
||||||
|
String captcha = RedisUtils.getCacheObject(verifyKey);
|
||||||
|
RedisUtils.deleteObject(verifyKey);
|
||||||
|
if (captcha == null) {
|
||||||
|
recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire"));
|
||||||
|
throw new CaptchaExpireException();
|
||||||
|
}
|
||||||
|
if (!code.equalsIgnoreCase(captcha)) {
|
||||||
|
recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error"));
|
||||||
|
throw new CaptchaException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录登录信息
|
||||||
|
*
|
||||||
|
* @param username 用户名
|
||||||
|
* @param status 状态
|
||||||
|
* @param message 消息内容
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public void recordLogininfor(String tenantId, String username, String status, String message) {
|
||||||
|
// 封装对象
|
||||||
|
LogininforEvent logininforEvent = new LogininforEvent();
|
||||||
|
logininforEvent.setTenantId(tenantId);
|
||||||
|
logininforEvent.setUsername(username);
|
||||||
|
logininforEvent.setStatus(status);
|
||||||
|
logininforEvent.setMessage(message);
|
||||||
|
SpringUtils.context().publishEvent(logininforEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录校验
|
||||||
|
*/
|
||||||
|
public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
|
||||||
|
String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;
|
||||||
|
String loginFail = Constants.LOGIN_FAIL;
|
||||||
|
Integer maxRetryCount = userPasswordProperties.getMaxRetryCount();
|
||||||
|
Integer lockTime = userPasswordProperties.getLockTime();
|
||||||
|
|
||||||
|
// 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip)
|
||||||
|
int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0);
|
||||||
|
// 锁定时间内登录 则踢出
|
||||||
|
if (errorNumber >= maxRetryCount) {
|
||||||
|
recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
|
||||||
|
throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supplier.get()) {
|
||||||
|
// 错误次数递增
|
||||||
|
errorNumber++;
|
||||||
|
RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
|
||||||
|
// 达到规定错误次数 则锁定登录
|
||||||
|
if (errorNumber >= maxRetryCount) {
|
||||||
|
recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
|
||||||
|
throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
|
||||||
|
} else {
|
||||||
|
// 未达到规定错误次数
|
||||||
|
recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
|
||||||
|
throw new UserException(loginType.getRetryLimitCount(), errorNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录成功 清空错误次数
|
||||||
|
RedisUtils.deleteObject(errorKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验租户
|
||||||
|
*
|
||||||
|
* @param tenantId 租户ID
|
||||||
|
*/
|
||||||
|
public void checkTenant(String tenantId) {
|
||||||
|
if (!TenantHelper.isEnable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(tenantId)) {
|
||||||
|
throw new TenantException("tenant.number.not.blank");
|
||||||
|
}
|
||||||
|
RemoteTenantVo tenant = remoteTenantService.queryByTenantId(tenantId);
|
||||||
|
if (ObjectUtil.isNull(tenant)) {
|
||||||
|
log.info("登录租户:{} 不存在.", tenantId);
|
||||||
|
throw new TenantException("tenant.not.exists");
|
||||||
|
} else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) {
|
||||||
|
log.info("登录租户:{} 已被停用.", tenantId);
|
||||||
|
throw new TenantException("tenant.blocked");
|
||||||
|
} else if (ObjectUtil.isNotNull(tenant.getExpireTime())
|
||||||
|
&& new Date().after(tenant.getExpireTime())) {
|
||||||
|
log.info("登录租户:{} 已超过有效期.", tenantId);
|
||||||
|
throw new TenantException("tenant.expired");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package com.pj.auth.service.impl;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.SaLoginModel;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.pj.auth.domain.vo.LoginVo;
|
||||||
|
import com.pj.auth.form.EmailLoginBody;
|
||||||
|
import com.pj.auth.service.IAuthStrategy;
|
||||||
|
import com.pj.auth.service.SysLoginService;
|
||||||
|
import com.pj.helper.LoginSaasHelper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
|
import org.dromara.common.core.constant.Constants;
|
||||||
|
import org.dromara.common.core.constant.GlobalConstants;
|
||||||
|
import org.dromara.common.core.enums.LoginType;
|
||||||
|
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
||||||
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.core.utils.ValidatorUtils;
|
||||||
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
|
import org.dromara.common.tenant.helper.TenantHelper;
|
||||||
|
import org.dromara.system.api.RemoteUserService;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteClientVo;
|
||||||
|
import org.dromara.system.api.model.LoginUser;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮件认证策略
|
||||||
|
*
|
||||||
|
* @author Michelle.Chung
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service("email" + IAuthStrategy.BASE_NAME)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class EmailAuthStrategy implements IAuthStrategy {
|
||||||
|
|
||||||
|
private final SysLoginService loginService;
|
||||||
|
|
||||||
|
@DubboReference
|
||||||
|
private RemoteUserService remoteUserService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginVo login(String body, RemoteClientVo client) {
|
||||||
|
EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class);
|
||||||
|
ValidatorUtils.validate(loginBody);
|
||||||
|
String tenantId = loginBody.getTenantId();
|
||||||
|
String email = loginBody.getEmail();
|
||||||
|
String emailCode = loginBody.getEmailCode();
|
||||||
|
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
||||||
|
LoginUser user = remoteUserService.getUserInfoByEmail(email, tenantId);
|
||||||
|
loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUsername(), () -> !validateEmailCode(tenantId, email, emailCode));
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
loginUser.setClientKey(client.getClientKey());
|
||||||
|
loginUser.setDeviceType(client.getDeviceType());
|
||||||
|
SaLoginModel model = new SaLoginModel();
|
||||||
|
model.setDevice(client.getDeviceType());
|
||||||
|
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||||
|
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||||
|
model.setTimeout(client.getTimeout());
|
||||||
|
model.setActiveTimeout(client.getActiveTimeout());
|
||||||
|
model.setExtra(LoginSaasHelper.CLIENT_KEY, client.getClientId());
|
||||||
|
// 生成token
|
||||||
|
LoginSaasHelper.login(loginUser, model);
|
||||||
|
|
||||||
|
LoginVo loginVo = new LoginVo();
|
||||||
|
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||||
|
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||||
|
loginVo.setClientId(client.getClientId());
|
||||||
|
return loginVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验邮箱验证码
|
||||||
|
*/
|
||||||
|
private boolean validateEmailCode(String tenantId, String email, String emailCode) {
|
||||||
|
String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email);
|
||||||
|
if (StringUtils.isBlank(code)) {
|
||||||
|
loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||||
|
throw new CaptchaExpireException();
|
||||||
|
}
|
||||||
|
return code.equals(emailCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
package com.pj.auth.service.impl;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.secure.BCrypt;
|
||||||
|
import cn.dev33.satoken.stp.SaLoginModel;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.pj.auth.domain.vo.LoginVo;
|
||||||
|
import com.pj.auth.form.PasswordLoginBody;
|
||||||
|
import com.pj.auth.properties.CaptchaProperties;
|
||||||
|
import com.pj.auth.service.IAuthStrategy;
|
||||||
|
import com.pj.auth.service.SysLoginService;
|
||||||
|
import com.pj.helper.LoginSaasHelper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
|
import org.dromara.common.core.constant.Constants;
|
||||||
|
import org.dromara.common.core.constant.GlobalConstants;
|
||||||
|
import org.dromara.common.core.enums.LoginType;
|
||||||
|
import org.dromara.common.core.exception.user.CaptchaException;
|
||||||
|
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
||||||
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.core.utils.ValidatorUtils;
|
||||||
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
|
import org.dromara.common.tenant.helper.TenantHelper;
|
||||||
|
import org.dromara.system.api.RemoteUserService;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteClientVo;
|
||||||
|
import org.dromara.system.api.model.LoginUser;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码认证策略
|
||||||
|
*
|
||||||
|
* @author Michelle.Chung
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service("password" + IAuthStrategy.BASE_NAME)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PasswordAuthStrategy implements IAuthStrategy {
|
||||||
|
|
||||||
|
private final CaptchaProperties captchaProperties;
|
||||||
|
|
||||||
|
private final SysLoginService loginService;
|
||||||
|
|
||||||
|
@DubboReference
|
||||||
|
private RemoteUserService remoteUserService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginVo login(String body, RemoteClientVo client) {
|
||||||
|
PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
|
||||||
|
ValidatorUtils.validate(loginBody);
|
||||||
|
String tenantId = loginBody.getTenantId();
|
||||||
|
String username = loginBody.getUsername();
|
||||||
|
String password = loginBody.getPassword();
|
||||||
|
String code = loginBody.getCode();
|
||||||
|
String uuid = loginBody.getUuid();
|
||||||
|
|
||||||
|
// 验证码开关
|
||||||
|
if (captchaProperties.getEnabled()) {
|
||||||
|
validateCaptcha(tenantId, username, code, uuid);
|
||||||
|
}
|
||||||
|
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
||||||
|
LoginUser user = remoteUserService.getUserInfo(username, tenantId);
|
||||||
|
loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
loginUser.setClientKey(client.getClientKey());
|
||||||
|
loginUser.setDeviceType(client.getDeviceType());
|
||||||
|
SaLoginModel model = new SaLoginModel();
|
||||||
|
model.setDevice(client.getDeviceType());
|
||||||
|
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||||
|
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||||
|
model.setTimeout(client.getTimeout());
|
||||||
|
model.setActiveTimeout(client.getActiveTimeout());
|
||||||
|
model.setExtra(LoginSaasHelper.CLIENT_KEY, client.getClientId());
|
||||||
|
// 生成token
|
||||||
|
LoginSaasHelper.login(loginUser, model);
|
||||||
|
|
||||||
|
LoginVo loginVo = new LoginVo();
|
||||||
|
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||||
|
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||||
|
loginVo.setClientId(client.getClientId());
|
||||||
|
return loginVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验验证码
|
||||||
|
*
|
||||||
|
* @param username 用户名
|
||||||
|
* @param code 验证码
|
||||||
|
* @param uuid 唯一标识
|
||||||
|
*/
|
||||||
|
private void validateCaptcha(String tenantId, String username, String code, String uuid) {
|
||||||
|
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
|
||||||
|
String captcha = RedisUtils.getCacheObject(verifyKey);
|
||||||
|
RedisUtils.deleteObject(verifyKey);
|
||||||
|
if (captcha == null) {
|
||||||
|
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||||
|
throw new CaptchaExpireException();
|
||||||
|
}
|
||||||
|
if (!code.equalsIgnoreCase(captcha)) {
|
||||||
|
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
|
||||||
|
throw new CaptchaException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package com.pj.auth.service.impl;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.SaLoginModel;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.pj.auth.domain.vo.LoginVo;
|
||||||
|
import com.pj.auth.form.SmsLoginBody;
|
||||||
|
import com.pj.auth.service.IAuthStrategy;
|
||||||
|
import com.pj.auth.service.SysLoginService;
|
||||||
|
import com.pj.helper.LoginSaasHelper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
|
import org.dromara.common.core.constant.Constants;
|
||||||
|
import org.dromara.common.core.constant.GlobalConstants;
|
||||||
|
import org.dromara.common.core.enums.LoginType;
|
||||||
|
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
||||||
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.core.utils.ValidatorUtils;
|
||||||
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
|
import org.dromara.common.tenant.helper.TenantHelper;
|
||||||
|
import org.dromara.system.api.RemoteUserService;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteClientVo;
|
||||||
|
import org.dromara.system.api.model.LoginUser;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信认证策略
|
||||||
|
*
|
||||||
|
* @author Michelle.Chung
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service("sms" + IAuthStrategy.BASE_NAME)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SmsAuthStrategy implements IAuthStrategy {
|
||||||
|
|
||||||
|
private final SysLoginService loginService;
|
||||||
|
|
||||||
|
@DubboReference
|
||||||
|
private RemoteUserService remoteUserService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginVo login(String body, RemoteClientVo client) {
|
||||||
|
SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
|
||||||
|
ValidatorUtils.validate(loginBody);
|
||||||
|
String tenantId = loginBody.getTenantId();
|
||||||
|
String phonenumber = loginBody.getPhonenumber();
|
||||||
|
String smsCode = loginBody.getSmsCode();
|
||||||
|
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
||||||
|
LoginUser user = remoteUserService.getUserInfoByPhonenumber(phonenumber, tenantId);
|
||||||
|
loginService.checkLogin(LoginType.SMS, tenantId, user.getUsername(), () -> !validateSmsCode(tenantId, phonenumber, smsCode));
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
loginUser.setClientKey(client.getClientKey());
|
||||||
|
loginUser.setDeviceType(client.getDeviceType());
|
||||||
|
SaLoginModel model = new SaLoginModel();
|
||||||
|
model.setDevice(client.getDeviceType());
|
||||||
|
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||||
|
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||||
|
model.setTimeout(client.getTimeout());
|
||||||
|
model.setActiveTimeout(client.getActiveTimeout());
|
||||||
|
model.setExtra(LoginSaasHelper.CLIENT_KEY, client.getClientId());
|
||||||
|
// 生成token
|
||||||
|
LoginSaasHelper.login(loginUser, model);
|
||||||
|
|
||||||
|
LoginVo loginVo = new LoginVo();
|
||||||
|
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||||
|
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||||
|
loginVo.setClientId(client.getClientId());
|
||||||
|
return loginVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验短信验证码
|
||||||
|
*/
|
||||||
|
private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) {
|
||||||
|
String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber);
|
||||||
|
if (StringUtils.isBlank(code)) {
|
||||||
|
loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||||
|
throw new CaptchaExpireException();
|
||||||
|
}
|
||||||
|
return code.equals(smsCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
package com.pj.auth.service.impl;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.SaLoginModel;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import cn.hutool.http.Method;
|
||||||
|
import com.pj.helper.LoginSaasHelper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import me.zhyd.oauth.model.AuthResponse;
|
||||||
|
import me.zhyd.oauth.model.AuthUser;
|
||||||
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
|
import com.pj.auth.domain.vo.LoginVo;
|
||||||
|
import com.pj.auth.form.SocialLoginBody;
|
||||||
|
import com.pj.auth.service.IAuthStrategy;
|
||||||
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
|
import org.dromara.common.core.utils.StreamUtils;
|
||||||
|
import org.dromara.common.core.utils.ValidatorUtils;
|
||||||
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
import org.dromara.common.social.config.properties.SocialProperties;
|
||||||
|
import org.dromara.common.social.utils.SocialUtils;
|
||||||
|
import org.dromara.common.tenant.helper.TenantHelper;
|
||||||
|
import org.dromara.system.api.RemoteSocialService;
|
||||||
|
import org.dromara.system.api.RemoteUserService;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteClientVo;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteSocialVo;
|
||||||
|
import org.dromara.system.api.model.LoginUser;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方授权策略
|
||||||
|
*
|
||||||
|
* @author thiszhc is 三三
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service("social" + IAuthStrategy.BASE_NAME)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SocialAuthStrategy implements IAuthStrategy {
|
||||||
|
|
||||||
|
private final SocialProperties socialProperties;
|
||||||
|
|
||||||
|
@DubboReference
|
||||||
|
private RemoteSocialService remoteSocialService;
|
||||||
|
@DubboReference
|
||||||
|
private RemoteUserService remoteUserService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录-第三方授权登录
|
||||||
|
*
|
||||||
|
* @param body 登录信息
|
||||||
|
* @param client 客户端信息
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public LoginVo login(String body, RemoteClientVo client) {
|
||||||
|
SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
|
||||||
|
ValidatorUtils.validate(loginBody);
|
||||||
|
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
|
||||||
|
loginBody.getSource(), loginBody.getSocialCode(),
|
||||||
|
loginBody.getSocialState(), socialProperties);
|
||||||
|
if (!response.ok()) {
|
||||||
|
throw new ServiceException(response.getMsg());
|
||||||
|
}
|
||||||
|
AuthUser authUserData = response.getData();
|
||||||
|
if ("GITEE".equals(authUserData.getSource())) {
|
||||||
|
// 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖
|
||||||
|
HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Vue-Plus")
|
||||||
|
.formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
|
||||||
|
.executeAsync();
|
||||||
|
HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Cloud-Plus")
|
||||||
|
.formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
|
||||||
|
.executeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RemoteSocialVo> list = remoteSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
|
||||||
|
if (CollUtil.isEmpty(list)) {
|
||||||
|
throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
|
||||||
|
}
|
||||||
|
RemoteSocialVo socialVo;
|
||||||
|
if (TenantHelper.isEnable()) {
|
||||||
|
Optional<RemoteSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId()));
|
||||||
|
if (opt.isEmpty()) {
|
||||||
|
throw new ServiceException("对不起,你没有权限登录当前租户!");
|
||||||
|
}
|
||||||
|
socialVo = opt.get();
|
||||||
|
} else {
|
||||||
|
socialVo = list.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoginUser loginUser = remoteUserService.getUserInfo(socialVo.getUserId(), socialVo.getTenantId());
|
||||||
|
loginUser.setClientKey(client.getClientKey());
|
||||||
|
loginUser.setDeviceType(client.getDeviceType());
|
||||||
|
SaLoginModel model = new SaLoginModel();
|
||||||
|
model.setDevice(client.getDeviceType());
|
||||||
|
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||||
|
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||||
|
model.setTimeout(client.getTimeout());
|
||||||
|
model.setActiveTimeout(client.getActiveTimeout());
|
||||||
|
model.setExtra(LoginSaasHelper.CLIENT_KEY, client.getClientId());
|
||||||
|
// 生成token
|
||||||
|
LoginSaasHelper.login(loginUser, model);
|
||||||
|
|
||||||
|
LoginVo loginVo = new LoginVo();
|
||||||
|
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||||
|
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||||
|
loginVo.setClientId(client.getClientId());
|
||||||
|
return loginVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
package com.pj.auth.service.impl;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.SaLoginModel;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.pj.auth.domain.vo.LoginVo;
|
||||||
|
import com.pj.auth.form.XcxLoginBody;
|
||||||
|
import com.pj.auth.service.IAuthStrategy;
|
||||||
|
import com.pj.auth.service.SysLoginService;
|
||||||
|
import com.pj.helper.LoginSaasHelper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
|
import org.dromara.common.core.utils.ValidatorUtils;
|
||||||
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
import org.dromara.system.api.RemoteUserService;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteClientVo;
|
||||||
|
import org.dromara.system.api.model.XcxLoginUser;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮件认证策略
|
||||||
|
*
|
||||||
|
* @author Michelle.Chung
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service("xcx" + IAuthStrategy.BASE_NAME)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class XcxAuthStrategy implements IAuthStrategy {
|
||||||
|
|
||||||
|
private final SysLoginService loginService;
|
||||||
|
|
||||||
|
@DubboReference
|
||||||
|
private RemoteUserService remoteUserService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginVo login(String body, RemoteClientVo client) {
|
||||||
|
XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class);
|
||||||
|
ValidatorUtils.validate(loginBody);
|
||||||
|
// xcxCode 为 小程序调用 wx.login 授权后获取
|
||||||
|
String xcxCode = loginBody.getXcxCode();
|
||||||
|
// 多个小程序识别使用
|
||||||
|
String appid = loginBody.getAppid();
|
||||||
|
|
||||||
|
// todo 以下自行实现
|
||||||
|
// 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
|
||||||
|
String openid = "";
|
||||||
|
XcxLoginUser loginUser = remoteUserService.getUserInfoByOpenid(openid);
|
||||||
|
loginUser.setClientKey(client.getClientKey());
|
||||||
|
loginUser.setDeviceType(client.getDeviceType());
|
||||||
|
|
||||||
|
SaLoginModel model = new SaLoginModel();
|
||||||
|
model.setDevice(client.getDeviceType());
|
||||||
|
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||||
|
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||||
|
model.setTimeout(client.getTimeout());
|
||||||
|
model.setActiveTimeout(client.getActiveTimeout());
|
||||||
|
model.setExtra(LoginSaasHelper.CLIENT_KEY, client.getClientId());
|
||||||
|
// 生成token
|
||||||
|
LoginSaasHelper.login(loginUser, model);
|
||||||
|
|
||||||
|
LoginVo loginVo = new LoginVo();
|
||||||
|
loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||||
|
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||||
|
loginVo.setClientId(client.getClientId());
|
||||||
|
loginVo.setOpenid(openid);
|
||||||
|
return loginVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,41 +0,0 @@
|
|||||||
package com.pj.config;
|
|
||||||
|
|
||||||
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
|
|
||||||
import cn.dev33.satoken.stp.StpInterface;
|
|
||||||
import cn.dev33.satoken.stp.StpLogic;
|
|
||||||
import org.dromara.common.satoken.core.service.SaPermissionImpl;
|
|
||||||
import org.dromara.common.satoken.handler.SaTokenExceptionHandler;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sa-token 配置
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class SaTokenConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public StpLogic getStpLogicJwt() {
|
|
||||||
// Sa-Token 整合 jwt (简单模式)
|
|
||||||
return new StpLogicJwtForSimple();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 权限接口实现(使用bean注入方便用户替换)
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
public StpInterface stpInterface() {
|
|
||||||
return new SaPermissionImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 异常处理器
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
public SaTokenExceptionHandler saTokenExceptionHandler() {
|
|
||||||
return new SaTokenExceptionHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
package com.pj.config;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
|
||||||
|
import cn.dev33.satoken.stp.StpInterface;
|
||||||
|
import cn.dev33.satoken.stp.StpLogic;
|
||||||
|
import com.pj.core.service.SaPermissionImpl;
|
||||||
|
import com.pj.handler.SaTokenExceptionHandler;
|
||||||
|
import com.pj.helper.LoginSaasHelper;
|
||||||
|
import org.dromara.common.log.aspect.CommonLogTenantIntercept;
|
||||||
|
import org.dromara.system.api.model.LoginUser;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sa-token 配置
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class SaTokenSsoConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public StpLogic getStpLogicJwt() {
|
||||||
|
// Sa-Token 整合 jwt (简单模式)
|
||||||
|
return new StpLogicJwtForSimple();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限接口实现(使用bean注入方便用户替换)
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public StpInterface stpInterface() {
|
||||||
|
return new SaPermissionImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常处理器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public SaTokenExceptionHandler saTokenExceptionHandler() {
|
||||||
|
return new SaTokenExceptionHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志拦截器,记录tenant
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public CommonLogTenantIntercept commonLogTenantIntercept() {
|
||||||
|
return new CommonLogTenantIntercept() {
|
||||||
|
@Override
|
||||||
|
public Object getTenantId() {
|
||||||
|
return LoginSaasHelper.getTenantId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
LoginUser loginUser = LoginSaasHelper.getLoginUser();
|
||||||
|
if (loginUser != null) {
|
||||||
|
return loginUser.getUsername();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDeptName() {
|
||||||
|
LoginUser loginUser = LoginSaasHelper.getLoginUser();
|
||||||
|
if (loginUser != null) {
|
||||||
|
return loginUser.getDeptName();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -5,13 +5,26 @@ import cn.dev33.satoken.context.model.SaRequest;
|
|||||||
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
|
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
|
||||||
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
|
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.dev33.satoken.util.SaResult;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
import com.pj.auth.domain.vo.LoginVo;
|
||||||
import com.pj.model.vo.LoginVo;
|
import com.pj.auth.service.IAuthStrategy;
|
||||||
import com.pj.model.vo.SysClientVo;
|
import com.pj.auth.service.SysLoginService;
|
||||||
import com.pj.service.IAuthStrategy;
|
import com.pj.helper.LoginSaasHelper;
|
||||||
import org.dromara.common.core.domain.model.PasswordLoginBody;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
|
import org.dromara.common.core.constant.UserConstants;
|
||||||
|
import org.dromara.common.core.domain.R;
|
||||||
|
import org.dromara.common.core.domain.model.LoginBody;
|
||||||
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.core.utils.ValidatorUtils;
|
||||||
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
import org.dromara.system.api.RemoteClientService;
|
||||||
|
import org.dromara.system.api.domain.vo.RemoteClientVo;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
@ -22,7 +35,13 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
* @author click33
|
* @author click33
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class SsoServerController {
|
public class SsoServerController {
|
||||||
|
private final SysLoginService sysLoginService;
|
||||||
|
|
||||||
|
@DubboReference
|
||||||
|
private RemoteClientService remoteClientService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SSO-Server端:处理所有SSO相关请求
|
* SSO-Server端:处理所有SSO相关请求
|
||||||
@ -41,6 +60,32 @@ public class SsoServerController {
|
|||||||
return SaSsoServerProcessor.instance.dister();
|
return SaSsoServerProcessor.instance.dister();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/sso/doLogin")
|
||||||
|
public Object ssoDoLoginRequest(@RequestBody String body) {
|
||||||
|
LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
|
||||||
|
ValidatorUtils.validate(loginBody);
|
||||||
|
// 授权类型和客户端id
|
||||||
|
String clientId = loginBody.getClientId();
|
||||||
|
String grantType = loginBody.getGrantType();
|
||||||
|
RemoteClientVo clientVo = remoteClientService.queryByClientId(clientId);
|
||||||
|
|
||||||
|
// 查询不到 client 或 client 内不包含 grantType
|
||||||
|
if (ObjectUtil.isNull(clientVo) || !StringUtils.contains(clientVo.getGrantType(), grantType)) {
|
||||||
|
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
|
||||||
|
return R.fail(MessageUtils.message("auth.grant.type.error"));
|
||||||
|
} else if (!UserConstants.NORMAL.equals(clientVo.getStatus())) {
|
||||||
|
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
|
||||||
|
}
|
||||||
|
// 校验租户
|
||||||
|
sysLoginService.checkTenant(loginBody.getTenantId());
|
||||||
|
// 登录
|
||||||
|
LoginVo loginVo = IAuthStrategy.login(body, clientVo, grantType);
|
||||||
|
|
||||||
|
Long userId = LoginSaasHelper.getUserId();
|
||||||
|
log.info(userId + "欢迎登录RuoYi-Cloud-Plus微服务管理系统");
|
||||||
|
return R.ok(loginVo);
|
||||||
|
}
|
||||||
|
|
||||||
// 配置SSO相关参数
|
// 配置SSO相关参数
|
||||||
@Autowired
|
@Autowired
|
||||||
private void configSso(SaSsoServerConfig ssoServer) {
|
private void configSso(SaSsoServerConfig ssoServer) {
|
||||||
@ -49,38 +94,6 @@ public class SsoServerController {
|
|||||||
ssoServer.notLoginView = () -> {
|
ssoServer.notLoginView = () -> {
|
||||||
return new ModelAndView("sa-login.html");
|
return new ModelAndView("sa-login.html");
|
||||||
};
|
};
|
||||||
|
|
||||||
// 配置:登录处理函数
|
|
||||||
ssoServer.doLoginHandle = (name, pwd) -> {
|
|
||||||
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
|
||||||
if ("sa".equals(name) && "123456".equals(pwd)) {
|
|
||||||
PasswordLoginBody body = new PasswordLoginBody();
|
|
||||||
body.setUsername("sa");
|
|
||||||
body.setPassword("123456");
|
|
||||||
body.setClientId("ClientId");
|
|
||||||
body.setGrantType("password");
|
|
||||||
body.setTenantId("1");
|
|
||||||
body.setCode("code1");
|
|
||||||
body.setUuid("qweqw");
|
|
||||||
|
|
||||||
SysClientVo client = new SysClientVo();
|
|
||||||
client.setId(1L);
|
|
||||||
client.setClientId("ClientId1");
|
|
||||||
client.setClientKey("setClientKey");
|
|
||||||
client.setClientSecret("setClientSecret");
|
|
||||||
client.setGrantType("password");
|
|
||||||
client.setDeviceType("pc");
|
|
||||||
client.setActiveTimeout(1800L);
|
|
||||||
client.setTimeout(300L);
|
|
||||||
client.setStatus("0");
|
|
||||||
|
|
||||||
LoginVo loginVo = IAuthStrategy.login(JSONUtil.toJsonStr(body), client, body.getGrantType());
|
|
||||||
|
|
||||||
return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
|
|
||||||
}
|
|
||||||
return SaResult.error("登录失败!");
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,172 @@
|
|||||||
|
package com.pj.core.dao;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.dao.SaTokenDao;
|
||||||
|
import cn.dev33.satoken.util.SaFoxUtil;
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一)
|
||||||
|
* <p>
|
||||||
|
* 采用 caffeine + redis 多级缓存 优化并发查询效率
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public class PlusSaTokenDao implements SaTokenDao {
|
||||||
|
|
||||||
|
private static final Cache<String, Object> CAFFEINE = Caffeine.newBuilder()
|
||||||
|
// 设置最后一次写入或访问后经过固定时间过期
|
||||||
|
.expireAfterWrite(5, TimeUnit.SECONDS)
|
||||||
|
// 初始的缓存空间大小
|
||||||
|
.initialCapacity(100)
|
||||||
|
// 缓存的最大条数
|
||||||
|
.maximumSize(1000)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Value,如无返空
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String get(String key) {
|
||||||
|
Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
|
||||||
|
return (String) o;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入Value,并设定存活时间 (单位: 秒)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void set(String key, String value, long timeout) {
|
||||||
|
if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 判断是否为永不过期
|
||||||
|
if (timeout == NEVER_EXPIRE) {
|
||||||
|
RedisUtils.setCacheObject(key, value);
|
||||||
|
} else {
|
||||||
|
RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
|
||||||
|
}
|
||||||
|
CAFFEINE.invalidate(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修修改指定key-value键值对 (过期时间不变)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void update(String key, String value) {
|
||||||
|
if (RedisUtils.hasKey(key)) {
|
||||||
|
RedisUtils.setCacheObject(key, value, true);
|
||||||
|
CAFFEINE.invalidate(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除Value
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void delete(String key) {
|
||||||
|
RedisUtils.deleteObject(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Value的剩余存活时间 (单位: 秒)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getTimeout(String key) {
|
||||||
|
long timeout = RedisUtils.getTimeToLive(key);
|
||||||
|
return timeout < 0 ? timeout : timeout / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改Value的剩余存活时间 (单位: 秒)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void updateTimeout(String key, long timeout) {
|
||||||
|
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Object,如无返空
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object getObject(String key) {
|
||||||
|
Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入Object,并设定存活时间 (单位: 秒)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setObject(String key, Object object, long timeout) {
|
||||||
|
if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 判断是否为永不过期
|
||||||
|
if (timeout == NEVER_EXPIRE) {
|
||||||
|
RedisUtils.setCacheObject(key, object);
|
||||||
|
} else {
|
||||||
|
RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
|
||||||
|
}
|
||||||
|
CAFFEINE.invalidate(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新Object (过期时间不变)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void updateObject(String key, Object object) {
|
||||||
|
if (RedisUtils.hasKey(key)) {
|
||||||
|
RedisUtils.setCacheObject(key, object, true);
|
||||||
|
CAFFEINE.invalidate(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除Object
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void deleteObject(String key) {
|
||||||
|
RedisUtils.deleteObject(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Object的剩余存活时间 (单位: 秒)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getObjectTimeout(String key) {
|
||||||
|
long timeout = RedisUtils.getTimeToLive(key);
|
||||||
|
return timeout < 0 ? timeout : timeout / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改Object的剩余存活时间 (单位: 秒)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void updateObjectTimeout(String key, long timeout) {
|
||||||
|
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索数据
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
|
||||||
|
String keyStr = prefix + "*" + keyword + "*";
|
||||||
|
return (List<String>) CAFFEINE.get(keyStr, k -> {
|
||||||
|
Collection<String> keys = RedisUtils.keys(keyStr);
|
||||||
|
List<String> list = new ArrayList<>(keys);
|
||||||
|
return SaFoxUtil.searchList(list, start, size, sortType);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
package com.pj.core.service;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpInterface;
|
||||||
|
import com.pj.helper.LoginSaasHelper;
|
||||||
|
import org.dromara.common.core.enums.UserType;
|
||||||
|
import org.dromara.system.api.model.LoginUser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sa-token 权限管理实现类
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public class SaPermissionImpl implements StpInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取菜单权限列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||||
|
LoginUser loginUser = LoginSaasHelper.getLoginUser();
|
||||||
|
UserType userType = UserType.getUserType(loginUser.getUserType());
|
||||||
|
if (userType == UserType.SYS_USER) {
|
||||||
|
return new ArrayList<>(loginUser.getMenuPermission());
|
||||||
|
} else if (userType == UserType.APP_USER) {
|
||||||
|
// 其他端 自行根据业务编写
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取角色权限列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<String> getRoleList(Object loginId, String loginType) {
|
||||||
|
LoginUser loginUser = LoginSaasHelper.getLoginUser();
|
||||||
|
UserType userType = UserType.getUserType(loginUser.getUserType());
|
||||||
|
if (userType == UserType.SYS_USER) {
|
||||||
|
return new ArrayList<>(loginUser.getRolePermission());
|
||||||
|
} else if (userType == UserType.APP_USER) {
|
||||||
|
// 其他端 自行根据业务编写
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
package com.pj.handler;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.exception.NotPermissionException;
|
||||||
|
import cn.dev33.satoken.exception.NotRoleException;
|
||||||
|
import cn.hutool.http.HttpStatus;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.common.core.domain.R;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SaToken异常处理器
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class SaTokenExceptionHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限码异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(NotPermissionException.class)
|
||||||
|
public R<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
|
||||||
|
return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色权限异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(NotRoleException.class)
|
||||||
|
public R<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) {
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage());
|
||||||
|
return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证失败
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(NotLoginException.class)
|
||||||
|
public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
|
||||||
|
return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,202 +0,0 @@
|
|||||||
//package com.pj.utils;
|
|
||||||
//
|
|
||||||
//import cn.dev33.satoken.session.SaSession;
|
|
||||||
//import cn.dev33.satoken.stp.SaLoginModel;
|
|
||||||
//import cn.dev33.satoken.stp.StpUtil;
|
|
||||||
//import cn.hutool.core.collection.CollUtil;
|
|
||||||
//import cn.hutool.core.convert.Convert;
|
|
||||||
//import cn.hutool.core.util.ObjectUtil;
|
|
||||||
//import lombok.AccessLevel;
|
|
||||||
//import lombok.NoArgsConstructor;
|
|
||||||
//import org.dromara.common.core.constant.TenantConstants;
|
|
||||||
//import org.dromara.common.core.constant.UserConstants;
|
|
||||||
//import org.dromara.common.core.domain.model.LoginUser;
|
|
||||||
//import org.dromara.common.core.enums.UserType;
|
|
||||||
//
|
|
||||||
//import java.util.Set;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * 登录鉴权助手
|
|
||||||
// * <p>
|
|
||||||
// * user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
|
|
||||||
// * deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
|
|
||||||
// * 可以组成 用户类型与设备类型多对多的 权限灵活控制
|
|
||||||
// * <p>
|
|
||||||
// * 多用户体系 针对 多种用户类型 但权限控制不一致
|
|
||||||
// * 可以组成 多用户类型表与多设备类型 分别控制权限
|
|
||||||
// *
|
|
||||||
// * @author Lion Li
|
|
||||||
// */
|
|
||||||
//@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
//public class LoginHelper {
|
|
||||||
//
|
|
||||||
// public static final String LOGIN_USER_KEY = "loginUser";
|
|
||||||
// public static final String TENANT_KEY = "tenantId";
|
|
||||||
// public static final String USER_KEY = "userId";
|
|
||||||
// public static final String USER_NAME_KEY = "userName";
|
|
||||||
// public static final String DEPT_KEY = "deptId";
|
|
||||||
// public static final String DEPT_NAME_KEY = "deptName";
|
|
||||||
// public static final String DEPT_CATEGORY_KEY = "deptCategory";
|
|
||||||
// public static final String CLIENT_KEY = "clientid";
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 登录系统 基于 设备类型
|
|
||||||
// * 针对相同用户体系不同设备
|
|
||||||
// *
|
|
||||||
// * @param loginUser 登录用户信息
|
|
||||||
// * @param model 配置参数
|
|
||||||
// */
|
|
||||||
// public static void login(LoginUser loginUser, SaLoginModel model) {
|
|
||||||
// model = ObjectUtil.defaultIfNull(model, new SaLoginModel());
|
|
||||||
// StpUtil.login(loginUser.getLoginId(),
|
|
||||||
// model.setExtra(TENANT_KEY, loginUser.getTenantId())
|
|
||||||
// .setExtra(USER_KEY, loginUser.getUserId())
|
|
||||||
// .setExtra(USER_NAME_KEY, loginUser.getUsername())
|
|
||||||
// .setExtra(DEPT_KEY, loginUser.getDeptId())
|
|
||||||
// .setExtra(DEPT_NAME_KEY, loginUser.getDeptName())
|
|
||||||
// .setExtra(DEPT_CATEGORY_KEY, loginUser.getDeptCategory())
|
|
||||||
// );
|
|
||||||
// StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取用户(多级缓存)
|
|
||||||
// */
|
|
||||||
// public static LoginUser getLoginUser() {
|
|
||||||
// SaSession session = StpUtil.getTokenSession();
|
|
||||||
// if (ObjectUtil.isNull(session)) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// return (LoginUser) session.get(LOGIN_USER_KEY);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取用户基于token
|
|
||||||
// */
|
|
||||||
// public static LoginUser getLoginUser(String token) {
|
|
||||||
// SaSession session = StpUtil.getTokenSessionByToken(token);
|
|
||||||
// if (ObjectUtil.isNull(session)) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// return (LoginUser) session.get(LOGIN_USER_KEY);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取用户id
|
|
||||||
// */
|
|
||||||
// public static Long getUserId() {
|
|
||||||
// return Convert.toLong(getExtra(USER_KEY));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取用户账户
|
|
||||||
// */
|
|
||||||
// public static String getUsername() {
|
|
||||||
// return Convert.toStr(getExtra(USER_NAME_KEY));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取租户ID
|
|
||||||
// */
|
|
||||||
// public static String getTenantId() {
|
|
||||||
// return Convert.toStr(getExtra(TENANT_KEY));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取部门ID
|
|
||||||
// */
|
|
||||||
// public static Long getDeptId() {
|
|
||||||
// return Convert.toLong(getExtra(DEPT_KEY));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取部门名
|
|
||||||
// */
|
|
||||||
// public static String getDeptName() {
|
|
||||||
// return Convert.toStr(getExtra(DEPT_NAME_KEY));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取部门类别编码
|
|
||||||
// */
|
|
||||||
// public static String getDeptCategory() {
|
|
||||||
// return Convert.toStr(getExtra(DEPT_CATEGORY_KEY));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取当前 Token 的扩展信息
|
|
||||||
// *
|
|
||||||
// * @param key 键值
|
|
||||||
// * @return 对应的扩展数据
|
|
||||||
// */
|
|
||||||
// private static Object getExtra(String key) {
|
|
||||||
// try {
|
|
||||||
// return StpUtil.getExtra(key);
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取用户类型
|
|
||||||
// */
|
|
||||||
// public static UserType getUserType() {
|
|
||||||
// String loginType = StpUtil.getLoginIdAsString();
|
|
||||||
// return UserType.getUserType(loginType);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 是否为超级管理员
|
|
||||||
// *
|
|
||||||
// * @param userId 用户ID
|
|
||||||
// * @return 结果
|
|
||||||
// */
|
|
||||||
// public static boolean isSuperAdmin(Long userId) {
|
|
||||||
// return UserConstants.SUPER_ADMIN_ID.equals(userId);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 是否为超级管理员
|
|
||||||
// *
|
|
||||||
// * @return 结果
|
|
||||||
// */
|
|
||||||
// public static boolean isSuperAdmin() {
|
|
||||||
// return isSuperAdmin(getUserId());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 是否为租户管理员
|
|
||||||
// *
|
|
||||||
// * @param rolePermission 角色权限标识组
|
|
||||||
// * @return 结果
|
|
||||||
// */
|
|
||||||
// public static boolean isTenantAdmin(Set<String> rolePermission) {
|
|
||||||
// if (CollUtil.isEmpty(rolePermission)) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 是否为租户管理员
|
|
||||||
// *
|
|
||||||
// * @return 结果
|
|
||||||
// */
|
|
||||||
// public static boolean isTenantAdmin() {
|
|
||||||
// return Convert.toBool(isTenantAdmin(getLoginUser().getRolePermission()));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 检查当前用户是否已登录
|
|
||||||
// *
|
|
||||||
// * @return 结果
|
|
||||||
// */
|
|
||||||
// public static boolean isLogin() {
|
|
||||||
// try {
|
|
||||||
// return getLoginUser() != null;
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
@ -0,0 +1,202 @@
|
|||||||
|
package com.pj.helper;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.session.SaSession;
|
||||||
|
import cn.dev33.satoken.stp.SaLoginModel;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.dromara.common.core.constant.TenantConstants;
|
||||||
|
import org.dromara.common.core.constant.UserConstants;
|
||||||
|
import org.dromara.common.core.enums.UserType;
|
||||||
|
import org.dromara.system.api.model.LoginUser;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录鉴权助手
|
||||||
|
* <p>
|
||||||
|
* user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
|
||||||
|
* deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
|
||||||
|
* 可以组成 用户类型与设备类型多对多的 权限灵活控制
|
||||||
|
* <p>
|
||||||
|
* 多用户体系 针对 多种用户类型 但权限控制不一致
|
||||||
|
* 可以组成 多用户类型表与多设备类型 分别控制权限
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class LoginSaasHelper {
|
||||||
|
|
||||||
|
public static final String LOGIN_USER_KEY = "loginUser";
|
||||||
|
public static final String TENANT_KEY = "tenantId";
|
||||||
|
public static final String USER_KEY = "userId";
|
||||||
|
public static final String USER_NAME_KEY = "userName";
|
||||||
|
public static final String DEPT_KEY = "deptId";
|
||||||
|
public static final String DEPT_NAME_KEY = "deptName";
|
||||||
|
public static final String DEPT_CATEGORY_KEY = "deptCategory";
|
||||||
|
public static final String CLIENT_KEY = "clientid";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录系统 基于 设备类型
|
||||||
|
* 针对相同用户体系不同设备
|
||||||
|
*
|
||||||
|
* @param loginUser 登录用户信息
|
||||||
|
* @param model 配置参数
|
||||||
|
*/
|
||||||
|
public static void login(LoginUser loginUser, SaLoginModel model) {
|
||||||
|
model = ObjectUtil.defaultIfNull(model, new SaLoginModel());
|
||||||
|
StpUtil.login(loginUser.getLoginId(),
|
||||||
|
model.setExtra(TENANT_KEY, loginUser.getTenantId())
|
||||||
|
.setExtra(USER_KEY, loginUser.getUserId())
|
||||||
|
.setExtra(USER_NAME_KEY, loginUser.getUsername())
|
||||||
|
.setExtra(DEPT_KEY, loginUser.getDeptId())
|
||||||
|
.setExtra(DEPT_NAME_KEY, loginUser.getDeptName())
|
||||||
|
.setExtra(DEPT_CATEGORY_KEY, loginUser.getDeptCategory())
|
||||||
|
);
|
||||||
|
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户(多级缓存)
|
||||||
|
*/
|
||||||
|
public static LoginUser getLoginUser() {
|
||||||
|
SaSession session = StpUtil.getTokenSession();
|
||||||
|
if (ObjectUtil.isNull(session)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (LoginUser) session.get(LOGIN_USER_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户基于token
|
||||||
|
*/
|
||||||
|
public static LoginUser getLoginUser(String token) {
|
||||||
|
SaSession session = StpUtil.getTokenSessionByToken(token);
|
||||||
|
if (ObjectUtil.isNull(session)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (LoginUser) session.get(LOGIN_USER_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户id
|
||||||
|
*/
|
||||||
|
public static Long getUserId() {
|
||||||
|
return Convert.toLong(getExtra(USER_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户账户
|
||||||
|
*/
|
||||||
|
public static String getUsername() {
|
||||||
|
return Convert.toStr(getExtra(USER_NAME_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取租户ID
|
||||||
|
*/
|
||||||
|
public static String getTenantId() {
|
||||||
|
return Convert.toStr(getExtra(TENANT_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取部门ID
|
||||||
|
*/
|
||||||
|
public static Long getDeptId() {
|
||||||
|
return Convert.toLong(getExtra(DEPT_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取部门名
|
||||||
|
*/
|
||||||
|
public static String getDeptName() {
|
||||||
|
return Convert.toStr(getExtra(DEPT_NAME_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取部门类别编码
|
||||||
|
*/
|
||||||
|
public static String getDeptCategory() {
|
||||||
|
return Convert.toStr(getExtra(DEPT_CATEGORY_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前 Token 的扩展信息
|
||||||
|
*
|
||||||
|
* @param key 键值
|
||||||
|
* @return 对应的扩展数据
|
||||||
|
*/
|
||||||
|
private static Object getExtra(String key) {
|
||||||
|
try {
|
||||||
|
return StpUtil.getExtra(key);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户类型
|
||||||
|
*/
|
||||||
|
public static UserType getUserType() {
|
||||||
|
String loginType = StpUtil.getLoginIdAsString();
|
||||||
|
return UserType.getUserType(loginType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为超级管理员
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static boolean isSuperAdmin(Long userId) {
|
||||||
|
return UserConstants.SUPER_ADMIN_ID.equals(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为超级管理员
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static boolean isSuperAdmin() {
|
||||||
|
return isSuperAdmin(getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为租户管理员
|
||||||
|
*
|
||||||
|
* @param rolePermission 角色权限标识组
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static boolean isTenantAdmin(Set<String> rolePermission) {
|
||||||
|
if (CollUtil.isEmpty(rolePermission)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为租户管理员
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static boolean isTenantAdmin() {
|
||||||
|
return Convert.toBool(isTenantAdmin(getLoginUser().getRolePermission()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查当前用户是否已登录
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static boolean isLogin() {
|
||||||
|
try {
|
||||||
|
return getLoginUser() != null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,40 +0,0 @@
|
|||||||
package com.pj.model;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class RoleDTO implements Serializable {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色ID
|
|
||||||
*/
|
|
||||||
private Long roleId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色名称
|
|
||||||
*/
|
|
||||||
private String roleName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色权限
|
|
||||||
*/
|
|
||||||
private String roleKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
|
|
||||||
*/
|
|
||||||
private String dataScope;
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
package com.pj.model.vo;
|
|
||||||
|
|
||||||
import io.github.linpeilie.annotations.AutoMapper;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 授权管理视图对象 sys_client
|
|
||||||
*
|
|
||||||
* @author Michelle.Chung
|
|
||||||
* @date 2023-05-15
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class SysClientVo implements Serializable {
|
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* id
|
|
||||||
*/
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端id
|
|
||||||
*/
|
|
||||||
private String clientId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端key
|
|
||||||
*/
|
|
||||||
private String clientKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端秘钥
|
|
||||||
*/
|
|
||||||
private String clientSecret;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 授权类型
|
|
||||||
*/
|
|
||||||
private List<String> grantTypeList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 授权类型
|
|
||||||
*/
|
|
||||||
private String grantType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备类型
|
|
||||||
*/
|
|
||||||
private String deviceType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* token活跃超时时间
|
|
||||||
*/
|
|
||||||
private Long activeTimeout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* token固定超时时间
|
|
||||||
*/
|
|
||||||
private Long timeout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态(0正常 1停用)
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
package com.pj.model.vo;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.dromara.common.core.constant.UserConstants;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色信息视图对象 sys_role
|
|
||||||
*
|
|
||||||
* @author Michelle.Chung
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class SysRoleVo implements Serializable {
|
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色ID
|
|
||||||
*/
|
|
||||||
private Long roleId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色名称
|
|
||||||
*/
|
|
||||||
private String roleName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色权限字符串
|
|
||||||
*/
|
|
||||||
private String roleKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示顺序
|
|
||||||
*/
|
|
||||||
private Integer roleSort;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)
|
|
||||||
*/
|
|
||||||
private String dataScope;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 菜单树选择项是否关联显示
|
|
||||||
*/
|
|
||||||
private Boolean menuCheckStrictly;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门树选择项是否关联显示
|
|
||||||
*/
|
|
||||||
private Boolean deptCheckStrictly;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色状态(0正常 1停用)
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 备注
|
|
||||||
*/
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建时间
|
|
||||||
*/
|
|
||||||
private Date createTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户是否存在此角色标识 默认不存在
|
|
||||||
*/
|
|
||||||
private boolean flag = false;
|
|
||||||
|
|
||||||
public boolean isSuperAdmin() {
|
|
||||||
return UserConstants.SUPER_ADMIN_ID.equals(this.roleId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,131 +0,0 @@
|
|||||||
package com.pj.model.vo;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户信息视图对象 sys_user
|
|
||||||
*
|
|
||||||
* @author Michelle.Chung
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class SysUserVo implements Serializable {
|
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户ID
|
|
||||||
*/
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 租户ID
|
|
||||||
*/
|
|
||||||
private String tenantId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门ID
|
|
||||||
*/
|
|
||||||
private Long deptId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户账号
|
|
||||||
*/
|
|
||||||
private String userName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户昵称
|
|
||||||
*/
|
|
||||||
private String nickName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户类型(sys_user系统用户)
|
|
||||||
*/
|
|
||||||
private String userType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户邮箱
|
|
||||||
*/
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手机号码
|
|
||||||
*/
|
|
||||||
private String phonenumber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户性别(0男 1女 2未知)
|
|
||||||
*/
|
|
||||||
private String sex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 头像地址
|
|
||||||
*/
|
|
||||||
private Long avatar;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 密码
|
|
||||||
*/
|
|
||||||
@JsonIgnore
|
|
||||||
@JsonProperty
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 帐号状态(0正常 1停用)
|
|
||||||
*/
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 最后登录IP
|
|
||||||
*/
|
|
||||||
private String loginIp;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 最后登录时间
|
|
||||||
*/
|
|
||||||
private Date loginDate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 备注
|
|
||||||
*/
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建时间
|
|
||||||
*/
|
|
||||||
private Date createTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门名
|
|
||||||
*/
|
|
||||||
private String deptName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色对象
|
|
||||||
*/
|
|
||||||
private List<SysRoleVo> roles;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色组
|
|
||||||
*/
|
|
||||||
private Long[] roleIds;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 岗位组
|
|
||||||
*/
|
|
||||||
private Long[] postIds;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据权限 当前角色ID
|
|
||||||
*/
|
|
||||||
private Long roleId;
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,121 +1,100 @@
|
|||||||
package com.pj.service.impl;
|
//package com.pj.service.impl;
|
||||||
|
//
|
||||||
import cn.dev33.satoken.stp.SaLoginModel;
|
//import cn.dev33.satoken.stp.SaLoginModel;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
//import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
//import cn.hutool.json.JSONUtil;
|
||||||
import cn.hutool.core.lang.Opt;
|
//import lombok.RequiredArgsConstructor;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import cn.hutool.json.JSONUtil;
|
//import org.dromara.common.core.domain.model.EmailLoginBody;
|
||||||
import com.pj.model.vo.LoginVo;
|
//import org.dromara.common.core.domain.model.LoginUser;
|
||||||
import com.pj.model.vo.SysClientVo;
|
//import org.dromara.common.core.utils.ValidatorUtils;
|
||||||
import com.pj.model.vo.SysRoleVo;
|
//import org.dromara.common.tenant.helper.TenantHelper;
|
||||||
import com.pj.model.vo.SysUserVo;
|
//import org.springframework.stereotype.Service;
|
||||||
import com.pj.service.IAuthStrategy;
|
//
|
||||||
import lombok.RequiredArgsConstructor;
|
///**
|
||||||
import lombok.extern.slf4j.Slf4j;
|
// * 邮件认证策略
|
||||||
import org.dromara.common.core.constant.Constants;
|
// *
|
||||||
import org.dromara.common.core.constant.GlobalConstants;
|
// * @author Michelle.Chung
|
||||||
import org.dromara.common.core.domain.dto.RoleDTO;
|
|
||||||
import org.dromara.common.core.domain.model.EmailLoginBody;
|
|
||||||
import org.dromara.common.core.domain.model.LoginUser;
|
|
||||||
import org.dromara.common.core.enums.LoginType;
|
|
||||||
import org.dromara.common.core.enums.UserStatus;
|
|
||||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
|
|
||||||
import org.dromara.common.core.exception.user.UserException;
|
|
||||||
import org.dromara.common.core.utils.MessageUtils;
|
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
|
||||||
import org.dromara.common.core.utils.ValidatorUtils;
|
|
||||||
import org.dromara.common.redis.utils.RedisUtils;
|
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
|
||||||
import org.dromara.common.tenant.helper.TenantHelper;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 邮件认证策略
|
|
||||||
*
|
|
||||||
* @author Michelle.Chung
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Service("email" + IAuthStrategy.BASE_NAME)
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class EmailAuthStrategy implements IAuthStrategy {
|
|
||||||
|
|
||||||
//后续改为远程的
|
|
||||||
// private final SysLoginService loginService;
|
|
||||||
// private final SysUserMapper userMapper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LoginVo login(String body, SysClientVo client) {
|
|
||||||
EmailLoginBody loginBody = JSONUtil.toBean(body, EmailLoginBody.class);
|
|
||||||
ValidatorUtils.validate(loginBody);
|
|
||||||
String tenantId = loginBody.getTenantId();
|
|
||||||
String email = loginBody.getEmail();
|
|
||||||
String emailCode = loginBody.getEmailCode();
|
|
||||||
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
|
||||||
// SysUserVo user = loadUserByEmail(email);
|
|
||||||
// loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode));
|
|
||||||
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
|
|
||||||
// return loginService.buildLoginUser(user);
|
|
||||||
return buildLoginUser();
|
|
||||||
});
|
|
||||||
loginUser.setClientKey(client.getClientKey());
|
|
||||||
loginUser.setDeviceType(client.getDeviceType());
|
|
||||||
SaLoginModel model = new SaLoginModel();
|
|
||||||
model.setDevice(client.getDeviceType());
|
|
||||||
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
|
||||||
// 例如: 后台用户30分钟过期 app用户1天过期
|
|
||||||
model.setTimeout(client.getTimeout());
|
|
||||||
model.setActiveTimeout(client.getActiveTimeout());
|
|
||||||
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
|
|
||||||
// 生成token
|
|
||||||
LoginHelper.login(loginUser, model);
|
|
||||||
|
|
||||||
LoginVo loginVo = new LoginVo();
|
|
||||||
loginVo.setAccessToken(StpUtil.getTokenValue());
|
|
||||||
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
|
||||||
loginVo.setClientId(client.getClientId());
|
|
||||||
return loginVo;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建登录用户
|
|
||||||
*/
|
|
||||||
private LoginUser buildLoginUser() {
|
|
||||||
LoginUser loginUser = new LoginUser();
|
|
||||||
loginUser.setTenantId("1");
|
|
||||||
loginUser.setUserId(1L);
|
|
||||||
loginUser.setDeptId(2L);
|
|
||||||
loginUser.setUsername("测试用户登录名");
|
|
||||||
loginUser.setNickname("测试用户名称");
|
|
||||||
loginUser.setUserType("用户类型1");
|
|
||||||
return loginUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 校验邮箱验证码
|
|
||||||
// */
|
// */
|
||||||
// private boolean validateEmailCode(String tenantId, String email, String emailCode) {
|
//@Slf4j
|
||||||
// String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email);
|
//@Service("email" + IAuthStrategy.BASE_NAME)
|
||||||
// if (StringUtils.isBlank(code)) {
|
//@RequiredArgsConstructor
|
||||||
// loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
//public class EmailAuthStrategy implements IAuthStrategy {
|
||||||
// throw new CaptchaExpireException();
|
//
|
||||||
// }
|
// //后续改为远程的
|
||||||
// return code.equals(emailCode);
|
//// private final SysLoginService loginService;
|
||||||
|
//// private final SysUserMapper userMapper;
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public LoginVo login(String body, SysClientVo client) {
|
||||||
|
// EmailLoginBody loginBody = JSONUtil.toBean(body, EmailLoginBody.class);
|
||||||
|
// ValidatorUtils.validate(loginBody);
|
||||||
|
// String tenantId = loginBody.getTenantId();
|
||||||
|
// String email = loginBody.getEmail();
|
||||||
|
// String emailCode = loginBody.getEmailCode();
|
||||||
|
// LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
||||||
|
//// SysUserVo user = loadUserByEmail(email);
|
||||||
|
//// loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode));
|
||||||
|
// // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
|
||||||
|
//// return loginService.buildLoginUser(user);
|
||||||
|
// return buildLoginUser();
|
||||||
|
// });
|
||||||
|
// loginUser.setClientKey(client.getClientKey());
|
||||||
|
// loginUser.setDeviceType(client.getDeviceType());
|
||||||
|
// SaLoginModel model = new SaLoginModel();
|
||||||
|
// model.setDevice(client.getDeviceType());
|
||||||
|
// // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||||
|
// // 例如: 后台用户30分钟过期 app用户1天过期
|
||||||
|
// model.setTimeout(client.getTimeout());
|
||||||
|
// model.setActiveTimeout(client.getActiveTimeout());
|
||||||
|
// model.setExtra(LoginSaasHelper.CLIENT_KEY, client.getClientId());
|
||||||
|
// // 生成token
|
||||||
|
// LoginSaasHelper.login(loginUser, model);
|
||||||
|
//
|
||||||
|
// LoginVo loginVo = new LoginVo();
|
||||||
|
// loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||||
|
// loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||||
|
// loginVo.setClientId(client.getClientId());
|
||||||
|
// return loginVo;
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// private SysUserVo loadUserByEmail(String email) {
|
//
|
||||||
// SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email));
|
// /**
|
||||||
// if (ObjectUtil.isNull(user)) {
|
// * 构建登录用户
|
||||||
// log.info("登录用户:{} 不存在.", email);
|
// */
|
||||||
// throw new UserException("user.not.exists", email);
|
// private LoginUser buildLoginUser() {
|
||||||
// } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
// LoginUser loginUser = new LoginUser();
|
||||||
// log.info("登录用户:{} 已被停用.", email);
|
// loginUser.setTenantId("1");
|
||||||
// throw new UserException("user.blocked", email);
|
// loginUser.setUserId(1L);
|
||||||
|
// loginUser.setDeptId(2L);
|
||||||
|
// loginUser.setUsername("测试用户登录名");
|
||||||
|
// loginUser.setNickname("测试用户名称");
|
||||||
|
// loginUser.setUserType("用户类型1");
|
||||||
|
// return loginUser;
|
||||||
// }
|
// }
|
||||||
// return user;
|
//
|
||||||
// }
|
//// /**
|
||||||
|
//// * 校验邮箱验证码
|
||||||
}
|
//// */
|
||||||
|
//// private boolean validateEmailCode(String tenantId, String email, String emailCode) {
|
||||||
|
//// String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email);
|
||||||
|
//// if (StringUtils.isBlank(code)) {
|
||||||
|
//// loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||||
|
//// throw new CaptchaExpireException();
|
||||||
|
//// }
|
||||||
|
//// return code.equals(emailCode);
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// private SysUserVo loadUserByEmail(String email) {
|
||||||
|
//// SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email));
|
||||||
|
//// if (ObjectUtil.isNull(user)) {
|
||||||
|
//// log.info("登录用户:{} 不存在.", email);
|
||||||
|
//// throw new UserException("user.not.exists", email);
|
||||||
|
//// } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
||||||
|
//// log.info("登录用户:{} 已被停用.", email);
|
||||||
|
//// throw new UserException("user.blocked", email);
|
||||||
|
//// }
|
||||||
|
//// return user;
|
||||||
|
//// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|||||||
@ -1,117 +1,117 @@
|
|||||||
package com.pj.service.impl;
|
//package com.pj.service.impl;
|
||||||
|
//
|
||||||
import cn.dev33.satoken.stp.SaLoginModel;
|
//import cn.dev33.satoken.stp.SaLoginModel;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
//import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
//import cn.hutool.json.JSONUtil;
|
||||||
import com.pj.model.vo.LoginVo;
|
//import com.pj.model.vo.LoginVo;
|
||||||
import com.pj.model.vo.SysClientVo;
|
//import com.pj.model.vo.SysClientVo;
|
||||||
import com.pj.service.IAuthStrategy;
|
//import com.pj.service.IAuthStrategy;
|
||||||
import lombok.RequiredArgsConstructor;
|
//import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.common.core.domain.model.LoginUser;
|
//import org.dromara.common.core.domain.model.LoginUser;
|
||||||
import org.dromara.common.core.domain.model.PasswordLoginBody;
|
//import org.dromara.common.core.domain.model.PasswordLoginBody;
|
||||||
import org.dromara.common.core.utils.ValidatorUtils;
|
//import org.dromara.common.core.utils.ValidatorUtils;
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
//import org.dromara.common.satoken.utils.LoginSaasHelper;
|
||||||
import org.dromara.common.tenant.helper.TenantHelper;
|
//import org.dromara.common.tenant.helper.TenantHelper;
|
||||||
import org.springframework.stereotype.Service;
|
//import org.springframework.stereotype.Service;
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* 密码认证策略
|
// * 密码认证策略
|
||||||
*
|
// *
|
||||||
* @author Michelle.Chung
|
// * @author Michelle.Chung
|
||||||
*/
|
// */
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
@Service("password" + IAuthStrategy.BASE_NAME)
|
//@Service("password" + IAuthStrategy.BASE_NAME)
|
||||||
@RequiredArgsConstructor
|
//@RequiredArgsConstructor
|
||||||
public class PasswordAuthStrategy implements IAuthStrategy {
|
//public class PasswordAuthStrategy implements IAuthStrategy {
|
||||||
|
//
|
||||||
// private final CaptchaProperties captchaProperties;
|
//// private final CaptchaProperties captchaProperties;
|
||||||
// private final SysLoginService loginService;
|
//// private final SysLoginService loginService;
|
||||||
// private final SysUserMapper userMapper;
|
//// private final SysUserMapper userMapper;
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public LoginVo login(String body, SysClientVo client) {
|
// public LoginVo login(String body, SysClientVo client) {
|
||||||
PasswordLoginBody loginBody = JSONUtil.toBean(body, PasswordLoginBody.class);
|
// PasswordLoginBody loginBody = JSONUtil.toBean(body, PasswordLoginBody.class);
|
||||||
ValidatorUtils.validate(loginBody);
|
// ValidatorUtils.validate(loginBody);
|
||||||
String tenantId = loginBody.getTenantId();
|
// String tenantId = loginBody.getTenantId();
|
||||||
String username = loginBody.getUsername();
|
// String username = loginBody.getUsername();
|
||||||
String password = loginBody.getPassword();
|
// String password = loginBody.getPassword();
|
||||||
String code = loginBody.getCode();
|
// String code = loginBody.getCode();
|
||||||
String uuid = loginBody.getUuid();
|
// String uuid = loginBody.getUuid();
|
||||||
|
//
|
||||||
// boolean captchaEnabled = captchaProperties.getEnable();
|
//// boolean captchaEnabled = captchaProperties.getEnable();
|
||||||
// // 验证码开关
|
//// // 验证码开关
|
||||||
// if (captchaEnabled) {
|
//// if (captchaEnabled) {
|
||||||
// validateCaptcha(tenantId, username, code, uuid);
|
//// validateCaptcha(tenantId, username, code, uuid);
|
||||||
// }
|
//// }
|
||||||
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
// LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
||||||
// SysUserVo user = loadUserByUsername(username);
|
//// SysUserVo user = loadUserByUsername(username);
|
||||||
// loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
|
//// loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
|
||||||
// // 此处可根据登录用户的数据不同 自行创建 loginUser
|
//// // 此处可根据登录用户的数据不同 自行创建 loginUser
|
||||||
// return loginService.buildLoginUser(user);
|
//// return loginService.buildLoginUser(user);
|
||||||
return buildLoginUser();
|
// return buildLoginUser();
|
||||||
});
|
// });
|
||||||
loginUser.setClientKey(client.getClientKey());
|
// loginUser.setClientKey(client.getClientKey());
|
||||||
loginUser.setDeviceType(client.getDeviceType());
|
// loginUser.setDeviceType(client.getDeviceType());
|
||||||
SaLoginModel model = new SaLoginModel();
|
// SaLoginModel model = new SaLoginModel();
|
||||||
model.setDevice(client.getDeviceType());
|
// model.setDevice(client.getDeviceType());
|
||||||
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
// // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||||
// 例如: 后台用户30分钟过期 app用户1天过期
|
// // 例如: 后台用户30分钟过期 app用户1天过期
|
||||||
model.setTimeout(client.getTimeout());
|
// model.setTimeout(client.getTimeout());
|
||||||
model.setActiveTimeout(client.getActiveTimeout());
|
// model.setActiveTimeout(client.getActiveTimeout());
|
||||||
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
|
// model.setExtra(LoginSaasHelper.CLIENT_KEY, client.getClientId());
|
||||||
// 生成token
|
// // 生成token
|
||||||
LoginHelper.login(loginUser, model);
|
// LoginSaasHelper.login(loginUser, model);
|
||||||
|
//
|
||||||
LoginVo loginVo = new LoginVo();
|
// LoginVo loginVo = new LoginVo();
|
||||||
loginVo.setAccessToken(StpUtil.getTokenValue());
|
// loginVo.setAccessToken(StpUtil.getTokenValue());
|
||||||
loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
// loginVo.setExpireIn(StpUtil.getTokenTimeout());
|
||||||
loginVo.setClientId(client.getClientId());
|
// loginVo.setClientId(client.getClientId());
|
||||||
return loginVo;
|
// return loginVo;
|
||||||
}
|
|
||||||
|
|
||||||
private LoginUser buildLoginUser() {
|
|
||||||
LoginUser loginUser = new LoginUser();
|
|
||||||
loginUser.setTenantId("1");
|
|
||||||
loginUser.setUserId(1L);
|
|
||||||
loginUser.setDeptId(2L);
|
|
||||||
loginUser.setUsername("测试用户登录名");
|
|
||||||
loginUser.setNickname("测试用户名称");
|
|
||||||
loginUser.setUserType("app_user");
|
|
||||||
return loginUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验验证码
|
|
||||||
*
|
|
||||||
* @param username 用户名
|
|
||||||
* @param code 验证码
|
|
||||||
* @param uuid 唯一标识
|
|
||||||
*/
|
|
||||||
// private void validateCaptcha(String tenantId, String username, String code, String uuid) {
|
|
||||||
// String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
|
|
||||||
// String captcha = RedisUtils.getCacheObject(verifyKey);
|
|
||||||
// RedisUtils.deleteObject(verifyKey);
|
|
||||||
// if (captcha == null) {
|
|
||||||
// loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
|
||||||
// throw new CaptchaExpireException();
|
|
||||||
// }
|
|
||||||
// if (!code.equalsIgnoreCase(captcha)) {
|
|
||||||
// loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
|
|
||||||
// throw new CaptchaException();
|
|
||||||
// }
|
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// private SysUserVo loadUserByUsername(String username) {
|
// private LoginUser buildLoginUser() {
|
||||||
// SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
|
// LoginUser loginUser = new LoginUser();
|
||||||
// if (ObjectUtil.isNull(user)) {
|
// loginUser.setTenantId("1");
|
||||||
// log.info("登录用户:{} 不存在.", username);
|
// loginUser.setUserId(1L);
|
||||||
// throw new UserException("user.not.exists", username);
|
// loginUser.setDeptId(2L);
|
||||||
// } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
// loginUser.setUsername("测试用户登录名");
|
||||||
// log.info("登录用户:{} 已被停用.", username);
|
// loginUser.setNickname("测试用户名称");
|
||||||
// throw new UserException("user.blocked", username);
|
// loginUser.setUserType("app_user");
|
||||||
|
// return loginUser;
|
||||||
// }
|
// }
|
||||||
// return user;
|
//
|
||||||
// }
|
// /**
|
||||||
|
// * 校验验证码
|
||||||
}
|
// *
|
||||||
|
// * @param username 用户名
|
||||||
|
// * @param code 验证码
|
||||||
|
// * @param uuid 唯一标识
|
||||||
|
// */
|
||||||
|
//// private void validateCaptcha(String tenantId, String username, String code, String uuid) {
|
||||||
|
//// String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
|
||||||
|
//// String captcha = RedisUtils.getCacheObject(verifyKey);
|
||||||
|
//// RedisUtils.deleteObject(verifyKey);
|
||||||
|
//// if (captcha == null) {
|
||||||
|
//// loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||||
|
//// throw new CaptchaExpireException();
|
||||||
|
//// }
|
||||||
|
//// if (!code.equalsIgnoreCase(captcha)) {
|
||||||
|
//// loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
|
||||||
|
//// throw new CaptchaException();
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// private SysUserVo loadUserByUsername(String username) {
|
||||||
|
//// SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
|
||||||
|
//// if (ObjectUtil.isNull(user)) {
|
||||||
|
//// log.info("登录用户:{} 不存在.", username);
|
||||||
|
//// throw new UserException("user.not.exists", username);
|
||||||
|
//// } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
||||||
|
//// log.info("登录用户:{} 已被停用.", username);
|
||||||
|
//// throw new UserException("user.blocked", username);
|
||||||
|
//// }
|
||||||
|
//// return user;
|
||||||
|
//// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|||||||
@ -3,12 +3,8 @@ server:
|
|||||||
port: 19000
|
port: 19000
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
cloud:
|
application:
|
||||||
nacos:
|
name: ruoyi-sso-server
|
||||||
discovery:
|
|
||||||
server-addr: http://127.0.0.1:8848
|
|
||||||
namespace: c5c00044-93e6-4e66-8060-3f39aa7ab82b
|
|
||||||
group: ${spring.profiles.active}
|
|
||||||
# Redis配置 (SSO模式一和模式二使用Redis来同步会话)
|
# Redis配置 (SSO模式一和模式二使用Redis来同步会话)
|
||||||
data:
|
data:
|
||||||
redis:
|
redis:
|
||||||
@ -77,27 +73,29 @@ security:
|
|||||||
- /*/api-docs/**
|
- /*/api-docs/**
|
||||||
- /sso/signout
|
- /sso/signout
|
||||||
|
|
||||||
# 内置配置 不允许修改 如需修改请在 nacos 上写相同配置覆盖
|
# dubbo
|
||||||
dubbo:
|
dubbo:
|
||||||
|
config:
|
||||||
|
protocol:
|
||||||
|
name: tri
|
||||||
|
port: 20880
|
||||||
application:
|
application:
|
||||||
|
qos-enable: false
|
||||||
logger: slf4j
|
logger: slf4j
|
||||||
# 元数据中心 local 本地 remote 远程 这里使用远程便于其他服务获取
|
name: ${spring.application.name}
|
||||||
metadataType: remote
|
|
||||||
# 可选值 interface、instance、all,默认是 all,即接口级地址、应用级地址都注册
|
|
||||||
register-mode: instance
|
|
||||||
service-discovery:
|
|
||||||
# FORCE_INTERFACE,只消费接口级地址,如无地址则报错,单订阅 2.x 地址
|
|
||||||
# APPLICATION_FIRST,智能决策接口级/应用级地址,双订阅
|
|
||||||
# FORCE_APPLICATION,只消费应用级地址,如无地址则报错,单订阅 3.x 地址
|
|
||||||
migration: FORCE_APPLICATION
|
|
||||||
# 注册中心配置
|
# 注册中心配置
|
||||||
registry:
|
registry:
|
||||||
address: nacos://127.0.0.1:8848
|
address: nacos://127.0.0.1:8848
|
||||||
group: DUBBO_GROUP
|
group: DUBBO_GROUP
|
||||||
username: nacos
|
|
||||||
password: nacos
|
|
||||||
parameters:
|
parameters:
|
||||||
namespace: c5c00044-93e6-4e66-8060-3f39aa7ab82b
|
namespace: c5c00044-93e6-4e66-8060-3f39aa7ab82b
|
||||||
|
# metadata-report:
|
||||||
|
# address: redis://${spring.data.redis.host}:${spring.data.redis.port}
|
||||||
|
# # 集群开关,目前单机
|
||||||
|
# cluster: false
|
||||||
|
# parameters:
|
||||||
|
# database: 6
|
||||||
|
# timeout: ${spring.data.redis.timeout}
|
||||||
# 消费者相关配置
|
# 消费者相关配置
|
||||||
consumer:
|
consumer:
|
||||||
# 结果缓存(LRU算法)
|
# 结果缓存(LRU算法)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user