anto: 初始化

This commit is contained in:
zheng020 2026-04-03 10:57:20 +08:00
parent 191aea0ece
commit 9b9ed034d0
640 changed files with 171535 additions and 0 deletions

87
txw-sso/README.md Normal file
View File

@ -0,0 +1,87 @@
# 1、项目启动类
无需再编写启动类
### 开发环境可使用
`com.css.txw.sso.app.DevAppStarter`
### 其他环境使用
`com.css.ggzc.framework.app.ApplicationStarter`
# 2、目录结构
```
│ .gitignore
│ pom.xml
│ README.md
├─txw-sso-service-api api包
│ │ pom.xml
│ └─src
│ └─main
│ ├─java
│ │ └─com.css.txw.sso 模块包
│ │ ├─api api接口
│ │ │ ISsoApi.java
│ │ │
│ │ ├─configuration 模块自动装配配置 需要在org.springframework.boot.autoconfigure.AutoConfiguration.imports中配置基于插件化开发各模块在该类中可指定要扫描的包等配置启动类不会自动扫描
│ │ │ SsoApiConfiguration.java
│ │ │
│ │ ├─constants api常量
│ │ │ SsoConstants.java
│ │ │
│ │ └─pojo api pojo
│ │
│ └─resources 模块相关的配置说明注意使用config类
│ │ additional-spring-configuration-metadata.json
│ │
│ └─META-INF
│ └─spring 模块自动装配配置
│ org.springframework.boot.autoconfigure.AutoConfiguration.imports
└─txw-sso-service-biz service包
│ pom.xml
└─src
└─main
├─java
│ └─com.css.txw.sso 模块包
│ ├─configuration 本模块自动装配设置 需要在org.springframework.boot.autoconfigure.AutoConfiguration.imports中配置基于插件化开发各模块在该类中可指定要扫描的包等配置启动类不会自动扫描
│ │ SsoServiceConfiguration.java
│ │
│ ├─constants
│ │
│ ├─consumer kafaka消费服务
│ │
│ ├─controller controller 建议按业务分包 注意禁止在controller中写跟sql相关的逻辑一律写到mapper中
│ │
│ ├─job 定时任务
│ │
│ ├─mapper mapper 建议按业务分包
│ │
│ ├─pojo
│ │ ├─domain
│ │ │
│ │ ├─dto
│ │ │
│ │ └─vo
│ │
│ │-properties 配置类
│ │
│ ├─service service 建议按业务分包 注意SQL相关内容写到mapper中不要写在service中
│ │
│ └─util 本业务域工具类 跟业务相关的写在这里 公共类的请优先使用framework中有的或者在framework中增加
└─resources
│ additional-spring-configuration-metadata.json 模块相关的配置说明注意使用config类
│ application.yaml 模块主配置文件
│ bootstrap-env.yml 模块env环境配置文件
│ bootstrap-local.yml 模块开发环境配置文件
│ bootstrap-nacos.yml 模块nacos配置文件
│ bootstrap.yml 模块主配置文件
│ logback-spring.xml 模块日志配置文件
├─mapper 模块mapper
└─META-INF
└─spring 模块自动装配配置
org.springframework.boot.autoconfigure.AutoConfiguration.imports
```

40
txw-sso/pom.xml Normal file
View File

@ -0,0 +1,40 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>txw-sso</artifactId>
<groupId>com.css.txw</groupId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>sso</description>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</build>
<modules>
<module>txw-sso-service-api</module>
<module>txw-sso-service-biz</module>
</modules>
</project>

61
txw-sso/settings.xml Normal file
View File

@ -0,0 +1,61 @@
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<!--<localRepository>[本地maven库目录]</localRepository>-->
<!-- omitted xml -->
<!-- 请妥善保管好您的配置,不要随意分享给他人 -->
<servers>
<server>
<id>codingcorp-qyd_repo-mvn_public</id>
<username>coding-user</username>
<password>coding-pwd</password>
</server>
</servers>
<!-- omitted xml -->
<profiles>
<profile>
<id>Repository Proxy</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<!--必须与 settings.xml 的 id 一致-->
<id>codingcorp-qyd_repo-mvn_public</id>
<name>mvn_public</name>
<url>http://codingcorp-maven.pkg.codingstd.xc01.cloud.sat.tax/repository/qyd_repo/mvn_public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>codingcorp-qyd_repo-mvn_public</id>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<url>http://codingcorp-maven.pkg.codingstd.xc01.cloud.sat.tax/repository/qyd_repo/mvn_public/</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<mirrors>
<mirror>
<id>codingcorp-qyd_repo-mvn_public</id>
<!-- 此配置避免了本仓库制品的拉取流量被切换到腾讯云镜像源,保证您在使用镜像加速的同时可以从本仓库拉取制品 -->
<mirrorOf>central</mirrorOf>
<name>mvn_public</name>
<url>http://codingcorp-maven.pkg.codingstd.xc01.cloud.sat.tax/repository/qyd_repo/mvn_public/</url>
</mirror>
</mirrors>
</settings>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--api的parent由于编译需要请自行修改 修改后需要调整 本模块groupId-->
<parent>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath/>
</parent>
<groupId>com.css.txw</groupId>
<artifactId>txw-sso-service-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>${project.artifactId}</name>
<description>sso service api</description>
<properties>
<version.ggzc-framework-dependencies>1.0.0-SNAPSHOT</version.ggzc-framework-dependencies>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-starter</artifactId>
<version>${version.ggzc-framework-dependencies}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-starter-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<distributionManagement>
<repository>
<!--必须与 settings.xml 的 id 一致-->
<id>codingcorp-qyd_repo-mvn_public</id>
<name>mvn_public</name>
<url>http://codingcorp-maven.pkg.codingstd.xc01.cloud.sat.tax/repository/qyd_repo/mvn_public/</url>
</repository>
</distributionManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<attach>true</attach>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,10 @@
package com.css.txw.sso.api;
import com.css.txw.sso.constants.SsoApiConstants;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(name = SsoApiConstants.NAME)
public interface ISsoApi {
}

View File

@ -0,0 +1,52 @@
package com.css.txw.sso.api.oauth2;
import com.css.txw.sso.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import com.css.txw.sso.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
import com.css.txw.sso.api.oauth2.dto.OAuth2AccessTokenRespDTO;
import com.css.txw.sso.constants.SsoApiConstants;
import com.css.ggzc.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@FeignClient(name = SsoApiConstants.NAME,contextId = "token")
@Tag(name = "RPC 服务 - OAuth2.0 令牌")
public interface OAuth2TokenApi {
String PREFIX = SsoApiConstants.PREFIX + "/oauth2/token";
/**
* 校验 Token URL 地址主要是提供给 Gateway 使用
*/
@SuppressWarnings("HttpUrlsUsage")
String URL_CHECK = "http://" + SsoApiConstants.NAME + PREFIX + "/check";
@PostMapping(PREFIX + "/create")
@Operation(summary = "创建访问令牌")
CommonResult<OAuth2AccessTokenRespDTO> createAccessToken(@Valid @RequestBody OAuth2AccessTokenCreateReqDTO reqDTO);
@GetMapping(PREFIX + "/check")
@Operation(summary = "校验访问令牌")
@Parameter(name = "accessToken", description = "访问令牌", required = true, example = "tudou")
CommonResult<OAuth2AccessTokenCheckRespDTO> checkAccessToken(@RequestParam("accessToken") String accessToken);
@DeleteMapping(PREFIX + "/remove")
@Operation(summary = "移除访问令牌")
@Parameter(name = "accessToken", description = "访问令牌", required = true, example = "tudou")
CommonResult<OAuth2AccessTokenRespDTO> removeAccessToken(@RequestParam("accessToken") String accessToken);
@PutMapping(PREFIX + "/refresh")
@Operation(summary = "刷新访问令牌")
@Parameters({
@Parameter(name = "refreshToken", description = "刷新令牌", required = true, example = "haha"),
@Parameter(name = "clientId", description = "客户端编号", required = true, example = "yudaoyuanma")
})
CommonResult<OAuth2AccessTokenRespDTO> refreshAccessToken(@RequestParam("refreshToken") String refreshToken,
@RequestParam("clientId") String clientId);
}

View File

@ -0,0 +1,50 @@
package com.css.txw.sso.api.oauth2.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@Schema(description = "RPC 服务 - OAuth2 访问令牌的session信息")
@Data
public class AccessTokenSessionInfo implements Serializable {
/**
* 用户信息
*/
@Schema(description = "用户UUID")
private String yhUuid;
@Schema(description = "真实姓名")
private String zsxm;
@Schema(description = "手机号码")
private String sjhm;
@Schema(description = "身份证件类型")
private String sfzjlx;
@Schema(description = "身份证件号码")
private String sfzjhm;
@Schema(description = "用户did")
private String yhdid;
/**
* 机构信息
*/
@Schema(description = "机构UUID")
private String qyuuid;
@Schema(description = "机构名称")
private String qymc;
@Schema(description = "纳税人识别号")
private String nsrsbh;
@Schema(description = "社会信用代码")
private String shxydm;
@Schema(description = "登记序号")
private String djxh;
@Schema(description = "行政区划")
private String xzqhszDm;
@Schema(description = "用户类型")
private String yhlx;
}

View File

@ -0,0 +1,23 @@
package com.css.txw.sso.api.oauth2.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Schema(description = "RPC 服务 - OAuth2 访问令牌的校验 Response DTO")
@Data
public class OAuth2AccessTokenCheckRespDTO implements Serializable {
@Schema(description = "是否刷新了访问令牌")
private Boolean hasRefreshedToken;
@Schema(description = "新的访问令牌")
private String newAccessToken;
@Schema(description = "新的过期时间")
private LocalDateTime newExpireTime;
@Schema(description = "session信息")
private AccessTokenSessionInfo sessionInfo;
}

View File

@ -0,0 +1,32 @@
package com.css.txw.sso.api.oauth2.dto;
import com.css.ggzc.framework.common.enums.UserTypeEnum;
import com.css.ggzc.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
@Schema(description = "RPC 服务 - OAuth2 访问令牌创建 Request DTO")
@Data
public class OAuth2AccessTokenCreateReqDTO implements Serializable {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "用户编号不能为空")
private String userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "用户类型不能为空")
@InEnum(value = UserTypeEnum.class, message = "用户类型必须是 {value}")
private Integer userType;
@Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma")
@NotNull(message = "客户端编号不能为空")
private String clientId;
@Schema(description = "授权范围的数组", example = "user_info")
private List<String> scopes;
}

View File

@ -0,0 +1,27 @@
package com.css.txw.sso.api.oauth2.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
@Schema(description = "RPC 服务 - OAuth2 访问令牌的信息 Response DTO")
@Data
@Accessors(chain = true)
public class OAuth2AccessTokenRespDTO implements Serializable {
@Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou")
private String accessToken;
@Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "haha")
private String refreshToken;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private String userId;
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime expiresTime;
}

View File

@ -0,0 +1,28 @@
package com.css.txw.sso.api.yhxx;
import com.css.txw.sso.constants.SsoApiConstants;
import com.css.txw.sso.pojo.yhxx.SwitchCompanyResDTO;
import com.css.ggzc.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = SsoApiConstants.NAME,contextId = "switch")
@Tag(name = "切换会话信息接口")
public interface SwitchSessionApi {
String PREFIX = SsoApiConstants.PREFIX + "/switchSession";
@PostMapping(PREFIX+"/company")
@Operation(summary = "切换机构")
CommonResult<SwitchCompanyResDTO> switchCompany(@RequestParam("token")String token, @RequestParam("jguuid")String jguuid);
@PostMapping(PREFIX+"/addCompany")
@Operation(summary = "新增机构")
CommonResult<String> addCompany(@RequestBody SwitchCompanyResDTO resDTO);
}

View File

@ -0,0 +1,18 @@
package com.css.txw.sso.api.yhxx;
import com.css.ggzc.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "sso-service")
@Tag(name = "用户信息接口")
public interface YhxxApi {
@PostMapping("/sso/yhxx/getYhxx")
@Operation(summary = "获取用户信息")
CommonResult<String> getYhxx(@RequestParam("djxh") String djxh);
}

View File

@ -0,0 +1,15 @@
package com.css.txw.sso.configuration;
import com.css.txw.sso.api.ISsoApi;
import com.css.txw.sso.api.yhxx.SwitchSessionApi;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = {
ISsoApi.class,
SwitchSessionApi.class
})
public class SsoApiConfiguration {
}

View File

@ -0,0 +1,12 @@
package com.css.txw.sso.constants;
public class SsoApiConstants {
private SsoApiConstants() {
// 构造方法
}
public static final String NAME = "txw-sso";
public static final String PREFIX = "/sso";
}

View File

@ -0,0 +1,27 @@
package com.css.txw.sso.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登录日志的类型枚举
*/
@Getter
@AllArgsConstructor
public enum LoginLogTypeEnum {
LOGIN_USERNAME(100), // 使用账号登录
LOGIN_SOCIAL(101), // 使用社交登录
LOGIN_MOBILE(103), // 使用手机登陆
LOGIN_SMS(104), // 使用短信登陆
LOGOUT_SELF(200), // 自己主动登出
LOGOUT_DELETE(202), // 强制退出
;
/**
* 日志类型
*/
private final Integer type;
}

View File

@ -0,0 +1,29 @@
package com.css.txw.sso.enums;
import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* OAuth2 授权类型模式的枚举
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum OAuth2GrantTypeEnum {
PASSWORD("password"), // 密码模式
AUTHORIZATION_CODE("authorization_code"), // 授权码模式
IMPLICIT("implicit"), // 简化模式
CLIENT_CREDENTIALS("client_credentials"), // 客户端模式
REFRESH_TOKEN("refresh_token"), // 刷新模式
;
private final String grantType;
public static OAuth2GrantTypeEnum getByGranType(String grantType) {
return ArrayUtil.firstMatch(o -> o.getGrantType().equals(grantType), values());
}
}

View File

@ -0,0 +1,12 @@
package com.css.txw.sso.pojo.yhxx;
import lombok.Data;
@Data
public class SwitchCompanyResDTO {
private String qyuuid;
private String qymc;
private String nsrsbh;
private String yhuuid;
}

View File

@ -0,0 +1 @@
com.css.txw.sso.configuration.SsoApiConfiguration

View File

@ -0,0 +1 @@
com.css.txw.sso.configuration.SsoApiConfiguration

View File

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>txw-sso</artifactId>
<groupId>com.css.txw</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>txw-sso-service-biz</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>sso service</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Spring Cloud 基础 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-starter-common</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-starter-cache</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-starter-rpc</artifactId>
</dependency>
<!-- <dependency>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-starter-trace</artifactId>
</dependency>-->
<!-- Job 相关 -->
<dependency>
<groupId>com.css.ggzc</groupId>
<artifactId>ggzc-framework-starter-job</artifactId>
</dependency>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.css.txw</groupId>
<artifactId>txw-sso-service-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.css.txw</groupId>
<artifactId>txw-mhzc-service-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.css.txw</groupId>
<artifactId>txw-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
</dependency>
<dependency>
<groupId>org.chainweaver</groupId>
<artifactId>did-sdk-java</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.60</version>
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<fork>true</fork>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,20 @@
package com.css.txw.sso.app;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* 仅开发使用正式环境不使用写在此处的配置在生产环境不会生效
*/
@Slf4j
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DevAppStarter {
public static void main(String[] args) {
SpringApplication.run(DevAppStarter.class, args);
log.info("txw-sso 0.0.2");
}
}

View File

@ -0,0 +1,20 @@
package com.css.txw.sso.configuration;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.teaopenapi.models.Config;
import com.css.ggzc.framework.cache.utils.CacheUtils;
import static com.css.txw.sso.constants.SsoConstants.*;
public class SMSClient {
public static Client createClient() throws Exception {
Config config = new Config()
.setAccessKeyId(CacheUtils.dm2mc("xt_xtcs",MHZC_SMS_KEY))
.setAccessKeySecret(CacheUtils.dm2mc("xt_xtcs",MHZC_SMS_SECRET));
// 配置 Endpoint
config.endpoint = MHZC_SMS_ENDPOINT;
return new Client(config);
}
}

View File

@ -0,0 +1,12 @@
package com.css.txw.sso.configuration;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@AutoConfiguration
@ComponentScan("com.css.txw.sso")
@MapperScan("com.css.txw.sso.mapper")
public class SsoServiceConfiguration {
}

View File

@ -0,0 +1,45 @@
package com.css.txw.sso.constants;
import com.css.ggzc.framework.common.exception.ErrorCode;
/**
* System 错误码枚举类
*
* system 系统使用 1-002-000-000
*/
public interface ErrorCodeConstants {
// ========== AUTH 模块 1-002-000-000 ==========
ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1004001, "登录失败,账号密码不正确");
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1004002, "登录失败,账号被禁用");
ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1004003, "请重新滑动验证");
ErrorCode AUTH_LOGIN_PASSWORD_ERROR_LOCK = new ErrorCode(1004004, "密码输入错误次数过多,账户锁定,请稍后再试");
ErrorCode AUTH_LOGIN_ACCOUNT_LOCK = new ErrorCode(1004005, "密码输入错误次数过多,账户锁定,请联系管理员解锁");
// ========== OAuth2 客户端 1-002-020-000 =========
ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1004006, "OAuth2 客户端不存在");
ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1004007, "OAuth2 客户端编号已存在");
ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1004008, "OAuth2 客户端已禁用");
ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1004009, "无效 redirect_uri: {}");
ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1004010, "无效 client_secret: {}");
// ========== OAuth2 授权 1-002-021-000 =========
ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1004011, "client_id 不匹配");
ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1004012, "redirect_uri 不匹配");
ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1004013, "state 不匹配");
ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1004014, "code 不存在");
// ========== OAuth2 授权 1-002-022-000 =========
ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1004015, "code 不存在");
ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1004016, "code 已过期");
ErrorCode OAUTH2_SJHM_NOT_EXISTS = new ErrorCode(1004017, "手机号码不存在");
ErrorCode OAUTH2_LOGIN_SJHM_NOT_EXISTS = new ErrorCode(1004018, "登录失败,验证码无效");
ErrorCode OAUTH2_SJHM_LOCK = new ErrorCode(1004019, "距离上次发送验证码不超过1分钟");
ErrorCode OAUTH2_LOGIN_SMS_NOT_EXISTS = new ErrorCode(1004020, "验证码无效或已过期");
}

View File

@ -0,0 +1,101 @@
package com.css.txw.sso.constants;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
/**
* System Redis Key 枚举类
*
* @author 芋道源码
*/
public interface RedisKeyConstants {
/**
* 指定部门的所有子部门编号数组的缓存
* <p>
* KEY 格式dept_children_ids:{id}
* VALUE 数据类型String 子部门编号集合
*/
String DEPT_CHILDREN_ID_LIST = "dept_children_ids";
/**
* 角色的缓存
* <p>
* KEY 格式role:{id}
* VALUE 数据类型String 角色信息
*/
String ROLE = "role";
/**
* 用户拥有的角色编号的缓存
* <p>
* KEY 格式user_role_ids:{userId}
* VALUE 数据类型String 角色编号集合
*/
String USER_ROLE_ID_LIST = "user_role_ids";
/**
* 拥有指定菜单的角色编号的缓存
* <p>
* KEY 格式user_role_ids:{menuId}
* VALUE 数据类型String 角色编号集合
*/
String MENU_ROLE_ID_LIST = "menu_role_ids";
/**
* 拥有权限对应的菜单编号数组的缓存
* <p>
* KEY 格式permission_menu_ids:{permission}
* VALUE 数据类型String 菜单编号数组
*/
String PERMISSION_MENU_ID_LIST = "permission_menu_ids";
/**
* OAuth2 客户端的缓存
* <p>
* KEY 格式oauth_client:{id}
* VALUE 数据类型String 客户端信息
*/
String OAUTH_CLIENT = "oauth_client";
/**
* 访问令牌的缓存
* <p>
* KEY 格式oauth2_access_token:{token}
* VALUE 数据类型String 访问令牌信息 {@link OAuth2AccessTokenDO}
* <p>
* 由于动态过期时间使用 RedisTemplate 操作
*/
String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s";
/**
* 站内信模版的缓存
* <p>
* KEY 格式notify_template:{code}
* VALUE 数据格式String 模版信息
*/
String NOTIFY_TEMPLATE = "notify_template";
/**
* 邮件账号的缓存
* <p>
* KEY 格式mail_account:{id}
* VALUE 数据格式String 账号信息
*/
String MAIL_ACCOUNT = "mail_account";
/**
* 邮件模版的缓存
* <p>
* KEY 格式mail_template:{code}
* VALUE 数据格式String 模版信息
*/
String MAIL_TEMPLATE = "mail_template";
/**
* 短信模版的缓存
* <p>
* KEY 格式sms_template:{id}
* VALUE 数据格式String 模版信息
*/
String SMS_TEMPLATE = "sms_template";
}

View File

@ -0,0 +1,27 @@
package com.css.txw.sso.constants;
public class SsoConstants {
public static final String COOKIE_TOKEN_KEY = "ACCESS_TOKEN";
public static final String ADMIN_COOKIE_TOKEN_KEY = "ACCESS_TOKEN_ADMIN";
public static final String COOKIE_PATH = "/";
public static final String CLIENT_DEFAULT = "default";
public static final String SMS_CLIENT_SIGNNAME = "智贸链";
public static final String SMS_TEMPLATE_CODE = "SMS_474450289";
public static final String MHZC_SMS_SECRET = "MHZC-SMS-SECRET";
public static final String MHZC_SMS_KEY = "MHZC-SMS-KEY";
public static final String MHZC_SMS_ENDPOINT = "dysmsapi.aliyuncs.com";
public static final String AUTHORIZATION_HEADER = "Authorization";
}

View File

@ -0,0 +1 @@
# kafaka消费服务

View File

@ -0,0 +1,67 @@
package com.css.txw.sso.controller.auth;
import cn.hutool.core.util.StrUtil;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.ggzc.framework.common.util.servlet.ServletUtils;
import com.css.txw.sso.constants.SsoApiConstants;
import com.css.txw.sso.constants.SsoConstants;
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
import com.css.txw.sso.service.auth.AdminAuthService;
import com.css.txw.sso.util.SecurityFrameworkUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
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.RestController;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import static com.css.ggzc.framework.common.pojo.CommonResult.success;
@Tag(name = "后台登录")
@RestController
@RequestMapping("/admin/auth")
@Validated
@Slf4j
public class AdminAuthController {
@Resource
private AdminAuthService adminAuthService;
@PostMapping("/login")
@Operation(summary = "后台登录-账号密码登录")
public CommonResult<Boolean> login(@RequestBody @Valid AuthLoginReqVO reqVO){
final String token = adminAuthService.login(reqVO);
final HttpServletResponse response = ServletUtils.getResponse();
final Cookie cookie = new Cookie(SsoConstants.ADMIN_COOKIE_TOKEN_KEY,token);
cookie.setPath(SsoConstants.COOKIE_PATH);
cookie.setHttpOnly(true);
response.addCookie(cookie);
return success(true);
}
@PostMapping("/logout")
@Operation(summary = "后台登录-退出登录")
public CommonResult<Boolean> logout(HttpServletRequest request){
String token = SecurityFrameworkUtils.obtainAuthorization(SsoConstants.ADMIN_COOKIE_TOKEN_KEY,request);
if (StrUtil.isNotBlank(token)) {
adminAuthService.logout(token);
final HttpServletResponse response = ServletUtils.getResponse();
final Cookie cookie = new Cookie(SsoConstants.ADMIN_COOKIE_TOKEN_KEY,token);
cookie.setPath(SsoConstants.COOKIE_PATH);
cookie.setHttpOnly(true);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
return success(true);
}
}

View File

@ -0,0 +1,147 @@
package com.css.txw.sso.controller.auth;
import static com.css.ggzc.framework.common.pojo.CommonResult.success;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import org.springframework.validation.annotation.Validated;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.css.ggzc.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.ggzc.framework.common.util.gy.GyUtils;
import com.css.ggzc.framework.common.util.servlet.ServletUtils;
import com.css.txw.sso.constants.SsoConstants;
import com.css.txw.sso.enums.LoginLogTypeEnum;
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
import com.css.txw.sso.pojo.vo.DidBindPhoneReqVO;
import com.css.txw.sso.pojo.vo.SMSLoginReqVO;
import com.css.txw.sso.pojo.vo.SendMsgReqVO;
import com.css.txw.sso.pojo.vo.oauth2.LogoutVO;
import com.css.txw.sso.pojo.vo.thirdparty.did.QrCodeInfo;
import com.css.txw.sso.service.auth.AuthService;
import com.css.txw.sso.util.SecurityFrameworkUtils;
import cn.hutool.core.util.StrUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
@Tag(name = "管理后台 - 认证")
@RestController
@RequestMapping("/auth")
@Validated
@Slf4j
public class AuthController {
@Resource
private AuthService authService;
@PostMapping("/login")
@PermitAll
@Operation(summary = "使用账号密码登录")
public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
final HttpServletResponse response = ServletUtils.getResponse();
final AuthLoginRespVO login = authService.login(reqVO);
final Cookie cookie = new Cookie(SsoConstants.COOKIE_TOKEN_KEY,login.getAccessToken());
cookie.setPath(SsoConstants.COOKIE_PATH);
cookie.setHttpOnly(true);
//cookie.setMaxAge(-1);
response.addCookie(cookie);
return success(login);
}
@PostMapping("/logout")
@PermitAll
@Operation(summary = "登出系统")
public CommonResult<List<LogoutVO>> logout(HttpServletRequest request) {
List<LogoutVO> logout = new ArrayList<>();
String token = SecurityFrameworkUtils.obtainAuthorization(SsoConstants.COOKIE_TOKEN_KEY,request);
if (StrUtil.isNotBlank(token)) {
logout = authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
final HttpServletResponse response = ServletUtils.getResponse();
final Cookie cookie = new Cookie(SsoConstants.COOKIE_TOKEN_KEY,token);
cookie.setPath(SsoConstants.COOKIE_PATH);
cookie.setHttpOnly(true);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
log.info("ssologout返回结果{}",logout);
return success(logout);
}
@PostMapping("/refresh-token")
@PermitAll
@Operation(summary = "刷新令牌")
@Parameter(name = "refreshToken", description = "刷新令牌", required = true)
public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
return success(authService.refreshToken(refreshToken));
}
@PostMapping("/sendMsg")
@PermitAll
@Operation(summary = "发送手机验证码")
public CommonResult<Integer> sendMsg(@RequestBody @Valid SendMsgReqVO reqVO) throws Exception{
return success(authService.sendMsg(reqVO));
}
@PostMapping("/loginBySMS")
@PermitAll
@Operation(summary = "发送手机验证码")
public CommonResult<AuthLoginRespVO> loginBySMS(@RequestBody @Valid SMSLoginReqVO reqVO) {
final HttpServletResponse response = ServletUtils.getResponse();
final AuthLoginRespVO login = authService.loginBySMS(reqVO);
final Cookie cookie = new Cookie(SsoConstants.COOKIE_TOKEN_KEY,login.getAccessToken());
cookie.setPath(SsoConstants.COOKIE_PATH);
cookie.setHttpOnly(true);
//cookie.setMaxAge(-1);
response.addCookie(cookie);
return success(login);
}
@PostMapping("/didBindPhone")
@PermitAll
@Operation(summary = "绑定手机号")
public CommonResult<AuthLoginRespVO> didBindPhone(@RequestBody @Valid DidBindPhoneReqVO reqVO) {
if (GyUtils.isNull(reqVO)) {
CommonResult<AuthLoginRespVO> result = CommonResult.success(null);
String msg = "入参为空!";
result.setMsg(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
return result;
}
if (GyUtils.isNull(reqVO.getReqId())) {
CommonResult<AuthLoginRespVO> result = CommonResult.success(null);
String msg = "入参reqId为空";
result.setMsg(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
return result;
}
final HttpServletResponse response = ServletUtils.getResponse();
final AuthLoginRespVO login = authService.didBindPhone(reqVO);
final Cookie cookie = new Cookie(SsoConstants.COOKIE_TOKEN_KEY,login.getAccessToken());
cookie.setPath(SsoConstants.COOKIE_PATH);
cookie.setHttpOnly(true);
//cookie.setMaxAge(-1);
response.addCookie(cookie);
return success(login);
}
}

View File

@ -0,0 +1,216 @@
package com.css.txw.sso.controller.oauth2;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.ggzc.framework.common.util.gy.GyUtils;
import com.css.txw.sso.constants.SsoApiConstants;
import com.css.txw.sso.constants.SsoConstants;
import com.css.txw.sso.convert.OAuth2OpenConvert;
import com.css.txw.sso.enums.OAuth2GrantTypeEnum;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
import com.css.txw.sso.pojo.dto.session.SessionInfo;
import com.css.txw.sso.pojo.vo.oauth2.OAuth2OpenAccessTokenRespVO;
import com.css.txw.sso.pojo.vo.oauth2.OAuth2OpenCheckTokenRespVO;
import com.css.txw.sso.service.oauth2.OAuth2ClientService;
import com.css.txw.sso.service.oauth2.OAuth2GrantService;
import com.css.txw.sso.service.oauth2.OAuth2TokenService;
import com.css.txw.sso.util.HttpUtils;
import com.css.txw.sso.util.OAuth2Utils;
import com.css.txw.sso.util.SecurityFrameworkUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import static com.css.ggzc.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.css.ggzc.framework.common.pojo.CommonResult.success;
@Tag(name = "基础OAuth2授权")
@RestController
@RequestMapping("/oauth2")
@Slf4j
public class OAuth2BasicController {
@Resource
private OAuth2GrantService oauth2GrantService;
@Resource
private OAuth2ClientService oauth2ClientService;
@Resource
private OAuth2TokenService oauth2TokenService;
@PostMapping("/authorize")
@Operation(summary = "申请授权", description = "OAuth2授权码模式第一步-获取授权码")
@Parameters({
@Parameter(name = "response_type", required = true, description = "响应类型", example = "code"),
@Parameter(name = "client_id", required = true, description = "客户端编号", example = "2f54a1f113d84130a867f559b7279365"),
@Parameter(name = "redirect_uri", required = true, description = "重定向 URI", example = "http://127.0.0.1"),
@Parameter(name = "state", example = "1")
})
public CommonResult<String> authorize(HttpServletRequest request,
@RequestParam("Response_type") String responseType,
@RequestParam("Client_id") String clientId,
@RequestParam("Redirect_uri") String redirectUri,
@RequestParam(value = "State", required = false) String state) {
// 校验 responseType 是否满足 code 或者 token
OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType);
//校验 redirectUri 重定向域名是否合法
log.info("authorize开始验证redirectUri{}",redirectUri);
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null,
grantTypeEnum.getGrantType(), null, redirectUri);
String currentToken = SecurityFrameworkUtils.obtainAuthorization(SsoConstants.COOKIE_TOKEN_KEY,request);
return success(getAuthorizationCodeRedirect(client, redirectUri, state,currentToken));
}
@PostMapping("/token")
@PermitAll
@Operation(summary = "获得访问令牌", description = "OAuth2授权码模式第二步-获取访问令牌")
@Parameters({
@Parameter(name = "grant_type", required = true, description = "授权类型", example = "authorization_code"),
@Parameter(name = "code", description = "授权码", example = "2f54a1f113d84130a867f559b7279365"),
@Parameter(name = "redirect_uri", description = "重定向 URI", example = "http://127.0.0.1"),
@Parameter(name = "client_id", description = "客户端ID", example = "2f54a1f113d84130a867f559b7279365"),
@Parameter(name = "client_secret", description = "客户端密钥", example = "2f54a1f113d84130a867f559b7279365"),
@Parameter(name = "refresh_token", example = "2f54a1f113d84130a867f559b7279365")
})
public CommonResult<OAuth2OpenAccessTokenRespVO> postAccessToken(@RequestParam("grant_type") String grantType,
@RequestParam(value = "code", required = false) String code,
@RequestParam(value = "redirect_uri", required = false) String redirectUri,
@RequestParam(value = "client_id") String clientId,
@RequestParam(value = "client_secret") String clientSecret,
@RequestParam(value = "refresh_token", required = false) String refreshToken) {
log.info("postAccessToken方法开启grant_type为"+grantType+"code="+code+"redirect_uri="+redirectUri+"client_id="+clientId+"client_secret="+clientSecret+"refresh_token="+refreshToken);
//校验授权类型
OAuth2GrantTypeEnum grantTypeEnum = OAuth2GrantTypeEnum.getByGranType(grantType);
log.info("授权类型"+ grantTypeEnum);
if (grantTypeEnum == null) {
throw exception0(BAD_REQUEST.getCode(), StrUtil.format("未知授权类型({})", grantType));
}
if (grantTypeEnum == OAuth2GrantTypeEnum.IMPLICIT) {
throw exception0(BAD_REQUEST.getCode(), "Token 接口不支持 implicit 授权模式");
}
log.info("开始校验客户端");
//校验客户端
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, clientSecret, grantType, null, redirectUri);
log.info("client返回值为"+client);
// 2. 根据授权模式获取访问令牌
OAuth2AccessTokenDO accessTokenDO;
switch (grantTypeEnum) {
case AUTHORIZATION_CODE:
accessTokenDO = oauth2GrantService.grantAuthorizationCodeForAccessToken(client, code, redirectUri);
log.info("授权类型为AUTHORIZATION_CODE,accessTokenDO返回值为"+accessTokenDO);
break;
case REFRESH_TOKEN:
accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, client.getClientid());
log.info("授权类型为REFRESH_TOKEN,accessTokenDO返回值为"+accessTokenDO);
break;
default:
throw new IllegalArgumentException("未知授权类型:" + grantType);
}
return success(OAuth2OpenConvert.INSTANCE.convert(accessTokenDO));
}
@PostMapping("/check-token")
@PermitAll
@Operation(summary = "校验访问令牌")
@Parameters({
@Parameter(name = "token", required = true, description = "访问令牌", example = "2f54a1f113d84130a867f559b7279365"),
@Parameter(name = "client_id", description = "客户端ID", example = "2f54a1f113d84130a867f559b7279365"),
@Parameter(name = "client_secret", description = "客户端密钥", example = "2f54a1f113d84130a867f559b7279365"),
})
public CommonResult<OAuth2OpenCheckTokenRespVO> checkToken(@RequestParam("token") String token,
@RequestParam(value = "client_id") String clientId,
@RequestParam(value = "client_secret") String clientSecret) {
// 校验客户端
oauth2ClientService.validOAuthClientFromCache(clientId,clientSecret, null, null, null);
// 校验令牌
final SessionInfo sessionInfo = oauth2TokenService.checkAccessToken(token);
Assert.notNull(sessionInfo, "访问令牌不能为空"); // 防御性检查
return success(OAuth2OpenConvert.INSTANCE.convert2(sessionInfo.getTokenInfo()));
}
@PostMapping("/remove-token")
@PermitAll
@Operation(summary = "删除访问令牌")
@Parameters({
@Parameter(name = "access_token", required = false, description = "访问令牌", example = "2f54a1f113d84130a867f559b7279365"),
@Parameter(name = "client_id", description = "客户端ID", example = "2f54a1f113d84130a867f559b7279365"),
@Parameter(name = "client_secret", description = "客户端密钥", example = "2f54a1f113d84130a867f559b7279365"),
})
public CommonResult<Boolean> revokeToken(HttpServletRequest request,
@RequestParam(value = "access_token", required = false) String token,
@RequestParam(value = "client_id") String clientId,
@RequestParam(value = "client_secret") String clientSecret) {
if (GyUtils.isNull(token)){
token = SecurityFrameworkUtils.obtainAuthorization(SsoConstants.COOKIE_TOKEN_KEY,request);
}
// 校验客户端
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, clientSecret, null, null, null);
// 删除访问令牌
return success(oauth2GrantService.revokeToken(client.getClientid(), token));
}
/*
@PostMapping("/refresh-token")
@PermitAll
@Operation(summary = "刷新访问令牌", description = "根据刷新令牌获取新的访问令牌")
@Parameters({
@Parameter(name = "redirect_uri", description = "重定向 URI", example = "http://127.0.0.1"),
@Parameter(name = "client_id", description = "客户端ID", example = "2f54a1f113d84130a867f559b7279365"),
@Parameter(name = "client_secret", description = "客户端密钥", example = "2f54a1f113d84130a867f559b7279365"),
@Parameter(name = "refresh_token", description = "刷新令牌", example = "2f54a1f113d84130a867f559b7279365")
})
public CommonResult<OAuth2OpenAccessTokenRespVO> refreshAccessToken(@RequestParam(value = "redirect_uri") String redirectUri,
@RequestParam(value = "client_id") String clientId,
@RequestParam(value = "client_secret") String clientSecret,
@RequestParam(value = "refresh_token") String refreshToken){
//校验客户端
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, clientSecret, null, null, redirectUri);
//获取访问令牌
final OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, client.getClientid());
return success(OAuth2OpenConvert.INSTANCE.convert(accessTokenDO));
}
*/
private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) {
if (StrUtil.equals(responseType, "code")) {
return OAuth2GrantTypeEnum.AUTHORIZATION_CODE;
}
if (StrUtil.equalsAny(responseType, "token")) {
return OAuth2GrantTypeEnum.IMPLICIT;
}
throw exception0(BAD_REQUEST.getCode(), "response_type 参数值只允许 code 和 token");
}
private String getAuthorizationCodeRedirect(OAuth2ClientDO client,String redirectUri, String state,String currentToken) {
// 1. 创建 code 授权码
String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(client.getClientid(), redirectUri, state,currentToken);
// 2. 拼接重定向的 URL
return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state);
}
private String[] obtainBasicAuthorization(HttpServletRequest request) {
return HttpUtils.obtainBasicAuthorization(request);
}
}

View File

@ -0,0 +1,214 @@
package com.css.txw.sso.controller.oauth2;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.css.txw.sso.convert.OAuth2OpenConvert;
import com.css.txw.sso.enums.OAuth2GrantTypeEnum;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
import com.css.txw.sso.pojo.dto.session.SessionInfo;
import com.css.txw.sso.pojo.vo.oauth2.OAuth2OpenAccessTokenRespVO;
import com.css.txw.sso.pojo.vo.oauth2.OAuth2OpenCheckTokenRespVO;
import com.css.txw.sso.service.oauth2.OAuth2ClientService;
import com.css.txw.sso.service.oauth2.OAuth2GrantService;
import com.css.txw.sso.service.oauth2.OAuth2TokenService;
import com.css.txw.sso.util.HttpUtils;
import com.css.txw.sso.util.OAuth2Utils;
import com.css.ggzc.framework.common.enums.UserTypeEnum;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.ggzc.framework.common.util.json.JsonUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.css.ggzc.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.css.ggzc.framework.common.pojo.CommonResult.success;
import static com.css.ggzc.framework.common.util.collection.CollectionUtils.convertList;
import static com.css.ggzc.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
/**
* 提供给外部应用调用为主
*/
@Tag(name = "管理后台 - OAuth2.0 授权")
@RestController
@RequestMapping("/open/oauth2")
@Validated
@Slf4j
public class OAuth2OpenController {
@Resource
private OAuth2GrantService oauth2GrantService;
@Resource
private OAuth2ClientService oauth2ClientService;
@Resource
private OAuth2TokenService oauth2TokenService;
/**
* 对应 Spring Security OAuth TokenEndpoint 类的 postAccessToken 方法
*
* 授权码 authorization_code 模式时code + redirectUri + state 参数
* 密码 password 模式时username + password + scope 参数
* 刷新 refresh_token 模式时refreshToken 参数
* 客户端 client_credentials 模式scope 参数
* 简化 implicit 模式时不支持
*
* 注意默认需要传递 client_id + client_secret 参数
*/
@PostMapping("/token")
@PermitAll
@Operation(summary = "获得访问令牌", description = "code 授权码模式")
@Parameters({
@Parameter(name = "grant_type", required = true, description = "授权类型", example = "code"),
@Parameter(name = "code", description = "授权范围", example = "userinfo.read"),
@Parameter(name = "redirect_uri", description = "重定向 URI", example = "https://www.iocoder.cn"),
@Parameter(name = "state", description = "状态", example = "1"),
@Parameter(name = "username", example = "tudou"),
@Parameter(name = "password", example = "cai"), // 多个使用空格分隔
@Parameter(name = "scope", example = "user_info"),
@Parameter(name = "refresh_token", example = "123424233"),
})
public CommonResult<OAuth2OpenAccessTokenRespVO> postAccessToken(HttpServletRequest request,
@RequestParam("grant_type") String grantType,
@RequestParam(value = "code", required = false) String code, // 授权码模式
@RequestParam(value = "redirect_uri", required = false) String redirectUri, // 授权码模式
@RequestParam(value = "state", required = false) String state, // 授权码模式
@RequestParam(value = "username", required = false) String username, // 密码模式
@RequestParam(value = "password", required = false) String password, // 密码模式
@RequestParam(value = "scope", required = false) String scope, // 密码模式
@RequestParam(value = "refresh_token", required = false) String refreshToken) { // 刷新模式
List<String> scopes = OAuth2Utils.buildScopes(scope);
// 1.1 校验授权类型
OAuth2GrantTypeEnum grantTypeEnum = OAuth2GrantTypeEnum.getByGranType(grantType);
if (grantTypeEnum == null) {
throw exception0(BAD_REQUEST.getCode(), StrUtil.format("未知授权类型({})", grantType));
}
// 1.2 校验客户端
String[] clientIdAndSecret = obtainBasicAuthorization(request);
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1],
grantType, scopes, redirectUri);
// 2. 根据授权模式获取访问令牌
OAuth2AccessTokenDO accessTokenDO = oauth2GrantService.grantAuthorizationCodeForAccessToken(client.getClientid(), code, redirectUri, state);
Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查
return success(OAuth2OpenConvert.INSTANCE.convert(accessTokenDO));
}
@DeleteMapping("/token")
@PermitAll
@Operation(summary = "删除访问令牌")
@Parameter(name = "token", required = true, description = "访问令牌", example = "biu")
public CommonResult<Boolean> revokeToken(HttpServletRequest request,
@RequestParam("token") String token) {
// 校验客户端
String[] clientIdAndSecret = obtainBasicAuthorization(request);
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1],
null, null, null);
// 删除访问令牌
return success(oauth2GrantService.revokeToken(client.getClientid(), token));
}
/**
* 对应 Spring Security OAuth CheckTokenEndpoint 类的 checkToken 方法
*/
@PostMapping("/check-token")
@PermitAll
@Operation(summary = "校验访问令牌")
@Parameter(name = "token", required = true, description = "访问令牌", example = "biu")
public CommonResult<OAuth2OpenCheckTokenRespVO> checkToken(HttpServletRequest request,
@RequestParam("token") String token) {
// 校验客户端
String[] clientIdAndSecret = obtainBasicAuthorization(request);
oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1],
null, null, null);
// 校验令牌
final SessionInfo sessionInfo = oauth2TokenService.checkAccessToken(token);
Assert.notNull(sessionInfo, "访问令牌不能为空"); // 防御性检查
return success(OAuth2OpenConvert.INSTANCE.convert2(sessionInfo.getTokenInfo()));
}
/**
* 对应 Spring Security OAuth AuthorizationEndpoint 类的 approveOrDeny 方法
* 因为前后端分离Axios 无法很好的处理 302 重定向所以和 Spring Security OAuth 略有不同返回结果是重定向的 URL剩余交给前端处理
*/
@PostMapping("/authorize")
@Operation(summary = "申请授权", description = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【提交】调用")
@Parameters({
@Parameter(name = "response_type", required = true, description = "响应类型", example = "code"),
@Parameter(name = "client_id", required = true, description = "客户端编号", example = "tudou"),
@Parameter(name = "scope", description = "授权范围", example = "userinfo.read"), // 使用 Map<String, Boolean> 格式Spring MVC 暂时不支持这么接收参数
@Parameter(name = "redirect_uri", required = true, description = "重定向 URI", example = "https://www.iocoder.cn"),
@Parameter(name = "auto_approve", required = true, description = "用户是否接受", example = "true"),
@Parameter(name = "state", example = "1")
})
public CommonResult<String> approveOrDeny(@RequestParam("Response_type") String responseType,
@RequestParam("Client_id") String clientId,
@RequestParam(value = "Scope", required = false) String scope,
@RequestParam("Redirect_uri") String redirectUri,
@RequestParam(value = "Auto_approve") Boolean autoApprove,
@RequestParam(value = "State", required = false) String state) {
@SuppressWarnings("unchecked")
Map<String, Boolean> scopes = JsonUtils.toBean(scope, Map.class);
scopes = ObjectUtil.defaultIfNull(scopes, new HashMap<String,Boolean>());
// 校验 responseType 是否满足 code 或者 token
OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType);
//校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内
OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null,
grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri);
// code 授权码模式则发放 code 授权码并重定向
List<String> approveScopes = convertList(scopes.entrySet(), Map.Entry::getKey, Map.Entry::getValue);
return success(getAuthorizationCodeRedirect(String.valueOf(getLoginUserId()), client, approveScopes, redirectUri, state));
}
private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) {
if (StrUtil.equals(responseType, "code")) {
return OAuth2GrantTypeEnum.AUTHORIZATION_CODE;
}
if (StrUtil.equalsAny(responseType, "token")) {
return OAuth2GrantTypeEnum.IMPLICIT;
}
throw exception0(BAD_REQUEST.getCode(), "response_type 参数值只允许 code 和 token");
}
private String getAuthorizationCodeRedirect(String userId, OAuth2ClientDO client,
List<String> scopes, String redirectUri, String state) {
// 1. 创建 code 授权码
String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId, getUserType(), client.getClientid(), scopes,
redirectUri, state);
// 2. 拼接重定向的 URL
return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state);
}
private Integer getUserType() {
return UserTypeEnum.ADMIN.getValue();
}
private String[] obtainBasicAuthorization(HttpServletRequest request) {
String[] clientIdAndSecret = HttpUtils.obtainBasicAuthorization(request);
if (ArrayUtil.isEmpty(clientIdAndSecret) || clientIdAndSecret.length != 2) {
throw exception0(BAD_REQUEST.getCode(), "client_id 或 client_secret 未正确传递");
}
return clientIdAndSecret;
}
}

View File

@ -0,0 +1,108 @@
package com.css.txw.sso.controller.oauth2;
import cn.hutool.core.bean.BeanUtil;
import com.css.ggzc.framework.common.util.gy.GyUtils;
import com.css.txw.sso.api.oauth2.OAuth2TokenApi;
import com.css.txw.sso.api.oauth2.dto.AccessTokenSessionInfo;
import com.css.txw.sso.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import com.css.txw.sso.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
import com.css.txw.sso.api.oauth2.dto.OAuth2AccessTokenRespDTO;
import com.css.txw.sso.constants.SsoApiConstants;
import com.css.txw.sso.constants.SsoConstants;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.txw.sso.pojo.dto.session.*;
import com.css.txw.sso.service.oauth2.OAuth2TokenService;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.ggzc.framework.common.util.object.BeanUtils;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static com.css.ggzc.framework.common.pojo.CommonResult.success;
@RequestMapping("/oauth2/token")
@RestController
@Validated
@Slf4j
public class OAuth2TokenController implements OAuth2TokenApi {
@Resource
private OAuth2TokenService oauth2TokenService;
@Override
@PostMapping("/create")
@Operation(description = "创建访问令牌")
public CommonResult<OAuth2AccessTokenRespDTO> createAccessToken(@Valid @RequestBody OAuth2AccessTokenCreateReqDTO reqDTO) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(
reqDTO.getUserId(), reqDTO.getClientId(), reqDTO.getScopes());
final OAuth2AccessTokenRespDTO bean = BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class);
bean.setUserId(accessTokenDO.getYhUuid());
bean.setExpiresTime(accessTokenDO.getGqsj());
return success(bean);
}
@GetMapping("/check")
@Override
public CommonResult<OAuth2AccessTokenCheckRespDTO> checkAccessToken(@RequestParam("accessToken") String accessToken) {
final SessionInfo sessionInfo = oauth2TokenService.checkAccessToken(accessToken);
log.info("/check getSessionfromRedis:{}",sessionInfo);
return success(covertSessionInfo(accessToken,sessionInfo));
}
@DeleteMapping("/remove")
@Override
public CommonResult<OAuth2AccessTokenRespDTO> removeAccessToken(@RequestParam("accessToken") String accessToken) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(SsoConstants.CLIENT_DEFAULT,accessToken);
final OAuth2AccessTokenRespDTO bean = BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class);
bean.setUserId(accessTokenDO.getYhUuid());
bean.setExpiresTime(accessTokenDO.getGqsj());
return success(bean);
}
@PutMapping("/refresh")
@Override
public CommonResult<OAuth2AccessTokenRespDTO> refreshAccessToken(@RequestParam("refreshToken") String refreshToken,
@RequestParam("clientId") String clientId) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId);
final OAuth2AccessTokenRespDTO bean = BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class);
bean.setUserId(accessTokenDO.getYhUuid());
bean.setExpiresTime(accessTokenDO.getGqsj());
return success(BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class));
}
private OAuth2AccessTokenCheckRespDTO covertSessionInfo(String accessToken,SessionInfo sessionInfo){
final OAuth2AccessTokenCheckRespDTO respDTO = new OAuth2AccessTokenCheckRespDTO();
final AccessTokenInfo tokenInfo = sessionInfo.getTokenInfo();
if (!accessToken.equals(tokenInfo.getAccessToken())){
respDTO.setHasRefreshedToken(true);
respDTO.setNewAccessToken(tokenInfo.getAccessToken());
respDTO.setNewExpireTime(tokenInfo.getGqsj());
}else {
respDTO.setHasRefreshedToken(false);
}
final AccessTokenSessionInfo accessTokenSessionInfo = new AccessTokenSessionInfo();
YhInfo yhxx = sessionInfo.getYhxx();
BeanUtil.copyProperties(yhxx,accessTokenSessionInfo,false);
accessTokenSessionInfo.setYhUuid(yhxx.getYhuuid());
String yhlx = sessionInfo.getCurrentYhlx();
if (!GyUtils.isNull(yhlx)){
accessTokenSessionInfo.setYhlx(yhlx);
}
accessTokenSessionInfo.setQyuuid(sessionInfo.getQyuuid());
accessTokenSessionInfo.setQymc(sessionInfo.getQymc());
accessTokenSessionInfo.setNsrsbh(sessionInfo.getNsrsbh());
log.info("/check接口返回sessionInfo:,{}",accessTokenSessionInfo);
respDTO.setSessionInfo(accessTokenSessionInfo);
return respDTO;
}
}

View File

@ -0,0 +1,34 @@
package com.css.txw.sso.controller.oauth2;
import com.css.txw.sso.pojo.vo.oauth2.OAuth2UserInfoRespVO;
import com.css.txw.sso.service.yhxx.YhxxService;
import com.css.ggzc.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import static com.css.ggzc.framework.common.pojo.CommonResult.success;
@Tag(name = "OAuth2用户信息")
@RestController
@RequestMapping("/userinfo")
@Slf4j
public class OAuth2UserController {
@Resource
private YhxxService yhxxService;
@PostMapping("/get")
@Operation(summary = "获得用户信息")
public CommonResult<OAuth2UserInfoRespVO> getUserInfo(HttpServletRequest request) {
return success(yhxxService.getUserInfo(request));
}
}

View File

@ -0,0 +1,39 @@
package com.css.txw.sso.controller.session;
import com.css.txw.sso.api.yhxx.SwitchSessionApi;
import com.css.txw.sso.pojo.yhxx.SwitchCompanyResDTO;
import com.css.txw.sso.service.session.SwitchSessionService;
import com.css.ggzc.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@Tag(name = "切换session信息")
@RestController
@RequestMapping("/switchSession")
@Slf4j
public class SwitchSessionController implements SwitchSessionApi {
@Resource
private SwitchSessionService switchSessionService;
@PostMapping("/company")
@Operation(summary = "切换机构")
@Override
public CommonResult<SwitchCompanyResDTO> switchCompany(@RequestParam("token")String token,@RequestParam("jguuid")String jguuid) {
return CommonResult.success(switchSessionService.switchCompany(token,jguuid));
}
@PostMapping("/addCompany")
@Operation(summary = "新增机构")
@Override
public CommonResult<String> addCompany(@RequestBody SwitchCompanyResDTO resDTO) {
return CommonResult.success(switchSessionService.addCompany(resDTO));
}
}

View File

@ -0,0 +1,860 @@
package com.css.txw.sso.controller.thirdparty;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.chainweaver.did.sdk.DidClient;
import org.chainweaver.did.sdk.IDidClient;
import org.chainweaver.did.sdk.model.AccessToken;
import org.chainweaver.did.sdk.model.Proof;
import org.chainweaver.did.sdk.model.VerifiableCredential;
import org.chainweaver.did.sdk.model.VerifiablePresentation;
import org.chainweaver.did.sdk.model.VerifiablePresentation.VerifiablePresentationBuilder;
import org.chainweaver.did.sdk.model.req.ApplyBusinessLicenseExtend;
import org.chainweaver.did.sdk.model.req.LoginExtend;
import org.chainweaver.did.sdk.model.resp.Response;
import org.springframework.web.bind.annotation.GetMapping;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.css.ggzc.framework.cache.utils.CacheUtils;
import com.css.ggzc.framework.cache.utils.XtcsUtils;
import com.css.ggzc.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.ggzc.framework.common.pojo.R;
import com.css.ggzc.framework.common.util.gy.GyUtils;
import com.css.ggzc.framework.common.util.json.JsonUtils;
import com.css.ggzc.framework.common.util.object.BeanUtils;
import com.css.ggzc.framework.common.util.servlet.ServletUtils;
import com.css.ggzc.framework.session.SessionUtils;
import com.css.txw.mhzc.pojo.YhxxbDTO;
import com.css.txw.sso.constants.SsoConstants;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
import com.css.txw.sso.pojo.vo.thirdparty.did.DidCallbackExtendInfo;
import com.css.txw.sso.pojo.vo.thirdparty.did.DidCredentialSubject;
import com.css.txw.sso.pojo.vo.thirdparty.did.DidExtendVcsInfo;
import com.css.txw.sso.pojo.vo.thirdparty.did.DidLoginExtend;
import com.css.txw.sso.pojo.vo.thirdparty.did.QrCodeInfo;
import com.css.txw.sso.service.oauth2.OAuth2TokenService;
import com.css.txw.sso.service.yhxx.YhxxService;
import cn.hutool.core.util.IdUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequestMapping("/did")
@Tag(name = "did接口对接")
@Slf4j
public class DidController {
/** 请求登录凭证的地址 */
public static final String XTCS_CODE_TXW_DID_LOGIN_VP_URL = "TXW_DID_LOGIN_VP_URL";
/** 请求企业实名凭证的地址 */
public static final String XTCS_CODE_TXW_DID_BUSI_LICE_VP_URL = "TXW_DID_BUSI_LICE_VP_URL";
/** 登录回调地址 */
public static final String XTCS_CODE_TXW_DID_LOGIN_CALLBACK_URL = "TXW_DID_LOGIN_CALLBACK_URL";
/** 企业实名认证回调地址 */
public static final String XTCS_CODE_TXW_DID_BUSI_LICE_CALLBACK_URL = "TXW_DID_BUSI_LICE_CALLBACK_URL";
/** DID控制台地址 */
public static final String XTCS_CODE_TXW_DID_CONSOLE_LOGIN_URL = "TXW_DID_CONSOLE_LOGIN_URL";
/** DID控制台登录用户名 */
public static final String XTCS_CODE_TXW_DID_CONSOLE_USERNAME = "TXW_DID_CONSOLE_USERNAME";
/** DID控制台登录密码 */
public static final String XTCS_CODE_TXW_DID_CONSOLE_PASSWORD = "TXW_DID_CONSOLE_PASSWORD";
/** 请求授权的平台的名称 */
public static final String XTCS_CODE_TXW_DID_AUTH_NAME = "TXW_DID_AUTH_NAME";
/** 请求授权的平台的英文名称 */
public static final String XTCS_CODE_TXW_DID_AUTH_EN_NAME = "TXW_DID_AUTH_EN_NAME";
public static final String XTCS_CODE_TXW_DID_LOGIN_TEMPLATE_ID = "TXW_DID_LOGIN_TEMPLATE_ID";
public static final String XTCS_CODE_TXW_DID_LOGIN_TEMPLATE_VERSION = "TXW_DID_LOGIN_TEMPLATE_VERSION";
/**
* 运营中心的 DID
*/
public static final String XTCS_CODE_TXW_DID_CONSOLE_DIDCODE="TXW_DID_CONSOLE_DIDCODE";
// TXW:DID:REQ:ID: 缓存请求标识前缀
/** 缓存请求标识前缀 */
public static final String REQ_ID_PREFIX = "TXW:DID:REQ:ID:";
/**
* 缓存用户信息前缀
*/
public static final String REQ_USER_DATA_PREFIX = "TXW:DID:USER:Data:";
/**
* 缓存用户did前缀
*/
public static final String REQ_USER_DID_PREFIX = "TXW:DID:USER:DID:";
/**
* 缓存did返回的用户信息前缀
*/
public static final String REQ_USER_DID_DATA_PREFIX = "TXW:DID:USER:DID:Data:";
public static final String REQ_USER_DID_DLZH_PREFIX = "TXW:DID:USER:DLZH:";
/**
* 页面请求
*/
public static final String DID_REQ_STATUS_PAGE = "1";
/**
* 扫描请求
*/
public static final String DID_REQ_STATUS_QR = "2";
/**
* 返回处理
*/
public static final String DID_REQ_STATUS_CALLBACK = "3";
/**
* 手机绑定
*/
public static final String DID_REQ_PHONE_BIND = "4";
/**
* did 认证失败
*/
public static final String DID_REQ_STATUS_BUSI_FAILURE = "5";
public static final String DID_REQ_STATUS_BUSI_FAILURE_MSG_PREFIX = "TXW:DID:BUSI:FAILURE:MSG:";
/**
* 认证成功
*/
public static final String DID_REQ_STATUS_BUSI_SUCCESS = "6";
public static final int ERROR_CODE_PARAM = 301001;
public static final long timeout = 1000 * 60 * 30;
@Resource
private YhxxService yhxxService;
@Resource
private OAuth2TokenService oauth2TokenService;
/**
* 获取登录二维码内容
*
* @name 获取登录二维码内容
* @time 2025-12-12
* @author 崔学志
* @history 修订历史历次修订内容修订人修订时间等
*/
@Operation(summary = "获取登录二维码内容")
@PostMapping("/pub/login/qrcode")
public CommonResult<QrCodeInfo> getLoginUrlQRCode() {
return getQrCode(XTCS_CODE_TXW_DID_LOGIN_VP_URL);
}
private CommonResult<QrCodeInfo> getQrCode(String xtcsCode) {
CommonResult<QrCodeInfo> result = CommonResult.success(null);
String url = XtcsUtils.getXtcs(xtcsCode);
if (GyUtils.isNull(url)) {
String msg = String.format("系统参数%s为空", xtcsCode);
log.info(msg);
result.setMsg(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
return result;
}
String reqId = GyUtils.getUuid() + GyUtils.getUuid();
String tmp = String.format("%s?reqId=%s", url, reqId);
QrCodeInfo info = QrCodeInfo.builder().reqId(reqId).url(tmp).build();
String key = String.format("%s%s", REQ_ID_PREFIX, reqId);
CacheUtils.cacheData(key, DID_REQ_STATUS_PAGE, timeout);
result.setData(info);
//缓存里设置登录账户
key = String.format("%s%s", REQ_USER_DID_DLZH_PREFIX, reqId);
String yhuuid = SessionUtils.getYhUuid();
CacheUtils.cacheData(key, yhuuid);
log.info("key:{},yhuuid:{}",key,yhuuid);
return result;
}
/**
* 获取认证二维码内容
*
* @name 获取认证二维码内容
* @time 2025-12-12
* @author 崔学志
* @history 修订历史历次修订内容修订人修订时间等
*/
@Operation(summary = "获取认证二维码内容")
@Parameter(name = "request", description = "请求报文")
@PostMapping("/busilice/qrcode")
public CommonResult<QrCodeInfo> getBusinessLicenseUrlQRCode() {
return getQrCode(XTCS_CODE_TXW_DID_BUSI_LICE_VP_URL);
}
/**
* 获取登录凭证信息
*
* @name 获取登录凭证信息
* @time 2025-12-12
* @author 崔学志
* @history 修订历史历次修订内容修订人修订时间等
*/
@Operation(summary = "获取登录凭证信息")
@Parameter(name = "request", description = "请求报文")
@GetMapping(value = "/pub/getvp/login", produces = "application/json")
public String getLoginVP(
@RequestParam(required = false, name = "reqId") String reqId) {
Response<VerifiablePresentation<DidLoginExtend>> result =Response.SuccessResponse();
// Map<String,Object> r =new HashMap<>();
if (GyUtils.isNull(reqId)) {
String msg = "reqId is null . ";
log.info(msg);
result.setMessage(msg);
result.setCode(ERROR_CODE_PARAM);
// return result;
return JsonUtils.toJson(result);
// return r;
}
String key = String.format("%s%s", REQ_ID_PREFIX, reqId);
String status = CacheUtils.getCacheData(key);
log.info("request {}, status :{}",reqId,status);
if (GyUtils.isNull(status)) {
String msg = String.format("该请求%s已过期", reqId);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
// return r;
}
if (!GyUtils.isEquals(status, DID_REQ_STATUS_PAGE)) {
String msg = String.format("该请求%s状态不正确请在页面上重新操作", reqId);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
CacheUtils.cacheData(key, DID_REQ_STATUS_QR);
String consoleUrl = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_LOGIN_URL);
// 新建客户端
IDidClient didClient = new DidClient(consoleUrl, DidClient.version15);
String username = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_USERNAME);
String pwd = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_PASSWORD);
// 登录
Response<AccessToken> login = didClient.login(username, pwd);
log.info("did控制台登录响应{}", login);
String url = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_LOGIN_CALLBACK_URL);
if (GyUtils.isNull(url)) {
String msg = String.format("系统参数%s为空", XTCS_CODE_TXW_DID_LOGIN_CALLBACK_URL);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
// return r;
}
String callbackUrl = String.format("%s?reqId=%s", url,reqId);
String authorizedName = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_AUTH_NAME);
if (GyUtils.isNull(authorizedName)) {
String msg = String.format("系统参数%s为空", XTCS_CODE_TXW_DID_AUTH_NAME);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
// return r;
}
String authorizedNameEn = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_AUTH_EN_NAME);
if (GyUtils.isNull(authorizedNameEn)) {
String msg = String.format("系统参数%s为空", XTCS_CODE_TXW_DID_AUTH_EN_NAME);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
// return r;
}
LoginExtend extend1 = LoginExtend.builder()
.requestId(reqId)
.method("POST")
.callbackUrl(callbackUrl)
.authorizedName(authorizedName)
.authorizedNameEn(authorizedNameEn)
.build();
DidLoginExtend extend = new DidLoginExtend();
BeanUtils.copyBean(extend1, extend);
List<DidExtendVcsInfo> vcs = new ArrayList<>();
String did = getXtcs(XTCS_CODE_TXW_DID_CONSOLE_DIDCODE);
// String issuer = "did:cndid:cndid";
String tmpid = getXtcs(XTCS_CODE_TXW_DID_LOGIN_TEMPLATE_ID);
// tmpid = "100752";
String version = getXtcs(XTCS_CODE_TXW_DID_LOGIN_TEMPLATE_VERSION);
// version = "v2";
// DidExtendVcsInfo vcsInfo = DidExtendVcsInfo.builder()
// .vctId(tmpid)
// .vctVersion(version)
// .issuer(did)
// .build();
// vcs.add(vcsInfo);
DidExtendVcsInfo vcsInfo = DidExtendVcsInfo.builder()
.vctId("100001")
.vctVersion("v9")
.issuer("did:cndid:cndid")
.build();
vcs.add(vcsInfo);
// vcsInfo = DidExtendVcsInfo.builder()
// .vctId("100000")
// .vctVersion("v7")
// .issuer("did:cndid:cndid")
// .build();
vcs.add(vcsInfo);
extend.setVcs(vcs);
List<Proof> proofList = new ArrayList<Proof>();
// VerifiablePresentation<DidLoginExtend> loginvp = new VerifiablePresentation<DidLoginExtend>();
VerifiablePresentationBuilder<DidLoginExtend> verifiablePresentationBuilder = VerifiablePresentation.builder();
verifiablePresentationBuilder.presentationUsage("DID_LOGIN_REQUEST");
verifiablePresentationBuilder.proof(proofList);
verifiablePresentationBuilder.extend(extend);
VerifiablePresentation<DidLoginExtend> loginvp = verifiablePresentationBuilder.build();
Response<VerifiablePresentation<DidLoginExtend>> res = didClient.vpSign(loginvp, DidLoginExtend.class);
log.info("res:{}", res);
String ret = JsonUtils.toJson(res.getData());
log.info("ret:{}", ret);
Response<Boolean> veri = didClient.vpVerify(ret);
log.info("vpVerify:{}", veri);
// veri = didClient.vpVerify(ret);
// r.put("code",200000);
// r.put("msg","ok");
// r.put("data", res.getData());
// log.info("return:{}", JsonUtils.toJson(r));
// return JsonUtils.toJson(r);
return JsonUtils.toJson(res);
}
private String getXtcs(String xtcsCode) {
return getXtcs(xtcsCode,null);
}
private String getXtcs(String xtcsCode, String prefix) {
if (GyUtils.isNull(xtcsCode)) {
return null;
}
String code =xtcsCode;
if (GyUtils.isNotNull(prefix)) {
code = xtcsCode.replaceFirst("TXW", prefix);
}
return XtcsUtils.getXtcs(code);
}
/**
* 获取登录凭证信息返回调用
*
* @name 获取登录凭证信息返回调用
* @time 2025-12-12
* @author 崔学志
* @history 修订历史历次修订内容修订人修订时间等
*/
@Operation(summary = "获取登录凭证信息返回调用")
@Parameter(name = "request", description = "请求报文")
@PostMapping(value = "/pub/callback/login", produces = "application/json")
public String getLoginVPCallback(@RequestParam(required = false, name = "reqId") String reqId,
@RequestBody Map<String, Object> vpRequestBody) {
Response<String> result =Response.SuccessResponse();
result.setCode(200000);
if (GyUtils.isNull(vpRequestBody)) {
String msg = "vpRequestBody is null . ";
log.info(msg);
result.setMessage(msg);
result.setCode(ERROR_CODE_PARAM);
// return result;
return JsonUtils.toJson(result);
}
VerifiablePresentation request = BeanUtils.toBean(vpRequestBody, VerifiablePresentation.class);
log.info("request 参数:{}",request);
String checkMsg = checkvp(request);
if (GyUtils.isNotNull(checkMsg)) {
log.info(checkMsg);
result.setMessage(checkMsg);
result.setCode(ERROR_CODE_PARAM);
// return result;
return JsonUtils.toJson(result);
}
result.setCode(200000);
result.setData("true");
if (GyUtils.isNull(reqId)) {
String msg = "reqId is null . ";
log.info(msg);
result.setMessage(msg);
result.setCode(ERROR_CODE_PARAM);
// return result;
return JsonUtils.toJson(result);
}
if (GyUtils.isNull(request)) {
String msg = "request is null . ";
log.info(msg);
result.setMessage(msg);
result.setCode(ERROR_CODE_PARAM);
// return result;
return JsonUtils.toJson(result);
}
String key = String.format("%s%s", REQ_ID_PREFIX, reqId);
String status = CacheUtils.getCacheData(key);
log.info("request {}, status :{}",reqId,status);
if (GyUtils.isNull(status)) {
String msg = String.format("该请求%s已过期", reqId);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
if (!GyUtils.isEquals(status, DID_REQ_STATUS_QR)) {
String msg = String.format("该请求%s状态不正确请在页面上重新操作", reqId);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
CacheUtils.cacheData(key, DID_REQ_STATUS_CALLBACK);
List<VerifiableCredential> vcList = request.getVerifiableCredential();
if (GyUtils.isNotNull(vcList)) {
VerifiableCredential vc = vcList.get(0);
String holderDid = vc.getHolder();
DidCallbackExtendInfo extInfo = BeanUtils.toBean(request.getExtend(), DidCallbackExtendInfo.class);
DidCredentialSubject cs = BeanUtils.toBean(vc.getCredentialSubject(), DidCredentialSubject.class);
YhxxbDTO yhxx = null;
String dlzh = cs.getPhone(); // 获取手机号
if (GyUtils.isNotNull(dlzh)) {
yhxx = yhxxService.getYhxxByDlzh(dlzh);
}
else if (GyUtils.isNotNull(extInfo) && GyUtils.isNotNull(extInfo.getAuthoriserIdCard())) {
yhxx = yhxxService.getYhxxBySfzjhm((extInfo.getAuthoriserIdCard()));
}
else {
yhxx = yhxxService.getYhxxByDid(holderDid);
}
//手机号码为空 进行页面绑定
if (GyUtils.isNull(yhxx) && GyUtils.isNull(dlzh)) {
//缓存did 一次请求对应一个did
String tmp = String.format("%s%s", REQ_USER_DID_PREFIX,reqId);
CacheUtils.cacheData(tmp, holderDid,timeout);
//缓存did数据为创建账号做准备
cs.setExtInfo(extInfo);
tmp = String.format("%s%s", REQ_USER_DID_DATA_PREFIX,reqId);
CacheUtils.cacheData(tmp, JsonUtils.toJson(cs),timeout);
//进行手机绑定
key = String.format("%s%s", REQ_ID_PREFIX, reqId);
CacheUtils.cacheData(key, DID_REQ_PHONE_BIND,timeout);
}
//手机号不为空 直接加入账户数据
else if (GyUtils.isNull(yhxx) && GyUtils.isNotNull(dlzh)) {
yhxx = yhxxService.getYhxxBySjhm(dlzh);
if (GyUtils.isNull(yhxx)) {
yhxx = new YhxxbDTO();
yhxx.setYhUuid(IdUtil.fastSimpleUUID());
yhxx.setDid(holderDid);
yhxx.setSjhm1(dlzh);
yhxx.setDlzh(dlzh);
if (GyUtils.isNotNull(cs.getLegalName())) {
yhxx.setZsxm1(cs.getLegalName());
}
else if (GyUtils.isNotNull(cs.getEntname())) {
yhxx.setZsxm1(cs.getEntname());
}
else {
yhxx.setZsxm1("--");
}
if (GyUtils.isNotNull(extInfo)) {
yhxx.setSfzjhm(extInfo.getAuthoriserIdCard());
}
yhxx = yhxxService.saveYhxxByDid(yhxx);
}
else {
yhxx.setDid(holderDid);
yhxxService.updateDid(yhxx);
}
// 企业认证成功
YhxxbDTO qyxx = new YhxxbDTO();
qyxx.setQymc(cs.getEntname());
qyxx.setNsrsbh(cs.getUniscid());
qyxx.setYhUuid(yhxx.getYhUuid());
log.info("qyxx:{}",qyxx);
qyxx = yhxxService.intQyxxByDid(qyxx);
String tmp = String.format("%s%s", REQ_USER_DATA_PREFIX,reqId);
CacheUtils.cacheData(tmp, JsonUtils.toJson(yhxx),timeout);
}
else {
String tmp = String.format("%s%s", REQ_USER_DATA_PREFIX,reqId);
CacheUtils.cacheData(tmp, JsonUtils.toJson(yhxx),timeout);
}
}
else {
String msg = String.format("返回参数VerifiableCredential为空");
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
// return result;
return JsonUtils.toJson(result);
}
private String checkvp(VerifiablePresentation request) {
if (GyUtils.isNull(request)) {
return null;
}
String consoleUrl = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_LOGIN_URL);
// 新建客户端
IDidClient didClient = new DidClient(consoleUrl, DidClient.version15);
String username = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_USERNAME);
String pwd = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_PASSWORD);
// 登录
Response<AccessToken> login = didClient.login(username, pwd);
Response<Boolean> veri = didClient.vpVerify(request);
log.info("checkvp veri:{}",veri);
if (GyUtils.isNotNull(veri) && GyUtils.isNotNull(veri.getData())
&& veri.getData()) {
return null;
}
else {
return null;
// return veri.getMessage();
}
}
/**
* 获取企业实名凭证信息
*
* @name 获取企业实名凭证信息
* @time 2025-12-12
* @author 崔学志
* @history 修订历史历次修订内容修订人修订时间等
*/
@Operation(summary = "获取企业实名凭证信息")
@Parameter(name = "request", description = "请求报文")
@GetMapping(value ="/pub/getvp/busi", produces = "application/json")
public String getBusinessLicenseVP(
@RequestParam(required = false, name = "reqId") String reqId) {
Response<VerifiablePresentation<ApplyBusinessLicenseExtend>> result =Response.SuccessResponse();
if (GyUtils.isNull(reqId)) {
String msg = "reqId is null . ";
log.info(msg);
result.setMessage(msg);
result.setCode(ERROR_CODE_PARAM);
// return result;
return JsonUtils.toJson(result);
}
String key = String.format("%s%s", REQ_ID_PREFIX, reqId);
String status = CacheUtils.getCacheData(key);
log.info("request {}, status :{}",reqId,status);
if (GyUtils.isNull(status)) {
String msg = String.format("该请求%s已过期", reqId);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
if (!GyUtils.isEquals(status, DID_REQ_STATUS_PAGE)) {
String msg = String.format("该请求%s状态不正确请在页面上重新操作", reqId);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
CacheUtils.cacheData(key, DID_REQ_STATUS_QR);
String consoleUrl = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_LOGIN_URL);
// 新建客户端
IDidClient didClient = new DidClient(consoleUrl, DidClient.version15);
String username = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_USERNAME);
String pwd = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_PASSWORD);
// 登录
Response<AccessToken> login = didClient.login(username, pwd);
log.info("did控制台登录响应{}", login);
List<Proof> proofList = new ArrayList<Proof>();
// Proof pr = Proof.builder()
// .build();
VerifiablePresentation<ApplyBusinessLicenseExtend> busivp = VerifiablePresentation.vpCreateApplyBusinessLicense(proofList);
busivp.setPresentationUsage("DID_GET_IDENTITY_VC_REQUEST");
String url = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_BUSI_LICE_CALLBACK_URL);
if (GyUtils.isNull(url)) {
String msg = String.format("系统参数%s为空", XTCS_CODE_TXW_DID_LOGIN_CALLBACK_URL);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
String callbackUrl = String.format("%s?reqId=%s", url,reqId);
String authorizedName = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_AUTH_NAME);
if (GyUtils.isNull(authorizedName)) {
String msg = String.format("系统参数%s为空", XTCS_CODE_TXW_DID_AUTH_NAME);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
String authorizedNameEn = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_AUTH_EN_NAME);
if (GyUtils.isNull(authorizedNameEn)) {
String msg = String.format("系统参数%s为空", XTCS_CODE_TXW_DID_AUTH_EN_NAME);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
List<String> vctId = Arrays.asList("100001");
String condid = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_DIDCODE);
condid = "did:cndid:cndid";
List<String> issuer = Arrays.asList(condid);
ApplyBusinessLicenseExtend extend = ApplyBusinessLicenseExtend.builder()
.requestId(reqId)
.method("POST")
.callbackUrl(callbackUrl)
.authorizedName(authorizedName)
.authorizedNameEn(authorizedNameEn)
.vctId(vctId)
.issuer(issuer)
.build();
busivp.setExtend(extend);
Response<VerifiablePresentation<ApplyBusinessLicenseExtend>> res = didClient.vpCreateApplyBusinessLicense(busivp);
log.info("res:{}", res);
// return res;
Response<Boolean> ver = didClient.vpVerify(res.getData());
log.info("ver:{}", ver);
return JsonUtils.toJson(res);
}
/**
* 获取企业实名凭证信息返回调用
*
* @name 获取企业实名凭证信息返回调用
* @time 2025-12-12
* @author 崔学志
* @history 修订历史历次修订内容修订人修订时间等
*/
@Operation(summary = "获取企业实名凭证信息返回调用")
@Parameter(name = "request", description = "请求报文")
@PostMapping(value = "/pub/callback/busi", produces = "application/json")
public String getBusinessLicenseVPCallback(
@RequestParam(required = false, name = "reqId") String reqId,
@RequestBody VerifiablePresentation request) {
log.info("request 参数:{}",request);
Response<String> result =Response.SuccessResponse();
result.setCode(200000);
result.setData("true");
if (GyUtils.isNull(reqId)) {
String msg = "reqId is null . ";
log.info(msg);
result.setMessage(msg);
result.setCode(ERROR_CODE_PARAM);
// return result;
return JsonUtils.toJson(result);
}
if (GyUtils.isNull(request)) {
String msg = "request is null . ";
log.info(msg);
result.setMessage(msg);
result.setCode(ERROR_CODE_PARAM);
// return result;
return JsonUtils.toJson(result);
}
String checkMsg = checkvp(request);
if (GyUtils.isNotNull(checkMsg)) {
log.info(checkMsg);
result.setMessage(checkMsg);
result.setCode(ERROR_CODE_PARAM);
// return result;
return JsonUtils.toJson(result);
}
String key = String.format("%s%s", REQ_ID_PREFIX, reqId);
String status = CacheUtils.getCacheData(key);
log.info("request {}, status :{}",reqId,status);
if (GyUtils.isNull(status)) {
String msg = String.format("该请求%s已过期", reqId);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
if (!GyUtils.isEquals(status, DID_REQ_STATUS_QR)) {
String msg = String.format("该请求%s状态不正确请在页面上重新操作", reqId);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
CacheUtils.cacheData(key, DID_REQ_STATUS_CALLBACK);
List<VerifiableCredential> vcList = request.getVerifiableCredential();
if (GyUtils.isNotNull(vcList)) {
VerifiableCredential vc = vcList.get(0);
String holderDid = vc.getHolder();
log.info("holderDid:{}",holderDid);
DidCallbackExtendInfo extInfo = BeanUtils.toBean(request.getExtend(), DidCallbackExtendInfo.class);
DidCredentialSubject cs = BeanUtils.toBean(vc.getCredentialSubject(), DidCredentialSubject.class);
log.info("cs:{}",cs);
YhxxbDTO yhxx = yhxxService.getYhxxByDid(holderDid);
if (GyUtils.isNull(yhxx)) {
String tmp = String.format("%s%s", REQ_USER_DID_DLZH_PREFIX, reqId);
log.info("key:{}",tmp);
String yhuuid = CacheUtils.getCacheData(tmp);
log.info("yhuuid:{}",yhuuid);
yhxx = yhxxService.getYhxx(yhuuid);
if (GyUtils.isNotNull(yhxx)) {
yhxx.setDid(holderDid);
yhxxService.updateDid(yhxx);
}
}
else {
String msg = String.format("已经进行过实名认证。不能进行重复认证!");
CacheUtils.cacheData(key, DID_REQ_STATUS_BUSI_FAILURE);
CacheUtils.cacheData(DID_REQ_STATUS_BUSI_FAILURE_MSG_PREFIX+reqId, msg);
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
log.info("yhxx:{}",yhxx);
if (GyUtils.isNull(yhxx)) {
CacheUtils.cacheData(key, DID_REQ_STATUS_BUSI_FAILURE);
CacheUtils.cacheData(DID_REQ_STATUS_BUSI_FAILURE_MSG_PREFIX+reqId, "根据did未查询到数据");
}
else {
if (GyUtils.isNotNull(cs) ) {
if (GyUtils.isEquals(cs.getEntname(), yhxx.getZsxm1())
|| GyUtils.isEquals(cs.getLegalName(), yhxx.getZsxm1())) {
CacheUtils.cacheData(key, DID_REQ_STATUS_BUSI_SUCCESS);
YhxxbDTO qyxx = new YhxxbDTO();
qyxx.setQymc(cs.getEntname());
qyxx.setNsrsbh(cs.getUniscid());
qyxx.setYhUuid(yhxx.getYhUuid());
log.info("qyxx:{}",qyxx);
qyxx = yhxxService.intQyxxByDid(qyxx);
}
else {
CacheUtils.cacheData(key, DID_REQ_STATUS_BUSI_FAILURE);
CacheUtils.cacheData(DID_REQ_STATUS_BUSI_FAILURE_MSG_PREFIX+reqId, "名称不一致!");
}
}
else {
CacheUtils.cacheData(key, DID_REQ_STATUS_BUSI_FAILURE);
CacheUtils.cacheData(DID_REQ_STATUS_BUSI_FAILURE_MSG_PREFIX+reqId, "根据did未查询到数据");
}
}
}
else {
CacheUtils.cacheData(key, DID_REQ_STATUS_BUSI_FAILURE);
CacheUtils.cacheData(DID_REQ_STATUS_BUSI_FAILURE_MSG_PREFIX+reqId, "返回参数VerifiableCredential为空");
String msg = String.format("返回参数VerifiableCredential为空");
log.info(msg);
result.setMessage(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
// return result;
return JsonUtils.toJson(result);
}
// return result;
return JsonUtils.toJson(result);
}
/**
* 获取登录回调响应结果
*
* @name 获取登录回调响应结果
* @time 2025-12-12
* @author 崔学志
* @history 修订历史历次修订内容修订人修订时间等
*/
@Operation(summary = "获取登录回调响应结果")
@Parameter(name = "request", description = "请求报文")
@GetMapping("/pub/backresult/login")
public CommonResult<R> getLoginCallbackResult(@RequestParam(required = false, name = "reqId") String reqId) {
CommonResult<R> result = CommonResult.success(null);
if (GyUtils.isNull(reqId)) {
String msg = "reqId is null . ";
log.info(msg);
result.setMsg(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
return result;
}
String key = String.format("%s%s", REQ_ID_PREFIX, reqId);
String status = CacheUtils.getCacheData(key);
// status = "3";
R r = R.ok().put("status", status);
key = String.format("%s%s", REQ_USER_DATA_PREFIX, reqId);
String userJson = CacheUtils.getCacheData(key);
if (GyUtils.isNotNull(userJson)) {
YhxxbDTO yhxx = JsonUtils.toBean(userJson, YhxxbDTO.class);
String userId = yhxx.getYhUuid();
// 创建访问令牌
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, "default", null);
// 构建返回结果
final AuthLoginRespVO respVO = new AuthLoginRespVO();
respVO.setAccessToken(accessTokenDO.getAccessToken());
respVO.setRefreshToken(accessTokenDO.getRefreshToken());
respVO.setExpiresTime(accessTokenDO.getGqsj());
AuthLoginRespVO login = respVO;
final Cookie cookie = new Cookie(SsoConstants.COOKIE_TOKEN_KEY,login.getAccessToken());
cookie.setPath(SsoConstants.COOKIE_PATH);
cookie.setHttpOnly(true);
//cookie.setMaxAge(-1);
final HttpServletResponse response = ServletUtils.getResponse();
response.addCookie(cookie);
// return success(login);
r.put("authLoginRespVO", login);
}
result.setData(r);
return result;
}
/**
* 获取认证回调响应结果
*
* @name 获取认证回调响应结果
* @time 2025-12-12
* @author 崔学志
* @history 修订历史历次修订内容修订人修订时间等
*/
@Operation(summary = "获取认证回调响应结果")
@Parameter(name = "request", description = "请求报文")
@GetMapping("/pub/backresult/busi")
public CommonResult<R> getBusiLiceCallbackResult(@RequestParam(required = false, name = "reqId") String reqId) {
CommonResult<R> result = CommonResult.success(null);
if (GyUtils.isNull(reqId)) {
String msg = "reqId is null . ";
log.info(msg);
result.setMsg(msg);
result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
return result;
}
String key = String.format("%s%s", REQ_ID_PREFIX, reqId);
String status = CacheUtils.getCacheData(key);
String msg = CacheUtils.getCacheData(DID_REQ_STATUS_BUSI_FAILURE_MSG_PREFIX+reqId);
// status = "6";
R r = R.ok().put("status", status)
.put("msg", msg);
result.setData(r);
return result;
}
}

View File

@ -0,0 +1,81 @@
package com.css.txw.sso.controller.thirdparty;//package com.css.lsl.sso.controller.thirdparty;
//
//
//import cn.hutool.crypto.SecureUtil;
//import cn.hutool.http.HttpRequest;
//import cn.hutool.http.HttpResponse;
//import cn.hutool.http.HttpStatus;
//import com.alibaba.fastjson.JSONObject;
//import com.css.znsb.framework.common.pojo.CommonResult;
//import com.css.ckts.sso.pojo.vo.thirdparty.GssbTokenVO;
//import com.css.ckts.sso.util.AesUtil;
//import io.swagger.v3.oas.annotations.Operation;
//import io.swagger.v3.oas.annotations.tags.Tag;
//import java.net.URLEncoder;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.web.bind.annotation.PostMapping;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//
//@Tag(name = "对接个税申报系统")
//@RestController
//@RequestMapping("/gssb")
//@Slf4j
//public class GssbController {
//
// //租户ID 平台分配
// private static String tenantId = "zrgssy_admin";
// //用户名 平台分配
// private static String username = "12586555655";
// //密码 平台分配
// private static String password = "tKlkv*oM";
//
// // 固定的
// private static String authorization = "Basic bWFnaWNjdWJlX2ludm9pY2U6bWFnaWNjdWJlX2ludm9pY2Vfc2VjcmV0";
// // 密码AES加密秘钥
// private static String aesKey = "O2BEeIv399qHQNhD6aGW8R8DEj4bqHXm";
//
// // 平台根url
// private static String baseUrl = "https://zr.jcsk100.com/api/";
//
// // token url
// private static String tokenUrl = "cube-auth/oauth/token";
//
//
//
// @Operation(summary = "获取token", description = "密码模式与个税申报系统对接")
// @PostMapping("/getToken")
// public CommonResult<GssbTokenVO> getToken(){
// final GssbTokenVO gssbTokenVO = new GssbTokenVO();
// HttpResponse response = reqSaasToken();
// log.info("获取 Saas 授权信息 body{}", response.body());
// if (response.getStatus() == HttpStatus.HTTP_OK) {
// String body = response.body();
// log.info("Gssb Response->{}",body);
// JSONObject responseJson = JSONObject.parseObject(body);
// String accessToken = responseJson.getString("access_token");
// String refreshToken = responseJson.getString("refresh_token");
// gssbTokenVO.setAccessToken(accessToken);
// gssbTokenVO.setRefreshToken(refreshToken);
// }
// gssbTokenVO.setTenantId(tenantId);
// return CommonResult.success(gssbTokenVO);
// }
//
//
// public static HttpResponse reqSaasToken() {
// String param = "?tenantId=" + tenantId + "&username=" + username + "&password=" + URLEncoder.encode(encodeAes(SecureUtil.md5(password))) + "&grant_type=password&scope=all&type=account";
// String url = baseUrl + tokenUrl + param;
//
// return HttpRequest.post(url)
// .header("Content-Type", "application/json")
// .header("Authorization", authorization)
// .header("Tenant-Id", tenantId)
// .execute();
// }
//
// public static String encodeAes(String txt) {
// return AesUtil.encryptToBase64(txt, aesKey);
// }
//
//}

View File

@ -0,0 +1,44 @@
package com.css.txw.sso.controller.verify;
import cn.hutool.core.util.StrUtil;
import com.css.txw.sso.service.verify.VerifyService;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.ggzc.framework.common.util.servlet.ServletUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
@Tag(name = "验证码")
@RestController
@RequestMapping("/verify")
public class VerifyController {
@Resource
private VerifyService verifyService;
@Operation(summary = "获取验证码")
@PermitAll
@PostMapping("/get")
public CommonResult<String> check(HttpServletRequest request){
final String remoteId = getRemoteId(request);
return verifyService.getVerifyToken(remoteId);
}
public static String getRemoteId(HttpServletRequest request) {
String ip = ServletUtils.getClientIP(request);
String ua = request.getHeader("user-agent");
if (StrUtil.isNotBlank(ip)) {
return ip + ua;
}
return request.getRemoteAddr() + ua;
}
}

View File

@ -0,0 +1,17 @@
package com.css.txw.sso.convert;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface AuthConvert {
AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
AuthLoginRespVO convert(OAuth2AccessTokenDO bean);
}

View File

@ -0,0 +1,35 @@
package com.css.txw.sso.convert;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.css.ggzc.framework.common.util.object.BeanUtils;
import com.css.txw.sso.enums.UserTypeEnum;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.txw.sso.pojo.dto.session.AccessTokenInfo;
import com.css.txw.sso.pojo.vo.oauth2.OAuth2OpenAccessTokenRespVO;
import com.css.txw.sso.pojo.vo.oauth2.OAuth2OpenCheckTokenRespVO;
import com.css.txw.sso.util.OAuth2Utils;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface OAuth2OpenConvert {
OAuth2OpenConvert INSTANCE = Mappers.getMapper(OAuth2OpenConvert.class);
default OAuth2OpenAccessTokenRespVO convert(OAuth2AccessTokenDO bean) {
OAuth2OpenAccessTokenRespVO respVO = BeanUtils.toBean(bean, OAuth2OpenAccessTokenRespVO.class);
//respVO.setTokenType(SecurityFrameworkUtils.AUTHORIZATION_BEARER.toLowerCase());
respVO.setExpiresIn(OAuth2Utils.getExpiresIn(bean.getGqsj()));
//respVO.setScope(OAuth2Utils.buildScopeStr(bean.getScopes()));
return respVO;
}
default OAuth2OpenCheckTokenRespVO convert2(AccessTokenInfo bean) {
OAuth2OpenCheckTokenRespVO respVO = BeanUtils.toBean(bean, OAuth2OpenCheckTokenRespVO.class);
respVO.setExp(LocalDateTimeUtil.toEpochMilli(bean.getGqsj()) / 1000L);
respVO.setUserType(UserTypeEnum.ADMIN.getValue());
return respVO;
}
}

View File

@ -0,0 +1,40 @@
package com.css.txw.sso.enums;
import cn.hutool.core.util.ObjUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 通用状态枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum CommonStatusEnum {
ENABLE(0, "开启"),
DISABLE(1, "关闭");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray();
/**
* 状态值
*/
private final Integer status;
/**
* 状态名
*/
private final String name;
public static boolean isEnable(Integer status) {
return ObjUtil.equal(ENABLE.status, status);
}
public static boolean isDisable(Integer status) {
return ObjUtil.equal(DISABLE.status, status);
}
}

View File

@ -0,0 +1,34 @@
package com.css.txw.sso.enums;
import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 全局用户类型枚举
*/
@AllArgsConstructor
@Getter
public enum UserTypeEnum {
MEMBER(1, "会员"), // 面向 c 普通用户
ADMIN(2, "管理员"); // 面向 b 管理后台
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray();
/**
* 类型
*/
private final Integer value;
/**
* 类型名
*/
private final String name;
public static UserTypeEnum valueOf(Integer value) {
return ArrayUtil.firstMatch(userType -> userType.getValue().equals(value), UserTypeEnum.values());
}
}

View File

@ -0,0 +1 @@
# 定时任务

View File

@ -0,0 +1,70 @@
package com.css.txw.sso.mapper.oauth2;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.css.ggzc.framework.common.util.gy.GyUtils;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.ggzc.framework.mybatis.core.mapper.BaseMapperX;
import com.css.ggzc.framework.mybatis.core.query.QueryWrapperX;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO> {
default OAuth2AccessTokenDO selectByAccessToken(String accessToken) {
return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken);
}
default List<OAuth2AccessTokenDO> selectByYhuuid(String yhuuid) {
return selectList(OAuth2AccessTokenDO::getYhUuid, yhuuid);
}
default List<OAuth2AccessTokenDO> selectRefreshTokenByAccessTokens(List<String> accessTokens) {
QueryWrapperX<OAuth2AccessTokenDO> wrapperX = new QueryWrapperX<>();
wrapperX.lambda()
// .select(OAuth2AccessTokenDO::getRefreshToken)
.in(OAuth2AccessTokenDO::getAccessToken, accessTokens);
return selectList(wrapperX);
}
default List<OAuth2AccessTokenDO> selectListByRefreshToken(String refreshToken) {
return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
}
default Integer updateCompany(String accessToken,String jguuid){
UpdateWrapper<OAuth2AccessTokenDO> updateWrapper = new UpdateWrapper<>();
if (!GyUtils.isNull(jguuid)){
updateWrapper.set("qyuuid",jguuid);
}
updateWrapper.set("xgrq", LocalDateTime.now());
updateWrapper.set("xgrsfid","SSO");
updateWrapper.eq("access_token",accessToken);
return update(updateWrapper);
}
default Integer updateGllp(String uuid,String gllp){
LambdaUpdateWrapper<OAuth2AccessTokenDO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(OAuth2AccessTokenDO::getGllp,gllp)
.eq(OAuth2AccessTokenDO::getUuid,uuid);
return update(updateWrapper);
}
default Integer deleteByTokens(List<String> tokens){
QueryWrapperX<OAuth2AccessTokenDO> wrapperX = new QueryWrapperX<>();
wrapperX.lambda().in(OAuth2AccessTokenDO::getAccessToken, tokens);
return delete(wrapperX);
}
default Integer updateGllpByTokens(List<String> tokens,String gllp){
LambdaUpdateWrapper<OAuth2AccessTokenDO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(OAuth2AccessTokenDO::getGllp,gllp)
.in(OAuth2AccessTokenDO::getUuid,tokens);
return update(updateWrapper);
}
}

View File

@ -0,0 +1,21 @@
package com.css.txw.sso.mapper.oauth2;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
import com.css.ggzc.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
/**
* OAuth2 客户端 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface OAuth2ClientMapper extends BaseMapperX<OAuth2ClientDO> {
default OAuth2ClientDO selectByClientId(String clientId) {
return selectOne(OAuth2ClientDO::getClientid, clientId);
}
}

View File

@ -0,0 +1,14 @@
package com.css.txw.sso.mapper.oauth2;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2CodeDO;
import com.css.ggzc.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface OAuth2CodeMapper extends BaseMapperX<OAuth2CodeDO> {
default OAuth2CodeDO selectByCode(String code) {
return selectOne(OAuth2CodeDO::getSqm, code);
}
}

View File

@ -0,0 +1,40 @@
package com.css.txw.sso.mapper.oauth2;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2RefreshTokenDO;
import com.css.ggzc.framework.mybatis.core.mapper.BaseMapperX;
import com.css.ggzc.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.css.ggzc.framework.mybatis.core.query.QueryWrapperX;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshTokenDO> {
default int deleteByRefreshToken(String refreshToken) {
return delete(new LambdaQueryWrapperX<OAuth2RefreshTokenDO>()
.eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken));
}
default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) {
return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
}
default Integer updateJguuid(String refreshToken,String currentJguuid){
UpdateWrapper<OAuth2RefreshTokenDO> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("jguuid_1",currentJguuid);
updateWrapper.set("xgrq", LocalDateTime.now());
updateWrapper.set("xgrsfid","SSO");
updateWrapper.eq("refresh_token",refreshToken);
return update(updateWrapper);
}
default Integer deleteByTokens(List<String> tokens){
QueryWrapperX<OAuth2RefreshTokenDO> wrapperX = new QueryWrapperX<>();
wrapperX.lambda().in(OAuth2RefreshTokenDO::getRefreshToken, tokens);
return delete(wrapperX);
}
}

View File

@ -0,0 +1 @@
# mapper 建议按业务分包

View File

@ -0,0 +1,71 @@
package com.css.txw.sso.pojo.domain.oauth2;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.css.ggzc.framework.mybatis.core.dataobject.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* OAuth2 访问令牌 DO
*/
@TableName(value = "txw_sso_access_token", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class OAuth2AccessTokenDO extends BaseDO {
/**
* UUID||uuid
*/
@TableId(value = "uuid")
private String uuid;
/**
* 用户UUID
*/
@TableField(value = "yh_uuid")
private String yhUuid;
@TableField(value = "qyuuid")
private String qyuuid;
/**
* access_token||access_token
*/
@TableField(value = "access_token")
private String accessToken;
/**
* refresh_token||refresh_token
*/
@TableField(value = "refresh_token")
private String refreshToken;
/**
* 客户端ID
*/
@TableField(value = "clientid")
private String clientid;
/**
* 授权内容||授权内容
*/
@TableField(value = "sqnr")
private String sqnr;
/**
* 挂起时间
*/
@TableField(value = "gqsj")
private LocalDateTime gqsj;
@TableField(value = "gllp")
private String gllp;
}

View File

@ -0,0 +1,63 @@
package com.css.txw.sso.pojo.domain.oauth2;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.css.txw.sso.enums.UserTypeEnum;
import com.css.ggzc.framework.mybatis.core.dataobject.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* OAuth2 批准 DO
*
* 用户在 sso.vue 界面时记录接受的 scope 列表
*
* @author 芋道源码
*/
@TableName(value = "system_oauth2_approve", autoResultMap = true)
@KeySequence("system_oauth2_approve_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
public class OAuth2ApproveDO extends BaseDO {
/**
* 编号数据库自增
*/
@TableId
private Long id;
/**
* 用户编号
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 客户端编号
*
* 关联 {@link OAuth2ClientDO#getId()}
*/
private String clientId;
/**
* 授权范围
*/
private String scope;
/**
* 是否接受
*
* true - 接受
* false - 拒绝
*/
private Boolean approved;
/**
* 过期时间
*/
private LocalDateTime expiresTime;
}

View File

@ -0,0 +1,76 @@
package com.css.txw.sso.pojo.domain.oauth2;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.css.ggzc.framework.mybatis.core.dataobject.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* OAuth2 客户端 DO
*
* @author 芋道源码
*/
@TableName(value = "txw_sso_client", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class OAuth2ClientDO extends BaseDO {
/**
* UUID||uuid
*/
@TableId(value = "uuid")
private String uuid;
/**
* 客户端ID
*/
@TableField(value = "clientid")
private String clientid;
/**
* 授权密钥||授权密钥
*/
@TableField(value = "sqmy")
private String sqmy;
/**
* 有效标志
*/
@TableField(value = "yxbz")
private String yxbz;
/**
* 访问令牌有效期||访问令牌有效期
*/
@TableField(value = "fwlpyxq")
private String fwlpyxq;
/**
* 刷新令牌有效期||刷新令牌有效期
*/
@TableField(value = "sxlpyxq")
private String sxlpyxq;
/**
* 重定向地址||重定向地址
*/
@TableField(value = "cdxdz")
private String cdxdz;
/**
* 授权内容||授权内容
*/
@TableField(value = "sqnr")
private String sqnr;
@TableField(value = "sm_1")
private String sm1;
@TableField(value = "dcdz")
private String dcdz;
}

View File

@ -0,0 +1,75 @@
package com.css.txw.sso.pojo.domain.oauth2;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.css.ggzc.framework.mybatis.core.dataobject.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* OAuth2 授权码 DO
*
* @author 芋道源码
*/
@TableName(value = "txw_sso_code", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class OAuth2CodeDO extends BaseDO {
/**
* UUID||uuid
*/
@TableId(value = "uuid")
private String uuid;
/**
* 用户UUID
*/
@TableField(value = "yh_uuid")
private String yhUuid;
/**
* 授权码||授权码
*/
@TableField(value = "sqm")
private String sqm;
/**
* 客户端ID
*/
@TableField(value = "clientid")
private String clientid;
/**
* 授权内容||授权内容
*/
@TableField(value = "sqnr")
private String sqnr;
/**
* 挂起时间
*/
@TableField(value = "gqsj")
private LocalDateTime gqsj;
/**
* 重定向地址||重定向地址
*/
@TableField(value = "cdxdz")
private String cdxdz;
/**
* 有效标志
*/
@TableField(value = "rzzt_1")
private String rzzt;
@TableField(value = "access_token")
private String accessToken;
}

View File

@ -0,0 +1,62 @@
package com.css.txw.sso.pojo.domain.oauth2;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.css.ggzc.framework.mybatis.core.dataobject.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* OAuth2 刷新令牌
*
* @author 芋道源码
*/
@TableName(value = "txw_sso_refresh_token", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class OAuth2RefreshTokenDO extends BaseDO {
/**
* UUID||uuid
*/
@TableId(value = "uuid")
private String uuid;
/**
* 用户UUID
*/
@TableField(value = "yh_uuid")
private String yhUuid;
@TableField(value = "qyuuid")
private String qyuuid;
/**
* refresh_token||refresh_token
*/
@TableField(value = "refresh_token")
private String refreshToken;
/**
* 客户端ID
*/
@TableField(value = "clientid")
private String clientid;
/**
* 授权内容||授权内容
*/
@TableField(value = "sqnr")
private String sqnr;
/**
* 挂起时间
*/
@TableField(value = "gqsj")
private LocalDateTime gqsj;
}

View File

@ -0,0 +1,23 @@
package com.css.txw.sso.pojo.dto.session;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class AccessTokenInfo {
private String uuid;
private String yhUuid;
private String qyuuid;
private String accessToken;
private String refreshToken;
private String clientid;
private String sqnr;
private LocalDateTime gqsj;
@Schema(description = "关联令牌")
private String gllp;
}

View File

@ -0,0 +1,20 @@
package com.css.txw.sso.pojo.dto.session;
import lombok.Data;
import java.util.List;
@Data
public class SessionInfo {
private AccessTokenInfo tokenInfo;
private YhInfo yhxx;
private String qyuuid;
private String qymc;
private String nsrsbh;
private String currentYhuuid;
private String currentYhlx;
}

View File

@ -0,0 +1,14 @@
package com.css.txw.sso.pojo.dto.session;
import lombok.Data;
@Data
public class YhInfo {
private String yhuuid;
private String zsxm;
private String sjhm;
private String sfzjlx;
private String sfzjhm;
private String yhdid;
}

View File

@ -0,0 +1,4 @@
package com.css.txw.sso.pojo.dto.yhxx;
public class YhxxDTO {
}

View File

@ -0,0 +1,71 @@
package com.css.txw.sso.pojo.vo;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
@Schema(description = "管理后台 - 账号密码登录 Request VO如果登录并绑定社交用户需要传递 social 开头的参数")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthLoginReqVO {
@Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma")
@NotEmpty(message = "登录账号不能为空")
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
@JsonProperty("Username")
private String username;
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
@NotEmpty(message = "密码不能为空")
@JsonProperty("Password")
private String password;
// ========== 图片验证码相关 ==========
@Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==")
@NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class)
@JsonProperty("CaptchaVerification")
private String captchaVerification;
// ========== 绑定社交登录时需要传递如下参数 ==========
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
//@InEnum(SocialTypeEnum.class)
@JsonProperty("SocialType")
private Integer socialType;
@Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@JsonProperty("SocialCode")
private String socialCode;
@Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
@JsonProperty("SocialState")
private String socialState;
/**
* 开启验证码的 Group
*/
public interface CodeEnableGroup {}
@AssertTrue(message = "授权码不能为空")
public boolean isSocialCodeValid() {
return socialType == null || StrUtil.isNotEmpty(socialCode);
}
@AssertTrue(message = "授权 state 不能为空")
public boolean isSocialState() {
return socialType == null || StrUtil.isNotEmpty(socialState);
}
}

View File

@ -0,0 +1,33 @@
package com.css.txw.sso.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 登录 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthLoginRespVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long userId;
@Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "happy")
private String accessToken;
@Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "nice")
private String refreshToken;
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime expiresTime;
@Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED)
private String yhlx;
}

View File

@ -0,0 +1,26 @@
package com.css.txw.sso.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Data
public class DidBindPhoneReqVO {
/**
* 验证码
*/
@Schema(description = "验证码")
@NotEmpty(message = "验证码不能为空")
private String sms;
/**
* 手机号码
*/
@Schema(description = "手机号码")
@NotEmpty(message = "手机号码不能为空")
private String sjhm;
private String reqId;
}

View File

@ -0,0 +1,29 @@
package com.css.txw.sso.pojo.vo;
import lombok.Data;
import java.util.List;
/**
* 登录用户信息
*
* @author 芋道源码
*/
@Data
public class LoginUser {
/**
* 用户编号
*/
private String useId;
/**
* 授权范围
*/
private List<String> scopes;
}

View File

@ -0,0 +1,26 @@
package com.css.txw.sso.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Data
public class SMSLoginReqVO {
/**
* 手机号码
*/
@Schema(description = "验证码")
@NotEmpty(message = "验证码不能为空")
private String sms;
/**
* 手机号码
*/
@Schema(description = "手机号码")
@NotEmpty(message = "手机号码不能为空")
private String sjhm;
}

View File

@ -0,0 +1,24 @@
package com.css.txw.sso.pojo.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Data
public class SendMsgReqVO {
/**
* 手机号码
*/
@Schema(description = "手机号码")
@NotEmpty(message = "手机号码不能为空")
private String sjhm1;
@Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "验证码不能为空", groups = AuthLoginReqVO.CodeEnableGroup.class)
private String captchaVerification;
}

View File

@ -0,0 +1,16 @@
package com.css.txw.sso.pojo.vo.oauth2;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class LogoutVO {
@Schema(description = "第三方访问令牌")
private String token;
@Schema(description = "登出地址")
private String dcdz;
@Schema(description = "客户端")
private String client;
}

View File

@ -0,0 +1,27 @@
package com.css.txw.sso.pojo.vo.oauth2;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 【开放接口】访问令牌 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2OpenAccessTokenRespVO {
@Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou")
@JsonProperty("access_token")
private String accessToken;
@Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "nice")
@JsonProperty("refresh_token")
private String refreshToken;
@Schema(description = "过期时间,单位:秒", requiredMode = Schema.RequiredMode.REQUIRED, example = "42430")
@JsonProperty("expires_in")
private Long expiresIn;
}

View File

@ -0,0 +1,38 @@
package com.css.txw.sso.pojo.vo.oauth2;
import com.css.ggzc.framework.common.core.KeyValue;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Schema(description = "管理后台 - 授权页的信息 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2OpenAuthorizeInfoRespVO {
/**
* 客户端
*/
private Client client;
@Schema(description = "scope 的选中信息,使用 List 保证有序性Key 是 scopeValue 为是否选中", requiredMode = Schema.RequiredMode.REQUIRED)
private List<KeyValue<String, Boolean>> scopes;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Client {
@Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆")
private String name;
@Schema(description = "应用图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
private String logo;
}
}

View File

@ -0,0 +1,40 @@
package com.css.txw.sso.pojo.vo.oauth2;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Schema(description = "管理后台 - 【开放接口】校验令牌 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2OpenCheckTokenRespVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
@JsonProperty("user_id")
private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@JsonProperty("user_type")
private Integer userType;
@Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@JsonProperty("tenant_id")
private Long tenantId;
@Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "car")
@JsonProperty("client_id")
private String clientId;
@Schema(description = "授权范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_info")
private List<String> scopes;
@Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou")
@JsonProperty("access_token")
private String accessToken;
@Schema(description = "过期时间,时间戳 / 1000即单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "1593092157")
private Long exp;
}

View File

@ -0,0 +1,42 @@
package com.css.txw.sso.pojo.vo.oauth2;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Schema(description = "管理后台 - 【开放接口】用户信息对象")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2UserInfoRespVO {
/**
* 用户信息
*/
@Schema(description = "用户UUID")
private String yhuuid;
@Schema(description = "登录账号")
private String dlzh;
@Schema(description = "真实姓名")
private String zsxm;
@Schema(description = "手机号码")
private String sjhm;
private String yhdid;
/**
* 机构信息
*/
@Schema(description = "机构UUID")
private String qyuuid;
@Schema(description = "机构名称")
private String qymc;
@Schema(description = "社会信用代码")
private String shxydm;
@Schema(description = "用户类型")
private String yhlx;
}

View File

@ -0,0 +1,38 @@
package com.css.txw.sso.pojo.vo.oauth2;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
@Data
public class SwjgTreeVO {
@Schema(description = "code")
private String code;
@Schema(description = "pcode")
private String pcode;
@Schema(description = "caption")
private String caption;
private String nsrsbh;
private String shxydm;
private String ssjswjgDm;
private String djxh;
private String bz;
private String yxbz;
private String xzqhszDm;
private String qydmz;
private String tbwczt;
private Date tbwcsj;
}

View File

@ -0,0 +1,17 @@
package com.css.txw.sso.pojo.vo.thirdparty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "个税申报Token信息")
@Data
public class GssbTokenVO {
@Schema(description = "访问令牌")
private String accessToken;
@Schema(description = "刷新令牌")
private String refreshToken;
@Schema(description = "租户ID")
private String tenantId;
}

View File

@ -0,0 +1,17 @@
package com.css.txw.sso.pojo.vo.thirdparty.did;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DidCallbackExtendInfo {
private String requestId;
private String authoriserIdCard;
private String authoriserIdCardDid;
private String authoriserPersonNameDid;
}

View File

@ -0,0 +1,32 @@
package com.css.txw.sso.pojo.vo.thirdparty.did;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DidCredentialSubject {
//姓名
protected String legalName;
//用户id
protected String uniscid;
//地址
protected String dom;
protected String licencesn;
//进出口企业在平台的名称
protected String entname;
//测试签发机构
protected String issuerName;
protected String opscope;
protected String opto;
//企业实名认证证书
protected String certificateName;
protected String operator;
protected String opfrom;
protected String phone;
protected DidCallbackExtendInfo extInfo ;
}

View File

@ -0,0 +1,16 @@
package com.css.txw.sso.pojo.vo.thirdparty.did;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DidExtendVcsInfo {
private String issuer;
private String vctId;
private String vctVersion ;
}

View File

@ -0,0 +1,25 @@
package com.css.txw.sso.pojo.vo.thirdparty.did;
import java.util.List;
import org.chainweaver.did.sdk.model.req.LoginExtend;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DidLoginExtend extends LoginExtend{
private Boolean personalSignature;
// private String authorizedName;
// private String authorizedNameEn;
// private String callbackUrl;
// private String logo;
// private String method;
// private String requestId;
// private String siteName;
private List<DidExtendVcsInfo> vcs;
}

View File

@ -0,0 +1,15 @@
package com.css.txw.sso.pojo.vo.thirdparty.did;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class QrCodeInfo {
private String url;
private String reqId;
}

View File

@ -0,0 +1,30 @@
package com.css.txw.sso.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;
/**
*
* @project qyd-txw
* @package com.css.txw.sso.properties
* @file SsoProperties.java 创建时间:2025/11/25 20:19
* @title 标题要求能简洁地表达出类的功能和职责
* @description 描述简要描述类的职责实现方式使用注意事项等
* @copyright Copyright (c) 2025 中国软件与技术服务股份有限公司
* @company 中国软件与技术服务股份有限公司
* @module 模块: 智能申报
* @author 商健
* @reviewer 审核人
* @version 1.0.0
* @history 修订历史历次修订内容修订人修订时间等
*
*/
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "css.sso")
@Data
public class SsoProperties {
private boolean loginCaptcha = true;
}

View File

@ -0,0 +1 @@
# 配置类

View File

@ -0,0 +1,10 @@
package com.css.txw.sso.service.auth;
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
public interface AdminAuthService {
String login(AuthLoginReqVO reqVO);
void logout(String accessToken);
}

View File

@ -0,0 +1,55 @@
package com.css.txw.sso.service.auth;
import com.css.txw.mhzc.pojo.YhxxbDTO;
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
import com.css.txw.sso.pojo.vo.DidBindPhoneReqVO;
import com.css.txw.sso.pojo.vo.SMSLoginReqVO;
import com.css.txw.sso.pojo.vo.SendMsgReqVO;
import com.css.txw.sso.pojo.vo.oauth2.LogoutVO;
import javax.validation.Valid;
import java.util.List;
public interface AuthService {
/**
* 验证账号 + 密码如果通过则返回用户
*
* @param username 账号
* @param password 密码
* @return 用户
*/
YhxxbDTO authenticate(String username, String password);
/**
* 账号登录
*
* @param reqVO 登录信息
* @return 登录结果
*/
AuthLoginRespVO login(@Valid AuthLoginReqVO reqVO);
/**
* 基于 token 退出登录
*
* @param token token
* @param logType 登出类型
*/
List<LogoutVO> logout(String token, Integer logType);
/**
* 刷新访问令牌
*
* @param refreshToken 刷新令牌
* @return 登录结果
*/
AuthLoginRespVO refreshToken(String refreshToken);
Integer sendMsg(SendMsgReqVO reqVO) throws Exception;
AuthLoginRespVO loginBySMS(SMSLoginReqVO reqVO);
AuthLoginRespVO didBindPhone(@Valid DidBindPhoneReqVO reqVO);
}

View File

@ -0,0 +1,89 @@
package com.css.txw.sso.service.auth.impl;
import cn.hutool.crypto.digest.MD5;
import com.css.txw.mhzc.pojo.HtYhxxbDTO;
import com.css.txw.sso.constants.SsoApiConstants;
import com.css.txw.sso.constants.SsoConstants;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
import com.css.txw.sso.service.auth.AdminAuthService;
import com.css.txw.sso.service.oauth2.OAuth2TokenService;
import com.css.txw.sso.service.verify.VerifyService;
import com.css.txw.sso.service.yhxx.AccountLockService;
import com.css.txw.sso.service.yhxx.YhxxService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import static com.css.txw.sso.constants.ErrorCodeConstants.*;
import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.exception;
@Service
@Slf4j
public class AdminAuthServiceImpl implements AdminAuthService {
@Resource
private AccountLockService accountLockService;
@Resource
private VerifyService verifyService;
@Resource
private OAuth2TokenService oauth2TokenService;
@Resource
private YhxxService yhxxService;
@Override
public String login(AuthLoginReqVO reqVO) {
final Boolean checked = verifyService.checkVerifyToken(reqVO.getCaptchaVerification());
if (!checked){
throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR);
}
final HtYhxxbDTO authenticate = authenticate(reqVO.getUsername(), reqVO.getPassword());
return createTokenAfterLoginSuccess(authenticate.getYhUuid());
}
@Override
public void logout(String accessToken) {
// 删除访问令牌
oauth2TokenService.removeAccessToken(SsoConstants.CLIENT_DEFAULT,accessToken);
}
private String createTokenAfterLoginSuccess(String userId) {
// 创建访问令牌
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAdminAccessToken(userId,"default");
return accessTokenDO.getAccessToken();
}
public HtYhxxbDTO authenticate(String username, String password) {
HtYhxxbDTO adminInfoByDlzh = yhxxService.getAdminInfoByDlzh(username);
if (adminInfoByDlzh == null) {
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
if (accountLockService.checkLockStatus(adminInfoByDlzh.getYhUuid())){
throw exception(AUTH_LOGIN_PASSWORD_ERROR_LOCK);
}
if (!isPasswordMatch(password, adminInfoByDlzh.getDlmm())) {
accountLockService.handlePasswordError(adminInfoByDlzh.getYhUuid());
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
if (!"Y".equals(adminInfoByDlzh.getYxbz())){
throw exception(AUTH_LOGIN_USER_DISABLED);
}
accountLockService.clearCache(adminInfoByDlzh.getYhUuid());
return adminInfoByDlzh;
}
private boolean isPasswordMatch(String rawPassword, String encodedPassword) {
final String rawPasswordHex = MD5.create().digestHex(rawPassword);
return rawPasswordHex.equals(encodedPassword);
}
}

View File

@ -0,0 +1,323 @@
package com.css.txw.sso.service.auth.impl;
import static com.aliyun.teautil.Common.toJSONString;
import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS;
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_CAPTCHA_CODE_ERROR;
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_PASSWORD_ERROR_LOCK;
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_LOGIN_SJHM_NOT_EXISTS;
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_LOGIN_SMS_NOT_EXISTS;
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_SJHM_LOCK;
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_SJHM_NOT_EXISTS;
import static com.css.txw.sso.constants.SsoConstants.SMS_CLIENT_SIGNNAME;
import static com.css.txw.sso.constants.SsoConstants.SMS_TEMPLATE_CODE;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.validation.Valid;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.css.ggzc.framework.cache.utils.CacheUtils;
import com.css.ggzc.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.css.ggzc.framework.common.util.gy.GyUtils;
import com.css.ggzc.framework.common.util.json.JsonUtils;
import com.css.txw.common.pojo.dto.sms.SMSResDTO;
import com.css.txw.common.service.ISMService;
import com.css.txw.mhzc.pojo.YhxxbDTO;
import com.css.txw.sso.configuration.SMSClient;
import com.css.txw.sso.controller.thirdparty.DidController;
import com.css.txw.sso.convert.AuthConvert;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
import com.css.txw.sso.pojo.vo.DidBindPhoneReqVO;
import com.css.txw.sso.pojo.vo.SMSLoginReqVO;
import com.css.txw.sso.pojo.vo.SendMsgReqVO;
import com.css.txw.sso.pojo.vo.oauth2.LogoutVO;
import com.css.txw.sso.pojo.vo.thirdparty.did.DidCallbackExtendInfo;
import com.css.txw.sso.pojo.vo.thirdparty.did.DidCredentialSubject;
import com.css.txw.sso.service.auth.AuthService;
import com.css.txw.sso.service.oauth2.OAuth2ClientService;
import com.css.txw.sso.service.oauth2.OAuth2TokenService;
import com.css.txw.sso.service.verify.VerifyService;
import com.css.txw.sso.service.yhxx.AccountLockService;
import com.css.txw.sso.service.yhxx.YhxxService;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.MD5;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {
@Resource
private OAuth2TokenService oauth2TokenService;
@Resource
private VerifyService verifyService;
@Resource
private AccountLockService accountLockService;
@Resource
private OAuth2ClientService clientService;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private YhxxService yhxxService;
@Resource
private ISMService smService;
@Override
public YhxxbDTO authenticate(String username, String password) {
YhxxbDTO yhxxbDO = yhxxService.getYhxxByDlzh(username);
if (yhxxbDO == null) {
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
if (accountLockService.checkLockStatus(yhxxbDO.getYhUuid())) {
throw exception(AUTH_LOGIN_PASSWORD_ERROR_LOCK);
}
if (!isPasswordMatch(password, yhxxbDO.getDlmm())) {
accountLockService.handlePasswordError(yhxxbDO.getYhUuid());
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
accountLockService.clearCache(yhxxbDO.getYhUuid());
return yhxxbDO;
}
@Override
public AuthLoginRespVO login(AuthLoginReqVO reqVO) {
final Boolean checked = verifyService.checkVerifyToken(reqVO.getCaptchaVerification());
if (!checked) {
throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR);
}
// 使用账号密码进行登录
YhxxbDTO user = authenticate(reqVO.getUsername(), reqVO.getPassword());
return createTokenAfterLoginSuccess(user.getYhUuid());
}
@Override
public List<LogoutVO> logout(String token, Integer logType) {
List<LogoutVO> result = new ArrayList<>();
final List<OAuth2AccessTokenDO> oAuth2AccessTokenDOS = oauth2TokenService.removeAccessToken(token);
if (!GyUtils.isNull(oAuth2AccessTokenDOS)) {
oAuth2AccessTokenDOS.forEach(oAuth2AccessTokenDO -> {
final String clientid = oAuth2AccessTokenDO.getClientid();
if (GyUtils.isNull(clientid)) {
log.info("logout方法clientid为空,入参token:{},loginType:{}", token, logType);
} else {
final OAuth2ClientDO oAuth2ClientFromCache = clientService.getOAuth2ClientFromCache(clientid);
final String dcdz = oAuth2ClientFromCache.getDcdz();
final LogoutVO logoutVO = new LogoutVO();
logoutVO.setToken(token);
logoutVO.setDcdz(dcdz);
logoutVO.setClient(clientid);
result.add(logoutVO);
}
});
}
return result;
}
@Override
public AuthLoginRespVO refreshToken(String refreshToken) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, "default");
return AuthConvert.INSTANCE.convert(accessTokenDO);
}
@Override
public Integer sendMsg(SendMsgReqVO reqVO) throws Exception{
final Boolean checked = verifyService.checkVerifyToken(reqVO.getCaptchaVerification());
if (!checked) {
throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR);
}
String sjhm = reqVO.getSjhm1();
Boolean aBoolean = stringRedisTemplate.hasKey(formatKeyPhone(sjhm));
if (!GyUtils.isNull(aBoolean)&&aBoolean) {
throw exception(OAUTH2_SJHM_LOCK);
}
stringRedisTemplate.opsForValue().set(formatKey(sjhm), sjhm, 1, TimeUnit.MINUTES);
// YhxxbDTO yhxx = yhxxService.getYhxxBySjhm(sjhm);
// if (GyUtils.isNull(yhxx)) {
// throw exception(OAUTH2_SJHM_NOT_EXISTS);
// }
Boolean aBoolean1 = stringRedisTemplate.hasKey(formatKeyPhone(sjhm));
if (GyUtils.isNull(aBoolean1)||!aBoolean1) {
throw exception(OAUTH2_LOGIN_SMS_NOT_EXISTS);
}
String s = CacheUtils.dm2mc("cs_ggzc_xtcs", "SSO_SMS");
String sms;
if (!GyUtils.isNull(s)){
sms = s;
}else {
sms = RandomStringUtils.randomNumeric(6);
Map<String,String> param = new HashMap<>();
param.put("code",sms);
//发送短信
SMSResDTO smsResDTO = smService.sendCaptcha(sjhm, sms);
//String resJson = send(sjhm, param);
stringRedisTemplate.opsForValue().set(formatKey(sjhm+sms), sjhm, 3, TimeUnit.MINUTES);
//Map<String, Object> map = JsonUtils.toMap(resJson);
Integer res = StringUtils.isBlank(smsResDTO.getCode()) ? 0 : Integer.parseInt(smsResDTO.getCode());
return res;
}
stringRedisTemplate.opsForValue().set(formatKey(sjhm+sms), sjhm, 3, TimeUnit.MINUTES);
return 1;
}
@Override
public AuthLoginRespVO loginBySMS(SMSLoginReqVO reqVO) {
String sms = reqVO.getSms();
String sjhm = reqVO.getSjhm();
Boolean aBoolean = stringRedisTemplate.hasKey(formatKey(sjhm+sms));
if (GyUtils.isNull(aBoolean)||!aBoolean) {
throw exception(OAUTH2_LOGIN_SJHM_NOT_EXISTS);
}
YhxxbDTO yhxx = yhxxService.getYhxxBySjhm(sjhm);
if (GyUtils.isNull(yhxx)) {
throw exception(OAUTH2_SJHM_NOT_EXISTS);
}
return createTokenAfterLoginSuccess(yhxx.getYhUuid());
}
@Override
public AuthLoginRespVO didBindPhone(@Valid DidBindPhoneReqVO reqVO) {
String sms = reqVO.getSms();
String sjhm = reqVO.getSjhm();
Boolean aBoolean = stringRedisTemplate.hasKey(formatKey(sjhm+sms));
if (GyUtils.isNull(aBoolean)||!aBoolean) {
throw exception(OAUTH2_LOGIN_SJHM_NOT_EXISTS);
}
String reqId =reqVO.getReqId() ;
// did用户注册
//缓存did 一次请求对应一个did
String tmp = String.format("%s%s", DidController.REQ_USER_DID_PREFIX,reqId);
String holderDid =CacheUtils.getCacheData(tmp);
if (GyUtils.isNull(holderDid)) {
throw exception(GlobalErrorCodeConstants.ERROR.getCode(),"缓存did为空");
}
//缓存did数据为创建账号做准备
// DidCredentialSubject cs = JsonUtils.toBean(csjson, DidCredentialSubject.class);
tmp = String.format("%s%s", DidController.REQ_USER_DID_DATA_PREFIX,reqId);
// CacheUtils.cacheData(tmp, JsonUtils.toJson(cs),timeout);
String csJson =CacheUtils.getCacheData(tmp);
if (GyUtils.isNull(csJson)) {
throw exception(GlobalErrorCodeConstants.ERROR.getCode(),"缓存did数据为空");
}
YhxxbDTO existYhxx = yhxxService.getYhxxBySjhm(sjhm);
YhxxbDTO yhxx = null;
DidCredentialSubject cs =JsonUtils.toBean(csJson,DidCredentialSubject.class );
if (GyUtils.isNull(existYhxx)) {
yhxx = new YhxxbDTO();
yhxx.setYhUuid(IdUtil.fastSimpleUUID());
yhxx.setDid(holderDid);
yhxx.setSjhm1(sjhm);
yhxx.setDlzh(sjhm);
if (GyUtils.isNotNull(cs.getLegalName())) {
yhxx.setZsxm1(cs.getLegalName());
}
else if (GyUtils.isNotNull(cs.getEntname())) {
yhxx.setZsxm1(cs.getEntname());
}
else {
yhxx.setZsxm1("--");
}
DidCallbackExtendInfo ext = cs.getExtInfo();
if (GyUtils.isNotNull(ext)) {
yhxx.setSfzjhm(ext.getAuthoriserIdCard());
}
yhxx = yhxxService.saveYhxxByDid(yhxx);
}
else {
// yhxx = existYhxx;
// yhxx.setDid(holderDid);
// yhxxService.updateDid(yhxx);
throw exception(GlobalErrorCodeConstants.ERROR.getCode(),"已经存在该手机号的账号信息!");
}
// 企业认证成功
YhxxbDTO qyxx = new YhxxbDTO();
qyxx.setQymc(cs.getEntname());
qyxx.setNsrsbh(cs.getUniscid());
qyxx.setYhUuid(yhxx.getYhUuid());
log.info("qyxx:{}",qyxx);
qyxx = yhxxService.intQyxxByDid(qyxx);
// YhxxbDTO yhxx = yhxxService.getYhxxBySjhm(sjhm);
// if (GyUtils.isNull(yhxx)) {
// throw exception(OAUTH2_SJHM_NOT_EXISTS);
// }
return createTokenAfterLoginSuccess(yhxx.getYhUuid());
}
private AuthLoginRespVO createTokenAfterLoginSuccess(String userId) {
// 创建访问令牌
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, "default", null);
// 构建返回结果
final AuthLoginRespVO respVO = new AuthLoginRespVO();
respVO.setAccessToken(accessTokenDO.getAccessToken());
respVO.setRefreshToken(accessTokenDO.getRefreshToken());
respVO.setExpiresTime(accessTokenDO.getGqsj());
return respVO;
}
private boolean isPasswordMatch(String rawPassword, String encodedPassword) {
final String rawPasswordHex = MD5.create().digestHex(rawPassword);
return rawPasswordHex.equals(encodedPassword);
}
public String send(String sjhm,Map<String,String> param) throws Exception {
// 初始化请求客户端
Client client = SMSClient.createClient();
// 构造请求对象请填入请求参数值
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setPhoneNumbers(sjhm)
.setSignName(SMS_CLIENT_SIGNNAME)
.setTemplateCode(SMS_TEMPLATE_CODE)
.setTemplateParam(JsonUtils.toJson(param));
// 获取响应对象
SendSmsResponse sendSmsResponse = client.sendSms(sendSmsRequest);
// 响应包含服务端响应的 body headers
return toJSONString(sendSmsResponse);
}
private static String formatKey(String sms) {
String VERIFY_TOKEN = "sms_token:%s";
return String.format(VERIFY_TOKEN, sms);
}
private static String formatKeyPhone(String phone) {
String PHONE_LOCK = "sms_token:%s";
return String.format(PHONE_LOCK, phone);
}
}

View File

@ -0,0 +1,48 @@
package com.css.txw.sso.service.oauth2;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
import java.util.Collection;
/**
* OAuth2.0 Client Service 接口
*
* 从功能上 JdbcClientDetailsService 的功能提供客户端的操作
*/
public interface OAuth2ClientService {
/**
* 获得 OAuth2 客户端从缓存中
*
* @param clientId 客户端编号
* @return OAuth2 客户端
*/
OAuth2ClientDO getOAuth2ClientFromCache(String clientId);
/**
* 从缓存中校验客户端是否合法
*
* @return 客户端
*/
default OAuth2ClientDO validOAuthClientFromCache(String clientId) {
return validOAuthClientFromCache(clientId, null, null, null, null);
}
/**
* 从缓存中校验客户端是否合法
*
* 非空时进行校验
*
* @param clientId 客户端编号
* @param clientSecret 客户端密钥
* @param authorizedGrantType 授权方式
* @param scopes 授权范围
* @param redirectUri 重定向地址
* @return 客户端
*/
OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType,
Collection<String> scopes, String redirectUri);
}

View File

@ -0,0 +1,91 @@
package com.css.txw.sso.service.oauth2;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.nacos.shaded.com.google.common.annotations.VisibleForTesting;
import com.css.txw.sso.constants.ErrorCodeConstants;
import com.css.txw.sso.constants.RedisKeyConstants;
import com.css.txw.sso.mapper.oauth2.OAuth2ClientMapper;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
import com.css.ggzc.framework.common.exception.ServiceException;
import com.css.ggzc.framework.common.util.string.StrUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Collection;
import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* OAuth2.0 Client Service 实现类
*/
@Service
@Validated
@Slf4j
public class OAuth2ClientServiceImpl implements OAuth2ClientService {
@Resource
private OAuth2ClientMapper oauth2ClientMapper;
@VisibleForTesting
void validateClientIdExists(String uuid, String clientId) {
OAuth2ClientDO client = oauth2ClientMapper.selectByClientId(clientId);
if (client == null) {
return;
}
// 如果 id 为空说明不用比较是否为相同 id 的客户端
if (uuid == null) {
throw new ServiceException(ErrorCodeConstants.OAUTH2_CLIENT_EXISTS);
}
if (!client.getUuid().equals(uuid)) {
throw exception(ErrorCodeConstants.OAUTH2_CLIENT_EXISTS);
}
}
@Override
@Cacheable(cacheNames = RedisKeyConstants.OAUTH_CLIENT, key = "#clientId",
unless = "#result == null")
public OAuth2ClientDO getOAuth2ClientFromCache(String clientId) {
return oauth2ClientMapper.selectByClientId(clientId);
}
@Override
public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType,
Collection<String> scopes, String redirectUri) {
// 校验客户端存在且开启
OAuth2ClientDO client = getSelf().getOAuth2ClientFromCache(clientId);
if (client == null) {
throw exception(ErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS);
}
if (!"Y".equals(client.getYxbz())) {
throw exception(ErrorCodeConstants.OAUTH2_CLIENT_DISABLE);
}
// 校验客户端密钥
if (StrUtil.isNotEmpty(clientSecret) && ObjectUtil.notEqual(client.getSqmy(), clientSecret)) {
throw exception(ErrorCodeConstants.OAUTH2_CLIENT_CLIENT_SECRET_ERROR);
}
// 校验回调地址
if (StrUtil.isNotEmpty(redirectUri) && !StrUtils.startWithAny(redirectUri, Arrays.asList(client.getCdxdz().split(";")))) {
throw exception(ErrorCodeConstants.OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, redirectUri);
}
return client;
}
/**
* 获得自身的代理对象解决 AOP 生效问题
*
* @return 自己
*/
private OAuth2ClientServiceImpl getSelf() {
return SpringUtil.getBean(getClass());
}
}

View File

@ -0,0 +1,48 @@
package com.css.txw.sso.service.oauth2;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2CodeDO;
import java.util.List;
/**
* OAuth2.0 授权码 Service 接口
*
* 从功能上 Spring Security OAuth JdbcAuthorizationCodeServices 的功能提供授权码的操作
*
* @author 芋道源码
*/
public interface OAuth2CodeService {
/**
* 创建授权码
*
* 参考 JdbcAuthorizationCodeServices createAuthorizationCode 方法
*
* @param userId 用户编号
* @param userType 用户类型
* @param clientId 客户端编号
* @param scopes 授权范围
* @param redirectUri 重定向 URI
* @param state 状态
* @return 授权码的信息
*/
OAuth2CodeDO createAuthorizationCode(String userId, Integer userType, String clientId,
List<String> scopes, String redirectUri, String state);
/**
* 创建授权码
* @param clientId 客户端编号
* @param redirectUri 重定向 URI
* @param state 状态
* @return 授权码的信息
*/
OAuth2CodeDO createAuthorizationCode(String clientId,String redirectUri, String state,String currentToken);
/**
* 使用授权码
*
* @param code 授权码
*/
OAuth2CodeDO consumeAuthorizationCode(String code);
}

Some files were not shown because too many files have changed in this diff Show More