微服务版后端初始化
This commit is contained in:
15
xjrsoft-auth/Dockerfile
Normal file
15
xjrsoft-auth/Dockerfile
Normal file
@ -0,0 +1,15 @@
|
||||
# 基础镜像
|
||||
FROM nexus.gdyditc.com:8082/openjdk:11-arm64
|
||||
# author
|
||||
MAINTAINER xjrsoft
|
||||
|
||||
# 挂载目录
|
||||
VOLUME /home/xjrsoft
|
||||
# 创建目录
|
||||
RUN mkdir -p /home/xjrsoft
|
||||
# 指定路径
|
||||
WORKDIR /home/xjrsoft
|
||||
# 复制jar文件到路径
|
||||
COPY ./xjrsoft-auth/target/xjrsoft-auth.jar /home/xjrsoft/xjrsoft-auth.jar
|
||||
# 启动认证服务
|
||||
ENTRYPOINT ["java","-jar","xjrsoft-auth.jar","-Dfile.encoding=UTF-8"]
|
||||
256
xjrsoft-auth/pom.xml
Normal file
256
xjrsoft-auth/pom.xml
Normal file
@ -0,0 +1,256 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>xjrsoft-cloud</artifactId>
|
||||
<groupId>com.xjrsoft</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>xjrsoft-auth</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${xjrsoft.framework.version}</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token插件:权限缓存与业务缓存分离 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-alone-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Http请求工具 -->
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xjrsoft</groupId>
|
||||
<artifactId>xjrsoft-service-system-api</artifactId>
|
||||
<version>${xjrsoft.framework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xjrsoft</groupId>
|
||||
<artifactId>xjrsoft-service-organization-api</artifactId>
|
||||
<version>${xjrsoft.framework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--引入nacos依赖-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!--引入spring-cloud-alibaba-nacos-config依赖-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--引入spring-cloud-alibaba-nacos-config bootstrap依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--引入sentinel依赖-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--引入sentinel-datasource-nacos依赖-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-nacos</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xjrsoft</groupId>
|
||||
<artifactId>xjrsoft-common-core</artifactId>
|
||||
<version>${xjrsoft.framework.version}</version>
|
||||
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xjrsoft</groupId>
|
||||
<artifactId>xjrsoft-service-organization-api</artifactId>
|
||||
<version>${xjrsoft.framework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xjrsoft</groupId>
|
||||
<artifactId>xjrsoft-common-tenant</artifactId>
|
||||
<version>${xjrsoft.framework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xjrsoft</groupId>
|
||||
<artifactId>xjrsoft-common-redis</artifactId>
|
||||
<version>${xjrsoft.framework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xjrsoft</groupId>
|
||||
<artifactId>xjrsoft-common-redis</artifactId>
|
||||
<version>${xjrsoft.framework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xjrsoft</groupId>
|
||||
<artifactId>xjrsoft-service-workflow-api</artifactId>
|
||||
<version>${xjrsoft.framework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.xjrsoft</groupId>-->
|
||||
<!-- <artifactId>xjrsoft-common-mybatis</artifactId>-->
|
||||
<!-- <version>${xjrsoft.framework.version}</version>-->
|
||||
<!-- <exclusions>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>com.baomidou</groupId>-->
|
||||
<!-- <artifactId>dynamic-datasource-spring-boot-starter</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- </exclusions>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-authz-client</artifactId>
|
||||
<version>${keycloak.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>${justauth.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xjrsoft</groupId>
|
||||
<artifactId>xjrsoft-service-organization-api</artifactId>
|
||||
<version>${xjrsoft.framework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>kingbase</groupId>
|
||||
<artifactId>kingbase</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,21 @@
|
||||
package com.xjrsoft.auth;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* 启动:Sa-SSO Server端
|
||||
* @author Zexy
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients(basePackages = "com.xjrsoft")
|
||||
@ComponentScan(value = "com.xjrsoft")
|
||||
public class AuthApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AuthApplication.class, args);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,155 @@
|
||||
package com.xjrsoft.auth.config;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* @Author: tzx
|
||||
* @Date: 2022/8/2 16:54
|
||||
*/
|
||||
@Configuration
|
||||
public class JacksonConfig {
|
||||
|
||||
@Value("${spring.jackson.date-format}")
|
||||
private String pattern;
|
||||
|
||||
/**
|
||||
* 默认设置所有long类型 序列化返回前端 全变成string
|
||||
* 所有LocalDatetime 序列号返回前端 全部格式化
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
|
||||
return builder -> {
|
||||
builder.serializerByType(Long.class, ToStringSerializer.instance);
|
||||
builder.serializerByType(Long.TYPE, ToStringSerializer.instance);
|
||||
builder.serializerByType(BigDecimal.class, bigDecimalSerializer());
|
||||
builder.serializerByType(LocalDateTime.class, localDateTimeSerializer());
|
||||
builder.deserializerByType(LocalDateTime.class, localDateTimeDeserializer());
|
||||
builder.featuresToEnable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
|
||||
builder.visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
||||
builder.visibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
|
||||
builder.visibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE);
|
||||
builder.visibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.NONE);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Jackson格式化BigDecimal类型数据
|
||||
* @return
|
||||
*/
|
||||
public ToStringSerializerBase bigDecimalSerializer() {
|
||||
return new ToStringSerializerBase(BigDecimal.class) {
|
||||
protected final static int MAX_BIG_DECIMAL_SCALE = 9999;
|
||||
@Override
|
||||
public String valueToString(Object value) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
if (gen.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN)) {
|
||||
final BigDecimal bd = (BigDecimal) value;
|
||||
// 24-Aug-2016, tatu: [core#315] prevent possible DoS vector, so we need this
|
||||
if (!_verifyBigDecimalRange(gen, bd)) {
|
||||
// ... but wouldn't it be nice to trigger error via generator? Alas,
|
||||
// no method to do that. So we'll do...
|
||||
final String errorMsg = String.format(
|
||||
"Attempt to write plain `java.math.BigDecimal` (see JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN) with illegal scale (%d): needs to be between [-%d, %d]",
|
||||
bd.scale(), MAX_BIG_DECIMAL_SCALE, MAX_BIG_DECIMAL_SCALE);
|
||||
provider.reportMappingProblem(errorMsg);
|
||||
}
|
||||
// text = bd.toPlainString();
|
||||
gen.writeNumber(bd);
|
||||
} else {
|
||||
gen.writeString(value.toString());
|
||||
}
|
||||
}
|
||||
// 24-Aug-2016, tatu: [core#315] prevent possible DoS vector, so we need this
|
||||
protected boolean _verifyBigDecimalRange(JsonGenerator gen, BigDecimal value) throws IOException {
|
||||
int scale = value.scale();
|
||||
return ((scale >= -MAX_BIG_DECIMAL_SCALE) && (scale <= MAX_BIG_DECIMAL_SCALE));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* localDatetime格式化
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public LocalDateTimeSerializer localDateTimeSerializer(){
|
||||
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* localDatetime格式化
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public JsonDeserializer<LocalDateTime> localDateTimeDeserializer(){
|
||||
final String pattern = this.pattern;
|
||||
return new JsonDeserializer<LocalDateTime>(){
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public LocalDateTime deserialize(JsonParser p, DeserializationContext context) throws IOException {
|
||||
String value = p.getText();
|
||||
if (StrUtil.isEmpty(value)) {
|
||||
return null;
|
||||
}
|
||||
Class<?> aClass = p.getCurrentValue().getClass();
|
||||
Field field = ReflectUtil.getField(aClass, p.getCurrentName());
|
||||
String format = pattern;
|
||||
JsonFormat annotation = field.getAnnotation(JsonFormat.class);
|
||||
if (annotation != null) {
|
||||
format = StringUtils.defaultIfEmpty(annotation.pattern(), pattern);
|
||||
}
|
||||
return LocalDateTimeUtil.parse(value, format);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Bean
|
||||
public Converter<String, LocalTime> stringToLocalTimeConverter() {
|
||||
return new Converter<String, LocalTime>(){
|
||||
|
||||
@Override
|
||||
public LocalTime convert(String value) {
|
||||
if (StrUtil.isNotBlank(value)) {
|
||||
return LocalTime.parse(value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package com.xjrsoft.auth.constant;
|
||||
|
||||
/**
|
||||
* @Author: tzx
|
||||
* @Date: 2023/9/20 14:50
|
||||
*/
|
||||
public interface AuthConstant {
|
||||
}
|
||||
@ -0,0 +1,263 @@
|
||||
package com.xjrsoft.auth.controller;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.jwt.JWT;
|
||||
import cn.hutool.jwt.JWTUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
import com.xjrsoft.auth.dto.LoginDto;
|
||||
import com.xjrsoft.auth.service.ILoginService;
|
||||
import com.xjrsoft.auth.service.SsoService;
|
||||
import com.xjrsoft.common.core.annotation.XjrLog;
|
||||
import com.xjrsoft.common.core.config.KeyCloakConfig;
|
||||
import com.xjrsoft.common.core.config.XjrSmsConfig;
|
||||
import com.xjrsoft.common.core.constant.GlobalConstant;
|
||||
import com.xjrsoft.common.core.domain.result.R;
|
||||
import com.xjrsoft.common.core.enums.EnabledMark;
|
||||
import com.xjrsoft.common.core.enums.YesOrNoEnum;
|
||||
import com.xjrsoft.common.core.exception.MyException;
|
||||
import com.xjrsoft.common.core.uitls.SmsSender;
|
||||
import com.xjrsoft.common.redis.service.RedisUtil;
|
||||
import com.xjrsoft.organization.client.IOauthClient;
|
||||
import com.xjrsoft.organization.client.IUserClient;
|
||||
import com.xjrsoft.organization.entity.User;
|
||||
import com.xjrsoft.system.client.ILoginConfigClient;
|
||||
import com.xjrsoft.system.dto.*;
|
||||
import com.xjrsoft.system.entity.LoginConfig;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.zhyd.oauth.request.AuthRequest;
|
||||
import me.zhyd.oauth.utils.AuthStateUtils;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.Configuration;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author: tzx
|
||||
* @Date: 2023/10/12 15:39
|
||||
*/
|
||||
@RestController
|
||||
@Tag(name = "登录模块")
|
||||
@RequestMapping(GlobalConstant.SYSTEM_MODULE_PREFIX)
|
||||
@RequiredArgsConstructor
|
||||
public class LoginController {
|
||||
|
||||
private final ILoginService loginService;
|
||||
|
||||
private final IUserClient userClient;
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
private KeyCloakConfig keyCloakConfig;
|
||||
|
||||
private SmsSender smsSender;
|
||||
|
||||
private XjrSmsConfig smsConfig;
|
||||
|
||||
private IOauthClient oauthClient;
|
||||
|
||||
private final ILoginConfigClient loginConfigClient;
|
||||
|
||||
private SsoService ssoService;
|
||||
|
||||
|
||||
@PostMapping(value = {"/login/{ltpasToken}", "/login"})
|
||||
@Operation(summary = "登录", description = "传入账号:account,密码:password")
|
||||
@XjrLog(value = "账号密码登录成功")
|
||||
public R login(@RequestBody @Valid LoginDto dto, @PathVariable(required = false) String ltpasToken) {
|
||||
if(StrUtil.isNotBlank (ltpasToken)) {
|
||||
String userName = ssoService.auth(ltpasToken);
|
||||
if(StrUtil.isNotBlank(userName)) {
|
||||
dto.setUserName(userName);
|
||||
User user = loginService.getUserInfoByName(userName);
|
||||
dto.setPassword(user.getPassword());
|
||||
return R.ok(loginService.login(dto));
|
||||
} else {
|
||||
return R.error("秘钥登录失败");
|
||||
}
|
||||
}
|
||||
return R.ok(loginService.login(dto));
|
||||
}
|
||||
|
||||
@PostMapping("/create-token")
|
||||
@Operation(summary = "创建token", description = "传入账号:account,密码:password")
|
||||
@XjrLog(value = "账号密码登录成功")
|
||||
public R createToken(@RequestBody @Valid CreateTokenDto dto) {
|
||||
return R.ok(loginService.createToken(dto));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
*/
|
||||
@GetMapping("/captcha")
|
||||
@XjrLog(value = "发送验证码")
|
||||
@Operation(summary = "发送验证码", description = "传入账号:mobile")
|
||||
public R captcha(@RequestParam String mobile) {
|
||||
// 验证短信限时发送次数验证
|
||||
// 获取该号码短信的发送次数
|
||||
Object o = redisUtil.get(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile);
|
||||
int sendCount = NumberUtils.toInt(String.valueOf(o));
|
||||
if (o == null) {
|
||||
// 未发送过验证码,初始化
|
||||
redisUtil.set(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile, 1, smsConfig.getLimitTime() * 3600);
|
||||
} else if (sendCount > smsConfig.getLimitCount()) {
|
||||
// 发送次数超过限制
|
||||
return R.error("发送验证码次数达上限,请稍后再试!");
|
||||
} else {
|
||||
// 更新发送次数
|
||||
long expire = redisUtil.getExpire(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile);
|
||||
redisUtil.set(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile, sendCount + 1, expire);
|
||||
}
|
||||
//生成六位数的字符串
|
||||
String code = RandomUtils.nextInt(100000, 999999) + StringPool.EMPTY;
|
||||
|
||||
smsSender.sendCaptcha(mobile, code);
|
||||
|
||||
redisUtil.set(GlobalConstant.CAPTCHA + StringPool.UNDERSCORE + mobile, code, 60L);
|
||||
return R.ok(Boolean.TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
@PostMapping("/captcha")
|
||||
@XjrLog(value = "验证码登录")
|
||||
public R loginByCaptcha(@RequestBody LoginCaptchaDto loginCaptchaDto) {
|
||||
String mobile = loginCaptchaDto.getMobile();
|
||||
String code = redisUtil.get(GlobalConstant.CAPTCHA + StringPool.UNDERSCORE + mobile);
|
||||
if (code != null && StringUtils.equals(code, loginCaptchaDto.getCode())) {
|
||||
User user = userClient.getUserByMobileFeign(mobile);
|
||||
if (user == null) {
|
||||
return R.error("用户不存在!");
|
||||
}
|
||||
if (user.getEnabledMark() == EnabledMark.DISABLED.getCode()) {
|
||||
return R.error("账户未启用");
|
||||
}
|
||||
//此登录接口登录web端
|
||||
StpUtil.login(user.getId(), "PC");
|
||||
|
||||
SaSession tokenSession = StpUtil.getTokenSession();
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, user);
|
||||
|
||||
Map<String, Object> vo = new HashMap<>(1);
|
||||
vo.put(GlobalConstant.TOKEN_KEY, StpUtil.getTokenValue());
|
||||
|
||||
return R.ok(vo);
|
||||
}
|
||||
return R.error("验证码不存在!");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 退出
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public R logout() {
|
||||
StpUtil.logout();
|
||||
return R.ok("登出成功!");
|
||||
}
|
||||
|
||||
@PostMapping("/token")
|
||||
@Operation(summary = "根据keycloak-token 登录", description = "传入keycloak-token")
|
||||
@XjrLog(value = "keycloak-token登录成功")
|
||||
public R loginByToken(@RequestBody KeyCloakLoginInfoDto dto) {
|
||||
Map<String, Object> credentialsMap = new HashMap<>(1);
|
||||
credentialsMap.put("secret", keyCloakConfig.getSecret());
|
||||
Configuration configuration = new Configuration(keyCloakConfig.getUrl(), keyCloakConfig.getRealm(), keyCloakConfig.getClientId(), credentialsMap, null);
|
||||
AuthzClient authzClient = AuthzClient.create(configuration);
|
||||
AccessTokenResponse response = authzClient.obtainAccessToken(keyCloakConfig.getUserName(), keyCloakConfig.getPassword());
|
||||
|
||||
if (StrUtil.isNotBlank(response.getError())) {
|
||||
return R.error(response.getError());
|
||||
}
|
||||
//TODO keycloak 登陆过 解析token获取数据 做框架登录操作
|
||||
JWT jwt = JWTUtil.parseToken(dto.getToken());
|
||||
Object code = jwt.getPayload(keyCloakConfig.getPayload());
|
||||
|
||||
User user = userClient.getUserByCodeFeign(String.valueOf(code));
|
||||
|
||||
List<LoginConfig> list = loginConfigClient.getAllListFeign();
|
||||
if (list.size() == 0){//如果没有,则设置为默认配置,并进行保存
|
||||
LoginConfig loginConfig = new LoginConfig();
|
||||
loginConfig.setId(1L);
|
||||
loginConfig.setMulLogin("0,1");
|
||||
loginConfig.setMutualExclusion(YesOrNoEnum.YES.getCode());
|
||||
loginConfig.setWithoutLogin(YesOrNoEnum.YES.getCode());
|
||||
loginConfig.setPasswordStrategy(YesOrNoEnum.YES.getCode());
|
||||
loginConfig.setStrategyMaxNumber(7);
|
||||
list.add(loginConfig);
|
||||
loginConfigClient.addLoginConfigFeign(loginConfig);
|
||||
}
|
||||
LoginConfig loginConfig = list.get(0);
|
||||
|
||||
if (user == null) {
|
||||
return R.error("帐号密码错误!");
|
||||
} else if (!Integer.valueOf(1).equals(user.getEnabledMark())) {
|
||||
return R.error("当前账号已被锁定,请联系管理员!");
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(dto.getDevice()) && dto.getDevice() == "PC" && !loginConfig.getMulLogin().contains("0")){
|
||||
throw new MyException("当前Web端登录未授权,请联系管理员!");
|
||||
}else if (StrUtil.isNotBlank(dto.getDevice()) && dto.getDevice() == "APP" && !loginConfig.getMulLogin().contains("1")){
|
||||
throw new MyException("当前APP端登录未授权,请联系管理员!");
|
||||
}else {
|
||||
if (StrUtil.isBlank(loginConfig.getMulLogin()) || !loginConfig.getMulLogin().contains("0")){
|
||||
throw new MyException("当前Web端登录未授权,请联系管理员!");
|
||||
}
|
||||
}
|
||||
|
||||
SaTokenConfig oldConfig = SaManager.getConfig();
|
||||
|
||||
if (loginConfig.getMutualExclusion() == YesOrNoEnum.NO.getCode()){
|
||||
//不开启同端互斥
|
||||
oldConfig.setIsConcurrent(true);
|
||||
}else {
|
||||
//开启同端互斥
|
||||
oldConfig.setIsConcurrent(false);
|
||||
}
|
||||
// 注入到 SaManager 中
|
||||
SaManager.setConfig(oldConfig);
|
||||
|
||||
if (loginConfig.getWithoutLogin() == YesOrNoEnum.YES.getCode()){//开启就设置为7天免登录
|
||||
StpUtil.login(user.getId(),new SaLoginModel().setDevice(dto.getDevice()).setTimeout(60 * 60 * 24 * 7));
|
||||
}else {
|
||||
//此登录接口登录web端,IsLastingCookie设置为false,也就是关闭浏览器后再次打开需要重新登录
|
||||
StpUtil.login(user.getId(), new SaLoginModel().setDevice(dto.getDevice()).setIsLastingCookie(false));
|
||||
}
|
||||
|
||||
SaSession tokenSession = StpUtil.getTokenSession();
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, user);
|
||||
|
||||
Map<String, Object> vo = new HashMap<>(1);
|
||||
vo.put(GlobalConstant.TOKEN_KEY, StpUtil.getTokenValue());
|
||||
|
||||
return R.ok("登录成功!", vo);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@PostMapping("/qrcode-login")
|
||||
@Operation(summary = "oauth 扫码登录", description = "oauth 扫码登录")
|
||||
@XjrLog(value = "oauth 扫码登录")
|
||||
public R createAuthorizeUrl(@Valid @RequestBody CreateAuthorizeUrlDto dto){
|
||||
AuthRequest authRequest = oauthClient.getAuthRequest(dto.getSource());
|
||||
String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
|
||||
return R.ok(authorizeUrl);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
package com.xjrsoft.auth.controller;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.secure.SaSecureUtil;
|
||||
import cn.dev33.satoken.sso.SaSsoHandle;
|
||||
import com.ejlchina.okhttps.OkHttps;
|
||||
import com.xjrsoft.common.core.constant.GlobalConstant;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Server端 Controller
|
||||
* @author Zexy
|
||||
*/
|
||||
@RestController
|
||||
@Tag(name = "授权中心")
|
||||
public class SsoServerController {
|
||||
|
||||
/**
|
||||
* SSO-Server:统一认证地址
|
||||
*/
|
||||
|
||||
@RequestMapping(GlobalConstant.AUTH_CENTER)
|
||||
@Operation(summary = "统一认证地址")
|
||||
public Object ssoAuth() {
|
||||
return SaSsoHandle.ssoAuth();
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server:RestAPI 登录接口
|
||||
*/
|
||||
|
||||
@RequestMapping(GlobalConstant.AUTH_LOGIN)
|
||||
@Operation(summary = "SSO-Server:RestAPI 登录接口")
|
||||
public Object ssoDoLogin() {
|
||||
return SaSsoHandle.ssoDoLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server:校验ticket 获取账号id
|
||||
*/
|
||||
|
||||
@RequestMapping(GlobalConstant.AUTH_CHECK_TICKET)
|
||||
@Operation(summary = "SSO-Server:校验ticket 获取账号id")
|
||||
public Object ssoCheckTicket() {
|
||||
return SaSsoHandle.ssoCheckTicket();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销单点登录
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(GlobalConstant.AUTH_LOGOUT)
|
||||
@Operation(summary = "SSO-Server:注销单点登录")
|
||||
|
||||
public Object ssoLogout() {
|
||||
return SaSsoHandle.ssoServerLogout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置SSO相关参数
|
||||
*
|
||||
* @param cfg
|
||||
*/
|
||||
@Autowired
|
||||
private void configSso(SaTokenConfig cfg) {
|
||||
cfg.sso
|
||||
// 配置:未登录时返回的View
|
||||
.setNotLoginView(() -> "当前会话在SSO-Server端尚未登录,请先访问"
|
||||
+ "<a href='/sso/doLogin?name=sa&pwd=123456' target='_blank'> doLogin登录 </a>"
|
||||
+ "进行登录之后,刷新页面开始授权")
|
||||
// 配置:登录处理函数
|
||||
.setDoLoginHandle((name, pwd) -> {
|
||||
|
||||
|
||||
SaRequest request = SaHolder.getRequest();
|
||||
name = request.getParam("username");
|
||||
pwd = request.getParam("password");
|
||||
|
||||
String s = SaSecureUtil.md5BySalt(pwd, GlobalConstant.SECRET_KEY);
|
||||
|
||||
|
||||
return null;
|
||||
})// 配置Http请求处理器
|
||||
.setSendHttp(url -> {
|
||||
// 此处为了提高响应速度这里可将sync换为async
|
||||
return OkHttps.async(url).get();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.xjrsoft.auth.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
|
||||
/**
|
||||
* @author Zexy
|
||||
*/
|
||||
@Data
|
||||
public class LoginDto {
|
||||
|
||||
@NotBlank(message = "账号不能为空!")
|
||||
private String userName;
|
||||
|
||||
@Schema(name = "密码")
|
||||
// @NotBlank(message = "密码不能为空!")
|
||||
// @Length(min = 6,max = 32,message = "密码长度不得小于6个字符,不得大于32个字符!")
|
||||
private String password;
|
||||
|
||||
@Schema(name = "租户码")
|
||||
private String tenantCode;
|
||||
|
||||
@Schema(name = "设备类型-默认为PC,pc为0,app为1")
|
||||
private Integer deviceType;
|
||||
|
||||
@Schema(name = "互信token")
|
||||
private String token;
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.xjrsoft.auth.handler;
|
||||
|
||||
import com.xjrsoft.common.core.domain.result.R;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@ControllerAdvice
|
||||
@Slf4j
|
||||
public class MyExceptionHandler {
|
||||
|
||||
// @ResponseBody
|
||||
// @ExceptionHandler(value =Exception.class)
|
||||
// public R exceptionHandler(Exception e){
|
||||
// e.printStackTrace();
|
||||
// return R.error("认证中心错误,请联系管理员!");
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.xjrsoft.auth.service;
|
||||
|
||||
import com.xjrsoft.auth.dto.LoginDto;
|
||||
import com.xjrsoft.organization.entity.User;
|
||||
import com.xjrsoft.system.dto.CreateTokenDto;
|
||||
import com.xjrsoft.system.entity.LoginConfig;
|
||||
import com.xjrsoft.system.vo.CreateTokenVo;
|
||||
import com.xjrsoft.system.vo.LoginVo;
|
||||
|
||||
/**
|
||||
* @Author: tzx
|
||||
* @Date: 2023/4/21 14:20
|
||||
*/
|
||||
public interface ILoginService {
|
||||
|
||||
User getUserInfoByName(String userName);
|
||||
|
||||
LoginVo login(LoginDto dto);
|
||||
|
||||
CreateTokenVo createToken(CreateTokenDto dto);
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.xjrsoft.auth.service;
|
||||
|
||||
import com.xjrsoft.auth.vo.ResponseVo;
|
||||
import com.xjrsoft.common.core.result.Response;
|
||||
import com.xjrsoft.organization.vo.LoginVo;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public interface SsoService {
|
||||
|
||||
void auth(String redirect, String ltpasToken, String method,HttpServletResponse response);
|
||||
|
||||
String auth(String ltpasToken);
|
||||
|
||||
Response<LoginVo> getUser();
|
||||
|
||||
ResponseVo getTodoTask(String type, String ltpasToken);
|
||||
|
||||
String getLtpasToken(String username);
|
||||
}
|
||||
@ -0,0 +1,340 @@
|
||||
package com.xjrsoft.auth.service.impl;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.secure.SaSecureUtil;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.temp.SaTempUtil;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.nacos.common.utils.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.xjrsoft.auth.dto.LoginDto;
|
||||
import com.xjrsoft.auth.service.ILoginService;
|
||||
import com.xjrsoft.common.core.config.LicenseConfig;
|
||||
import com.xjrsoft.common.core.constant.GlobalConstant;
|
||||
import com.xjrsoft.common.core.constant.StringPool;
|
||||
import com.xjrsoft.common.core.enums.EnabledMark;
|
||||
import com.xjrsoft.common.core.enums.YesOrNoEnum;
|
||||
import com.xjrsoft.common.core.exception.MyException;
|
||||
import com.xjrsoft.common.core.exception.ValidationException;
|
||||
import com.xjrsoft.common.redis.service.RedisUtil;
|
||||
import com.xjrsoft.organization.client.*;
|
||||
import com.xjrsoft.organization.entity.*;
|
||||
import com.xjrsoft.organization.service.*;
|
||||
import com.xjrsoft.system.client.ILoginConfigClient;
|
||||
import com.xjrsoft.system.client.ITenantClient;
|
||||
import com.xjrsoft.system.dto.CreateTokenDto;
|
||||
import com.xjrsoft.system.entity.LoginConfig;
|
||||
import com.xjrsoft.system.entity.Tenant;
|
||||
import com.xjrsoft.system.service.ILoginConfigService;
|
||||
import com.xjrsoft.system.vo.CreateTokenVo;
|
||||
import com.xjrsoft.system.vo.LoginVo;
|
||||
import com.xjrsoft.tenant.config.TenantConfig;
|
||||
import com.xjrsoft.tenant.util.SecureUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Author: tzx
|
||||
* @Date: 2023/4/21 14:22
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class LoginServiceImpl implements ILoginService {
|
||||
|
||||
private final IUserClient userClient;
|
||||
|
||||
private final IUserDeptRelationClient userDeptRelationClient;
|
||||
|
||||
private final IUserPostRelationClient userPostRelationClient;
|
||||
|
||||
private final IPostClient postClient;
|
||||
|
||||
private final IDepartmentClient departmentClient;
|
||||
|
||||
private final ITenantClient tenantClient;
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
private final LicenseConfig licenseConfig;
|
||||
|
||||
private final TenantConfig tenantConfig;
|
||||
|
||||
private final ILoginConfigClient loginConfigClient;
|
||||
|
||||
private final IUserRoleRelationClient userRoleRelationClient;
|
||||
|
||||
@Override
|
||||
public User getUserInfoByName(String userName) {
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(User::getUserName, userName);
|
||||
User loginUser = userService.getOne(queryWrapper);
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginVo login(@RequestBody(required = false) LoginDto dto) {
|
||||
LoginVo result = new LoginVo();
|
||||
|
||||
if (licenseConfig.getEnabled()) {
|
||||
//查出所有在线用户
|
||||
List<String> onlineUser = StpUtil.searchSessionId("", 0, Integer.MAX_VALUE);
|
||||
|
||||
//如果已经登录人数超过授权人数 不允许登录
|
||||
if (onlineUser.size() >= licenseConfig.getLoginMax()) {
|
||||
throw new MyException("登录人数超过授权人数,无法登录,请联系管理员!");
|
||||
}
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(User::getUserName, dto.getUserName());
|
||||
User loginUser = userService.getOne(queryWrapper);
|
||||
|
||||
List<LoginConfig> list = loginConfigService.list();
|
||||
if (list.size() == 0){//如果没有,则设置为默认配置,并进行保存
|
||||
LoginConfig loginConfig = new LoginConfig();
|
||||
loginConfig.setId(1L);
|
||||
loginConfig.setMulLogin("0,1");
|
||||
loginConfig.setMutualExclusion(YesOrNoEnum.YES.getCode());
|
||||
loginConfig.setWithoutLogin(YesOrNoEnum.YES.getCode());
|
||||
loginConfig.setPasswordStrategy(YesOrNoEnum.YES.getCode());
|
||||
loginConfig.setStrategyMaxNumber(7);
|
||||
list.add(loginConfig);
|
||||
loginConfigService.save(loginConfig);
|
||||
}
|
||||
LoginConfig loginConfig = list.get(0);
|
||||
|
||||
if (StrUtil.isNotBlank(loginConfig.getMulLogin())){
|
||||
if (ObjectUtil.isNotEmpty(dto.getDeviceType()) && dto.getDeviceType() == 0 && !loginConfig.getMulLogin().contains("0")){
|
||||
throw new MyException("当前Web端登录未授权,请联系管理员!");
|
||||
}else if (ObjectUtil.isNotEmpty(dto.getDeviceType()) && dto.getDeviceType() == 1 && !loginConfig.getMulLogin().contains("1")){
|
||||
throw new MyException("当前APP端登录未授权,请联系管理员!");
|
||||
}
|
||||
}else {
|
||||
throw new MyException("当前Web端、app端登录未授权,请联系管理员!");
|
||||
}
|
||||
|
||||
//if (loginUser == null || !StrUtil.equals(loginUser.getPassword(), SaSecureUtil.md5BySalt(dto.getPassword(), GlobalConstant.SECRET_KEY))) {
|
||||
if(checkPassword(loginUser,dto)){
|
||||
if (loginUser.getEnabledMark() == EnabledMark.DISABLED.getCode()) {
|
||||
throw new MyException("当前账号已被锁定,请联系管理员!");
|
||||
}
|
||||
if (loginConfig.getPasswordStrategy() == YesOrNoEnum.YES.getCode()){ // 如果开启了密码策略,密码错误次数超过最大次数就锁定
|
||||
Integer number = 1;
|
||||
String value = redisUtil.get(GlobalConstant.LOGIN_ERROR_NUMBER + loginUser.getId());
|
||||
if (StrUtil.isNotBlank(value)){
|
||||
if (loginConfig.getStrategyMaxNumber() - 1 > Integer.valueOf(value)){
|
||||
redisUtil.set(GlobalConstant.LOGIN_ERROR_NUMBER + loginUser.getId() , Integer.valueOf(value) + 1);
|
||||
}else {
|
||||
loginUser.setEnabledMark(EnabledMark.DISABLED.getCode());
|
||||
userService.updateById(loginUser);
|
||||
throw new MyException("当前账号已被锁定,请联系管理员");
|
||||
}
|
||||
}else {
|
||||
if (loginConfig.getStrategyMaxNumber() == 1){
|
||||
loginUser.setEnabledMark(EnabledMark.DISABLED.getCode());
|
||||
userService.updateById(loginUser);
|
||||
throw new MyException("当前账号已被锁定,请联系管理员");
|
||||
}else {
|
||||
redisUtil.set(GlobalConstant.LOGIN_ERROR_NUMBER + loginUser.getId() , number);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new MyException("账号或密码不正确");
|
||||
}
|
||||
//登录成功之后将对应的密码错误次数给删除掉
|
||||
redisUtil.delete(GlobalConstant.LOGIN_ERROR_NUMBER + loginUser.getId());
|
||||
|
||||
SaTokenConfig oldConfig = SaManager.getConfig();
|
||||
|
||||
if (loginConfig.getMutualExclusion() == YesOrNoEnum.NO.getCode()){
|
||||
//不开启同端互斥
|
||||
oldConfig.setIsConcurrent(true);
|
||||
}else {
|
||||
//开启同端互斥
|
||||
oldConfig.setIsConcurrent(false);
|
||||
}
|
||||
// 注入到 SaManager 中
|
||||
SaManager.setConfig(oldConfig);
|
||||
|
||||
//此登录接口
|
||||
if (loginConfig.getWithoutLogin() == YesOrNoEnum.YES.getCode()){//开启就设置为7天免登录
|
||||
if (ObjectUtil.isNotEmpty(dto.getDeviceType()) && dto.getDeviceType() == 1){
|
||||
StpUtil.login(loginUser.getId(),new SaLoginModel().setDevice("APP").setTimeout(60 * 60 * 24 * 7));
|
||||
} else {//默认为PC端登录
|
||||
StpUtil.login(loginUser.getId(),new SaLoginModel().setDevice("PC").setTimeout(60 * 60 * 24 * 7));
|
||||
}
|
||||
}else {
|
||||
//此登录接口登录web端,IsLastingCookie设置为false,也就是关闭浏览器后再次打开需要重新登录
|
||||
if (ObjectUtil.isNotEmpty(dto.getDeviceType()) && dto.getDeviceType() == 1){
|
||||
StpUtil.login(loginUser.getId(), new SaLoginModel().setDevice("APP").setIsLastingCookie(false));
|
||||
} else {//默认为PC端登录
|
||||
StpUtil.login(loginUser.getId(), new SaLoginModel().setDevice("PC").setIsLastingCookie(false));
|
||||
}
|
||||
}
|
||||
// StpUtil.login(loginUser.getId(),"PC");
|
||||
|
||||
SaSession tokenSession = StpUtil.getTokenSession();
|
||||
|
||||
List<UserDeptRelation> userDeptRelations = userDeptRelationService.list(Wrappers.lambdaQuery(UserDeptRelation.class)
|
||||
.eq(UserDeptRelation::getUserId, StpUtil.getLoginIdAsLong()));
|
||||
|
||||
List<UserPostRelation> userPostRelations = userPostRelationService.list(Wrappers.lambdaQuery(UserPostRelation.class)
|
||||
.eq(UserPostRelation::getUserId, StpUtil.getLoginIdAsLong()));
|
||||
|
||||
//获取登陆人所选择的身份缓存
|
||||
String postId = redisUtil.get(GlobalConstant.LOGIN_IDENTITY_CACHE_PREFIX + loginUser.getId());
|
||||
|
||||
Post post = new Post();
|
||||
if (StrUtil.isNotBlank(postId) && !postId.equals("null")) {
|
||||
post = postService.getById(Long.valueOf(postId));
|
||||
}
|
||||
|
||||
if (userPostRelations.size() > 0) {
|
||||
List<Long> postIds = userPostRelations.stream().map(UserPostRelation::getPostId).collect(Collectors.toList());
|
||||
|
||||
List<Post> postList = postService.listByIds(postIds);
|
||||
if ((post == null || StrUtil.isBlank(postId)) && com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isNotEmpty(postList)) {
|
||||
post = postList.get(0);
|
||||
}
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_POST_INFO_KEY, post);
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_POST_LIST_KEY, postList);
|
||||
loginUser.setPostId(post.getId());
|
||||
|
||||
//将登陆人所选择的身份缓存起来
|
||||
//切换身份的时候 会一起修改
|
||||
//redisUtil.set(GlobalConstant.LOGIN_IDENTITY_CACHE_PREFIX + loginUser.getId(), post.getId());
|
||||
}
|
||||
|
||||
if (userDeptRelations.size() > 0) {
|
||||
// 存当前用户所有部门到缓存
|
||||
List<Long> departmentIds = userDeptRelations.stream().map(UserDeptRelation::getDeptId).collect(Collectors.toList());
|
||||
List<Department> departmentList = departmentService.listByIds(departmentIds);
|
||||
departmentService.queryPathFromRoot(departmentList);
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_DEPT_LIST_KEY, departmentList);
|
||||
|
||||
//主部门
|
||||
List<Long> mainDepartmentIds = userDeptRelations.stream().filter(x->YesOrNoEnum.YES.getTextCode().equals(x.getIsMain()))
|
||||
.map(UserDeptRelation::getDeptId).collect(Collectors.toList());
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_MAIN_DEPT_ID_KEY, mainDepartmentIds);
|
||||
|
||||
Department department = departmentList.get(0);
|
||||
//如果此人有岗位 使用岗位的deptId 找到当前组织机构
|
||||
if (ObjectUtil.isNotNull(post.getId())) {
|
||||
department = departmentService.getById(post.getDeptId());
|
||||
}else if(ObjectUtil.isNotEmpty(mainDepartmentIds)){
|
||||
//找一个主部门
|
||||
department=departmentList.stream().filter(x->mainDepartmentIds.contains(x.getId())).findFirst().get();
|
||||
}
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_DEPT_INFO_KEY, department);
|
||||
loginUser.setDepartmentId(department.getId());
|
||||
}
|
||||
|
||||
//根据登录信息 将post 和 department 信息存入用户信息中
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, loginUser);
|
||||
|
||||
result.setToken(StpUtil.getTokenValue());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断用户名和密码是否正确,有2中情况,1:账号密码登录,2:互信token登录,
|
||||
* 方式1是传入的未加密的密码,方式2传入的是通过md5已加密的密码,所以通过判断密码的长度来判断是否已加密
|
||||
* */
|
||||
public boolean checkPassword(User loginUser,LoginDto dto)
|
||||
{
|
||||
//如果长度是32,即传入的是密文
|
||||
if(dto.getPassword().length() == 32){
|
||||
return (loginUser == null || !StrUtil.equals(loginUser.getPassword(), dto.getPassword()));
|
||||
}
|
||||
else {
|
||||
return (loginUser == null || !StrUtil.equals(loginUser.getPassword(), SaSecureUtil.md5BySalt(dto.getPassword(), GlobalConstant.SECRET_KEY)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CreateTokenVo createToken(CreateTokenDto dto) {
|
||||
CreateTokenVo vo = new CreateTokenVo();
|
||||
|
||||
if(dto.getExpire() == -1){
|
||||
String token = SaTempUtil.createToken(IdUtil.fastSimpleUUID() + StringPool.UNDERSCORE + GlobalConstant.SECRET_KEY, Integer.MAX_VALUE);
|
||||
vo.setToken(token);
|
||||
return vo;
|
||||
}
|
||||
else {
|
||||
String token = SaTempUtil.createToken(IdUtil.fastSimpleUUID() + StringPool.UNDERSCORE + GlobalConstant.SECRET_KEY, dto.getExpire());
|
||||
vo.setToken(token);
|
||||
return vo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验登录的配置是否满足
|
||||
* @param deviceType
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public LoginConfig validateLoginConfig(Integer deviceType) {
|
||||
if (licenseConfig.getEnabled()) {
|
||||
//查出所有在线用户
|
||||
List<String> onlineUser = StpUtil.searchSessionId("", 0, Integer.MAX_VALUE);
|
||||
|
||||
//如果已经登录人数超过授权人数 不允许登录
|
||||
if (onlineUser.size() >= licenseConfig.getLoginMax()) {
|
||||
throw new MyException("登录人数超过授权人数,无法登录,请联系管理员!");
|
||||
}
|
||||
}
|
||||
|
||||
List<LoginConfig> list = loginConfigClient.getAllListFeign();
|
||||
if (list.size() == 0){//如果没有,则设置为默认配置,并进行保存
|
||||
LoginConfig loginConfig = new LoginConfig();
|
||||
loginConfig.setMulLogin("0,1");
|
||||
loginConfig.setMutualExclusion(YesOrNoEnum.YES.getCode());
|
||||
loginConfig.setWithoutLogin(YesOrNoEnum.YES.getCode());
|
||||
loginConfig.setPasswordStrategy(YesOrNoEnum.YES.getCode());
|
||||
loginConfig.setStrategyMaxNumber(7);
|
||||
list.add(loginConfig);
|
||||
loginConfigClient.addLoginConfigFeign(loginConfig);
|
||||
}
|
||||
LoginConfig loginConfig = list.get(0);
|
||||
|
||||
if (StrUtil.isNotBlank(loginConfig.getMulLogin())){
|
||||
if (ObjectUtil.isNotEmpty(deviceType) && deviceType == 0 && !loginConfig.getMulLogin().contains("0")){
|
||||
throw new MyException("当前Web端登录未授权,请联系管理员!");
|
||||
}else if (ObjectUtil.isNotEmpty(deviceType) && deviceType == 1 && !loginConfig.getMulLogin().contains("1")){
|
||||
throw new MyException("当前APP端登录未授权,请联系管理员!");
|
||||
}
|
||||
}else {
|
||||
throw new MyException("当前Web端、app端登录未授权,请联系管理员!");
|
||||
}
|
||||
|
||||
SaTokenConfig oldConfig = SaManager.getConfig();
|
||||
if (loginConfig.getMutualExclusion() == YesOrNoEnum.NO.getCode()){
|
||||
//不开启同端互斥
|
||||
oldConfig.setIsConcurrent(true);
|
||||
}else {
|
||||
//开启同端互斥
|
||||
oldConfig.setIsConcurrent(false);
|
||||
}
|
||||
// 注入到 SaManager 中
|
||||
SaManager.setConfig(oldConfig);
|
||||
|
||||
return loginConfig;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,486 @@
|
||||
package com.xjrsoft.auth.service.impl;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.xjrsoft.auth.vo.ResponseVo;
|
||||
import com.xjrsoft.auth.vo.TodoItem;
|
||||
import com.xjrsoft.auth.service.SsoService;
|
||||
import com.xjrsoft.common.core.constant.GlobalConstant;
|
||||
import com.xjrsoft.common.core.result.Response;
|
||||
import com.xjrsoft.common.core.uitls.BeanUtil;
|
||||
import com.xjrsoft.common.core.uitls.DateUtil;
|
||||
import com.xjrsoft.common.redis.service.RedisUtil;
|
||||
import com.xjrsoft.organization.client.IDepartmentClient;
|
||||
import com.xjrsoft.organization.client.IUserClient;
|
||||
import com.xjrsoft.organization.entity.Department;
|
||||
import com.xjrsoft.organization.entity.User;
|
||||
import com.xjrsoft.organization.vo.UserDeptVo;
|
||||
import com.xjrsoft.organization.vo.UserInfoVo;
|
||||
import com.xjrsoft.organization.vo.UserRoleVo;
|
||||
import com.xjrsoft.organization.vo.UserVo;
|
||||
import com.xjrsoft.organization.vo.LoginVo;
|
||||
import com.xjrsoft.workflow.client.IWorkflowExecuteClient;
|
||||
import com.xjrsoft.workflow.dto.PendingTaskDto;
|
||||
import com.xjrsoft.workflow.vo.PendingTaskVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class SsoServiceImpl implements SsoService {
|
||||
|
||||
@Value("${xjrsoft.token_secret_key:}")
|
||||
private String secretKey;
|
||||
|
||||
@Value("${xjrsoft.dist.pc_url:}")
|
||||
private String pcUrl;
|
||||
|
||||
@Value("${xjrsoft.dist.mobile_url:}")
|
||||
private String mobileUrl;
|
||||
|
||||
@Value("${xjrsoft.dist.config:}")
|
||||
private String config;
|
||||
|
||||
@Value("${xjrsoft.dist.pc_config:}")
|
||||
private String pc_config;
|
||||
|
||||
@Value("${xjrsoft.dist.type_name:}")
|
||||
private String type_name;
|
||||
|
||||
@Value("${xjrsoft.dist.url:}")
|
||||
private String url;
|
||||
|
||||
private String pcPath = "/flow/%s/%s/approveFlow?taskId=%s";
|
||||
|
||||
private String mobilePath = "/pages/workflow/approval?taskId=%s&processId=%s&type=todo";
|
||||
|
||||
@Autowired
|
||||
private IUserClient userClient;
|
||||
|
||||
@Autowired
|
||||
private IDepartmentClient departmentClient;
|
||||
|
||||
@Autowired
|
||||
private IWorkflowExecuteClient iWorkflowExecuteClient;
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Value("${xjrsoft.dist.tenant_code_name:}")
|
||||
private String tenantCodeName;
|
||||
|
||||
@Override
|
||||
public String getLtpasToken(String username) {
|
||||
String token = encodeToken(username);
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<LoginVo> getUser() {
|
||||
SaSession tokenSession = StpUtil.getTokenSession();
|
||||
User user = tokenSession.get("user", new User());
|
||||
UserInfoVo userInfoVo = BeanUtil.copy(user, UserInfoVo.class);
|
||||
List<Department> departments = userClient.queryDepartmentsOfUserIdFeign(user.getId());
|
||||
Department companies = departmentClient.getDepartmentByIdFeign(user.getDepartmentId());
|
||||
List<UserRoleVo> role = userClient.queryRolesOfUserFeign(user.getId());
|
||||
|
||||
if (departments != null) {
|
||||
userInfoVo.setDepartments(BeanUtil.copyList(departments, UserDeptVo.class));
|
||||
}
|
||||
if (role != null) {
|
||||
userInfoVo.setRoles(BeanUtil.copyList(role, UserRoleVo.class));
|
||||
}
|
||||
|
||||
LoginVo loginVo = new LoginVo();
|
||||
loginVo.setUserInfoVo(userInfoVo);
|
||||
loginVo.setToken(StpUtil.getTokenValue());
|
||||
return Response.ok(loginVo, "操作成功!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseVo getTodoTask(String type, String ltpasToken) {
|
||||
ResponseVo responseVo = new ResponseVo();
|
||||
if(StringUtils.isEmpty(type) || !("pc".equals(type) || "app".equals(type))){
|
||||
responseVo.setMsg("传参有误!");
|
||||
return responseVo;
|
||||
}
|
||||
List<TodoItem> list = new ArrayList<>();
|
||||
|
||||
|
||||
/* SaSession tokenSession = StpUtil.getTokenSession();
|
||||
User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
|
||||
String userId = String.valueOf(user.getId());*/
|
||||
String loginId = this.decodeToken(ltpasToken);
|
||||
UserVo user = BeanUtil.copy(userClient.getUserByUserNameFeign(loginId), UserVo.class);
|
||||
getToken(user);
|
||||
|
||||
log.info("开始查询待办列表");
|
||||
List<TodoItem> tasks = this.findTask(type);
|
||||
log.info("查询待办列表结束");
|
||||
list.addAll(tasks);
|
||||
|
||||
//时间倒序
|
||||
if(list.size() != 0){
|
||||
list.sort(Comparator.comparing(TodoItem::getTs).reversed());
|
||||
}
|
||||
responseVo.setMsg("操作成功");
|
||||
responseVo.setTotalCount(list.size());
|
||||
responseVo.setItems(list);
|
||||
return responseVo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询待办任务信息
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
private List<TodoItem> findTask(String type){
|
||||
List<TodoItem> list = new ArrayList<>();
|
||||
|
||||
//获取用户信息
|
||||
PendingTaskDto dt = new PendingTaskDto();
|
||||
//dt.setOriginator(userId);
|
||||
long time= System.currentTimeMillis();
|
||||
log.info("查询待办pengding方法开始执行{}",time);
|
||||
List<PendingTaskVo> taskVos = iWorkflowExecuteClient.getPendingListFeign(dt);
|
||||
log.info("查询待办pengding方法执行结束,总耗时:{}ms",(System.currentTimeMillis()-time));
|
||||
|
||||
if(taskVos == null || taskVos.isEmpty()){
|
||||
return list;
|
||||
}
|
||||
for (PendingTaskVo taskVo : taskVos) {
|
||||
TodoItem item = new TodoItem();
|
||||
item.setTs(taskVo.getStartTime().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
|
||||
item.setSubtitle("工作待办");
|
||||
item.setTitle(taskVo.getSchemaName()+ "-" + taskVo.getStartUserName());
|
||||
if("pc".equals(type)){
|
||||
item.setType(type_name);
|
||||
item.setUrl(url +"?config="+pc_config);// "url": "data/gdyd/trust/redirect2?config=gdyd-fcd-xfj",
|
||||
JSONObject params = new JSONObject();
|
||||
params.put("config",pc_config);
|
||||
//item.setParams(params);
|
||||
String dateTime = DateUtil.formatDateTime(taskVo.getStartTime());
|
||||
String targetURL = String.format(pcPath,taskVo.getSchemaId(),taskVo.getProcessId(),taskVo.getTaskId());
|
||||
item.setTargetURL(targetURL);
|
||||
|
||||
|
||||
}else{
|
||||
item.setType(type_name);
|
||||
item.setSrc("api/"+url +"?config="+config);
|
||||
item.setRoute("/route/std/iframe");
|
||||
String targetURL = String.format(mobilePath,taskVo.getTaskId(),taskVo.getProcessId());
|
||||
item.setTargetURL(targetURL);
|
||||
}
|
||||
list.add(item);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String auth(String ltpasToken){
|
||||
String loginid = this.decodeToken(ltpasToken);
|
||||
if(StringUtils.isNotBlank(loginid)){
|
||||
return loginid;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void auth(String redirect, String ltpasToken, String method, HttpServletResponse response) {
|
||||
redirect = StringEscapeUtils.unescapeHtml(redirect);
|
||||
String loginid = this.decodeToken(ltpasToken);
|
||||
|
||||
|
||||
if(StringUtils.isEmpty(loginid)){
|
||||
toResponse(response,Response.notOk(-1, "ltpasToken已失效"),null);
|
||||
return;
|
||||
}
|
||||
|
||||
User userByUserNameFeign = userClient.getUserByUserNameFeign(loginid);
|
||||
|
||||
UserVo user = BeanUtil.copy(userByUserNameFeign, UserVo.class);
|
||||
if (userByUserNameFeign == null) {
|
||||
toResponse(response,Response.notOk(-1, "该账号不存在!"),null);
|
||||
return;
|
||||
} else if (!Integer.valueOf(1).equals(user.getEnabledMark())) {
|
||||
toResponse(response,Response.notOk(-1, "该账号已被禁用!"),null);
|
||||
return;
|
||||
}
|
||||
|
||||
String token = getToken(user);
|
||||
|
||||
|
||||
String tokenName = StpUtil.stpLogic.getConfig().getTokenName();
|
||||
String tokenPrefix = StpUtil.stpLogic.getConfig().getTokenPrefix();
|
||||
log.info("method->"+method);
|
||||
if(StringUtils.isEmpty(method)){
|
||||
//跳转到页面
|
||||
try {
|
||||
String url = makeUrl(redirect);
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, "Authorization".concat("=").concat(token)/*.concat("; SameSite=None ;Max-Age=3600; Path=/; Secure; HttpOnly")*/);
|
||||
response.sendRedirect(url);
|
||||
}catch (Exception e){
|
||||
log.error("failed to redirect->"+redirect,e);
|
||||
toResponse(response,Response.notOk(-1, "出现异常,页面鉴权跳转失败"),null);
|
||||
}
|
||||
}else {
|
||||
//调用接口
|
||||
String data = null;
|
||||
if("get".equals(method)){
|
||||
data = HttpRequest.get(redirect)
|
||||
.header(tokenName,tokenPrefix + " " + token)
|
||||
.execute().body();
|
||||
}else if("post".equals(method)){
|
||||
String[] paramsStrs = redirect.split("\\?params=");
|
||||
redirect = paramsStrs[0];
|
||||
if(paramsStrs.length > 1) {
|
||||
JSONObject params = JSON.parseObject(paramsStrs[1]);
|
||||
data = HttpRequest.post(redirect)
|
||||
.header(tokenName,tokenPrefix + " " + ltpasToken)
|
||||
.header("Cookie",tokenName + "=" + ltpasToken)
|
||||
.form(params)
|
||||
.execute().body();
|
||||
}
|
||||
}
|
||||
toResponse(response,Response.ok(JSON.parseObject(data)),token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台token,已登录,则从缓存获取,否则进行登录操作
|
||||
* @param user
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
private String getToken(UserVo user){
|
||||
|
||||
SaSession tokenSession = null;
|
||||
String token = null;
|
||||
User userInfo = null;
|
||||
try
|
||||
{
|
||||
tokenSession = StpUtil.getTokenSession();
|
||||
}
|
||||
catch (NotLoginException e)
|
||||
{
|
||||
//如果没有登陆,直接调用登陆接口
|
||||
StpUtil.login(user.getId(), new SaLoginModel().setDevice("PC").setIsLastingCookie(false));
|
||||
|
||||
tokenSession = StpUtil.getTokenSession();
|
||||
|
||||
token = StpUtil.getTokenValue();
|
||||
userInfo = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
|
||||
userInfo.setId(user.getId());
|
||||
userInfo.setUserName(user.getUserName());
|
||||
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, userInfo);
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
token = (String)tokenSession.get(GlobalConstant.TOKEN_KEY);
|
||||
|
||||
if(StringUtils.isNotEmpty(token)){
|
||||
StpUtil.setTokenValue(token);
|
||||
}
|
||||
|
||||
|
||||
userInfo = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
|
||||
String userId = String.valueOf(userInfo.getId());
|
||||
|
||||
if(StringUtils.isEmpty(token) || !user.getId().equals(userId)){
|
||||
//此登录接口登录web端
|
||||
//StpUtil.login(user.getId(), "pc");
|
||||
StpUtil.login(user.getId(), new SaLoginModel().setDevice("PC").setIsLastingCookie(false));
|
||||
tokenSession = StpUtil.getTokenSession();
|
||||
//tokenSession.set("user", user);
|
||||
token = StpUtil.getTokenValue();
|
||||
userInfo.setId(user.getId());
|
||||
userInfo.setUserName(user.getUserName());
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, userInfo);
|
||||
//todo 把获取用户信息的代码都注释了,要分析为什么之前要写这个,现在不写是可以获取待办的
|
||||
//List<Department> departments = userService.queryDepartmentsOfUser(user.getId());
|
||||
//XjrBaseCompany companies = companyService.getById(user.getCompanyId());
|
||||
|
||||
//List<UserRoleVo> role = userService.queryRolesOfUser(user.getId());
|
||||
// 设置返回数据
|
||||
// UserInfoVo userInfoVo = BeanUtil.copy(user, UserInfoVo.class);
|
||||
/* if (companies != null) {
|
||||
userInfoVo.setCompany(BeanUtil.copy(companies, CompanyPageListVo.class));
|
||||
tokenSession.set("company", companyService.getById(user.getCompanyId()));
|
||||
}*/
|
||||
/* if (departments != null) {
|
||||
|
||||
userInfoVo.setDepartments(BeanUtil.copyList(departments, UserDeptVo.class));
|
||||
tokenSession.set("dept", userService.queryDepartmentsOfUser(user.getId()));
|
||||
}*/
|
||||
/* if (role != null) {
|
||||
userInfoVo.setRoles(BeanUtil.copyList(role, UserRoleVo.class));
|
||||
tokenSession.set(GlobalConstant.LOGIN_USER_ROLE_CODE_KEY, role.stream().map(UserRoleVo::getCode).collect(Collectors.toList()));
|
||||
tokenSession.set("role", userService.queryRolesOfUser(user.getId()));
|
||||
}*/
|
||||
//如果开启了多租户 需要把租户信息也要存储一份
|
||||
/* if(properties.getEnabledTenant()) {
|
||||
tokenSession.set("tenant",tenant);
|
||||
userInfoVo.setTenantId(tenant.getId());
|
||||
userInfoVo.setTenantName(tenant.getName());
|
||||
}*/
|
||||
// tokenSession.set("userInfoVo",userInfoVo);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
private void toResponse(HttpServletResponse response, Response result, String token){
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
if (StringUtils.isNotEmpty(token)) {
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, "Authorization".concat("=").concat(token).concat("; SameSite=None ;Max-Age=3600; Path=/; Secure; HttpOnly"));
|
||||
}
|
||||
writer = response.getWriter();
|
||||
writer.write(JSON.toJSONString(result));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
if (writer != null){
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String decodeToken(String ltpasToken) {
|
||||
if (StringUtils.isEmpty(ltpasToken)) {
|
||||
return null;
|
||||
}
|
||||
Long now = System.currentTimeMillis() / 1000;
|
||||
String ltpasTokenDecode = new String(Base64.getDecoder().decode(ltpasToken), StandardCharsets.UTF_8);
|
||||
String[] ltpasTokenInfo = ltpasTokenDecode.split("%");
|
||||
String eIN = ltpasTokenInfo[0];
|
||||
String sign = ltpasTokenInfo[3];
|
||||
ltpasTokenInfo[3] = secretKey;
|
||||
String ltpasTokenPlain = StringUtils.join(ltpasTokenInfo, "%");
|
||||
if(!sign.equals(DigestUtils.sha1Hex(ltpasTokenPlain))) {
|
||||
return null;
|
||||
}
|
||||
Long begin = Long.parseLong(ltpasTokenInfo[1]);
|
||||
Long end = Long.parseLong(ltpasTokenInfo[2]);
|
||||
if(now >= begin && now <= end) {
|
||||
return eIN;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过登录账号username,等到加密后的互信token
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
public String encodeToken(String username) {
|
||||
//过期时间
|
||||
Long cycle = 24 * 60L * 60L;
|
||||
Long begin = System.currentTimeMillis() /1000;
|
||||
Long end = begin + cycle;
|
||||
String[] ltpasTokenPlainInfo = new String[4];
|
||||
ltpasTokenPlainInfo[0] = username;
|
||||
ltpasTokenPlainInfo[1] = begin.toString();
|
||||
ltpasTokenPlainInfo[2] = end.toString();
|
||||
ltpasTokenPlainInfo[3] = secretKey;
|
||||
String sign = DigestUtils.sha1Hex(StringUtils.join(ltpasTokenPlainInfo, "%"));
|
||||
ltpasTokenPlainInfo[3] = sign;
|
||||
String token = Base64.getEncoder().encodeToString(StringUtils.join(ltpasTokenPlainInfo, "%").getBytes(StandardCharsets.UTF_8));
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 对重定向的url进行解析,中文乱码处理
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
private String makeUrl(String url){
|
||||
String[] urlStrs = url.split("\\?");
|
||||
if(urlStrs.length == 1){
|
||||
return url;
|
||||
}
|
||||
String domain = url.split("\\?")[0];
|
||||
StringBuffer urlBuff = new StringBuffer();
|
||||
urlBuff.append(domain).append("?");
|
||||
String paramStrs = url.substring(url.indexOf("?") + 1);
|
||||
String[] params = paramStrs.split("&");
|
||||
if(params.length == 1){
|
||||
makeUrlParams(0,params,urlBuff);
|
||||
return urlBuff.toString();
|
||||
}
|
||||
makeUrlParams(0,params,urlBuff);
|
||||
urlBuff.append("&");
|
||||
for (int i = 1; i < params.length; i++) {
|
||||
makeUrlParams(i,params,urlBuff);
|
||||
urlBuff.append("&");
|
||||
}
|
||||
return urlBuff.substring(0, urlBuff.length() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析url参数,将参数值进行编码转换
|
||||
* @param index
|
||||
* @param params
|
||||
* @param urlBuff
|
||||
*/
|
||||
private void makeUrlParams(Integer index,String[] params,StringBuffer urlBuff){
|
||||
String[] kv = params[index].split("=");
|
||||
if(kv.length == 1 || StringUtils.isEmpty(kv[1])){
|
||||
urlBuff.append(kv[0]).append("=").append("");
|
||||
}else {
|
||||
try {
|
||||
urlBuff.append(kv[0]).append("=").append(URLEncoder.encode(kv[1], "utf-8"));
|
||||
}catch (Exception e){
|
||||
urlBuff.append(kv[0]).append("=").append("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求地址url中获取某个参数值
|
||||
* @param url
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
private String getParamByUrl(String url, String name) {
|
||||
url += "&";
|
||||
String pattern = "(\\?|&){1}#{0,1}" + name + "=[a-zA-Z0-9]*(&{1})";
|
||||
Pattern r = Pattern.compile(pattern);
|
||||
Matcher matcher = r.matcher(url);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(0).split("=")[1].replace("&", "");
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.xjrsoft.auth.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ResponseVo {
|
||||
private Integer retcode = 1;
|
||||
private Integer totalCount = 0;
|
||||
private String msg;
|
||||
private List<TodoItem> items;
|
||||
}
|
||||
20
xjrsoft-auth/src/main/java/com/xjrsoft/auth/vo/TodoItem.java
Normal file
20
xjrsoft-auth/src/main/java/com/xjrsoft/auth/vo/TodoItem.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.xjrsoft.auth.vo;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TodoItem {
|
||||
private boolean unread = true;
|
||||
private boolean useBlank = true;
|
||||
private boolean useHref = true;
|
||||
private String subtitle;
|
||||
private String title;
|
||||
private String type;
|
||||
private JSONObject params;
|
||||
private String targetURL;//这个返回值根据app和pc判断,会有不同的返回值,把这两个值写到配置文件里,到时候方便修改。
|
||||
private String url;
|
||||
private String src;
|
||||
private String route;
|
||||
private long ts;
|
||||
}
|
||||
3
xjrsoft-auth/src/main/resources/application.yml
Normal file
3
xjrsoft-auth/src/main/resources/application.yml
Normal file
@ -0,0 +1,3 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: public
|
||||
60
xjrsoft-auth/src/main/resources/bootstrap.yml
Normal file
60
xjrsoft-auth/src/main/resources/bootstrap.yml
Normal file
@ -0,0 +1,60 @@
|
||||
server:
|
||||
port: 1024
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: auth-service
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://10.0.252.7:3306/fcd2-msat-init?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
|
||||
username: learun4dev
|
||||
password: ABcd1234@
|
||||
|
||||
cloud:
|
||||
nacos: #nacos监控
|
||||
discovery:
|
||||
server-addr: 10.0.252.1:8848
|
||||
namespace: ITC
|
||||
group: DNE
|
||||
username: nacos
|
||||
password: ABcd1234@
|
||||
config:
|
||||
server-addr: 10.0.252.1:8848 # nacos 配置中心地址
|
||||
namespace: ITC
|
||||
group: DNE
|
||||
username: nacos
|
||||
password: ABcd1234@
|
||||
file-extension: yml # 指定格式 xjrsoft-auth-service-dev.yml
|
||||
extension-configs:
|
||||
- data-id: global-config.yml
|
||||
refresh: true
|
||||
group: DNE
|
||||
- data-id: redis-config.yml
|
||||
refresh: true
|
||||
group: DNE
|
||||
- data-id: sa-token-client-config.yml
|
||||
refresh: true
|
||||
group: DNE
|
||||
- data-id: datasource-config.yml
|
||||
refresh: true
|
||||
group: DNE
|
||||
sentinel:
|
||||
transport:
|
||||
dashboard: localhost:8080 #sentinel dashboard 地址
|
||||
port: 8719 #默认端口, 如果 被占用,会一直+1 直到未被占用为止
|
||||
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html
|
||||
tags-sorter: alpha
|
||||
operations-sorter: alpha
|
||||
show-extensions: true
|
||||
api-docs:
|
||||
path: '/sso/v3/api-docs'
|
||||
group-configs:
|
||||
- group: 'default'
|
||||
paths-to-match: '/sso/**'
|
||||
packages-to-scan: com.xjrsoft.auth
|
||||
default-flat-param-object: false
|
||||
Reference in New Issue
Block a user