getYhxx(@RequestParam("djxh") String djxh);
+
+}
diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/configuration/SsoApiConfiguration.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/configuration/SsoApiConfiguration.java
new file mode 100644
index 0000000..62f4188
--- /dev/null
+++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/configuration/SsoApiConfiguration.java
@@ -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 {
+
+}
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/constants/SsoApiConstants.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/constants/SsoApiConstants.java
new file mode 100644
index 0000000..f7d077c
--- /dev/null
+++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/constants/SsoApiConstants.java
@@ -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";
+}
diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/enums/LoginLogTypeEnum.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/enums/LoginLogTypeEnum.java
new file mode 100644
index 0000000..7524026
--- /dev/null
+++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/enums/LoginLogTypeEnum.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/enums/OAuth2GrantTypeEnum.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/enums/OAuth2GrantTypeEnum.java
new file mode 100644
index 0000000..da7fce5
--- /dev/null
+++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/enums/OAuth2GrantTypeEnum.java
@@ -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());
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/pojo/yhxx/SwitchCompanyResDTO.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/pojo/yhxx/SwitchCompanyResDTO.java
new file mode 100644
index 0000000..35f1f92
--- /dev/null
+++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/pojo/yhxx/SwitchCompanyResDTO.java
@@ -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;
+}
diff --git a/txw-sso/txw-sso-service-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/txw-sso/txw-sso-service-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..a41a883
--- /dev/null
+++ b/txw-sso/txw-sso-service-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.css.txw.sso.configuration.SsoApiConfiguration
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-api/src/main/resources/additional-spring-configuration-metadata.json b/txw-sso/txw-sso-service-api/src/main/resources/additional-spring-configuration-metadata.json
new file mode 100644
index 0000000..7a73a41
--- /dev/null
+++ b/txw-sso/txw-sso-service-api/src/main/resources/additional-spring-configuration-metadata.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-api/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/txw-sso/txw-sso-service-api/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..a41a883
--- /dev/null
+++ b/txw-sso/txw-sso-service-api/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.css.txw.sso.configuration.SsoApiConfiguration
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-api/target/classes/additional-spring-configuration-metadata.json b/txw-sso/txw-sso-service-api/target/classes/additional-spring-configuration-metadata.json
new file mode 100644
index 0000000..7a73a41
--- /dev/null
+++ b/txw-sso/txw-sso-service-api/target/classes/additional-spring-configuration-metadata.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/ISsoApi.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/ISsoApi.class
new file mode 100644
index 0000000..7dc1109
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/ISsoApi.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/OAuth2TokenApi.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/OAuth2TokenApi.class
new file mode 100644
index 0000000..9e9e863
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/OAuth2TokenApi.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/AccessTokenSessionInfo.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/AccessTokenSessionInfo.class
new file mode 100644
index 0000000..b2cbf6c
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/AccessTokenSessionInfo.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.class
new file mode 100644
index 0000000..2363eeb
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCreateReqDTO.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCreateReqDTO.class
new file mode 100644
index 0000000..c439045
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCreateReqDTO.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenRespDTO.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenRespDTO.class
new file mode 100644
index 0000000..bab525e
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenRespDTO.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/yhxx/SwitchSessionApi.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/yhxx/SwitchSessionApi.class
new file mode 100644
index 0000000..364c208
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/yhxx/SwitchSessionApi.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/yhxx/YhxxApi.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/yhxx/YhxxApi.class
new file mode 100644
index 0000000..867a4a4
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/yhxx/YhxxApi.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/configuration/SsoApiConfiguration.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/configuration/SsoApiConfiguration.class
new file mode 100644
index 0000000..bbde6c3
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/configuration/SsoApiConfiguration.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/constants/SsoApiConstants.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/constants/SsoApiConstants.class
new file mode 100644
index 0000000..35fa438
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/constants/SsoApiConstants.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/enums/LoginLogTypeEnum.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/enums/LoginLogTypeEnum.class
new file mode 100644
index 0000000..29cc9cf
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/enums/LoginLogTypeEnum.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/enums/OAuth2GrantTypeEnum.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/enums/OAuth2GrantTypeEnum.class
new file mode 100644
index 0000000..df41921
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/enums/OAuth2GrantTypeEnum.class differ
diff --git a/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/pojo/yhxx/SwitchCompanyResDTO.class b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/pojo/yhxx/SwitchCompanyResDTO.class
new file mode 100644
index 0000000..954e37f
Binary files /dev/null and b/txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/pojo/yhxx/SwitchCompanyResDTO.class differ
diff --git a/txw-sso/txw-sso-service-biz/pom.xml b/txw-sso/txw-sso-service-biz/pom.xml
new file mode 100644
index 0000000..639badd
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/pom.xml
@@ -0,0 +1,144 @@
+
+
+ 4.0.0
+
+
+ txw-sso
+ com.css.txw
+ 1.0.0-SNAPSHOT
+
+ txw-sso-service-biz
+ jar
+
+ ${project.artifactId}
+ sso service
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-bootstrap
+
+
+
+
+ com.css.ggzc
+ ggzc-framework-starter-web
+
+
+
+ com.css.ggzc
+ ggzc-framework-starter-common
+
+
+
+
+ com.css.ggzc
+ ggzc-framework-starter-mybatis
+
+
+
+ com.css.ggzc
+ ggzc-framework-starter-cache
+
+
+
+
+ com.css.ggzc
+ ggzc-framework-starter-rpc
+
+
+
+
+
+
+ com.css.ggzc
+ ggzc-framework-starter-job
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ com.css.txw
+ txw-sso-service-api
+ 1.0.0-SNAPSHOT
+
+
+
+ com.css.txw
+ txw-mhzc-service-api
+ 1.0.0-SNAPSHOT
+
+
+
+ com.aliyun
+ dysmsapi20170525
+ 3.1.0
+
+
+ com.css.txw
+ txw-common
+ 1.0.0-SNAPSHOT
+ compile
+
+
+ com.dameng
+ DmJdbcDriver18
+
+
+ org.chainweaver
+ did-sdk-java
+ 1.5.0
+
+
+ com.alibaba.fastjson2
+ fastjson2
+ 2.0.60
+
+
+
+
+
+ ${project.artifactId}
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+ true
+
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/app/DevAppStarter.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/app/DevAppStarter.java
new file mode 100644
index 0000000..b68c3c6
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/app/DevAppStarter.java
@@ -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");
+ }
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/configuration/SMSClient.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/configuration/SMSClient.java
new file mode 100644
index 0000000..53085a7
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/configuration/SMSClient.java
@@ -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);
+ }
+
+}
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/configuration/SsoServiceConfiguration.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/configuration/SsoServiceConfiguration.java
new file mode 100644
index 0000000..e5dfb1d
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/configuration/SsoServiceConfiguration.java
@@ -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 {
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/ErrorCodeConstants.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/ErrorCodeConstants.java
new file mode 100644
index 0000000..e846282
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/ErrorCodeConstants.java
@@ -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, "验证码无效或已过期");
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/RedisKeyConstants.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/RedisKeyConstants.java
new file mode 100644
index 0000000..1667458
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/RedisKeyConstants.java
@@ -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 {
+
+ /**
+ * 指定部门的所有子部门编号数组的缓存
+ *
+ * KEY 格式:dept_children_ids:{id}
+ * VALUE 数据类型:String 子部门编号集合
+ */
+ String DEPT_CHILDREN_ID_LIST = "dept_children_ids";
+
+ /**
+ * 角色的缓存
+ *
+ * KEY 格式:role:{id}
+ * VALUE 数据类型:String 角色信息
+ */
+ String ROLE = "role";
+
+ /**
+ * 用户拥有的角色编号的缓存
+ *
+ * KEY 格式:user_role_ids:{userId}
+ * VALUE 数据类型:String 角色编号集合
+ */
+ String USER_ROLE_ID_LIST = "user_role_ids";
+
+ /**
+ * 拥有指定菜单的角色编号的缓存
+ *
+ * KEY 格式:user_role_ids:{menuId}
+ * VALUE 数据类型:String 角色编号集合
+ */
+ String MENU_ROLE_ID_LIST = "menu_role_ids";
+
+ /**
+ * 拥有权限对应的菜单编号数组的缓存
+ *
+ * KEY 格式:permission_menu_ids:{permission}
+ * VALUE 数据类型:String 菜单编号数组
+ */
+ String PERMISSION_MENU_ID_LIST = "permission_menu_ids";
+
+ /**
+ * OAuth2 客户端的缓存
+ *
+ * KEY 格式:oauth_client:{id}
+ * VALUE 数据类型:String 客户端信息
+ */
+ String OAUTH_CLIENT = "oauth_client";
+
+ /**
+ * 访问令牌的缓存
+ *
+ * KEY 格式:oauth2_access_token:{token}
+ * VALUE 数据类型:String 访问令牌信息 {@link OAuth2AccessTokenDO}
+ *
+ * 由于动态过期时间,使用 RedisTemplate 操作
+ */
+ String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s";
+
+ /**
+ * 站内信模版的缓存
+ *
+ * KEY 格式:notify_template:{code}
+ * VALUE 数据格式:String 模版信息
+ */
+ String NOTIFY_TEMPLATE = "notify_template";
+
+ /**
+ * 邮件账号的缓存
+ *
+ * KEY 格式:mail_account:{id}
+ * VALUE 数据格式:String 账号信息
+ */
+ String MAIL_ACCOUNT = "mail_account";
+
+ /**
+ * 邮件模版的缓存
+ *
+ * KEY 格式:mail_template:{code}
+ * VALUE 数据格式:String 模版信息
+ */
+ String MAIL_TEMPLATE = "mail_template";
+
+ /**
+ * 短信模版的缓存
+ *
+ * KEY 格式:sms_template:{id}
+ * VALUE 数据格式:String 模版信息
+ */
+ String SMS_TEMPLATE = "sms_template";
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/SsoConstants.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/SsoConstants.java
new file mode 100644
index 0000000..d7aeb7e
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/SsoConstants.java
@@ -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";
+
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/consumer/readme.md b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/consumer/readme.md
new file mode 100644
index 0000000..e430f1c
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/consumer/readme.md
@@ -0,0 +1 @@
+# kafaka消费服务
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/auth/AdminAuthController.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/auth/AdminAuthController.java
new file mode 100644
index 0000000..7a12201
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/auth/AdminAuthController.java
@@ -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 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 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);
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/auth/AuthController.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/auth/AuthController.java
new file mode 100644
index 0000000..f3d2cb1
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/auth/AuthController.java
@@ -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 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> logout(HttpServletRequest request) {
+ List 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 refreshToken(@RequestParam("refreshToken") String refreshToken) {
+ return success(authService.refreshToken(refreshToken));
+ }
+
+ @PostMapping("/sendMsg")
+ @PermitAll
+ @Operation(summary = "发送手机验证码")
+ public CommonResult sendMsg(@RequestBody @Valid SendMsgReqVO reqVO) throws Exception{
+ return success(authService.sendMsg(reqVO));
+ }
+
+ @PostMapping("/loginBySMS")
+ @PermitAll
+ @Operation(summary = "发送手机验证码")
+ public CommonResult 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 didBindPhone(@RequestBody @Valid DidBindPhoneReqVO reqVO) {
+ if (GyUtils.isNull(reqVO)) {
+ CommonResult result = CommonResult.success(null);
+ String msg = "入参为空!";
+ result.setMsg(msg);
+ result.setCode(GlobalErrorCodeConstants.ERROR.getCode());
+ return result;
+ }
+ if (GyUtils.isNull(reqVO.getReqId())) {
+ CommonResult 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);
+ }
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2BasicController.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2BasicController.java
new file mode 100644
index 0000000..17c1530
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2BasicController.java
@@ -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 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 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 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 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 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);
+ }
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2OpenController.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2OpenController.java
new file mode 100644
index 0000000..8bf857e
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2OpenController.java
@@ -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 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 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 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 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 格式,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 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 scopes = JsonUtils.toBean(scope, Map.class);
+ scopes = ObjectUtil.defaultIfNull(scopes, new HashMap());
+ // 校验 responseType 是否满足 code 或者 token 值
+ OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType);
+ //校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内
+ OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null,
+ grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri);
+
+ // code 授权码模式,则发放 code 授权码,并重定向
+ List 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 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;
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2TokenController.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2TokenController.java
new file mode 100644
index 0000000..79d80fb
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2TokenController.java
@@ -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 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 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 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 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;
+ }
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2UserController.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2UserController.java
new file mode 100644
index 0000000..d6ed7b4
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2UserController.java
@@ -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 getUserInfo(HttpServletRequest request) {
+ return success(yhxxService.getUserInfo(request));
+ }
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/session/SwitchSessionController.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/session/SwitchSessionController.java
new file mode 100644
index 0000000..302c244
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/session/SwitchSessionController.java
@@ -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 switchCompany(@RequestParam("token")String token,@RequestParam("jguuid")String jguuid) {
+ return CommonResult.success(switchSessionService.switchCompany(token,jguuid));
+ }
+
+ @PostMapping("/addCompany")
+ @Operation(summary = "新增机构")
+ @Override
+ public CommonResult addCompany(@RequestBody SwitchCompanyResDTO resDTO) {
+ return CommonResult.success(switchSessionService.addCompany(resDTO));
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/thirdparty/DidController.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/thirdparty/DidController.java
new file mode 100644
index 0000000..1eed654
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/thirdparty/DidController.java
@@ -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 getLoginUrlQRCode() {
+ return getQrCode(XTCS_CODE_TXW_DID_LOGIN_VP_URL);
+ }
+
+ private CommonResult getQrCode(String xtcsCode) {
+ CommonResult 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 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> result =Response.SuccessResponse();
+// Map 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 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 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 proofList = new ArrayList();
+// VerifiablePresentation loginvp = new VerifiablePresentation();
+ VerifiablePresentationBuilder verifiablePresentationBuilder = VerifiablePresentation.builder();
+ verifiablePresentationBuilder.presentationUsage("DID_LOGIN_REQUEST");
+ verifiablePresentationBuilder.proof(proofList);
+ verifiablePresentationBuilder.extend(extend);
+ VerifiablePresentation loginvp = verifiablePresentationBuilder.build();
+ Response> res = didClient.vpSign(loginvp, DidLoginExtend.class);
+ log.info("res:{}", res);
+ String ret = JsonUtils.toJson(res.getData());
+ log.info("ret:{}", ret);
+
+ Response 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 vpRequestBody) {
+ Response 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 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 login = didClient.login(username, pwd);
+ Response 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> 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 login = didClient.login(username, pwd);
+ log.info("did控制台登录响应:{}", login);
+ List proofList = new ArrayList();
+// Proof pr = Proof.builder()
+// .build();
+ VerifiablePresentation 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 vctId = Arrays.asList("100001");
+ String condid = XtcsUtils.getXtcs(XTCS_CODE_TXW_DID_CONSOLE_DIDCODE);
+ condid = "did:cndid:cndid";
+ List 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> res = didClient.vpCreateApplyBusinessLicense(busivp);
+ log.info("res:{}", res);
+// return res;
+ Response 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 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 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 getLoginCallbackResult(@RequestParam(required = false, name = "reqId") String reqId) {
+ CommonResult 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 getBusiLiceCallbackResult(@RequestParam(required = false, name = "reqId") String reqId) {
+ CommonResult 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;
+ }
+
+}
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/thirdparty/GssbController.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/thirdparty/GssbController.java
new file mode 100644
index 0000000..7fada22
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/thirdparty/GssbController.java
@@ -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 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);
+// }
+//
+//}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/verify/VerifyController.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/verify/VerifyController.java
new file mode 100644
index 0000000..66a1cf9
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/verify/VerifyController.java
@@ -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 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;
+ }
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/convert/AuthConvert.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/convert/AuthConvert.java
new file mode 100644
index 0000000..a54ab08
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/convert/AuthConvert.java
@@ -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);
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/convert/OAuth2OpenConvert.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/convert/OAuth2OpenConvert.java
new file mode 100644
index 0000000..2cda29e
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/convert/OAuth2OpenConvert.java
@@ -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;
+ }
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/enums/CommonStatusEnum.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/enums/CommonStatusEnum.java
new file mode 100644
index 0000000..567f1ec
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/enums/CommonStatusEnum.java
@@ -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);
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/enums/UserTypeEnum.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/enums/UserTypeEnum.java
new file mode 100644
index 0000000..7eed315
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/enums/UserTypeEnum.java
@@ -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());
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/job/readme.md b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/job/readme.md
new file mode 100644
index 0000000..99d906d
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/job/readme.md
@@ -0,0 +1 @@
+# 定时任务
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2AccessTokenMapper.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2AccessTokenMapper.java
new file mode 100644
index 0000000..13bdd9f
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2AccessTokenMapper.java
@@ -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 {
+
+ default OAuth2AccessTokenDO selectByAccessToken(String accessToken) {
+ return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken);
+ }
+
+ default List selectByYhuuid(String yhuuid) {
+ return selectList(OAuth2AccessTokenDO::getYhUuid, yhuuid);
+ }
+
+ default List selectRefreshTokenByAccessTokens(List accessTokens) {
+ QueryWrapperX wrapperX = new QueryWrapperX<>();
+ wrapperX.lambda()
+// .select(OAuth2AccessTokenDO::getRefreshToken)
+ .in(OAuth2AccessTokenDO::getAccessToken, accessTokens);
+ return selectList(wrapperX);
+ }
+
+
+ default List selectListByRefreshToken(String refreshToken) {
+ return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
+ }
+
+ default Integer updateCompany(String accessToken,String jguuid){
+ UpdateWrapper 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 updateWrapper = new LambdaUpdateWrapper<>();
+ updateWrapper.set(OAuth2AccessTokenDO::getGllp,gllp)
+ .eq(OAuth2AccessTokenDO::getUuid,uuid);
+ return update(updateWrapper);
+ }
+
+ default Integer deleteByTokens(List tokens){
+ QueryWrapperX wrapperX = new QueryWrapperX<>();
+ wrapperX.lambda().in(OAuth2AccessTokenDO::getAccessToken, tokens);
+ return delete(wrapperX);
+ }
+
+ default Integer updateGllpByTokens(List tokens,String gllp){
+ LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>();
+ updateWrapper.set(OAuth2AccessTokenDO::getGllp,gllp)
+ .in(OAuth2AccessTokenDO::getUuid,tokens);
+ return update(updateWrapper);
+ }
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2ClientMapper.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2ClientMapper.java
new file mode 100644
index 0000000..640029e
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2ClientMapper.java
@@ -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 {
+
+ default OAuth2ClientDO selectByClientId(String clientId) {
+ return selectOne(OAuth2ClientDO::getClientid, clientId);
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2CodeMapper.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2CodeMapper.java
new file mode 100644
index 0000000..016de7e
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2CodeMapper.java
@@ -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 {
+
+ default OAuth2CodeDO selectByCode(String code) {
+ return selectOne(OAuth2CodeDO::getSqm, code);
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2RefreshTokenMapper.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2RefreshTokenMapper.java
new file mode 100644
index 0000000..39e3568
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2RefreshTokenMapper.java
@@ -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 {
+
+ default int deleteByRefreshToken(String refreshToken) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken));
+ }
+
+ default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) {
+ return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
+ }
+
+ default Integer updateJguuid(String refreshToken,String currentJguuid){
+ UpdateWrapper 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 tokens){
+ QueryWrapperX wrapperX = new QueryWrapperX<>();
+ wrapperX.lambda().in(OAuth2RefreshTokenDO::getRefreshToken, tokens);
+ return delete(wrapperX);
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/readme.md b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/readme.md
new file mode 100644
index 0000000..2e03f4f
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/readme.md
@@ -0,0 +1 @@
+# mapper 建议按业务分包
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2AccessTokenDO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2AccessTokenDO.java
new file mode 100644
index 0000000..e453280
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2AccessTokenDO.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2ApproveDO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2ApproveDO.java
new file mode 100644
index 0000000..dd4c14f
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2ApproveDO.java
@@ -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") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2ClientDO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2ClientDO.java
new file mode 100644
index 0000000..375e2a6
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2ClientDO.java
@@ -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;
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2CodeDO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2CodeDO.java
new file mode 100644
index 0000000..57b0309
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2CodeDO.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2RefreshTokenDO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2RefreshTokenDO.java
new file mode 100644
index 0000000..9f5b714
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2RefreshTokenDO.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/readme.md b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/readme.md
new file mode 100644
index 0000000..bde432c
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/readme.md
@@ -0,0 +1 @@
+# dto包
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/AccessTokenInfo.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/AccessTokenInfo.java
new file mode 100644
index 0000000..4d048d7
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/AccessTokenInfo.java
@@ -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;
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/SessionInfo.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/SessionInfo.java
new file mode 100644
index 0000000..ecded9f
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/SessionInfo.java
@@ -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;
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/YhInfo.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/YhInfo.java
new file mode 100644
index 0000000..9fb6e6e
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/YhInfo.java
@@ -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;
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/yhxx/YhxxDTO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/yhxx/YhxxDTO.java
new file mode 100644
index 0000000..0eeaeeb
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/yhxx/YhxxDTO.java
@@ -0,0 +1,4 @@
+package com.css.txw.sso.pojo.dto.yhxx;
+
+public class YhxxDTO {
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/AuthLoginReqVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/AuthLoginReqVO.java
new file mode 100644
index 0000000..26c9f2c
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/AuthLoginReqVO.java
@@ -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);
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/AuthLoginRespVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/AuthLoginRespVO.java
new file mode 100644
index 0000000..58624ab
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/AuthLoginRespVO.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/DidBindPhoneReqVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/DidBindPhoneReqVO.java
new file mode 100644
index 0000000..d341709
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/DidBindPhoneReqVO.java
@@ -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;
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/LoginUser.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/LoginUser.java
new file mode 100644
index 0000000..caee559
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/LoginUser.java
@@ -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 scopes;
+
+
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/SMSLoginReqVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/SMSLoginReqVO.java
new file mode 100644
index 0000000..57f638d
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/SMSLoginReqVO.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/SendMsgReqVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/SendMsgReqVO.java
new file mode 100644
index 0000000..bab0724
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/SendMsgReqVO.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/LogoutVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/LogoutVO.java
new file mode 100644
index 0000000..7bfa833
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/LogoutVO.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenAccessTokenRespVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenAccessTokenRespVO.java
new file mode 100644
index 0000000..5cc0683
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenAccessTokenRespVO.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenAuthorizeInfoRespVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenAuthorizeInfoRespVO.java
new file mode 100644
index 0000000..0f80df9
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenAuthorizeInfoRespVO.java
@@ -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 是 scope,Value 为是否选中", requiredMode = Schema.RequiredMode.REQUIRED)
+ private List> 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;
+
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenCheckTokenRespVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenCheckTokenRespVO.java
new file mode 100644
index 0000000..3b6a8f6
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenCheckTokenRespVO.java
@@ -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 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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2UserInfoRespVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2UserInfoRespVO.java
new file mode 100644
index 0000000..4670ecc
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2UserInfoRespVO.java
@@ -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;
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/SwjgTreeVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/SwjgTreeVO.java
new file mode 100644
index 0000000..fe7a3b3
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/SwjgTreeVO.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/readme.md b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/readme.md
new file mode 100644
index 0000000..0303c54
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/readme.md
@@ -0,0 +1 @@
+# vo包
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/GssbTokenVO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/GssbTokenVO.java
new file mode 100644
index 0000000..31b315d
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/GssbTokenVO.java
@@ -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;
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidCallbackExtendInfo.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidCallbackExtendInfo.java
new file mode 100644
index 0000000..c4e5f63
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidCallbackExtendInfo.java
@@ -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;
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidCredentialSubject.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidCredentialSubject.java
new file mode 100644
index 0000000..39dccda
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidCredentialSubject.java
@@ -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 ;
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidExtendVcsInfo.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidExtendVcsInfo.java
new file mode 100644
index 0000000..8e6b674
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidExtendVcsInfo.java
@@ -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 ;
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidLoginExtend.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidLoginExtend.java
new file mode 100644
index 0000000..1f9c745
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidLoginExtend.java
@@ -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 vcs;
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/QrCodeInfo.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/QrCodeInfo.java
new file mode 100644
index 0000000..9889512
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/QrCodeInfo.java
@@ -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;
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/properties/SsoProperties.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/properties/SsoProperties.java
new file mode 100644
index 0000000..922e817
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/properties/SsoProperties.java
@@ -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;
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/properties/readme.md b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/properties/readme.md
new file mode 100644
index 0000000..a94e6df
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/properties/readme.md
@@ -0,0 +1 @@
+# 配置类
\ No newline at end of file
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/AdminAuthService.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/AdminAuthService.java
new file mode 100644
index 0000000..619adf1
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/AdminAuthService.java
@@ -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);
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/AuthService.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/AuthService.java
new file mode 100644
index 0000000..8826fc4
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/AuthService.java
@@ -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 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);
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/impl/AdminAuthServiceImpl.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/impl/AdminAuthServiceImpl.java
new file mode 100644
index 0000000..80142cc
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/impl/AdminAuthServiceImpl.java
@@ -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);
+ }
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/impl/AuthServiceImpl.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/impl/AuthServiceImpl.java
new file mode 100644
index 0000000..f369ad0
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/impl/AuthServiceImpl.java
@@ -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 logout(String token, Integer logType) {
+ List result = new ArrayList<>();
+
+ final List 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 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 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 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);
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2ClientService.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2ClientService.java
new file mode 100644
index 0000000..45bc50a
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2ClientService.java
@@ -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 scopes, String redirectUri);
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2ClientServiceImpl.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2ClientServiceImpl.java
new file mode 100644
index 0000000..b78e71b
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2ClientServiceImpl.java
@@ -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 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());
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2CodeService.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2CodeService.java
new file mode 100644
index 0000000..bc85c7b
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2CodeService.java
@@ -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 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);
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2CodeServiceImpl.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2CodeServiceImpl.java
new file mode 100644
index 0000000..8c9159e
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2CodeServiceImpl.java
@@ -0,0 +1,103 @@
+package com.css.txw.sso.service.oauth2;
+
+import cn.hutool.core.util.IdUtil;
+import com.css.txw.sso.constants.ErrorCodeConstants;
+import com.css.txw.sso.mapper.oauth2.OAuth2CodeMapper;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2CodeDO;
+import com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil;
+import com.css.ggzc.framework.common.util.date.DateUtils;
+import com.css.ggzc.framework.session.SessionUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.util.Date;
+import java.util.List;
+
+import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.exception;
+
+
+/**
+ * OAuth2.0 授权码 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class OAuth2CodeServiceImpl implements OAuth2CodeService {
+
+ /**
+ * 授权码的过期时间,默认 5 分钟
+ */
+ private static final Integer TIMEOUT = 5 * 60;
+
+ @Resource
+ private OAuth2CodeMapper oauth2CodeMapper;
+
+ @Override
+ public OAuth2CodeDO createAuthorizationCode(String userId, Integer userType, String clientId,
+ List scopes, String redirectUri, String state) {
+ OAuth2CodeDO codeDO = new OAuth2CodeDO().setSqm(generateCode())
+ .setUuid(IdUtil.fastSimpleUUID())
+ .setYhUuid(SessionUtils.getYhUuid())
+ .setClientid(clientId)
+ .setGqsj(LocalDateTime.now().plusSeconds(TIMEOUT))
+ .setCdxdz(redirectUri)
+ .setRzzt(state);
+ codeDO.setYwqdDm("ZNSB.SSO");
+ SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
+ String now = sdf.format(new Date());
+ codeDO.setLrrq(now);
+ codeDO.setXgrq(now);
+ codeDO.setSjcsdq("00000000000");
+ codeDO.setSjgsdq("00000000000");
+ codeDO.setLrrsfid(SessionUtils.getYhUuid());
+ codeDO.setXgrsfid(SessionUtils.getYhUuid());
+ codeDO.setSjtbSj(now);
+ oauth2CodeMapper.insert(codeDO);
+ return codeDO;
+ }
+
+ @Override
+ public OAuth2CodeDO createAuthorizationCode(String clientId, String redirectUri, String state,String currentToken) {
+ OAuth2CodeDO codeDO = new OAuth2CodeDO().setSqm(generateCode())
+ .setUuid(IdUtil.fastSimpleUUID())
+ .setYhUuid(SessionUtils.getYhUuid())
+ .setClientid(clientId)
+ .setGqsj(LocalDateTime.now().plusSeconds(TIMEOUT))
+ .setCdxdz(redirectUri)
+ .setRzzt(state).setAccessToken(currentToken);
+ codeDO.setYwqdDm("ZNSB.SSO");
+ SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
+ String now = sdf.format(new Date());
+ codeDO.setLrrq(now);
+ codeDO.setXgrq(now);
+ codeDO.setSjcsdq("00000000000");
+ codeDO.setSjgsdq("00000000000");
+ codeDO.setLrrsfid(SessionUtils.getYhUuid());
+ codeDO.setXgrsfid(SessionUtils.getYhUuid());
+ codeDO.setSjtbSj(now);
+ oauth2CodeMapper.insert(codeDO);
+ return codeDO;
+ }
+
+ @Override
+ public OAuth2CodeDO consumeAuthorizationCode(String code) {
+ OAuth2CodeDO codeDO = oauth2CodeMapper.selectByCode(code);
+ if (codeDO == null) {
+ throw ServiceExceptionUtil.exception(ErrorCodeConstants.OAUTH2_CODE_NOT_EXISTS);
+ }
+ if (DateUtils.isExpired(codeDO.getGqsj())) {
+ throw exception(ErrorCodeConstants.OAUTH2_CODE_EXPIRE);
+ }
+ oauth2CodeMapper.deleteById(codeDO.getUuid());
+ return codeDO;
+ }
+
+ private static String generateCode() {
+ return IdUtil.fastSimpleUUID();
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2GrantService.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2GrantService.java
new file mode 100644
index 0000000..23a1490
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2GrantService.java
@@ -0,0 +1,83 @@
+package com.css.txw.sso.service.oauth2;
+
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
+
+import java.util.List;
+
+/**
+ * OAuth2 授予 Service 接口
+ *
+ * 从功能上,和 Spring Security OAuth 的 TokenGranter 的功能,提供访问令牌、刷新令牌的操作
+ *
+ * 将自身的 AdminUser 用户,授权给第三方应用,采用 OAuth2.0 的协议。
+ *
+ * 问题:为什么自身也作为一个第三方应用,也走这套流程呢?
+ * 回复:当然可以这么做,采用 password 模式。考虑到大多数开发者使用不到这个特性,OAuth2.0 毕竟有一定学习成本,所以暂时没有采取这种方式。
+ *
+ * @author 芋道源码
+ */
+public interface OAuth2GrantService {
+
+
+ /**
+ * 授权码模式,第一阶段,获得 code 授权码
+ *
+ * 对应 Spring Security OAuth2 的 AuthorizationEndpoint 的 generateCode 方法
+ *
+ * @param userId 用户编号
+ * @param userType 用户类型
+ * @param clientId 客户端编号
+ * @param scopes 授权范围
+ * @param redirectUri 重定向 URI
+ * @param state 状态
+ * @return 授权码
+ */
+ String grantAuthorizationCodeForCode(String userId, Integer userType,
+ String clientId, List scopes,
+ String redirectUri, String state);
+
+ /**
+ * 授权码模式,第一阶段,获得 code 授权码
+ * @param clientId 客户端编号
+ * @param redirectUri 重定向 URI
+ * @param state 状态
+ * @return 授权码
+ */
+ String grantAuthorizationCodeForCode(String clientId, String redirectUri, String state,String currentToken);
+
+ /**
+ * 授权码模式,第二阶段,获得 accessToken 访问令牌
+ * @param clientId 客户端编号
+ * @param code 授权码
+ * @param redirectUri 重定向 URI
+ * @param state 状态
+ * @return 访问令牌
+ */
+ OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code,
+ String redirectUri, String state);
+
+
+ /**
+ * 授权码模式,第二阶段,获得 accessToken 访问令牌
+ * @param clientId 客户端编号
+ * @param code 授权码
+ * @param redirectUri 重定向 URI
+ * @param state 状态
+ * @return 访问令牌
+ */
+ OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(OAuth2ClientDO client, String code,
+ String redirectUri);
+
+ /**
+ * 移除访问令牌
+ *
+ * 对应 Spring Security OAuth2 的 ConsumerTokenServices 的 revokeToken 方法
+ *
+ * @param accessToken 访问令牌
+ * @param clientId 客户端编号
+ * @return 是否移除到
+ */
+ boolean revokeToken(String clientId, String accessToken);
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2GrantServiceImpl.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2GrantServiceImpl.java
new file mode 100644
index 0000000..d8b6556
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2GrantServiceImpl.java
@@ -0,0 +1,92 @@
+package com.css.txw.sso.service.oauth2;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.css.txw.sso.constants.ErrorCodeConstants;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2CodeDO;
+import com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+import java.util.List;
+
+import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.exception;
+
+
+/**
+ * OAuth2 授予 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+public class OAuth2GrantServiceImpl implements OAuth2GrantService {
+
+ @Resource
+ private OAuth2TokenService oauth2TokenService;
+ @Resource
+ private OAuth2CodeService oauth2CodeService;
+
+ @Override
+ public String grantAuthorizationCodeForCode(String userId, Integer userType,
+ String clientId, List scopes,
+ String redirectUri, String state) {
+ return oauth2CodeService.createAuthorizationCode(userId, userType, clientId, scopes,
+ redirectUri, state).getSqm();
+ }
+
+
+ @Override
+ public String grantAuthorizationCodeForCode(String clientId, String redirectUri, String state,String currentToken) {
+ return oauth2CodeService.createAuthorizationCode(clientId,redirectUri,state,currentToken).getSqm();
+ }
+
+ @Override
+ public OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code,
+ String redirectUri, String state) {
+ OAuth2CodeDO codeDO = oauth2CodeService.consumeAuthorizationCode(code);
+ Assert.notNull(codeDO, "授权码不能为空"); // 防御性编程
+ // 校验 clientId 是否匹配
+ if (!StrUtil.equals(clientId, codeDO.getClientid())) {
+ throw ServiceExceptionUtil.exception(ErrorCodeConstants.OAUTH2_GRANT_CLIENT_ID_MISMATCH);
+ }
+ // 校验 redirectUri 是否匹配
+ if (!StrUtil.equals(redirectUri, codeDO.getCdxdz())) {
+ throw exception(ErrorCodeConstants.OAUTH2_GRANT_REDIRECT_URI_MISMATCH);
+ }
+
+ // 创建访问令牌
+ return oauth2TokenService.createAccessToken(codeDO.getYhUuid(), codeDO.getClientid(), Collections.singletonList(codeDO.getSqnr()));
+ }
+
+ @Override
+ public OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(OAuth2ClientDO client, String code, String redirectUri) {
+ OAuth2CodeDO codeDO = oauth2CodeService.consumeAuthorizationCode(code);
+ Assert.notNull(codeDO, "授权码不能为空"); // 防御性编程
+ // 校验 clientId 是否匹配
+ if (!StrUtil.equals(client.getClientid(), codeDO.getClientid())) {
+ throw exception(ErrorCodeConstants.OAUTH2_GRANT_CLIENT_ID_MISMATCH);
+ }
+ // 校验 redirectUri 是否匹配
+ if (!StrUtil.equals(redirectUri, codeDO.getCdxdz())) {
+ throw exception(ErrorCodeConstants.OAUTH2_GRANT_REDIRECT_URI_MISMATCH);
+ }
+
+ return oauth2TokenService.createAccessToken(codeDO,client);
+ }
+
+ @Override
+ public boolean revokeToken(String clientId, String accessToken) {
+ // 先查询,保证 clientId 时匹配的
+ OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.getAccessToken(accessToken);
+ if (accessTokenDO == null || ObjectUtil.notEqual(clientId, accessTokenDO.getClientid())) {
+ return false;
+ }
+ // 再删除
+ return oauth2TokenService.removeAccessToken(clientId,accessToken) != null;
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2TokenService.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2TokenService.java
new file mode 100644
index 0000000..a4d27ad
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2TokenService.java
@@ -0,0 +1,88 @@
+package com.css.txw.sso.service.oauth2;
+
+
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2CodeDO;
+import com.css.txw.sso.pojo.dto.session.AccessTokenInfo;
+import com.css.txw.sso.pojo.dto.session.SessionInfo;
+
+import java.util.List;
+
+/**
+ * OAuth2.0 Token Service 接口
+ * 从功能上,和 Spring Security OAuth 的 DefaultTokenServices + JdbcTokenStore 的功能,提供访问令牌、刷新令牌的操作
+ */
+public interface OAuth2TokenService {
+
+ /**
+ * 创建访问令牌
+ * 注意:该流程中,会包含创建刷新令牌的创建
+ *
+ * 参考 DefaultTokenServices 的 createAccessToken 方法
+ *
+ * @param userId 用户编号
+ * @param clientId 客户端编号
+ * @param scopes 授权范围
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenDO createAccessToken(String userId, String clientId, List scopes);
+
+
+ OAuth2AccessTokenDO createAdminAccessToken(String userId, String clientId);
+
+
+ /**
+ * 创建令牌
+ * @param codeDO 授权码
+ * @return 访问令牌
+ */
+ OAuth2AccessTokenDO createAccessToken(OAuth2CodeDO codeDO, OAuth2ClientDO client);
+
+ /**
+ * 刷新访问令牌
+ *
+ * 参考 DefaultTokenServices 的 refreshAccessToken 方法
+ *
+ * @param refreshToken 刷新令牌
+ * @param clientId 客户端编号
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId);
+
+ /**
+ * 获得访问令牌
+ *
+ * 参考 DefaultTokenServices 的 getAccessToken 方法
+ *
+ * @param accessToken 访问令牌
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenDO getAccessToken(String accessToken);
+
+ /**
+ * 校验访问令牌
+ *
+ * @param accessToken 访问令牌
+ * @return 访问令牌的信息
+ */
+ SessionInfo checkAccessToken(String accessToken);
+
+
+ AccessTokenInfo checkThirdPartyToken(String accessToken);
+
+ /**
+ * 移除访问令牌
+ * 注意:该流程中,会移除相关的刷新令牌
+ *
+ * 参考 DefaultTokenServices 的 revokeToken 方法
+ *
+ * @param accessToken 刷新令牌
+ * @return 访问令牌的信息
+ */
+ OAuth2AccessTokenDO removeAccessToken(String clientId,String accessToken);
+
+ List removeAccessToken(String accessToken);
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2TokenServiceImpl.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2TokenServiceImpl.java
new file mode 100644
index 0000000..f235ecb
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2TokenServiceImpl.java
@@ -0,0 +1,475 @@
+package com.css.txw.sso.service.oauth2;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.ObjectUtil;
+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.mapper.oauth2.OAuth2AccessTokenMapper;
+import com.css.txw.sso.mapper.oauth2.OAuth2RefreshTokenMapper;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2CodeDO;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2RefreshTokenDO;
+import com.css.txw.sso.pojo.dto.session.AccessTokenInfo;
+import com.css.txw.sso.pojo.dto.session.SessionInfo;
+import com.css.txw.sso.service.yhxx.YhxxService;
+import com.css.txw.sso.util.OAuth2AccessTokenRedisDAO;
+import com.css.ggzc.framework.common.exception.enums.GlobalErrorCodeConstants;
+import com.css.ggzc.framework.common.util.date.DateUtils;
+import com.css.ggzc.framework.common.util.object.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.exception0;
+import static com.css.ggzc.framework.common.util.collection.CollectionUtils.convertSet;
+
+
+/**
+ * OAuth2.0 Token Service 实现类
+ */
+@Service
+public class OAuth2TokenServiceImpl implements OAuth2TokenService {
+
+ @Resource
+ private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
+ @Resource
+ private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;
+
+ @Resource
+ private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;
+
+ @Resource
+ private OAuth2ClientService oauth2ClientService;
+
+ @Resource
+ private YhxxService yhxxService;
+
+ @Override
+ @Transactional
+ public OAuth2AccessTokenDO createAccessToken(String userId, String clientId, List scopes) {
+ OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
+ SessionInfo sessionInfo = new SessionInfo();
+ if (!SsoConstants.CLIENT_DEFAULT.equals(clientDO.getClientid())){
+ // 创建刷新令牌
+ sessionInfo = createOAuth2RefreshToken(userId, clientDO, scopes);
+ }else {
+ sessionInfo.setCurrentYhuuid(userId);
+ }
+ // 创建访问令牌
+ return createOAuth2AccessToken(sessionInfo, clientDO,null);
+ }
+
+ @Override
+ public OAuth2AccessTokenDO createAdminAccessToken(String userId, String clientId) {
+ OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
+ final SessionInfo sessionInfo = yhxxService.initAdminInfo(userId);
+
+ OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
+ .setUuid(IdUtil.fastSimpleUUID())
+ .setYhUuid(sessionInfo.getCurrentYhuuid())
+ .setClientid(clientDO.getClientid())
+ .setRefreshToken("")
+ .setGqsj(LocalDateTime.now().plusSeconds(Long.parseLong(clientDO.getFwlpyxq())));
+ accessTokenDO.setYwqdDm("ZNSB.SSO");
+ accessTokenDO.setLrrsfid("ZNSB.SSO");
+ accessTokenDO.setXgrsfid("ZNSB.SSO");
+ accessTokenDO.setSjcsdq("0000000000");
+ accessTokenDO.setSjgsdq("0000000000");
+ oauth2AccessTokenMapper.insert(accessTokenDO);
+
+ final AccessTokenInfo accessTokenInfo = new AccessTokenInfo();
+ accessTokenInfo.setAccessToken(accessTokenDO.getAccessToken());
+ accessTokenInfo.setRefreshToken(accessTokenDO.getRefreshToken());
+ accessTokenInfo.setUuid(accessTokenDO.getUuid());
+ accessTokenInfo.setYhUuid(accessTokenDO.getYhUuid());
+ accessTokenInfo.setQyuuid(accessTokenDO.getQyuuid());
+ accessTokenInfo.setGqsj(accessTokenDO.getGqsj());
+ accessTokenInfo.setClientid(accessTokenDO.getClientid());
+ accessTokenInfo.setSqnr(accessTokenDO.getSqnr());
+ accessTokenInfo.setGllp(accessTokenDO.getGllp());
+ sessionInfo.setTokenInfo(accessTokenInfo);
+ // 记录到 Redis 中
+ oauth2AccessTokenRedisDAO.set(sessionInfo);
+ return accessTokenDO;
+
+ }
+
+ @Override
+ public OAuth2AccessTokenDO createAccessToken(OAuth2CodeDO codeDO, OAuth2ClientDO client) {
+ //创建刷新令牌
+ final String oAuth2RefreshToken = createOAuth2RefreshToken(codeDO, client);
+ //创建访问令牌
+ final OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(oAuth2RefreshToken, codeDO, client);
+ //更新本系统令牌的关联第三方令牌
+ final OAuth2AccessTokenDO tokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getGllp());
+ final String oldGllp = tokenDO.getGllp();
+ if (!GyUtils.isNull(oldGllp)){
+ oauth2AccessTokenMapper.updateGllp(tokenDO.getUuid(),oldGllp+","+accessTokenDO.getAccessToken());
+ tokenDO.setGllp(oldGllp+","+accessTokenDO.getAccessToken());
+ }else {
+ oauth2AccessTokenMapper.updateGllp(tokenDO.getUuid(),accessTokenDO.getAccessToken());
+ tokenDO.setGllp(accessTokenDO.getAccessToken());
+ }
+
+ oauth2AccessTokenRedisDAO.updateAccessTokenInfo(tokenDO);
+
+ return accessTokenDO;
+ }
+
+ @Override
+ public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId) {
+ OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(refreshToken);
+ if (refreshTokenDO == null) {
+ throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "无效的刷新令牌");
+ }
+
+ // 校验 Client 匹配
+ OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
+ if (ObjectUtil.notEqual(clientId, refreshTokenDO.getClientid())) {
+ throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "刷新令牌的客户端编号不正确");
+ }
+
+ // 移除相关的访问令牌
+ List accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshToken);
+ if (CollUtil.isNotEmpty(accessTokenDOs)) {
+ oauth2AccessTokenMapper.deleteBatchIds(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getUuid));
+ oauth2AccessTokenRedisDAO.deleteList(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getAccessToken));
+ }
+
+ // 已过期的情况下,删除刷新令牌
+ if (DateUtils.isExpired(refreshTokenDO.getGqsj())) {
+ oauth2RefreshTokenMapper.deleteById(refreshTokenDO.getUuid());
+ throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "刷新令牌已过期");
+ }
+ final OAuth2AccessTokenDO oAuth2AccessTokenDO = accessTokenDOs.stream().max(Comparator.comparing(OAuth2AccessTokenDO::getGqsj)).get();
+ final OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(refreshTokenDO, clientDO, oAuth2AccessTokenDO.getGllp());
+
+ //更新本系统令牌的关联第三方令牌
+ final OAuth2AccessTokenDO tokenDO = oauth2AccessTokenMapper.selectByAccessToken(oAuth2AccessTokenDO.getGllp());
+ final List list = Arrays.asList(tokenDO.getGllp().split(","));
+ if (!GyUtils.isNull(list)){
+ StringJoiner stringJoiner = new StringJoiner(",");
+ final List collect = accessTokenDOs.stream().map(OAuth2AccessTokenDO::getAccessToken).collect(Collectors.toList());
+ final List collect1 = list.stream().filter(s -> !collect.contains(s)).collect(Collectors.toList());
+ if (!GyUtils.isNull(collect1)) {
+ collect1.forEach(stringJoiner::add);
+ }
+ stringJoiner.add(accessTokenDO.getAccessToken());
+ oauth2AccessTokenMapper.updateGllp(tokenDO.getUuid(),stringJoiner.toString());
+ tokenDO.setGllp(stringJoiner.toString());
+ }else {
+ oauth2AccessTokenMapper.updateGllp(tokenDO.getUuid(),"");
+ tokenDO.setGllp(accessTokenDO.getAccessToken());
+ }
+ oauth2AccessTokenRedisDAO.updateAccessTokenInfo(tokenDO);
+
+ return accessTokenDO;
+ }
+
+ @Override
+ public OAuth2AccessTokenDO getAccessToken(String accessToken) {
+ final AccessTokenInfo tokenInfo = oauth2AccessTokenRedisDAO.getTokenInfo(accessToken);
+ // 优先从 Redis 中获取
+ OAuth2AccessTokenDO accessTokenDO;
+ if (tokenInfo != null && tokenInfo.getAccessToken()!=null) {
+ return BeanUtils.toBean(tokenInfo,OAuth2AccessTokenDO.class);
+ }
+
+ // 获取不到,从 MySQL 中获取
+ accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken);
+ // 如果在 MySQL 存在,则往 Redis 中写入
+ if (accessTokenDO != null && !DateUtils.isExpired(accessTokenDO.getGqsj())) {
+ final AccessTokenInfo accessTokenInfo = new AccessTokenInfo();
+ accessTokenInfo.setAccessToken(accessTokenDO.getAccessToken());
+ accessTokenInfo.setRefreshToken(accessTokenDO.getRefreshToken());
+ accessTokenInfo.setUuid(accessTokenDO.getUuid());
+ accessTokenInfo.setYhUuid(accessTokenDO.getYhUuid());
+ accessTokenInfo.setQyuuid(accessTokenDO.getQyuuid());
+ accessTokenInfo.setGqsj(accessTokenDO.getGqsj());
+ accessTokenInfo.setClientid(accessTokenDO.getClientid());
+ accessTokenInfo.setSqnr(accessTokenDO.getSqnr());
+ accessTokenInfo.setGllp(accessTokenDO.getGllp());
+ final SessionInfo sessionInfo = yhxxService.getSessionInfo(accessTokenDO.getQyuuid(), accessTokenDO.getYhUuid());
+ oauth2AccessTokenRedisDAO.set(sessionInfo);
+ }
+ return accessTokenDO;
+ }
+
+ @Transactional
+ @Override
+ public SessionInfo checkAccessToken(String accessToken) {
+ OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
+ if (accessTokenDO == null) {
+ throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌不存在");
+ }
+ if (DateUtils.isExpired(accessTokenDO.getGqsj())) {
+ throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌已过期");
+ }
+
+ if (SsoConstants.CLIENT_DEFAULT.equals(accessTokenDO.getClientid())){
+ final long seconds = Duration.between(LocalDateTime.now(), accessTokenDO.getGqsj()).getSeconds();
+ if (seconds > 0 && 10 * 60 > seconds){
+ OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(accessTokenDO.getClientid());
+ final SessionInfo sessionInfo = yhxxService.getSessionInfo(accessTokenDO.getYhUuid(), accessTokenDO.getQyuuid());
+ sessionInfo.setQyuuid(accessTokenDO.getQyuuid());
+ sessionInfo.setCurrentYhuuid(accessTokenDO.getYhUuid());
+ final OAuth2AccessTokenDO newAccessToken = createOAuth2AccessToken(sessionInfo, clientDO,accessTokenDO.getGllp());
+ //刷新第三方令牌关联
+ if (!GyUtils.isNull(accessTokenDO.getGllp())){
+ final List list = Arrays.asList(accessTokenDO.getGllp().split(","));
+ oauth2AccessTokenMapper.updateGllpByTokens(list,newAccessToken.getAccessToken());
+ }
+ return oauth2AccessTokenRedisDAO.getSessionInfo(newAccessToken.getAccessToken());
+ }
+ }else {
+ //第三方令牌会话信息取关联令牌
+ return oauth2AccessTokenRedisDAO.getSessionInfo(accessTokenDO.getGllp());
+ }
+
+ return oauth2AccessTokenRedisDAO.getSessionInfo(accessToken);
+ }
+
+ @Override
+ public AccessTokenInfo checkThirdPartyToken(String accessToken) {
+ OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
+ if (accessTokenDO == null) {
+ throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌不存在");
+ }
+ if (DateUtils.isExpired(accessTokenDO.getGqsj())) {
+ throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌已过期");
+ }
+
+ return oauth2AccessTokenRedisDAO.getTokenInfo(accessToken);
+ }
+
+ @Transactional
+ @Override
+ public OAuth2AccessTokenDO removeAccessToken(String clientId,String accessToken) {
+ // 删除访问令牌
+ OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken);
+ if (SsoConstants.CLIENT_DEFAULT.equals(clientId)){
+ if (accessTokenDO == null) {
+ return null;
+ }
+ final String gllp = accessTokenDO.getGllp();
+ if (!GyUtils.isNull(gllp)){
+ final List list = Arrays.asList(gllp.split(","));
+ final List oAuth2AccessTokenDOS = oauth2AccessTokenMapper.selectRefreshTokenByAccessTokens(list);
+ if (!GyUtils.isNull(oAuth2AccessTokenDOS)){
+ final List collect = oAuth2AccessTokenDOS.stream().map(OAuth2AccessTokenDO::getRefreshToken).collect(Collectors.toList());
+ oauth2RefreshTokenMapper.deleteByTokens(collect);
+ }
+ oauth2AccessTokenMapper.deleteByTokens(list);
+ oauth2AccessTokenRedisDAO.deleteList(list);
+ }
+ oauth2AccessTokenMapper.deleteById(accessTokenDO.getUuid());
+ oauth2AccessTokenRedisDAO.delete(accessToken);
+ // 删除刷新令牌
+ oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken());
+ }else {
+ if (accessTokenDO == null) {
+ return null;
+ }
+ final String gllp = accessTokenDO.getGllp();
+ //更新本系统令牌的关联第三方令牌
+ final OAuth2AccessTokenDO tokenDO = oauth2AccessTokenMapper.selectByAccessToken(gllp);
+ final List list = Arrays.asList(tokenDO.getGllp().split(","));
+ final List collect = list.stream().filter(s -> !s.equals(accessTokenDO.getAccessToken())).collect(Collectors.toList());
+ if (!GyUtils.isNull(collect)){
+ StringJoiner stringJoiner = new StringJoiner(",");
+ collect.forEach(stringJoiner::add);
+ oauth2AccessTokenMapper.updateGllp(tokenDO.getUuid(),stringJoiner.toString());
+ tokenDO.setGllp(stringJoiner.toString());
+ }else {
+ oauth2AccessTokenMapper.updateGllp(tokenDO.getUuid(),"");
+ tokenDO.setGllp("");
+ }
+ oauth2AccessTokenRedisDAO.updateAccessTokenInfo(tokenDO);
+
+ oauth2AccessTokenMapper.deleteById(accessTokenDO.getUuid());
+ oauth2AccessTokenRedisDAO.delete(accessToken);
+ // 删除刷新令牌
+ oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken());
+ }
+
+ return accessTokenDO;
+ }
+
+ @Override
+ public List removeAccessToken(String accessToken) {
+ List result = new ArrayList<>();
+ OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken);
+ if (accessTokenDO == null) {
+ return null;
+ }
+ final String gllp = accessTokenDO.getGllp();
+ if (!GyUtils.isNull(gllp)){
+ final List list = Arrays.asList(gllp.split(","));
+ final List oAuth2AccessTokenDOS = oauth2AccessTokenMapper.selectRefreshTokenByAccessTokens(list);
+ result.addAll(oAuth2AccessTokenDOS);
+ if (!GyUtils.isNull(oAuth2AccessTokenDOS)){
+ final List collect = oAuth2AccessTokenDOS.stream().map(OAuth2AccessTokenDO::getRefreshToken).collect(Collectors.toList());
+ oauth2RefreshTokenMapper.deleteByTokens(collect);
+ }
+ oauth2AccessTokenMapper.deleteByTokens(list);
+ oauth2AccessTokenRedisDAO.deleteList(list);
+ }
+ oauth2AccessTokenMapper.deleteById(accessTokenDO.getUuid());
+ oauth2AccessTokenRedisDAO.delete(accessToken);
+ // 删除刷新令牌
+ oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken());
+
+ return result;
+ }
+
+ private OAuth2AccessTokenDO createOAuth2AccessToken(SessionInfo sessionInfo, OAuth2ClientDO clientDO, String gllp) {
+
+ String refreshToken = "";
+ if (GyUtils.isNull(sessionInfo.getTokenInfo())){
+ sessionInfo = yhxxService.initYhxx(sessionInfo.getCurrentYhuuid());
+ }else {
+ refreshToken = sessionInfo.getTokenInfo().getRefreshToken();
+ }
+ OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
+ .setUuid(IdUtil.fastSimpleUUID())
+ .setYhUuid(sessionInfo.getCurrentYhuuid())
+ .setClientid(clientDO.getClientid())
+ .setRefreshToken(refreshToken)
+ .setGqsj(LocalDateTime.now().plusSeconds(Long.parseLong(clientDO.getFwlpyxq())))
+ .setGllp(gllp);
+ accessTokenDO.setYwqdDm("SSO");
+ accessTokenDO.setLrrsfid("SSO");
+ accessTokenDO.setXgrsfid("SSO");
+ accessTokenDO.setSjcsdq("0000000000");
+ accessTokenDO.setSjgsdq("0000000000");
+ oauth2AccessTokenMapper.insert(accessTokenDO);
+
+ final AccessTokenInfo accessTokenInfo = new AccessTokenInfo();
+ accessTokenInfo.setAccessToken(accessTokenDO.getAccessToken());
+ accessTokenInfo.setRefreshToken(accessTokenDO.getRefreshToken());
+ accessTokenInfo.setUuid(accessTokenDO.getUuid());
+ accessTokenInfo.setYhUuid(accessTokenDO.getYhUuid());
+ accessTokenInfo.setQyuuid(accessTokenDO.getQyuuid());
+ accessTokenInfo.setGqsj(accessTokenDO.getGqsj());
+ accessTokenInfo.setClientid(accessTokenDO.getClientid());
+ accessTokenInfo.setSqnr(accessTokenDO.getSqnr());
+ accessTokenInfo.setGllp(accessTokenDO.getGllp());
+ sessionInfo.setTokenInfo(accessTokenInfo);
+ // 记录到 Redis 中
+ oauth2AccessTokenRedisDAO.set(sessionInfo);
+ return accessTokenDO;
+ }
+
+
+
+ private SessionInfo createOAuth2RefreshToken(String userId, OAuth2ClientDO clientDO, List scopes) {
+ final SessionInfo sessionInfo = yhxxService.initYhxx(userId);
+
+ OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken())
+ .setUuid(IdUtil.fastSimpleUUID())
+ .setYhUuid(userId)
+ .setQyuuid(sessionInfo.getQyuuid())
+ .setClientid(clientDO.getClientid())
+ .setGqsj(LocalDateTime.now().plusSeconds(Long.parseLong(clientDO.getSxlpyxq())));
+ refreshToken.setYwqdDm("SSO");
+ refreshToken.setLrrsfid("SSO");
+ refreshToken.setXgrsfid("SSO");
+ refreshToken.setSjcsdq("0000000000");
+ refreshToken.setSjgsdq("0000000000");
+ oauth2RefreshTokenMapper.insert(refreshToken);
+
+ final AccessTokenInfo accessTokenInfo = new AccessTokenInfo();
+ accessTokenInfo.setRefreshToken(refreshToken.getRefreshToken());
+ accessTokenInfo.setYhUuid(refreshToken.getYhUuid());
+ sessionInfo.setTokenInfo(accessTokenInfo);
+ return sessionInfo;
+ }
+
+ private String createOAuth2RefreshToken(OAuth2CodeDO codeDO,OAuth2ClientDO client) {
+ OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken())
+ .setUuid(IdUtil.fastSimpleUUID())
+ .setYhUuid(codeDO.getYhUuid())
+ .setClientid(client.getClientid())
+ .setGqsj(LocalDateTime.now().plusSeconds(Long.parseLong(client.getSxlpyxq())));
+ refreshToken.setYwqdDm("ZNSB.SSO");
+ refreshToken.setLrrsfid("ZNSB.SSO");
+ refreshToken.setXgrsfid("ZNSB.SSO");
+ refreshToken.setSjcsdq("0000000000");
+ refreshToken.setSjgsdq("0000000000");
+ oauth2RefreshTokenMapper.insert(refreshToken);
+ return refreshToken.getRefreshToken();
+ }
+
+ private OAuth2AccessTokenDO createOAuth2AccessToken(String refreshToken,OAuth2CodeDO codeDO, OAuth2ClientDO clientDO) {
+ OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
+ .setUuid(IdUtil.fastSimpleUUID())
+ .setYhUuid(codeDO.getYhUuid())
+ .setClientid(clientDO.getClientid())
+ .setRefreshToken(refreshToken)
+ .setGqsj(LocalDateTime.now().plusSeconds(Long.parseLong(clientDO.getFwlpyxq())))
+ .setGllp(codeDO.getAccessToken());
+ accessTokenDO.setYwqdDm("ZNSB.SSO");
+ accessTokenDO.setLrrsfid("ZNSB.SSO");
+ accessTokenDO.setXgrsfid("ZNSB.SSO");
+ accessTokenDO.setSjcsdq("0000000000");
+ accessTokenDO.setSjgsdq("0000000000");
+ oauth2AccessTokenMapper.insert(accessTokenDO);
+
+ final AccessTokenInfo thirdPartyTokenInfo = new AccessTokenInfo();
+ thirdPartyTokenInfo.setAccessToken(accessTokenDO.getAccessToken());
+ thirdPartyTokenInfo.setRefreshToken(accessTokenDO.getRefreshToken());
+ thirdPartyTokenInfo.setUuid(accessTokenDO.getUuid());
+ thirdPartyTokenInfo.setGqsj(accessTokenDO.getGqsj());
+ thirdPartyTokenInfo.setClientid(accessTokenDO.getClientid());
+ thirdPartyTokenInfo.setGllp(codeDO.getAccessToken());
+
+ // 记录到 Redis 中
+ oauth2AccessTokenRedisDAO.set(thirdPartyTokenInfo);
+ return accessTokenDO;
+ }
+
+ private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO,String gllp) {
+ OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
+ .setUuid(IdUtil.fastSimpleUUID())
+ .setYhUuid(refreshTokenDO.getYhUuid())
+ .setClientid(clientDO.getClientid())
+ .setRefreshToken(refreshTokenDO.getRefreshToken())
+ .setGqsj(LocalDateTime.now().plusSeconds(Long.parseLong(clientDO.getFwlpyxq())))
+ .setGllp(gllp);
+ accessTokenDO.setYwqdDm("ZNSB.SSO");
+ accessTokenDO.setLrrsfid("ZNSB.SSO");
+ accessTokenDO.setXgrsfid("ZNSB.SSO");
+ accessTokenDO.setSjcsdq("0000000000");
+ accessTokenDO.setSjgsdq("0000000000");
+ oauth2AccessTokenMapper.insert(accessTokenDO);
+
+ final AccessTokenInfo thirdPartyTokenInfo = new AccessTokenInfo();
+ thirdPartyTokenInfo.setAccessToken(accessTokenDO.getAccessToken());
+ thirdPartyTokenInfo.setRefreshToken(accessTokenDO.getRefreshToken());
+ thirdPartyTokenInfo.setUuid(accessTokenDO.getUuid());
+ thirdPartyTokenInfo.setGqsj(accessTokenDO.getGqsj());
+ thirdPartyTokenInfo.setClientid(accessTokenDO.getClientid());
+ thirdPartyTokenInfo.setGllp(gllp);
+
+ // 记录到 Redis 中
+ oauth2AccessTokenRedisDAO.set(thirdPartyTokenInfo);
+ return accessTokenDO;
+ }
+
+ private static String generateAccessToken() {
+ return IdUtil.fastSimpleUUID();
+ }
+
+ private static String generateRefreshToken() {
+ return IdUtil.fastSimpleUUID();
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/session/SwitchSessionService.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/session/SwitchSessionService.java
new file mode 100644
index 0000000..c9fead4
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/session/SwitchSessionService.java
@@ -0,0 +1,11 @@
+package com.css.txw.sso.service.session;
+
+
+import com.css.txw.sso.pojo.yhxx.SwitchCompanyResDTO;
+
+public interface SwitchSessionService {
+
+ SwitchCompanyResDTO switchCompany(String token, String jguuid);
+
+ String addCompany(SwitchCompanyResDTO resDTO);
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/session/SwitchSessionServiceImpl.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/session/SwitchSessionServiceImpl.java
new file mode 100644
index 0000000..ca1f1ee
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/session/SwitchSessionServiceImpl.java
@@ -0,0 +1,111 @@
+package com.css.txw.sso.service.session;
+
+import com.css.ggzc.framework.common.util.gy.GyUtils;
+import com.css.txw.sso.mapper.oauth2.OAuth2AccessTokenMapper;
+import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
+import com.css.txw.sso.pojo.dto.session.SessionInfo;
+import com.css.txw.sso.pojo.yhxx.SwitchCompanyResDTO;
+import com.css.txw.sso.util.OAuth2AccessTokenRedisDAO;
+import com.css.ggzc.framework.common.util.object.BeanUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class SwitchSessionServiceImpl implements SwitchSessionService{
+
+ @Resource
+ private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
+
+ @Resource
+ private OAuth2AccessTokenRedisDAO redisDAO;
+
+ @Transactional
+ @Override
+ public SwitchCompanyResDTO switchCompany(String token, String jguuid) {
+ log.info("切换企业接口收到入参 token:{},jguuid:{}",token,jguuid);
+ final SwitchCompanyResDTO switchCompanyResDTO = new SwitchCompanyResDTO();
+// SessionInfo sessionInfo = redisDAO.getSessionInfo(token);
+// final JgInfo oldjgxx = sessionInfo.getQyxx();
+// if (!GyUtils.isNull(oldjgxx)&&!GyUtils.isNull(oldjgxx.getQyuuid())){
+// final String oldId = oldjgxx.getQyuuid();
+// if (oldId.equals(jguuid)){
+// return switchCompanyResDTO;
+// }
+// }
+// final CktsMhqxQyxxbDO jgxx = jgxxbMapper.getQyxx(jguuid);
+// log.info("切换企业接口切换企业信息:{}",jgxx);
+// if (!GyUtils.isNull(jgxx)){
+// asyncTask.rpa(jgxx.getNsrsbh(),jgxx.getXzqhszDm());
+//
+// switchCompanyResDTO.setQyuuid(jguuid);
+// switchCompanyResDTO.setQymc(jgxx.getQymc());
+// switchCompanyResDTO.setNsrsbh(jgxx.getNsrsbh());
+// //机构信息
+// JgInfo jgInfo = new JgInfo();
+// jgInfo.setQyuuid(jgxx.getQyuuid());
+// jgInfo.setQymc(jgxx.getQymc());
+// jgInfo.setNsrsbh(jgxx.getNsrsbh());
+// jgInfo.setShxydm(jgxx.getShxydm());
+// jgInfo.setDjxh(jgxx.getDjxh());
+// jgInfo.setQydid(jgxx.getQydid());
+// jgInfo.setXzqhszDm(jgxx.getXzqhszDm());
+// jgInfo.setQylx2(jgxx.getQylx2());
+//
+// ZnsbMhzcQyjbxxmxDO qyjbxx = qyjbxxmxMapper.selectByDjxh(jgxx.getDjxh());
+// if (!GyUtils.isNull(qyjbxx)) {
+// jgInfo.setFddbrxm(qyjbxx.getFddbrxm());
+// jgInfo.setNsrztDm(qyjbxx.getNsrztDm());
+// }
+//
+// List nsrlxs = nsrzgxxMapper.getNsrlx(jgxx.getDjxh());
+// String ybnsrbz = !GyUtils.isNull(nsrlxs)&& Arrays.asList("1","3","4").contains(nsrlxs.get(0).getNsrlx())?"Y":"N";
+// jgInfo.setYbnsrbz(ybnsrbz);
+//
+// //备案信息
+// BaxxInfo baxxInfo = new BaxxInfo();
+// List cktsMhzcBaBaxxJgbs = baxxJgbMapper.queryBaxx(jgxx.getDjxh());
+// if (!GyUtils.isNull(cktsMhzcBaBaxxJgbs)){
+// CktsMhzcBaBaxxJgb cktsMhzcBaBaxxJgb = cktsMhzcBaBaxxJgbs.get(0);
+// baxxInfo = BeanUtils.toBean(cktsMhzcBaBaxxJgb, BaxxInfo.class);
+// String bachbz = cktsMhzcBaBaxxJgb.getBachbz();
+// //根据bachbz(备案撤回标志)判断,如果为Y,则nsrbaxxyxbz为N,否则为Y
+// String nsrbaxxyxbz = !GyUtils.isNull(baxxInfo)&&"Y".equals(bachbz)?"N":"Y";
+// baxxInfo.setNsrbaxxyxbz(nsrbaxxyxbz);
+// baxxInfo.setQylx(cktsMhzcBaBaxxJgb.getCktsqylxDm());
+// }
+//
+// //用户类型
+// List qyxx = yhjgGxbMapper.getQyxx(sessionInfo.getYhxx().getYhuuid(), jguuid,null);
+// String yhlx = "";
+// if (!GyUtils.isNull(qyxx)){
+// yhlx = qyxx.get(0).getYhlx();
+// }
+// redisDAO.refreshJgxx(token,jgInfo,baxxInfo,jgxx.getSsjswjgDm(),yhlx);
+// }
+// oauth2AccessTokenMapper.updateCompany(token,jguuid);
+// CktsMhqxYhxxbDO cktsMhqxYhxxbDO = new CktsMhqxYhxxbDO();
+// cktsMhqxYhxxbDO.setScdljguuid(jguuid);
+// cktsMhqxYhxxbDO.setYhUuid(sessionInfo.getYhxx().getYhuuid());
+// yhxxbMapper.updateById(cktsMhqxYhxxbDO);
+
+ return switchCompanyResDTO;
+ }
+
+ @Override
+ public String addCompany(SwitchCompanyResDTO resDTO) {
+ List oAuth2AccessTokenDOS = oauth2AccessTokenMapper.selectByYhuuid(resDTO.getYhuuid());
+ if (GyUtils.isNull(oAuth2AccessTokenDOS)) return "null";
+ oAuth2AccessTokenDOS.forEach(v->{
+ redisDAO.refreshQyxx(v.getAccessToken(),resDTO.getQyuuid(),resDTO.getQymc(),resDTO.getNsrsbh());
+ });
+ return "success";
+ }
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/verify/VerifyService.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/verify/VerifyService.java
new file mode 100644
index 0000000..f5b8a59
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/verify/VerifyService.java
@@ -0,0 +1,13 @@
+package com.css.txw.sso.service.verify;
+
+import com.css.ggzc.framework.common.pojo.CommonResult;
+
+public interface VerifyService {
+
+ CommonResult getVerifyToken(String remoteId);
+
+ Boolean checkVerifyToken(String verifyToken);
+
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/verify/VerifyServiceImpl.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/verify/VerifyServiceImpl.java
new file mode 100644
index 0000000..04e0c20
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/verify/VerifyServiceImpl.java
@@ -0,0 +1,41 @@
+package com.css.txw.sso.service.verify;
+
+import cn.hutool.core.util.IdUtil;
+import com.css.ggzc.framework.common.pojo.CommonResult;
+import com.css.txw.sso.properties.SsoProperties;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.concurrent.TimeUnit;
+
+
+@Service
+public class VerifyServiceImpl implements VerifyService {
+
+ @Resource
+ private StringRedisTemplate stringRedisTemplate;
+ @Resource
+ private SsoProperties ssoProperties;
+
+ @Override
+ public CommonResult getVerifyToken(String remoteId) {
+ //TODO: 根据ip限制访问次数
+ final String verifyToken = IdUtil.fastSimpleUUID();
+ stringRedisTemplate.opsForValue().set(formatKey(verifyToken), "1", 1, TimeUnit.MINUTES);
+ return CommonResult.success(verifyToken);
+ }
+
+ @Override
+ public Boolean checkVerifyToken(String verifyToken) {
+ if (!this.ssoProperties.isLoginCaptcha()) {
+ return true;
+ }
+ return stringRedisTemplate.hasKey(formatKey(verifyToken));
+ }
+
+ private static String formatKey(String verifyToken) {
+ String VERIFY_TOKEN = "verify_token:%s";
+ return String.format(VERIFY_TOKEN, verifyToken);
+ }
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/AccountLockService.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/AccountLockService.java
new file mode 100644
index 0000000..d24e7f1
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/AccountLockService.java
@@ -0,0 +1,11 @@
+package com.css.txw.sso.service.yhxx;
+
+public interface AccountLockService {
+
+ void handlePasswordError(String yhuuid);
+
+ Boolean checkLockStatus(String yhuuid);
+
+ void clearCache(String yhuuid);
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/YhxxService.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/YhxxService.java
new file mode 100644
index 0000000..bccf744
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/YhxxService.java
@@ -0,0 +1,44 @@
+package com.css.txw.sso.service.yhxx;
+
+import com.css.txw.mhzc.pojo.HtYhxxbDTO;
+import com.css.txw.mhzc.pojo.YhxxbDTO;
+import com.css.txw.sso.pojo.dto.session.SessionInfo;
+import com.css.txw.sso.pojo.vo.oauth2.OAuth2UserInfoRespVO;
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface YhxxService {
+
+ SessionInfo initYhxx(String yhuuid);
+
+ YhxxbDTO getYhxx(String yhuuid);
+
+ YhxxbDTO getYhxxByDid(String did);
+
+ SessionInfo initYhxxByDlzh(String dlzh);
+
+ YhxxbDTO getYhxxByDlzh(String dlzh);
+
+ SessionInfo initYhxxBySjhm(String sjhm);
+
+ YhxxbDTO getYhxxBySjhm(String sjhm);
+
+ SessionInfo initAdminInfo(String yhuuid);
+
+ HtYhxxbDTO getAdminInfo(String yhuuid);
+
+ HtYhxxbDTO getAdminInfoByDlzh(String dlzh);
+
+ SessionInfo getSessionInfo(String yhuuid,String jguuid);
+
+ OAuth2UserInfoRespVO getUserInfo(HttpServletRequest request);
+
+ YhxxbDTO saveYhxxByDid(YhxxbDTO yhxx);
+
+ YhxxbDTO getYhxxBySfzjhm(String string);
+
+ YhxxbDTO intQyxxByDid(YhxxbDTO qyxx);
+
+ YhxxbDTO updateDid(YhxxbDTO yhxx);
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/impl/AccountLockServiceImpl.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/impl/AccountLockServiceImpl.java
new file mode 100644
index 0000000..8e8190e
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/impl/AccountLockServiceImpl.java
@@ -0,0 +1,85 @@
+package com.css.txw.sso.service.yhxx.impl;
+
+import com.css.txw.sso.service.yhxx.AccountLockService;
+import com.css.ggzc.framework.common.util.gy.GyUtils;
+import com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_ACCOUNT_LOCK;
+import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_PASSWORD_ERROR_LOCK;
+
+@RefreshScope
+@Service
+public class AccountLockServiceImpl implements AccountLockService {
+
+ @Value("${accountlock.enable:false}")
+ private Boolean enableLock;
+
+ @Value("${accountlock.lockTime:30}")
+ private String lockTime;
+ @Value("${accountlock.lockLimit:4}")
+ private String lockLimit;
+ @Value("${accountlock.accountLockLimit:10}")
+ private String accountLockLimit;
+
+
+ @Resource
+ private StringRedisTemplate stringRedisTemplate;
+
+
+ @Override
+ public void handlePasswordError(String yhuuid) {
+ if (!enableLock){
+ return;
+ }
+ final String redisKey = formatCountKey(yhuuid);
+ final String value = stringRedisTemplate.opsForValue().get(redisKey);
+ if (!GyUtils.isNull(value)){
+ final Long increment = stringRedisTemplate.opsForValue().increment(redisKey);
+ if (increment % Long.parseLong(lockLimit) ==0 && increment!= Long.parseLong(accountLockLimit)){
+ stringRedisTemplate.opsForValue().set(formatStatusKey(yhuuid),"1",Long.parseLong(lockTime), TimeUnit.MINUTES);
+ throw ServiceExceptionUtil.exception(AUTH_LOGIN_PASSWORD_ERROR_LOCK,"密码输入错误次数过多,锁定{}分钟",lockTime);
+ }else if (increment == Long.parseLong(accountLockLimit)){
+// yhxxbMapper.lock(yhuuid,"密码输入错误次数过多",new Date(),"SSO");
+ throw ServiceExceptionUtil.exception(AUTH_LOGIN_ACCOUNT_LOCK);
+ }
+ }else {
+ stringRedisTemplate.opsForValue().increment(redisKey);
+ stringRedisTemplate.opsForValue().getOperations().expire(redisKey,2,TimeUnit.DAYS);
+ }
+ }
+
+ @Override
+ public Boolean checkLockStatus(String yhuuid) {
+ if (enableLock){
+ final String redisKey = formatStatusKey(yhuuid);
+ return stringRedisTemplate.hasKey(redisKey);
+ }else {
+ return false;
+ }
+ }
+
+ @Override
+ public void clearCache(String yhuuid) {
+ if (enableLock){
+ stringRedisTemplate.delete(formatCountKey(yhuuid));
+ }
+ }
+
+ private static String formatStatusKey(String yhuuid) {
+ String redisKeyPrefix = "account_lock_status:%s";
+ return String.format(redisKeyPrefix, yhuuid);
+ }
+
+ private static String formatCountKey(String yhuuid) {
+ String redisKeyPrefix = "account_lock_count:%s";
+ return String.format(redisKeyPrefix, yhuuid);
+ }
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/impl/YhxxServiceImpl.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/impl/YhxxServiceImpl.java
new file mode 100644
index 0000000..51f49eb
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/impl/YhxxServiceImpl.java
@@ -0,0 +1,247 @@
+package com.css.txw.sso.service.yhxx.impl;
+
+import com.css.ggzc.framework.common.pojo.CommonResult;
+import com.css.ggzc.framework.common.util.gy.GyUtils;
+import com.css.txw.mhzc.api.IMhzcApi;
+import com.css.txw.mhzc.pojo.HtYhxxbDTO;
+import com.css.txw.mhzc.pojo.YhxxReqDTO;
+import com.css.txw.mhzc.pojo.YhxxbDTO;
+import com.css.txw.sso.pojo.dto.session.SessionInfo;
+import com.css.txw.sso.pojo.dto.session.YhInfo;
+import com.css.txw.sso.pojo.vo.oauth2.OAuth2UserInfoRespVO;
+import com.css.txw.sso.service.oauth2.OAuth2TokenService;
+import com.css.txw.sso.service.yhxx.YhxxService;
+import com.css.ggzc.framework.common.util.object.BeanUtils;
+import com.css.ggzc.framework.session.SessionUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.server.ServerWebExchange;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.css.txw.sso.constants.SsoConstants.AUTHORIZATION_HEADER;
+
+@Slf4j
+@Service
+public class YhxxServiceImpl implements YhxxService {
+
+ @Resource
+ private IMhzcApi mhzcApi;
+
+ @Resource
+ private OAuth2TokenService oauth2TokenService;
+
+ @Override
+ public SessionInfo initYhxx(String yhuuid) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setYhuuid(yhuuid);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initSessionByYhuuid(yhxxReqDTO);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ SessionInfo sessionInfo = BeanUtils.toBean(data,SessionInfo.class);
+ sessionInfo.setCurrentYhuuid(data.getYhUuid());
+ YhInfo yhInfo = new YhInfo();
+ yhInfo.setYhuuid(data.getYhUuid());
+ yhInfo.setZsxm(data.getZsxm1());
+ yhInfo.setSjhm(data.getSjhm1());
+ yhInfo.setSfzjhm(data.getSfzjhm());
+ yhInfo.setSfzjlx(data.getSfzjlx());
+ yhInfo.setYhdid(data.getDid());
+ sessionInfo.setYhxx(yhInfo);
+ sessionInfo.setCurrentYhlx("0");
+
+ return sessionInfo;
+ }
+
+ @Override
+ public YhxxbDTO getYhxx(String yhuuid) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setYhuuid(yhuuid);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initSessionByYhuuid(yhxxReqDTO);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ return data;
+ }
+ @Override
+ public YhxxbDTO getYhxxByDid(String did) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setDid(did);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initSessionByDid(yhxxReqDTO);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ return data;
+ }
+
+ @Override
+ public YhxxbDTO updateDid(YhxxbDTO yhxx) {
+ CommonResult yhxxbDTOCommonResult = mhzcApi.updateDid(yhxx);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ return data;
+ }
+
+ @Override
+ public YhxxbDTO intQyxxByDid(YhxxbDTO qyxx) {
+ CommonResult yhxxbDTOCommonResult = mhzcApi.intQyxxByDid(qyxx);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ return data;
+ }
+
+ @Override
+ public YhxxbDTO getYhxxBySfzjhm(String sfzjhm) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setSfzjhm(sfzjhm);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initSessionBySfzjhm(yhxxReqDTO);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ return data;
+ }
+
+ @Override
+ public SessionInfo initYhxxByDlzh(String dlzh) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setDlzh(dlzh);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initSessionByDlzh(yhxxReqDTO);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ SessionInfo sessionInfo = new SessionInfo();
+ sessionInfo.setCurrentYhuuid(data.getYhUuid());
+ YhInfo yhInfo = new YhInfo();
+ yhInfo.setYhuuid(data.getYhUuid());
+ yhInfo.setZsxm(data.getZsxm1());
+ yhInfo.setSjhm(data.getSjhm1());
+ yhInfo.setSfzjhm(data.getSfzjhm());
+ yhInfo.setSfzjlx(data.getSfzjlx());
+ yhInfo.setYhdid(data.getDid());
+ sessionInfo.setYhxx(yhInfo);
+ sessionInfo.setCurrentYhlx("0");
+ return sessionInfo;
+ }
+
+ @Override
+ public YhxxbDTO getYhxxByDlzh(String dlzh) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setDlzh(dlzh);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initSessionByDlzh(yhxxReqDTO);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ return data;
+ }
+
+ @Override
+ public SessionInfo initYhxxBySjhm(String sjhm) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setSjhm1(sjhm);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initSessionByDlzh(yhxxReqDTO);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ SessionInfo sessionInfo = BeanUtils.toBean(data, SessionInfo.class);
+ return sessionInfo;
+ }
+
+ @Override
+ public YhxxbDTO getYhxxBySjhm(String sjhm) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setDlzh(sjhm);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initSessionByDlzh(yhxxReqDTO);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ return data;
+ }
+
+ @Override
+ public SessionInfo initAdminInfo(String yhuuid) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setYhuuid(yhuuid);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initHtSessionByYhuuid(yhxxReqDTO);
+ HtYhxxbDTO data = yhxxbDTOCommonResult.getData();
+ SessionInfo sessionInfo = new SessionInfo();
+ sessionInfo.setCurrentYhuuid(data.getYhUuid());
+ YhInfo yhInfo = new YhInfo();
+ yhInfo.setYhuuid(data.getYhUuid());
+ yhInfo.setZsxm(data.getZsxm1());
+ yhInfo.setSjhm(data.getSjhm1());
+ yhInfo.setSfzjhm(data.getSfzjhm());
+ yhInfo.setSfzjlx(data.getSfzjlx());
+ sessionInfo.setYhxx(yhInfo);
+ sessionInfo.setCurrentYhlx("0");
+ return sessionInfo;
+ }
+
+ @Override
+ public HtYhxxbDTO getAdminInfo(String yhuuid) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setYhuuid(yhuuid);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initHtSessionByYhuuid(yhxxReqDTO);
+ HtYhxxbDTO data = yhxxbDTOCommonResult.getData();
+ return data;
+ }
+
+ @Override
+ public HtYhxxbDTO getAdminInfoByDlzh(String dlzh) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setDlzh(dlzh);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initHtSessionByDlzh(yhxxReqDTO);
+ HtYhxxbDTO data = yhxxbDTOCommonResult.getData();
+ return data;
+ }
+
+ @Override
+ public SessionInfo getSessionInfo(String yhuuid, String qyuuid) {
+ YhxxReqDTO yhxxReqDTO = new YhxxReqDTO();
+ yhxxReqDTO.setYhuuid(yhuuid);
+ CommonResult yhxxbDTOCommonResult = mhzcApi.initSessionByYhuuid(yhxxReqDTO);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ SessionInfo sessionInfo = new SessionInfo();
+ sessionInfo.setCurrentYhuuid(data.getYhUuid());
+ YhInfo yhInfo = new YhInfo();
+ yhInfo.setYhuuid(data.getYhUuid());
+ yhInfo.setZsxm(data.getZsxm1());
+ yhInfo.setSjhm(data.getSjhm1());
+ yhInfo.setSfzjhm(data.getSfzjhm());
+ yhInfo.setSfzjlx(data.getSfzjlx());
+ yhInfo.setYhdid(data.getDid());
+ sessionInfo.setNsrsbh(data.getNsrsbh());
+ sessionInfo.setQymc(data.getQymc());
+ sessionInfo.setQyuuid(data.getQyuuid());
+ sessionInfo.setYhxx(yhInfo);
+ sessionInfo.setCurrentYhlx("0");
+ return sessionInfo;
+ }
+
+
+ @Override
+ public OAuth2UserInfoRespVO getUserInfo(HttpServletRequest request) {
+ String token = obtainAuthorization(request);
+ SessionInfo sessionInfo = oauth2TokenService.checkAccessToken(token);
+ if (GyUtils.isNull(sessionInfo)) throw new IllegalStateException("token无效");
+ YhInfo yhxx = sessionInfo.getYhxx();
+ if (GyUtils.isNull(yhxx)) throw new IllegalStateException("token无效");
+ OAuth2UserInfoRespVO resp = new OAuth2UserInfoRespVO();
+ resp.setYhuuid(yhxx.getYhuuid());
+ resp.setZsxm(yhxx.getZsxm());
+ resp.setSjhm(yhxx.getSjhm());
+ resp.setYhdid(yhxx.getYhdid());
+ resp.setShxydm(sessionInfo.getNsrsbh());
+ resp.setQyuuid(sessionInfo.getQyuuid());
+ resp.setQymc(sessionInfo.getQymc());
+ resp.setYhlx(sessionInfo.getCurrentYhlx());
+ log.info("getUserInfo获取:{}", resp);
+ return resp;
+ }
+
+ public static String obtainAuthorization(HttpServletRequest request) {
+ String authorization = request.getHeader(AUTHORIZATION_HEADER);
+ if (!StringUtils.hasText(authorization)) {
+ return null;
+ }
+ final int length = authorization.length();
+ if (!authorization.startsWith("Bearer")) return null;
+ return authorization.substring(length - 32);
+ }
+
+ @Override
+ public YhxxbDTO saveYhxxByDid(YhxxbDTO yhxx) {
+ CommonResult yhxxbDTOCommonResult = mhzcApi.saveYhxxByDid(yhxx);
+ YhxxbDTO data = yhxxbDTOCommonResult.getData();
+ return data;
+ }
+
+
+}
diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/AesUtil.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/AesUtil.java
new file mode 100644
index 0000000..47de80e
--- /dev/null
+++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/AesUtil.java
@@ -0,0 +1,151 @@
+package com.css.txw.sso.util;
+
+import com.css.ggzc.framework.common.util.gy.GyUtils;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.Base64Utils;
+import org.springframework.util.StringUtils;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+/**
+ * @author KEBAO
+ * @date 2023/5/9.
+ */
+public class AesUtil {
+
+ public static final Charset DEFAULT_CHARSET;
+
+ static {
+ DEFAULT_CHARSET = StandardCharsets.UTF_8;
+ }
+
+ @Nullable
+ public static String decryptFormBase64ToString(@Nullable String content, String aesTextKey) {
+ byte[] hexBytes = decryptFormBase64(content, aesTextKey);
+ return hexBytes == null ? null : new String(hexBytes, DEFAULT_CHARSET);
+ }
+
+ @Nullable
+ public static byte[] decryptFormBase64(@Nullable String content, String aesTextKey) {
+ return StringUtils.isEmpty(content) ? null : decryptFormBase64(content.getBytes(DEFAULT_CHARSET), aesTextKey);
+ }
+
+ public static byte[] decryptFormBase64(byte[] content, String aesTextKey) {
+ return decrypt(Base64Utils.decode(content), aesTextKey);
+ }
+
+ public static byte[] decrypt(byte[] content, String aesTextKey) {
+ return decrypt(content, ((String) Objects.requireNonNull(aesTextKey)).getBytes(DEFAULT_CHARSET));
+ }
+
+ public static byte[] decrypt(byte[] encrypted, byte[] aesKey) {
+ return Pkcs7Encoder.decode(aes(encrypted, aesKey, 2));
+ }
+
+ private static byte[] aes(byte[] encrypted, byte[] aesKey, int mode) {
+ Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32");
+
+ try {
+ Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+ SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
+ IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
+ cipher.init(mode, keySpec, iv);
+ return cipher.doFinal(encrypted);
+ } catch (Exception var6) {
+ var6.printStackTrace();
+ throw new RuntimeException();
+ }
+ }
+
+ public static String encryptToBase64(String content, String aesTextKey) {
+ return Base64Utils.encodeToString(encrypt(content, aesTextKey));
+ }
+
+ public static byte[] encrypt(String content, String aesTextKey) {
+ return encrypt(content.getBytes(DEFAULT_CHARSET), aesTextKey);
+ }
+
+ public static byte[] encrypt(byte[] content, String aesTextKey) {
+ return encrypt(content, ((String)Objects.requireNonNull(aesTextKey)).getBytes(DEFAULT_CHARSET));
+ }
+
+ public static byte[] encrypt(byte[] content, byte[] aesKey) {
+ return aes(Pkcs7Encoder.encode(content), aesKey, 1);
+ }
+
+ private static class Pkcs7Encoder {
+ private static final int BLOCK_SIZE = 32;
+
+ private Pkcs7Encoder() {
+ }
+
+ private static byte[] encode(byte[] src) {
+ int count = src.length;
+ int amountToPad = 32 - count % 32;
+ byte pad = (byte)(amountToPad & 255);
+ byte[] pads = new byte[amountToPad];
+
+ int length;
+ for(length = 0; length < amountToPad; ++length) {
+ pads[length] = pad;
+ }
+
+ length = count + amountToPad;
+ byte[] dest = new byte[length];
+ System.arraycopy(src, 0, dest, 0, count);
+ System.arraycopy(pads, 0, dest, count, amountToPad);
+ return dest;
+ }
+
+ private static byte[] decode(byte[] decrypted) {
+ int pad = decrypted[decrypted.length - 1];
+ if (pad < 1 || pad > 32) {
+ pad = 0;
+ }
+
+ return pad > 0 ? Arrays.copyOfRange(decrypted, 0, decrypted.length - pad) : decrypted;
+ }
+ }
+
+ public static List