From 6c4d87172a8e44dac53f47305975555ee4ed0c50 Mon Sep 17 00:00:00 2001 From: liulujian Date: Fri, 3 Apr 2026 13:45:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20txw-sso=20=E5=8D=95?= =?UTF-8?q?=E7=82=B9=E7=99=BB=E5=BD=95=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- txw-sso/README.md | 87 ++ txw-sso/pom.xml | 40 + txw-sso/settings.xml | 61 ++ txw-sso/txw-sso-service-api/pom.xml | 88 ++ .../java/com/css/txw/sso/api/ISsoApi.java | 10 + .../txw/sso/api/oauth2/OAuth2TokenApi.java | 52 ++ .../oauth2/dto/AccessTokenSessionInfo.java | 50 + .../dto/OAuth2AccessTokenCheckRespDTO.java | 23 + .../dto/OAuth2AccessTokenCreateReqDTO.java | 32 + .../oauth2/dto/OAuth2AccessTokenRespDTO.java | 27 + .../txw/sso/api/yhxx/SwitchSessionApi.java | 28 + .../com/css/txw/sso/api/yhxx/YhxxApi.java | 18 + .../configuration/SsoApiConfiguration.java | 15 + .../txw/sso/constants/SsoApiConstants.java | 12 + .../css/txw/sso/enums/LoginLogTypeEnum.java | 27 + .../txw/sso/enums/OAuth2GrantTypeEnum.java | 29 + .../sso/pojo/yhxx/SwitchCompanyResDTO.java | 12 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...itional-spring-configuration-metadata.json | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...itional-spring-configuration-metadata.json | 2 + .../classes/com/css/txw/sso/api/ISsoApi.class | Bin 0 -> 229 bytes .../txw/sso/api/oauth2/OAuth2TokenApi.class | Bin 0 -> 2839 bytes .../oauth2/dto/AccessTokenSessionInfo.class | Bin 0 -> 7288 bytes .../dto/OAuth2AccessTokenCheckRespDTO.class | Bin 0 -> 3708 bytes .../dto/OAuth2AccessTokenCreateReqDTO.class | Bin 0 -> 4276 bytes .../oauth2/dto/OAuth2AccessTokenRespDTO.class | Bin 0 -> 3735 bytes .../txw/sso/api/yhxx/SwitchSessionApi.class | Bin 0 -> 1490 bytes .../com/css/txw/sso/api/yhxx/YhxxApi.class | Bin 0 -> 820 bytes .../configuration/SsoApiConfiguration.class | Bin 0 -> 630 bytes .../txw/sso/constants/SsoApiConstants.class | Bin 0 -> 434 bytes .../css/txw/sso/enums/LoginLogTypeEnum.class | Bin 0 -> 1623 bytes .../txw/sso/enums/OAuth2GrantTypeEnum.class | Bin 0 -> 2633 bytes .../sso/pojo/yhxx/SwitchCompanyResDTO.class | Bin 0 -> 2683 bytes txw-sso/txw-sso-service-biz/pom.xml | 144 +++ .../com/css/txw/sso/app/DevAppStarter.java | 20 + .../css/txw/sso/configuration/SMSClient.java | 20 + .../SsoServiceConfiguration.java | 12 + .../txw/sso/constants/ErrorCodeConstants.java | 45 + .../txw/sso/constants/RedisKeyConstants.java | 101 ++ .../css/txw/sso/constants/SsoConstants.java | 27 + .../java/com/css/txw/sso/consumer/readme.md | 1 + .../controller/auth/AdminAuthController.java | 67 ++ .../sso/controller/auth/AuthController.java | 147 +++ .../oauth2/OAuth2BasicController.java | 216 +++++ .../oauth2/OAuth2OpenController.java | 214 +++++ .../oauth2/OAuth2TokenController.java | 108 +++ .../oauth2/OAuth2UserController.java | 34 + .../session/SwitchSessionController.java | 39 + .../controller/thirdparty/DidController.java | 860 ++++++++++++++++++ .../controller/thirdparty/GssbController.java | 81 ++ .../controller/verify/VerifyController.java | 44 + .../com/css/txw/sso/convert/AuthConvert.java | 17 + .../txw/sso/convert/OAuth2OpenConvert.java | 35 + .../css/txw/sso/enums/CommonStatusEnum.java | 40 + .../com/css/txw/sso/enums/UserTypeEnum.java | 34 + .../main/java/com/css/txw/sso/job/readme.md | 1 + .../oauth2/OAuth2AccessTokenMapper.java | 70 ++ .../sso/mapper/oauth2/OAuth2ClientMapper.java | 21 + .../sso/mapper/oauth2/OAuth2CodeMapper.java | 14 + .../oauth2/OAuth2RefreshTokenMapper.java | 40 + .../java/com/css/txw/sso/mapper/readme.md | 1 + .../domain/oauth2/OAuth2AccessTokenDO.java | 71 ++ .../pojo/domain/oauth2/OAuth2ApproveDO.java | 63 ++ .../pojo/domain/oauth2/OAuth2ClientDO.java | 76 ++ .../sso/pojo/domain/oauth2/OAuth2CodeDO.java | 75 ++ .../domain/oauth2/OAuth2RefreshTokenDO.java | 62 ++ .../java/com/css/txw/sso/pojo/dto/readme.md | 1 + .../sso/pojo/dto/session/AccessTokenInfo.java | 23 + .../txw/sso/pojo/dto/session/SessionInfo.java | 20 + .../css/txw/sso/pojo/dto/session/YhInfo.java | 14 + .../css/txw/sso/pojo/dto/yhxx/YhxxDTO.java | 4 + .../css/txw/sso/pojo/vo/AuthLoginReqVO.java | 71 ++ .../css/txw/sso/pojo/vo/AuthLoginRespVO.java | 33 + .../txw/sso/pojo/vo/DidBindPhoneReqVO.java | 26 + .../com/css/txw/sso/pojo/vo/LoginUser.java | 29 + .../css/txw/sso/pojo/vo/SMSLoginReqVO.java | 26 + .../com/css/txw/sso/pojo/vo/SendMsgReqVO.java | 24 + .../css/txw/sso/pojo/vo/oauth2/LogoutVO.java | 16 + .../oauth2/OAuth2OpenAccessTokenRespVO.java | 27 + .../oauth2/OAuth2OpenAuthorizeInfoRespVO.java | 38 + .../vo/oauth2/OAuth2OpenCheckTokenRespVO.java | 40 + .../pojo/vo/oauth2/OAuth2UserInfoRespVO.java | 42 + .../txw/sso/pojo/vo/oauth2/SwjgTreeVO.java | 38 + .../java/com/css/txw/sso/pojo/vo/readme.md | 1 + .../sso/pojo/vo/thirdparty/GssbTokenVO.java | 17 + .../thirdparty/did/DidCallbackExtendInfo.java | 17 + .../thirdparty/did/DidCredentialSubject.java | 32 + .../vo/thirdparty/did/DidExtendVcsInfo.java | 16 + .../vo/thirdparty/did/DidLoginExtend.java | 25 + .../pojo/vo/thirdparty/did/QrCodeInfo.java | 15 + .../css/txw/sso/properties/SsoProperties.java | 30 + .../java/com/css/txw/sso/properties/readme.md | 1 + .../sso/service/auth/AdminAuthService.java | 10 + .../css/txw/sso/service/auth/AuthService.java | 55 ++ .../auth/impl/AdminAuthServiceImpl.java | 89 ++ .../service/auth/impl/AuthServiceImpl.java | 323 +++++++ .../service/oauth2/OAuth2ClientService.java | 48 + .../oauth2/OAuth2ClientServiceImpl.java | 91 ++ .../sso/service/oauth2/OAuth2CodeService.java | 48 + .../service/oauth2/OAuth2CodeServiceImpl.java | 103 +++ .../service/oauth2/OAuth2GrantService.java | 83 ++ .../oauth2/OAuth2GrantServiceImpl.java | 92 ++ .../service/oauth2/OAuth2TokenService.java | 88 ++ .../oauth2/OAuth2TokenServiceImpl.java | 475 ++++++++++ .../service/session/SwitchSessionService.java | 11 + .../session/SwitchSessionServiceImpl.java | 111 +++ .../txw/sso/service/verify/VerifyService.java | 13 + .../sso/service/verify/VerifyServiceImpl.java | 41 + .../sso/service/yhxx/AccountLockService.java | 11 + .../css/txw/sso/service/yhxx/YhxxService.java | 44 + .../yhxx/impl/AccountLockServiceImpl.java | 85 ++ .../service/yhxx/impl/YhxxServiceImpl.java | 247 +++++ .../java/com/css/txw/sso/util/AesUtil.java | 151 +++ .../java/com/css/txw/sso/util/HttpUtils.java | 124 +++ .../sso/util/OAuth2AccessTokenRedisDAO.java | 151 +++ .../com/css/txw/sso/util/OAuth2Utils.java | 53 ++ .../java/com/css/txw/sso/util/SSOGyUtils.java | 6 + .../txw/sso/util/SecurityFrameworkUtils.java | 48 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...itional-spring-configuration-metadata.json | 2 + .../src/main/resources/application.yaml | 112 +++ .../src/main/resources/bootstrap-env.yml | 22 + .../src/main/resources/bootstrap-local.yml | 27 + .../src/main/resources/bootstrap-nacos.yml | 71 ++ .../src/main/resources/bootstrap.yml | 16 + .../src/main/resources/logback-spring.xml | 75 ++ .../src/main/resources/mapper/readme.md | 1 + 128 files changed, 6971 insertions(+) create mode 100644 txw-sso/README.md create mode 100644 txw-sso/pom.xml create mode 100644 txw-sso/settings.xml create mode 100644 txw-sso/txw-sso-service-api/pom.xml create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/ISsoApi.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/OAuth2TokenApi.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/AccessTokenSessionInfo.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCreateReqDTO.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenRespDTO.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/yhxx/SwitchSessionApi.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/yhxx/YhxxApi.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/configuration/SsoApiConfiguration.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/constants/SsoApiConstants.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/enums/LoginLogTypeEnum.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/enums/OAuth2GrantTypeEnum.java create mode 100644 txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/pojo/yhxx/SwitchCompanyResDTO.java create mode 100644 txw-sso/txw-sso-service-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 txw-sso/txw-sso-service-api/src/main/resources/additional-spring-configuration-metadata.json create mode 100644 txw-sso/txw-sso-service-api/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 txw-sso/txw-sso-service-api/target/classes/additional-spring-configuration-metadata.json create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/ISsoApi.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/OAuth2TokenApi.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/AccessTokenSessionInfo.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCreateReqDTO.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenRespDTO.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/yhxx/SwitchSessionApi.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/api/yhxx/YhxxApi.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/configuration/SsoApiConfiguration.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/constants/SsoApiConstants.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/enums/LoginLogTypeEnum.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/enums/OAuth2GrantTypeEnum.class create mode 100644 txw-sso/txw-sso-service-api/target/classes/com/css/txw/sso/pojo/yhxx/SwitchCompanyResDTO.class create mode 100644 txw-sso/txw-sso-service-biz/pom.xml create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/app/DevAppStarter.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/configuration/SMSClient.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/configuration/SsoServiceConfiguration.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/ErrorCodeConstants.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/RedisKeyConstants.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/SsoConstants.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/consumer/readme.md create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/auth/AdminAuthController.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/auth/AuthController.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2BasicController.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2OpenController.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2TokenController.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/oauth2/OAuth2UserController.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/session/SwitchSessionController.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/thirdparty/DidController.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/thirdparty/GssbController.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/verify/VerifyController.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/convert/AuthConvert.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/convert/OAuth2OpenConvert.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/enums/CommonStatusEnum.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/enums/UserTypeEnum.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/job/readme.md create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2AccessTokenMapper.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2ClientMapper.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2CodeMapper.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/oauth2/OAuth2RefreshTokenMapper.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/mapper/readme.md create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2AccessTokenDO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2ApproveDO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2ClientDO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2CodeDO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/domain/oauth2/OAuth2RefreshTokenDO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/readme.md create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/AccessTokenInfo.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/SessionInfo.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/session/YhInfo.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/dto/yhxx/YhxxDTO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/AuthLoginReqVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/AuthLoginRespVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/DidBindPhoneReqVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/LoginUser.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/SMSLoginReqVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/SendMsgReqVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/LogoutVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenAccessTokenRespVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenAuthorizeInfoRespVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2OpenCheckTokenRespVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/OAuth2UserInfoRespVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/oauth2/SwjgTreeVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/readme.md create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/GssbTokenVO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidCallbackExtendInfo.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidCredentialSubject.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidExtendVcsInfo.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/DidLoginExtend.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/thirdparty/did/QrCodeInfo.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/properties/SsoProperties.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/properties/readme.md create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/AdminAuthService.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/AuthService.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/impl/AdminAuthServiceImpl.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/impl/AuthServiceImpl.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2ClientService.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2ClientServiceImpl.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2CodeService.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2CodeServiceImpl.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2GrantService.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2GrantServiceImpl.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2TokenService.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/oauth2/OAuth2TokenServiceImpl.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/session/SwitchSessionService.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/session/SwitchSessionServiceImpl.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/verify/VerifyService.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/verify/VerifyServiceImpl.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/AccountLockService.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/YhxxService.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/impl/AccountLockServiceImpl.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/impl/YhxxServiceImpl.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/AesUtil.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/HttpUtils.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/OAuth2AccessTokenRedisDAO.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/OAuth2Utils.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/SSOGyUtils.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/SecurityFrameworkUtils.java create mode 100644 txw-sso/txw-sso-service-biz/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 txw-sso/txw-sso-service-biz/src/main/resources/additional-spring-configuration-metadata.json create mode 100644 txw-sso/txw-sso-service-biz/src/main/resources/application.yaml create mode 100644 txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-env.yml create mode 100644 txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-local.yml create mode 100644 txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-nacos.yml create mode 100644 txw-sso/txw-sso-service-biz/src/main/resources/bootstrap.yml create mode 100644 txw-sso/txw-sso-service-biz/src/main/resources/logback-spring.xml create mode 100644 txw-sso/txw-sso-service-biz/src/main/resources/mapper/readme.md diff --git a/txw-sso/README.md b/txw-sso/README.md new file mode 100644 index 0000000..09df273 --- /dev/null +++ b/txw-sso/README.md @@ -0,0 +1,87 @@ +# 1、项目启动类 + +无需再编写启动类 +### 开发环境可使用 +`com.css.txw.sso.app.DevAppStarter` +### 其他环境使用 +`com.css.ggzc.framework.app.ApplicationStarter` + +# 2、目录结构 + +``` +│ .gitignore +│ pom.xml +│ README.md +├─txw-sso-service-api api包 +│ │ pom.xml +│ └─src +│ └─main +│ ├─java +│ │ └─com.css.txw.sso 模块包 +│ │ ├─api api接口 +│ │ │ ISsoApi.java +│ │ │ +│ │ ├─configuration 模块自动装配配置 需要在org.springframework.boot.autoconfigure.AutoConfiguration.imports中配置,基于插件化开发,各模块在该类中可指定要扫描的包等配置,启动类不会自动扫描 +│ │ │ SsoApiConfiguration.java +│ │ │ +│ │ ├─constants api常量 +│ │ │ SsoConstants.java +│ │ │ +│ │ └─pojo api pojo +│ │ +│ └─resources 模块相关的配置说明,注意使用config类 +│ │ additional-spring-configuration-metadata.json +│ │ +│ └─META-INF +│ └─spring 模块自动装配配置 +│ org.springframework.boot.autoconfigure.AutoConfiguration.imports +│ +└─txw-sso-service-biz service包 + │ pom.xml + │ + └─src + └─main + ├─java + │ └─com.css.txw.sso 模块包 + │ ├─configuration 本模块自动装配设置 需要在org.springframework.boot.autoconfigure.AutoConfiguration.imports中配置,基于插件化开发,各模块在该类中可指定要扫描的包等配置,启动类不会自动扫描 + │ │ SsoServiceConfiguration.java + │ │ + │ ├─constants + │ │ + │ ├─consumer kafaka消费服务 + │ │ + │ ├─controller controller 建议按业务分包 注意:禁止在controller中写跟sql相关的逻辑,一律写到mapper中 + │ │ + │ ├─job 定时任务 + │ │ + │ ├─mapper mapper 建议按业务分包 + │ │ + │ ├─pojo + │ │ ├─domain + │ │ │ + │ │ ├─dto + │ │ │ + │ │ └─vo + │ │ + │ │-properties 配置类 + │ │ + │ ├─service service 建议按业务分包 注意:SQL相关内容写到mapper中,不要写在service中 + │ │ + │ └─util 本业务域工具类 跟业务相关的写在这里 公共类的请优先使用framework中有的,或者在framework中增加 + │ + └─resources + │ additional-spring-configuration-metadata.json 模块相关的配置说明,注意使用config类 + │ application.yaml 模块主配置文件 + │ bootstrap-env.yml 模块env环境配置文件 + │ bootstrap-local.yml 模块开发环境配置文件 + │ bootstrap-nacos.yml 模块nacos配置文件 + │ bootstrap.yml 模块主配置文件 + │ logback-spring.xml 模块日志配置文件 + │ + ├─mapper 模块mapper + └─META-INF + └─spring 模块自动装配配置 + org.springframework.boot.autoconfigure.AutoConfiguration.imports + +``` + diff --git a/txw-sso/pom.xml b/txw-sso/pom.xml new file mode 100644 index 0000000..d1d0a46 --- /dev/null +++ b/txw-sso/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.css.ggzc + ggzc-framework-dependencies + 1.0.0-SNAPSHOT + + txw-sso + com.css.txw + 1.0.0-SNAPSHOT + pom + ${project.artifactId} + sso + + + + com.css.ggzc + ggzc-framework-starter + 1.0.0-SNAPSHOT + pom + import + + + + + ${project.artifactId} + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + + + txw-sso-service-api + txw-sso-service-biz + + diff --git a/txw-sso/settings.xml b/txw-sso/settings.xml new file mode 100644 index 0000000..e089e4a --- /dev/null +++ b/txw-sso/settings.xml @@ -0,0 +1,61 @@ + + + + + + + codingcorp-qyd_repo-mvn_public + coding-user + coding-pwd + + + + + + + Repository Proxy + + true + + + + + codingcorp-qyd_repo-mvn_public + mvn_public + http://codingcorp-maven.pkg.codingstd.xc01.cloud.sat.tax/repository/qyd_repo/mvn_public/ + + true + + + true + always + + + + + + codingcorp-qyd_repo-mvn_public + + true + + + true + + http://codingcorp-maven.pkg.codingstd.xc01.cloud.sat.tax/repository/qyd_repo/mvn_public/ + + + + + + + + codingcorp-qyd_repo-mvn_public + + central + mvn_public + http://codingcorp-maven.pkg.codingstd.xc01.cloud.sat.tax/repository/qyd_repo/mvn_public/ + + + diff --git a/txw-sso/txw-sso-service-api/pom.xml b/txw-sso/txw-sso-service-api/pom.xml new file mode 100644 index 0000000..261550b --- /dev/null +++ b/txw-sso/txw-sso-service-api/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + + + com.css.ggzc + ggzc-framework-dependencies + 1.0.0-SNAPSHOT + + + + com.css.txw + txw-sso-service-api + 1.0.0-SNAPSHOT + + ${project.artifactId} + sso service api + + + 1.0.0-SNAPSHOT + + + + + + + com.css.ggzc + ggzc-framework-starter + ${version.ggzc-framework-dependencies} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + com.css.ggzc + ggzc-framework-starter-common + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + io.swagger.core.v3 + swagger-annotations + compile + + + + + + + codingcorp-qyd_repo-mvn_public + mvn_public + http://codingcorp-maven.pkg.codingstd.xc01.cloud.sat.tax/repository/qyd_repo/mvn_public/ + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.0 + + true + + + + compile + + jar + + + + + + + diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/ISsoApi.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/ISsoApi.java new file mode 100644 index 0000000..2768f41 --- /dev/null +++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/ISsoApi.java @@ -0,0 +1,10 @@ +package com.css.txw.sso.api; + +import com.css.txw.sso.constants.SsoApiConstants; +import org.springframework.cloud.openfeign.FeignClient; + +@FeignClient(name = SsoApiConstants.NAME) +public interface ISsoApi { + + +} diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/OAuth2TokenApi.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/OAuth2TokenApi.java new file mode 100644 index 0000000..afd1c50 --- /dev/null +++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/OAuth2TokenApi.java @@ -0,0 +1,52 @@ +package com.css.txw.sso.api.oauth2; + +import com.css.txw.sso.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO; +import com.css.txw.sso.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO; +import com.css.txw.sso.api.oauth2.dto.OAuth2AccessTokenRespDTO; +import com.css.txw.sso.constants.SsoApiConstants; +import com.css.ggzc.framework.common.pojo.CommonResult; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@FeignClient(name = SsoApiConstants.NAME,contextId = "token") +@Tag(name = "RPC 服务 - OAuth2.0 令牌") +public interface OAuth2TokenApi { + + String PREFIX = SsoApiConstants.PREFIX + "/oauth2/token"; + + /** + * 校验 Token 的 URL 地址,主要是提供给 Gateway 使用 + */ + @SuppressWarnings("HttpUrlsUsage") + String URL_CHECK = "http://" + SsoApiConstants.NAME + PREFIX + "/check"; + + @PostMapping(PREFIX + "/create") + @Operation(summary = "创建访问令牌") + CommonResult createAccessToken(@Valid @RequestBody OAuth2AccessTokenCreateReqDTO reqDTO); + + @GetMapping(PREFIX + "/check") + @Operation(summary = "校验访问令牌") + @Parameter(name = "accessToken", description = "访问令牌", required = true, example = "tudou") + CommonResult checkAccessToken(@RequestParam("accessToken") String accessToken); + + @DeleteMapping(PREFIX + "/remove") + @Operation(summary = "移除访问令牌") + @Parameter(name = "accessToken", description = "访问令牌", required = true, example = "tudou") + CommonResult removeAccessToken(@RequestParam("accessToken") String accessToken); + + @PutMapping(PREFIX + "/refresh") + @Operation(summary = "刷新访问令牌") + @Parameters({ + @Parameter(name = "refreshToken", description = "刷新令牌", required = true, example = "haha"), + @Parameter(name = "clientId", description = "客户端编号", required = true, example = "yudaoyuanma") + }) + CommonResult refreshAccessToken(@RequestParam("refreshToken") String refreshToken, + @RequestParam("clientId") String clientId); + +} diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/AccessTokenSessionInfo.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/AccessTokenSessionInfo.java new file mode 100644 index 0000000..674fc33 --- /dev/null +++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/AccessTokenSessionInfo.java @@ -0,0 +1,50 @@ +package com.css.txw.sso.api.oauth2.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Schema(description = "RPC 服务 - OAuth2 访问令牌的session信息") +@Data +public class AccessTokenSessionInfo implements Serializable { + + + /** + * 用户信息 + */ + @Schema(description = "用户UUID") + private String yhUuid; + @Schema(description = "真实姓名") + private String zsxm; + @Schema(description = "手机号码") + private String sjhm; + @Schema(description = "身份证件类型") + private String sfzjlx; + @Schema(description = "身份证件号码") + private String sfzjhm; + @Schema(description = "用户did") + private String yhdid; + /** + * 机构信息 + */ + @Schema(description = "机构UUID") + private String qyuuid; + @Schema(description = "机构名称") + private String qymc; + @Schema(description = "纳税人识别号") + private String nsrsbh; + @Schema(description = "社会信用代码") + private String shxydm; + @Schema(description = "登记序号") + private String djxh; + @Schema(description = "行政区划") + private String xzqhszDm; + @Schema(description = "用户类型") + private String yhlx; + + +} diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java new file mode 100644 index 0000000..4b85abe --- /dev/null +++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCheckRespDTO.java @@ -0,0 +1,23 @@ +package com.css.txw.sso.api.oauth2.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Schema(description = "RPC 服务 - OAuth2 访问令牌的校验 Response DTO") +@Data +public class OAuth2AccessTokenCheckRespDTO implements Serializable { + + @Schema(description = "是否刷新了访问令牌") + private Boolean hasRefreshedToken; + @Schema(description = "新的访问令牌") + private String newAccessToken; + @Schema(description = "新的过期时间") + private LocalDateTime newExpireTime; + + @Schema(description = "session信息") + private AccessTokenSessionInfo sessionInfo; + +} diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCreateReqDTO.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCreateReqDTO.java new file mode 100644 index 0000000..424e417 --- /dev/null +++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenCreateReqDTO.java @@ -0,0 +1,32 @@ +package com.css.txw.sso.api.oauth2.dto; + +import com.css.ggzc.framework.common.enums.UserTypeEnum; +import com.css.ggzc.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; + +@Schema(description = "RPC 服务 - OAuth2 访问令牌创建 Request DTO") +@Data +public class OAuth2AccessTokenCreateReqDTO implements Serializable { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "用户编号不能为空") + private String userId; + + @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "用户类型不能为空") + @InEnum(value = UserTypeEnum.class, message = "用户类型必须是 {value}") + private Integer userType; + + @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma") + @NotNull(message = "客户端编号不能为空") + private String clientId; + + @Schema(description = "授权范围的数组", example = "user_info") + private List scopes; + +} diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenRespDTO.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenRespDTO.java new file mode 100644 index 0000000..b630579 --- /dev/null +++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/oauth2/dto/OAuth2AccessTokenRespDTO.java @@ -0,0 +1,27 @@ +package com.css.txw.sso.api.oauth2.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Schema(description = "RPC 服务 - OAuth2 访问令牌的信息 Response DTO") +@Data +@Accessors(chain = true) +public class OAuth2AccessTokenRespDTO implements Serializable { + + @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") + private String accessToken; + + @Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "haha") + private String refreshToken; + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private String userId; + + @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime expiresTime; + +} diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/yhxx/SwitchSessionApi.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/yhxx/SwitchSessionApi.java new file mode 100644 index 0000000..270a03f --- /dev/null +++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/yhxx/SwitchSessionApi.java @@ -0,0 +1,28 @@ +package com.css.txw.sso.api.yhxx; + +import com.css.txw.sso.constants.SsoApiConstants; +import com.css.txw.sso.pojo.yhxx.SwitchCompanyResDTO; +import com.css.ggzc.framework.common.pojo.CommonResult; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = SsoApiConstants.NAME,contextId = "switch") +@Tag(name = "切换会话信息接口") +public interface SwitchSessionApi { + + String PREFIX = SsoApiConstants.PREFIX + "/switchSession"; + + + @PostMapping(PREFIX+"/company") + @Operation(summary = "切换机构") + CommonResult switchCompany(@RequestParam("token")String token, @RequestParam("jguuid")String jguuid); + + @PostMapping(PREFIX+"/addCompany") + @Operation(summary = "新增机构") + CommonResult addCompany(@RequestBody SwitchCompanyResDTO resDTO); + +} diff --git a/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/yhxx/YhxxApi.java b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/yhxx/YhxxApi.java new file mode 100644 index 0000000..fa6bd9c --- /dev/null +++ b/txw-sso/txw-sso-service-api/src/main/java/com/css/txw/sso/api/yhxx/YhxxApi.java @@ -0,0 +1,18 @@ +package com.css.txw.sso.api.yhxx; + +import com.css.ggzc.framework.common.pojo.CommonResult; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "sso-service") +@Tag(name = "用户信息接口") +public interface YhxxApi { + + @PostMapping("/sso/yhxx/getYhxx") + @Operation(summary = "获取用户信息") + CommonResult 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 0000000000000000000000000000000000000000..7dc110996fb8559ba7c94a00824375b3562f3ee0 GIT binary patch literal 229 zcmXYr!Ab)`5Jan!n2m`Cui`goBY5)^gn%Fj2E0$k$gAV#e%Ma z>N>pdpU*DBgnHWCBj>|vX2-OKgCy*t9i^Pok#@FH1uF|1XsxFV*R zaPw%X<=iLHcAs1qj+WloM;l(*`k?zDt@38{`zS%n9bu+*M`+5&@hWE{Tzc!+=ghU? r+OX?|{}~A9BLzpNEkErJ!IwFKFsb~bCYb*D3@0^L^)s*6WGsFGA80=b literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9e9e863361ae3fadc4c8771ba702547c9a4cda4c GIT binary patch literal 2839 zcmc&$&u<$=6nCBe+sOt*Fl!K8cFgz*z0+pWT zV>=E@e3s5Su1#6Em*#|2Z!^!U5!bP(2_w*FqF;}^()7?8VFU&y9YN(1D-^g-7f&NS zGJ+@Rl*fHz2AY_VLAlKQ3d*Fq@=bO9!Jn(YZGE-+$NJsPPj3-8((J)Z#cTGW&uD9z zqEU`*FewVku@U)b^U)mBWGn7JWE4FpM)-(OUrjW zEo%a)X7r?wtT{fnAH(N~pxXE{D|;mb=}>_!0s?2-_OdPjV-OnC?enr&Y<`|T{(q>8 zvUaX*f5vzT(_uTYcSz`(i^$yO59?cB-))X$~9B(0Le zU3J|iaG<*S{l?1L=DnXbf4N=#VhuB_64*>s0_K)+>*LcR@GU;!=)5^pE3gK8)N_V( zX(w=UN7Y#+5!h6C+@0fCL+OP6&6<1B%mh}nL9F0dVJ+b*zHE%?Fr|b*|1KUYR*-at z=~m)!K9|jGeEWIzvpbo=OwGQb6Pbp?1OMz8@HW$VNIESA`q1b^uqd|djdT~yj$;Yf z?rNrH=eecuO=#hjz_FrI-UX`r742=y4r(gNJ_0e&k5mlO5Qh%H{>jV|I8R3Aoe+;B zCD4VtQf9|~cmn@>@#FzKb?^fq61t%W$A=_52~Xi-Cy=M{Pk+7mb?M9Wqz?eW((nvk zT?2d8fKi+u4#AEzz@CHW@z#ul7vQLb7vZJ%;TXIu;T40LB6y--pTZ3YI}m|A9>TsF z!oJpk9o!S_5NPXa)}Ao1d7PgNVNW$+Ph&r7;t{*txL{b@eKxfFT)1#vFT5TuTrdvx o#_)XIq2cY}c?{kN1}_E|&Gl&<);df^W-yt7%mfqq zuoP-RKu~C@l%iBCR8zoL&_qSss;zyuYFlls?Ux7bpZ@OGTDtZ<=VpePtB(EC?@RWq zd-huUo_qG$``npV{xkX<5q*UI>Zgn7n2*ZoIGYn}9%b_wo5$Ha!RAReBWzBxd5X=` zY)09PdFdHHKA&at9EUu`=y^6Tu=xR-AF}xon-|%<#O7r-KW6h2Hm|VxDF^tOmwxW2 zW;&+PFZlD98vRP6Uu*Omjee`qs~Wwg(d!!hPNUyz^aqXJ(CAH#{>a=vdFd@L{aKJ_ zD86bS(IY6-xh}FX67G#8*M_??{fXq-`GSlU1IbLHFSa_7PORyTEleg;nMfv)N~Q(1 zcP3Kd^lg!~Yh(T4jTeVgk#sncC4~E8J&8!TD;kgWMGz{`6H7?I*j{_k3C?`+tssf5K4XO-m%liPE3sMwlvz{rVa7*rp5f6rw{L(+;?hX=jg9#AnJWa9&YrUxq(!Non4t2`L^8H)pl?mAe`N$|!W24F(Ma#= zNPmK_)u1aAPatoXc1BZu;b=M?&J5lbPN!4h$od4bH;{>66z<8S!V9C(SUSBjb!#ly z1%P~YByUL}g}$}1Ot;!DRkPj zJb=?vwJ`4#zTxy#fxGOf0;{P4g@^aQZuUoS&7R}6`bE6=U;(TU{ zQ@I4WQ<-?IAC<*=XoQx16{!`Wq~!r7NuCofBmlP~>h zB34QR_B`a3<+5u{>Fi566D)K_!&8|A7vM<$C)I!@+QI|5LtT`1@UIeX)WXrBh zPBS|om9qMjQeFhRGLh)5OC#&8azZ;t3&kdmMv_Z7gMjf!IxZ^`<=KG*Enm^GbO|yD z?CFoC2YNHucGIbGx5`YoTjj!gTkTbrh?mp@wzYuXR(rMFK)mI=t@cWQt{&UnDxcEV zntdQQLU1e5GAY^Dk*KcJKz}rLb%J|EgLA!~&pS@g1uK>>YMwl}YvRts&GVYCTgY9a z`Sh_BZUGhJ5DV_GyMxDCpMx6#2eVWl{*nF1FO>92L=4Lh#Hfz{K+4Qi9 zvAKoKH|btMwb{y(%Xig4qPK_ZuE}}fX3I)kpwrv*7o@4ZnbW+$LY!zIUwgAmbrxIW zT6?pVZaFLyE`^+OIVzK`gjU)mgA)%FXm7StFUL9gKw^$g4-?u+MfQ1QClOR-4dFEJ zis8WMO>B~fh$pgseJqLda-LHM&FM&It;>8m7cVFmk)VqyNF}@xfIo^?sQ^AN#sA0B zbQv7j9EFbgaA3m}Iu^izO;qUkI2_nmg^q=AV6zoE7QulHSm;;+2R3D);~F@yQ41X% zaA5NmIz9;pHgut5DID13g^ugsz{W3hTuw{eH%466KC3)wz&_H+cn(w0+k;icj<83 z^HjdHeeMWVSeF1ZmbJr}u6Zw0z;L}pGmelx&v1=U$YxZUZcr*skKtyH=Oqdqp`hV5 zJUOH)(+g6SsTp1-X^P}Ev>Z~k=>w_S^cy}V`4!1$_;W}#W&orbQ#S%k(iI6Phg53@ zL8>*&j3ATB6e(zw<&f&ka**oG3ZtA!6^c}DROFBha|TF;88T)tDWpgQxeICd~*NCU+3qWpKDjuUR0(q!c?4>UOd8uFQqT7Kqx?3da z%RoNbFXFTr$WJ4pgT4Y3pl8G)+5)80o0!K|pdeoVo9L@RWq4mZLw5j`3lII1z6Mkw zX3-n84QPgFqgQD=P)J-$Ptu)0m0~#^ryW35B1L;>Cs4H*fNvL2jo614e-}`#IDjuK zyMgM&(|CE^17wI(cq6_Ws9wB9s&_ATA#`Ib)v<{{6+OAqtvs~J=3k^l8>P(;ILN8G&pY1YCzvc2zyU<~zs@@D( zE;JXV3fQg&Q@33Dv`e>Ljb_ktq1z}`(00u<%Pd#fvy&T8XXIh zz9W}kC8<>0Ob6&&NRC_FL{CLYlQ64&SGSV;-$kB{@LmiRB=GJ*vT@#rq(bTM87dp?{dp>2@cja2 zV-`hKVY%H}`wH^+__KDM%WBTWuvPH#;(}*q{s9E)78OO`B72t)rooNS7Vtxr?A-J^ zS_A*sF+4`i-6!eX(1%8-g&R0NLah>4GM*(d-db8hAC|b9@p%&CMWrP)TjE;Ab0o&Q zNlOUvIHtk4O=7%`w1n^y!s88$aYQi28%Rs&e2Hf=4olp`_yU*_x-kE%XYOw@!>R?A P&?DAqHcWZHjD7#V{)di* literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2363eeb3c4fb0074551d45c40b7d5ba0aa3db621 GIT binary patch literal 3708 zcmbVO|8Emz9Dd&JuDx})vXyO6K`n^7uM|N+IzV86pc~*eQBaYiJJw@euk<=V<1Z#g z(V)>^#1M><7)S^KBsdaCM1R4=zXOK+4Wj1e&vkGiV+tnoian8MepFyujT4gT`Sal{50Asg1UsGmU(&Kw@wr zU$Vx{U6y0*&zT$YdAnqkEIaQAboX0!$~j_;j+({P;T0*{a8gDEkQz5fEF(3P$(rK^ zuWA@EolMaxNVdR&sUI%h{`So6V^^oXzjW)`=l@)|areZ9Ti1WN^W|}YSl&FcA(Js3 z$MY`k^KPhAwDP08F;428lb=;n8%f=Kv|ts@VfxJoT&4^n)o*8v+$N(`Hu)~N@x|1s zpQcVs-aT=JemeYF`nWBBz!q55pRvbN8OKSLjvh%lj-4_J7DJsVWtXQ$N_NWU=1_&m z%NxCWZrwON_31@{@ER*`mDUN=wRh|iQ2Oi zVP}fbT=3 z(15@sBSuD5XzBJRj7;7ZHV;i0Iaa^Q!5#Y#nwe5>#~uN}KJ^cXXA)5DQr0XAw8`#S z5-{}h%77rt+%lzh3|m}$tvWsp_UCBs4A!r0Dnq@|a7V1!1y<=Ahe}3fY``d#`=2|I zt4l!382QaI859{!)*T_v(>BJsb8y?hW+sDjn44`PM?yR(v^h&^{f|kF-%EW-l{Z?+ zcKbmfI%H22Gv*dcj_Trvj@weXPz6>F?(9oWo%;IrSErLbNtcoI?<}scsh>~ZJ$F1Q z=aj3}OmbLtbf8nmQ&^xQiA6e|!x9}Y;6;HokC>8&dZlA0Ht1M}mvpR`(sHcO`38DL z$4acy@fHRJ=2hMWZr~dytlWrfrDYGjynR-Wt<`ZDN91voQ`eoWPW@U=S6Np{K2@>q zq%Zwchz=clu!7) z2ja1aui*LMYX%BJvRafE@|E7e=hOT=WcEq`xvrMn6i8-| z1(20may*dC-V7kCwdB@7@~c?wk<-jjNM@+B>o!I+9KG&KTX`jd{?Z0fXH9wZuTlFJ63k8DoY@Pn?2fY zqpCB(sSHU5C*%@$uMwY<7a_aQ)VoGmTQ1werrs=zrZsh)MWkmEjr*iNnpUJCp?DTe zmeMsfq}8&CRV6X} zQOC_VfOjYjS jgVeXWvM%K|%9EH^yZrnMf4!IE5Z){I&il1*r4RlGe$qB> literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c439045731505cc3db354b8520e0049061ba5d07 GIT binary patch literal 4276 zcmbVPYjYdr6+SCV+Lg3UjDwx1iK$9W@HLwR%0+fk2giU~+X;4(7J7k=w2?QFcE#>0 zH=r#fPy@M?-sptF44qEXOD0@~I+>V4`-RT*cSLsh)IU&4pR*ThB`Y-TO#JTI^FHV7 zInO!geU*Rw``SlDbT9oZNgJq|pg0}nMz%?UR(adHGz5>x!ZT5(#W60 z%(GUYbs_F^hNFQ|e5K#_{oZ2jtyll~;hEa`7j9g9KC|C9n=q>7%+gyIu3!B~AYofQ z78lj?5ImrWy_an4}J_NT{9eRytTEp;oq)t=|s(GTS8f8_W zjZOQFnZ@nAS>cxG@HnhGdP$koZ3jUlX7!?>I#ml<^fqmehA8*jeJ4!1md?#Dy?N@! znNzhlK3KkRdg&MME-#*jD=yZPx^c+F$c*ZS?(7t(t*`%pK+$pZEYg_N4Q;wwK8T{K zm=&HVGq08osFu#}zB1~}>4RTNjQ7$lVsD{w@96YS$oss@0FmPyu@R8|nVQfriPO}7R=R)^#+j!ZnarGfZY6ZYGlRH!^#J8?1JnNwOee2-0eb!O)N2b(@n>Vfvoc26(sxiT7L7Y?VIj?Wg#5qL0cP2MA zfwUmUEzPc$9Q0TyTG5p`v_x+`3b8l%q~w_17zvb|HLF%$dsycoxbq7ec?;h;IAUh@ zj%SvBeYW<>+nM2vXOX#a`QuMtzkGf1x63b|sm;GpTfCOxlU%c%3`RhvuhW=JU!sjN z4biYnUt{S($_g|ZoOS=tXvtKhZ_D&3ybqHCGj|y8~1YWjuZ%3;i-ZO^@K9tAE^UIvfD5 z3fJ+hhQp?$pX38aq@N0O|E=HA4fiCvK&@0X1N9ybkEMT9u0C&k25>Sp;Dd=-HUD+| zz^#W!U%{`O4TRq%6r~6kAL}OkYRcf*Lv3`2O<$&rM!j?wZMNxd>I1mnrU4oRs-1*& zR98i~aeN0`VC*~C5^|sf=&iI3$nAN$huz)ly6d4x-Q8A*jgSbpn`j5}!FM^)9h~7#PKrkX>n`9FJOw;s16N3R&oz7qYur_~(>P?= z1JG`oz?;Agj53#+XL%&>VKG+4znY z#vY+l`1UmI@grcOBh1vm;GZednz?Qd;WNjqatAWud+D)Oe%fx;4>uzFnQ0wQ^j7_F z$FZMT+UJwQB(3BXV?S-w+!3qu;>HtbKhR#c#L#u zAcTxF+(&Cg>0h))B<(Zm7U>ZFNy2Be!4nfV5%Fc* zS6(GhBm74OzG7SgPA+njQpZ-3iyP22s{KWm*cA--+&awxwxH7NHG*|JyjsZU7G($(&tb_L9-x$S`viM zVOW2Uu%e4%0U*Fc8&y6m9)i`8%~~HUHiGN;78@`@j=1l=njq&0B%-lJTJ!i7THD#N zK(}!T@aXLHWOVU|bZV&!&<2WFg OK(~M%K1+9wrT+s$r0V?*bPoW`#G$N{814G!xda(GyWu5Zq>OJ`>7hEGDa zD0UX!uaaGxQsGjp*ls1@F-bkmXpR@BXQvo0Nwu? z>OrFtNX^w-&60e;Q5R&_^+Tm+Pi^mV=R^NW*>L1?CP2>V_F4~W-z4hXkSKeKH z;lrG|9c*?o$17stF5GS52e`??uo}kHa0l`N_kBlxTA0U_g`Z-Ng(FHTC}~m+d$G^L z39u1t@9xWoT28gBIC1Os0v(R)SlbGL2aH z4fc?I>pld`WOwjYd;;bqJ5kknQgtH(&oLXDe-}D>Gbv#=8nVX5IuX4l;cK+98HI^p z@9My}uhoV|vW_3!yo$20G5(|gy;sUK ze!|fT0N9QDI1`F_96OCaMeHLkMvVu18k4L&jN{$LH}o_n$$J=U-NxH`8uN1WFxI<` zvptRXLHwvgE za0rKcg{E(MqkwKgHKSl7@ifCvGe@6{js6+Bc6scdNW29-etGO4NX(9pF2nGr6wIUJ z917aRzmU#qUtr56SQA-o8Ob&xRnQ4Z6%tupVG>^;a|wM}J)4Mt(glN{bivFT3S@>L zBWp%LRv}4{RY+x%3X}>#$!w|((k|2M_=X$M2%=dl=O=goKVz(0@h3dUTM^?ueihTy z#W9Kt_&Ftw|7Y+Ejyh%`F++*JyJ+BZCC?#=`xBEDy6ds}_ORDaRVymu7pn{9u&?r~+wcvDyq=FObB?-0> zzo(r+>&D>ZUTvRWww;5M6&B4L9J>T#Vi|ppD}AbP+~GDs z9t$*KjC@&xn&33!R89K8=jeZU1p}GEWo%KU;U|*`WD7ytRH*i0~(Zb1$|i60%v{tu>?Co2E| literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..364c20870bc10ee07f09c03e71b7181bfe1d534b GIT binary patch literal 1490 zcmcIkTTc@~6h2d-1q4MDxhSG|YfW@wyu3h65G5h0fwTloe41{DWyJJ)mh&i9?4zc;@Fz-_pef-5Pwnu0We z6YiiriExPD4msga(OmW*)l6tD7DB8q%j_bfN=)5y8Y{^XEa{{Y}<7Hwu zEK{vetyLVz%2KsT^UZl;P!Hy3$M0cSL%Bd2;qnEcMZw{dQYyoYK%fZR$tvHb+6&=p z7O1E-PW&jPMZ zWCq=~8@0?OvZ`bUfhZE_V#OksHBO+A{ztg~hc)FxcQVmR?~oTG`_!&E`|jGiuiNWu z^+Ft+Dmxh8SH%@{)0J0&Z}Dm2AYb}plI~Dt(i1mA5fiw%Zvibw1x2bnE*CjUi%y5* zlq0wd<<=&V3CJ8&!gW*v>d#Wr@T!@?IwoUI5=j4rgofFg&a<$2493jc`u6_U#>cH6 zpK32QYHz-5zx_-ggQSkSL0xv}D(W+u=C#5+>U{?_)pD14f;K6%kvxIkMh}}Ul(=QE zueFvXNgx47uoa@1p$*#cpTt!HQjlmP&;gxz+7;~_g>L*0;8_peI`$NZhU3tS^Aj5S z;AD81z}2bnR6~DbdKxVaz~HW>p?xe3*DaliETsrOKO6ZSiNsh4wt_cSFD*6_mtjJLT0cv0%gaploqvM-J98Fu*LAp3+D%188kPi% zw~V$j6kZ9~_#c?}e&=ge9{=wd>uP@&XU+jPR^m_RGy?|?{sjthZYf%2w40r!|hNgO2C<-MEH?MTM zgKYs5Em!zNt(uevs>K~I=jKLSv4ER@#7ZYka&uEzIDYLtz8tym^0V3L)& zPRjR8!rsDPLXeX$wa3pii64=4RTP-OKLY=Jsd# zB>3PD@JESr6Dgv3>C4QSnZq3B%+Fune*k!ioffKi+{BY6wgnn*$WnYOP~ATm2-Lc6 zWCS*PWX(lB9UA{h4O8xH^_*7eKzSN%gtka@Dn z*sH0TIe%TMM^i+p?x^x#@lNJlar@3Fdv4;yjY5^ZfxzqK3saYmq|1yQ8%nHvZ%Y%- zO59D!*ckYprd~jSXaD)3GLjz_Co=Q-()sPAC}e*|QBV3N1U8OMJ87eay1>!B9Tw=6 z{>fC?L|zWBjE>ktg@3=elz)vnU)gAd zYDID7=H7em0`I|-zjqcp5Zw-b8xkH_2QQTJrZR^kcpm5;9 z#S)?YZ&pI{$N5D#i6cvJSj;|)VN-=BY@@_~298^(pbGXrLk{EGFTBW;QlWse#~X*7 jiF0-L1@t^SsdIFBiJMI2V3{lLYY7cB84*@Emf(K^4RK)I literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..29cc9cf106572dac0a0457e1a7cd6ca7979f08ac GIT binary patch literal 1623 zcmb7ETT|0e5dPApO&S8UR5Q|pRcE$iR9UhoaVhK}Hr%k>57XO6S z3yh8qkB3BjLP+y9)X-!(f_s z_hbwxn8kgCkU56uWel=?AY+K_f{fE_AIcbJn~^cXc2UA30bP3GQ8vG}Y-FD0=W`hW z3B6R^)=Krd=DvQV)$3KwavIxpO|O=1hnA%`FRTn9GXm88T%)j%o!9@mxx$02PDBy^ zZO#zTLHok;(wdRc7X@_s-ouQZS)xeMB~Af7`j+|9)GDS^*0PRkm8}{bB3)dW*OxOy zl3YD`Y2lYzn{9k4|xoo+txq!YzvMu01A<^Bmsnc-Uc5HW+bULQO z>@k_i6}lwDE<2{%s1bjEdtQs4gXDCn;&TTR4BTyF<5@_bwEtPUP{pV=Y9(vYW?lP^ zj@2a3fQoZG>gQEFMpnSs$tF_87@nv&gR?3o@l=A&NRAy%#Q;vJ802M$m(#oq^D=@{ z^cftC9ycT)RH{@RDzNvE=!NwytK?D>H`=vYsF?NoOq(Wu$Ko(6Y0N_8@Y6;81*m5OqHaNu)f! zaa{2kG_rhVDD@HHqsfCfNDg7bYaqavNk?$Ga|n_UM4uWT-hq^w_=NC#^4kkgJdS?C zxQu-c-mwfpT=k--AmbXY`z8G%QTTcL6=E?i2csLii0nZ5fDR8)#i-RnIz6N-6fL)q zZV!ouqnj-x<{@z@h951c$Afz17=E^(zG(OxofmAP!zhYPbQ;A_6WvBJ+(gVMN=@__ vMY)MS;{yb4a{pG&K%#vo-D?*~(v6#l(kBzgDth41T?PCWr*VVg0=W4H{ElJt literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..df41921e637de9db5c086d63750bf686c9e7ec0c GIT binary patch literal 2633 zcmb7G?^n}S7=A9aB#l98Koq7<5ocSFh|~QLR7$DRStx4?4%pU(G|*_8V3MNvzwAHQ zhYdINxU(HU@9b~d*?ZF{MS48a554!j_kExDd4JvZpZ{L}4d6PSB@n}mge+zSTo#a% za29h4-F+j`&3y?)Eby~Mu2|w{OA@|CS;RRJ8kZ~lW?95}5i5NAK*R+R-|_9Lh!GKM ze7i2eJdAbu+ zcDKzu9ZoSQ+2zW7sa#mgRtlxXYOXYwC$z%CQn8RLROmieEaVp})m%A0M{6Nl)EIip z`Mc%3Heaoj?&lX75?gOk$)Na9-_ccDceYe5@GNJGya=N!+2V4ZOvl%cM6YK@x0|NV zFp)|h%`%pAYbHZq(Q?eiW@E$jD*A>^Cqf8W+GMzsI?OeFBzj8GHFSGL_biSbIfyxW zgZ%VvnZfKX&o@suL(jD3Six*1vBbbvNtka|_le>4t zWf(}M4?{8x+InN7rjH)rHhPs|rUUE|QjuO`U@mpev|&eKcBeS4TWu_O%S|5v^fq;l zq`6JcFz;Htbf-J?@C0u+8K3cayDX!MpBT~~JpnStp)(|BT{rLpPv2fJgSuPu`+BgE zgh5L!2~)-j0>SArO$MNs^OV6S}oo7WsLBfUeDb0Out?Y z+()J(<03yizCUX`xeRf`b{)#p!Sa_j9-2l#*}TzS<(y4ar`qZhjca@3gs+4I2UObw zofko-*Z8Bg&FAw7h2`B6Z|H$hXP9iK^p1hysP$&xy0&V#p4oa$7Px`wQ5a|6yKV-S zt!6z>-(991HO;2w`9Y}9Fh|TjfVSS2fVV*%KBCF#z_Y+RJh@+k7JV9Z^Kp|ZC*ZLR z4?8lx!q*HV`-+ycLse9Z;V@Fm>6^L{xZWjr)Hb}Xv?ref!o46V94L8cJ+_HY`OffZF^fq$T{7%H7hWu1W@Rg4&|p) zJPhYPtT!`B9eI|1bA_29K7IhLG;HBYLl&+yQsGKdM4Ad8WFR@rpAl|}1Rr)VWJnsJ zm4Bo?uovihgYI8Rq90oN?g{T?(jUi_NdG#z!c$3!WnLlv1~IPRbwGcDPUMyrRPLJ) zuD(OK86hMyugL>mL3jnR1I{q^agwbV$dcY2`sU1M&`r4@PnoeZFOkSx`2*5#Bp>E7 zNN1eU=V3nhj%TEs{?l;n8^n}exhOuBR-Tb~=66W%hjyxsQoIG1lz9I~#C7+}p^4AN zCljAcOnkM8;X#R(L=&SgnE0DS{MOm$^u{r%$z<(s@Aa*9)@|?k>z`kK1Mmt8X&l1E z6f8`Om=Q56V$Q~;G}p@_=7qH&Xi>yFBCg2UyEfiSV;?R$Sd#UsgZCYL;NU|CAIZVT zHm)gHx7OP2@^yvm^oswfpR4%Q8@Yv6qg=f)s$kq&TP=`OZ8nqUkA)8%S$w!OMsY%KcA6`o|LYX!e@#cz~lA08U5Qn{&+n=aH=bA@Iz z*Sd2%*KF2u_1a1;w^q7yC%16B+$xkNYO8g>x;9^Io?V<{IH?=O*5xqX1N{SW5!(Sp zf)?^@7`Nc1F!WS&%Sm_t<31!GJKzLfh&H_tM_#Prt+rob7CT(bEw2;{t8=3E{$YYrx@v_`Kg&nrb28rip_SV#nNoXXc9?Cc3<>;yD$2f z)2itqV)zTScB4={SC)C`iD~RH89WzH;b|8=@LW78;;@J#@DxV=*O<6Cjhu@<9CdL_ z&B`}l0Q+^5#$5PVX1>F{lK2=$Y`9mauCY#@SPi(i zfTIeXTi?mW_WM|cCaOIW``;A#>k(@c*X;Ct-~Ghj18nBMmlejlFnP6DIDtovg_#ZOwZaz z%H(a5GI__dh2(@J+jF*&+?&1Ns};Uyer zuHE<+CwL$x;v|uU3eMp*A{z~i<8>kjU-27n z5T)=npR_zt8l#NkB=+0R-`Hp4)I%J!F~*;x9->FX)PF#Jna`W<1pTUD4ozu+B)9!` z>g4pGnw!a)Oi7Yc@(yY@>T}xJw61rKo-)RQwjJplEwO1%=g>OrkqvZQlk;@m5QS%i z8b?~GRhhgQsMy88$nnfb<>swGWo@e1TUs57Z^s24OBityt9T1%7^%sEPY@+oz)x_N zStQw<8B7vMQBJV<(vBs*lIMx6pv>RHdBg*QWmbx%g;I}bxV2BnK{w2Z5OIv1M#d9}xX#lEHc*q`m(K$o + + 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> castListMapToTree(List> listMap, String FieldName, String parentFieldName, String childFieldName) { + //返回的map Tree树形结构 + List> treeMap = new ArrayList<>(); + //将传进的参数entityList转为MapList + //List> listMap = JSON.parseObject(JSON.toJSONString(entityList), List.class); + //声明一个map用来存listMap中的对象,key为对象id,value为对象本身 + Map> entityMap = new Hashtable<>(); + //循环listMap把map对象put到entityMap中去 + listMap.forEach(map -> entityMap.put(map.get(FieldName).toString(), map)); + //循环listMap进行Tree树形结构组装 + listMap.forEach(map -> { + //获取map的pid + Object pid = map.get(parentFieldName); + if (GyUtils.isNull(pid)){ //判断pid是否为空,为空说明是最顶级,直接add到返回的treeMap中去 + treeMap.add(map); + } else { //如果pid不为空 + //根据当前map的pid获取上级 parentMap + Map parentMap = entityMap.get(pid); + if (GyUtils.isNull(parentMap)){ //如果parentMap为空,则说明当前map没有父级,当前map就是顶级 + treeMap.add(map); + } else { //如果parentMap不为空,则当前map为parentMap的子级 + //取出parentMap的所有子级的List集合 + List> children = (List>)parentMap.get(childFieldName); + if (GyUtils.isNull(children)){ //判断子级集合是否为空,为空则新创建List + children = new ArrayList<>(); + parentMap.put(childFieldName, children); + } + //把当前map对象add到parentMap的子级List中去 + children.add(map); + } + } + }); + return treeMap; + } + +} diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/HttpUtils.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/HttpUtils.java new file mode 100644 index 0000000..946152e --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/HttpUtils.java @@ -0,0 +1,124 @@ +package com.css.txw.sso.util; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.map.TableMap; +import cn.hutool.core.net.url.UrlBuilder; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import org.springframework.util.StringUtils; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.servlet.http.HttpServletRequest; +import java.net.URI; +import java.nio.charset.Charset; +import java.util.Map; + +/** + * HTTP 工具类 + */ +public class HttpUtils { + + @SuppressWarnings("unchecked") + public static String replaceUrlQuery(String url, String key, String value) { + UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset()); + // 先移除 + TableMap query = (TableMap) + ReflectUtil.getFieldValue(builder.getQuery(), "query"); + query.remove(key); + // 后添加 + builder.addQuery(key, value); + return builder.build(); + } + + private String append(String base, Map query, boolean fragment) { + return append(base, query, null, fragment); + } + + /** + * 拼接 URL + * + * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 append 方法 + * + * @param base 基础 URL + * @param query 查询参数 + * @param keys query 的 key,对应的原本的 key 的映射。例如说 query 里有个 key 是 xx,实际它的 key 是 extra_xx,则通过 keys 里添加这个映射 + * @param fragment URL 的 fragment,即拼接到 # 中 + * @return 拼接后的 URL + */ + public static String append(String base, Map query, Map keys, boolean fragment) { + UriComponentsBuilder template = UriComponentsBuilder.newInstance(); + UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base); + URI redirectUri; + try { + // assume it's encoded to start with (if it came in over the wire) + redirectUri = builder.build(true).toUri(); + } catch (Exception e) { + // ... but allow client registrations to contain hard-coded non-encoded values + redirectUri = builder.build().toUri(); + builder = UriComponentsBuilder.fromUri(redirectUri); + } + template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost()) + .userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath()); + + if (fragment) { + StringBuilder values = new StringBuilder(); + if (redirectUri.getFragment() != null) { + String append = redirectUri.getFragment(); + values.append(append); + } + for (String key : query.keySet()) { + if (values.length() > 0) { + values.append("&"); + } + String name = key; + if (keys != null && keys.containsKey(key)) { + name = keys.get(key); + } + values.append(name).append("={").append(key).append("}"); + } + if (values.length() > 0) { + template.fragment(values.toString()); + } + UriComponents encoded = template.build().expand(query).encode(); + builder.fragment(encoded.getFragment()); + } else { + for (String key : query.keySet()) { + String name = key; + if (keys != null && keys.containsKey(key)) { + name = keys.get(key); + } + template.queryParam(name, "{" + key + "}"); + } + template.fragment(redirectUri.getFragment()); + UriComponents encoded = template.build().expand(query).encode(); + builder.query(encoded.getQuery()); + } + return builder.build().toUriString(); + } + + public static String[] obtainBasicAuthorization(HttpServletRequest request) { + String clientId; + String clientSecret; + // 先从 Header 中获取 + String authorization = request.getHeader("Authorization"); + authorization = StrUtil.subAfter(authorization, "Basic ", true); + if (StringUtils.hasText(authorization)) { + authorization = Base64.decodeStr(authorization); + clientId = StrUtil.subBefore(authorization, ":", false); + clientSecret = StrUtil.subAfter(authorization, ":", false); + // 再从 Param 中获取 + } else { + clientId = request.getParameter("client_id"); + clientSecret = request.getParameter("client_secret"); + } + + // 如果两者非空,则返回 + if (StrUtil.isNotEmpty(clientId) && StrUtil.isNotEmpty(clientSecret)) { + return new String[]{clientId, clientSecret}; + } + return null; + } + + +} diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/OAuth2AccessTokenRedisDAO.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/OAuth2AccessTokenRedisDAO.java new file mode 100644 index 0000000..b306c4c --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/OAuth2AccessTokenRedisDAO.java @@ -0,0 +1,151 @@ +package com.css.txw.sso.util; + + +import cn.hutool.core.date.LocalDateTimeUtil; +import com.css.ggzc.framework.common.util.gy.GyUtils; +import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO; +import com.css.txw.sso.pojo.dto.session.*; +import com.css.ggzc.framework.common.util.collection.CollectionUtils; +import com.css.ggzc.framework.common.util.json.JsonUtils; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Component +public class OAuth2AccessTokenRedisDAO { + + + private static final String KEY_QYUUID = "qyuuid"; + private static final String KEY_QYMC = "qymc"; + private static final String KEY_NSRSBH = "nsrsbh"; + private static final String KEY_YHXX = "yhxx"; + private static final String KEY_TOKEN_INFO = "tokenInfo"; + private static final String KEY_THIRDPARTY = "isThirdParty"; + private static final String KEY_YHLX = "yhlx"; + + @Resource + private StringRedisTemplate stringRedisTemplate; + + + public AccessTokenInfo getTokenInfo(String accessToken){ + String redisKey = formatKey(accessToken); + return JsonUtils.toBean((String) stringRedisTemplate.opsForHash().get(redisKey,KEY_TOKEN_INFO), AccessTokenInfo.class); + } + + public SessionInfo getSessionInfo(String accessToken){ + String redisKey = formatKey(accessToken); + final SessionInfo sessionInfo = new SessionInfo(); + + sessionInfo.setTokenInfo(JsonUtils.toBean((String) stringRedisTemplate.opsForHash().get(redisKey,KEY_TOKEN_INFO), AccessTokenInfo.class)); + sessionInfo.setYhxx(JsonUtils.toBean((String) stringRedisTemplate.opsForHash().get(redisKey,KEY_YHXX), YhInfo.class)); + + final String yhlx = (String) stringRedisTemplate.opsForHash().get(redisKey, KEY_YHLX); + if (!GyUtils.isNull(yhlx)){ + sessionInfo.setCurrentYhlx(yhlx); + } + sessionInfo.setQyuuid((String) stringRedisTemplate.opsForHash().get(redisKey, KEY_QYUUID)); + sessionInfo.setQymc((String) stringRedisTemplate.opsForHash().get(redisKey, KEY_QYMC)); + sessionInfo.setNsrsbh((String) stringRedisTemplate.opsForHash().get(redisKey, KEY_NSRSBH)); + return sessionInfo; + } + + public void set(SessionInfo sessionInfo){ + String redisKey = formatKey(sessionInfo.getTokenInfo().getAccessToken()); + long time = LocalDateTimeUtil.between(LocalDateTime.now(), sessionInfo.getTokenInfo().getGqsj(), ChronoUnit.SECONDS); + if (time > 0) { + stringRedisTemplate.opsForHash().put(redisKey,KEY_TOKEN_INFO,JsonUtils.toJson(sessionInfo.getTokenInfo())); +// stringRedisTemplate.opsForHash().put(redisKey,KEY_JGXX,GyUtils.isNull(sessionInfo.getQyxx())?"":JsonUtils.toJson(sessionInfo.getQyxx())); + stringRedisTemplate.opsForHash().put(redisKey,KEY_YHXX,GyUtils.isNull(sessionInfo.getYhxx())?"":JsonUtils.toJson(sessionInfo.getYhxx())); + stringRedisTemplate.opsForHash().put(redisKey,KEY_YHLX,GyUtils.isNull(sessionInfo.getCurrentYhlx())?"":sessionInfo.getCurrentYhlx()); + stringRedisTemplate.opsForHash().put(redisKey,KEY_QYUUID,GyUtils.isNull(sessionInfo.getQyuuid())?"":sessionInfo.getQyuuid()); + stringRedisTemplate.opsForHash().put(redisKey,KEY_QYMC,GyUtils.isNull(sessionInfo.getQymc())?"":sessionInfo.getQymc()); + stringRedisTemplate.opsForHash().put(redisKey,KEY_NSRSBH,GyUtils.isNull(sessionInfo.getNsrsbh())?"":sessionInfo.getNsrsbh()); + stringRedisTemplate.opsForHash().getOperations().expire(redisKey,time, TimeUnit.SECONDS); + } + } + + public void set(AccessTokenInfo accessTokenInfo){ + String redisKey = formatKey(accessTokenInfo.getAccessToken()); + long time = LocalDateTimeUtil.between(LocalDateTime.now(), accessTokenInfo.getGqsj(), ChronoUnit.SECONDS); + if (time > 0) { + stringRedisTemplate.opsForHash().put(redisKey,KEY_TOKEN_INFO,JsonUtils.toJson(accessTokenInfo)); + stringRedisTemplate.opsForHash().getOperations().expire(redisKey,time, TimeUnit.SECONDS); + } + } + + public void updateAccessTokenInfo(OAuth2AccessTokenDO tokenDO){ + final AccessTokenInfo accessTokenInfo = new AccessTokenInfo(); + accessTokenInfo.setAccessToken(tokenDO.getAccessToken()); + accessTokenInfo.setRefreshToken(tokenDO.getRefreshToken()); + accessTokenInfo.setUuid(tokenDO.getUuid()); + accessTokenInfo.setYhUuid(tokenDO.getYhUuid()); + accessTokenInfo.setQyuuid(tokenDO.getQyuuid()); + accessTokenInfo.setGqsj(tokenDO.getGqsj()); + accessTokenInfo.setClientid(tokenDO.getClientid()); + accessTokenInfo.setSqnr(tokenDO.getSqnr()); + accessTokenInfo.setGllp(tokenDO.getGllp()); + String redisKey = formatKey(accessTokenInfo.getAccessToken()); + stringRedisTemplate.opsForHash().put(redisKey,KEY_TOKEN_INFO,JsonUtils.toJson(accessTokenInfo)); + } + + public void refreshQyxx(String accessToken, String qyuuid,String qymc,String nsrsbh){ + String redisKey = formatKey(accessToken); + stringRedisTemplate.opsForHash().put(redisKey,KEY_QYUUID,qyuuid); + stringRedisTemplate.opsForHash().put(redisKey,KEY_QYMC,qymc); + stringRedisTemplate.opsForHash().put(redisKey,KEY_NSRSBH,nsrsbh); + } + +// public void refreshCompany(String accessToken, CktsMhqxQyxxbDO jgxx,List kczDjxhList,String swjgDm,List jsuuidList){ +// String redisKey = formatKey(accessToken); +// +// +// if (GyUtils.isNull(jgxx)){ +// stringRedisTemplate.opsForHash().put(redisKey,KEY_JGXX,""); +// }else { +// JgInfo jgInfo = new JgInfo(); +// jgInfo.setQyuuid(jgxx.getQyuuid()); +// jgInfo.setQymc(jgxx.getQymc()); +// jgInfo.setNsrsbh(jgxx.getNsrsbh()); +// jgInfo.setShxydm(jgxx.getShxydm()); +// jgInfo.setDjxh(jgxx.getDjxh()); +// stringRedisTemplate.opsForHash().put(redisKey,KEY_JGXX,JsonUtils.toJson(jgInfo)); +// } +// +// if (GyUtils.isNull(kczDjxhList)){ +// stringRedisTemplate.opsForHash().put(redisKey,KEY_KZCDJXHLIST,""); +// }else { +// stringRedisTemplate.opsForHash().put(redisKey,KEY_KZCDJXHLIST,JsonUtils.toJson(kczDjxhList)); +// } +// if (GyUtils.isNull(jsuuidList)){ +// stringRedisTemplate.opsForHash().put(redisKey,KEY_JSUUIDLIST,""); +// }else { +// stringRedisTemplate.opsForHash().put(redisKey,KEY_JSUUIDLIST,JsonUtils.toJson(jsuuidList)); +// } +// stringRedisTemplate.opsForHash().put(redisKey,KEY_SWJGDM,GyUtils.isNull(swjgDm)?"":swjgDm); +// final AccessTokenInfo tokenInfo = JsonUtils.toBean((String) stringRedisTemplate.opsForHash().get(redisKey, KEY_TOKEN_INFO), AccessTokenInfo.class); +// tokenInfo.setQyuuid(GyUtils.isNull(jgxx)?"":jgxx.getQyuuid()); +// stringRedisTemplate.opsForHash().put(redisKey,KEY_TOKEN_INFO,JsonUtils.toJson(tokenInfo)); +// } + + + public void delete(String accessToken) { + String redisKey = formatKey(accessToken); + stringRedisTemplate.delete(redisKey); + } + + public void deleteList(Collection accessTokens) { + List redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey); + stringRedisTemplate.delete(redisKeys); + } + + private static String formatKey(String accessToken) { + String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s"; + return String.format(OAUTH2_ACCESS_TOKEN, accessToken); + } +} diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/OAuth2Utils.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/OAuth2Utils.java new file mode 100644 index 0000000..3714c6d --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/OAuth2Utils.java @@ -0,0 +1,53 @@ +package com.css.txw.sso.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * OAuth2 相关的工具类 + */ +@Slf4j +public class OAuth2Utils { + + /** + * 构建授权码模式下,重定向的 URI + * + * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 getSuccessfulRedirect 方法 + * + * @param redirectUri 重定向 URI + * @param authorizationCode 授权码 + * @param state 状态 + * @return 授权码模式下的重定向 URI + */ + public static String buildAuthorizationCodeRedirectUri(String redirectUri, String authorizationCode, String state) { + Map query = new LinkedHashMap<>(); + query.put("code", authorizationCode); + if (state != null) { + query.put("state", state); + } + return HttpUtils.append(redirectUri, query, null, false); + } + + + public static long getExpiresIn(LocalDateTime expireTime) { + return LocalDateTimeUtil.between(LocalDateTime.now(), expireTime, ChronoUnit.SECONDS); + } + + public static String buildScopeStr(Collection scopes) { + return CollUtil.join(scopes, " "); + } + + public static List buildScopes(String scope) { + return StrUtil.split(scope, ' '); + } + +} diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/SSOGyUtils.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/SSOGyUtils.java new file mode 100644 index 0000000..4d42750 --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/SSOGyUtils.java @@ -0,0 +1,6 @@ +package com.css.txw.sso.util; + +public class SSOGyUtils { + + +} diff --git a/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/SecurityFrameworkUtils.java b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/SecurityFrameworkUtils.java new file mode 100644 index 0000000..bdd3fbf --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/util/SecurityFrameworkUtils.java @@ -0,0 +1,48 @@ +package com.css.txw.sso.util; + +import com.css.ggzc.framework.common.util.gy.GyUtils; +import org.springframework.util.StringUtils; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +/** + * 安全服务工具类 + */ +public class SecurityFrameworkUtils { + + private static final String AUTHORIZATION_HEADER = "Authorization"; + + private static final String AUTHORIZATION_BEARER = "Bearer"; + + private static final String LOGIN_USER_HEADER = "login-user"; + + private static final String LOGIN_USER_ID_ATTR = "login-user-id"; + private static final String LOGIN_USER_TYPE_ATTR = "login-user-type"; + + private SecurityFrameworkUtils() {} + + public static String obtainAuthorization(String cookeiKey,HttpServletRequest request) { + String token = ""; + final Cookie[] cookies = request.getCookies(); + if (!GyUtils.isNull(cookies)){ + for (Cookie cookie : cookies) { + final String name = cookie.getName(); + if (cookeiKey.equals(name)){ + token = cookie.getValue(); + break; + } + } + } + if (GyUtils.isNull(token)){ + token = request.getHeader(AUTHORIZATION_HEADER); + } + + if (!StringUtils.hasText(token)) { + return null; + } + final int length = token.length(); + return token.substring(length - 32); + } + +} diff --git a/txw-sso/txw-sso-service-biz/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/txw-sso/txw-sso-service-biz/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..3520def --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.css.txw.sso.configuration.SsoServiceConfiguration \ No newline at end of file diff --git a/txw-sso/txw-sso-service-biz/src/main/resources/additional-spring-configuration-metadata.json b/txw-sso/txw-sso-service-biz/src/main/resources/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/txw-sso/txw-sso-service-biz/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-biz/src/main/resources/application.yaml b/txw-sso/txw-sso-service-biz/src/main/resources/application.yaml new file mode 100644 index 0000000..b11d0c7 --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/resources/application.yaml @@ -0,0 +1,112 @@ +spring: + main: + allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 + allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务 + + # Servlet 配置 + servlet: + # 文件上传相关配置项 + multipart: + max-file-size: 16MB # 单个文件大小 + max-request-size: 32MB # 设置总上传的文件大小 + mvc: + pathmatch: + matching-strategy: ANT_PATH_MATCHER # 解决 SpringFox 与 SpringBoot 2.6.x 不兼容的问题,参见 SpringFoxHandlerProviderBeanPostProcessor 类 + + # Jackson 配置项 + jackson: + serialization: + write-dates-as-timestamps: false # 设置 LocalDateTime 的格式,使用时间戳 + write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: false # 设置 Duration 的格式,使用时间戳 + fail-on-empty-beans: false # 允许序列化无属性的 Bean + date-format: yyyy-MM-dd HH:MM:ss + timeZone: GMT+8 + + # Cache 配置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 小时 + +--- #################### 接口文档配置 #################### +springdoc: + api-docs: + enabled: true # 1. 是否开启 Swagger 接文档的元数据 + path: /v3/api-docs + swagger-ui: + enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面 + path: /swagger-ui.html + default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: true # 2.2 是否开启 Swagger 文档的 Knife4j UI 界面 + setting: + language: zh_cn + + # MyBatis Plus 的配置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 + # id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库 + # id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库 + # id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制台的 Banner 打印 + enable-sql-runner: true #使用SQLrunner + type-aliases-package: ${znsb.info.base-package}.*.dal.dataobject + encryptor: + password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 + +mybatis-plus-join: + banner: false # 关闭控制台的 Banner 打印 + + + # Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + + # Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + + # Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + + # 日志文件配置 +logging: + level: + # 配置自己写的 MyBatis Mapper 打印日志 + com.css.txw.sso.pojo: debug + + +css: + application: + version: 1.0.0-SNAPSHOT + base-package: com.css.txw + swagger: + title: ${spring.application.name} + description: ${spring.application.name} + version: ${css.application.version} + base-package: ${css.application.base-package} + tenant: # 多租户相关配置项 + enable: true + +debug: false diff --git a/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-env.yml b/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-env.yml new file mode 100644 index 0000000..8347294 --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-env.yml @@ -0,0 +1,22 @@ + +--- #################### 注册中心相关配置 #################### + +spring: + cloud: + nacos: + server-addr: 10.23.10.91:8848 + discovery: + namespace: qyd-txw # 命名空间。这里使用 dev 开发环境 + +--- #################### 配置中心相关配置 #################### + +spring: + cloud: + nacos: + # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 + config: + server-addr: 10.23.10.91:8848 # Nacos 服务器地址 + namespace: qyd-txw # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + name: ${spring.application.name} # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name + file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties diff --git a/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-local.yml b/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-local.yml new file mode 100644 index 0000000..98fb7f4 --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-local.yml @@ -0,0 +1,27 @@ +--- #################### 注册中心相关配置 #################### + +spring: + cloud: + nacos: + server-addr: 10.23.10.91:8848 + username: qyddev + password: 9!1%Sw#QydNacosDev + discovery: + namespace: 0ac06fee-c2c9-4ca0-9a26-4921d553adde # 命名空间。这里使用开发环境 + metadata: + version: 1.0.0-SNAPSHOT # 服务实例的版本号,可用于灰度发布 + +--- #################### 配置中心相关配置 #################### + +spring: + cloud: + nacos: + # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类 + config: + server-addr: 10.23.10.91:8848 # Nacos 服务器地址 + username: qyddev + password: 9!1%Sw#QydNacosDev + namespace: 0ac06fee-c2c9-4ca0-9a26-4921d553adde # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + name: ${spring.application.name} # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name + file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties diff --git a/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-nacos.yml b/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-nacos.yml new file mode 100644 index 0000000..bc13ec7 --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap-nacos.yml @@ -0,0 +1,71 @@ +--- #################### sentinel相关配置 #################### +spring: + cloud: + sentinel: + transport: + dashboard: sentinel_IP:PORT #控制台地址 + http-method-specify: true #开启请求方式前缀 + +feign: + sentinel: + enabled: true +--- #################### xxl-job相关配置 #################### +xxl: + job: + enable: false + admin: + addresses: http://XXL_IP:PORT/xxl-job-admin + executor: + appname: ${spring.application.name} # 执行器 AppName + logpath: ${user.dir}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 + access-token: xxl_password +spring: + # 数据源配置项 + autoconfigure: + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: znsb_sso + datasource: + znsb_sso: + url: jdbc:mysql://DB_IP:PORT/DBNAME?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + username: DB_username + password: DB_password + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + redis: + host: REDIS_IP # 地址 + port: 6379 # 端口 + password: REDIS_password + database: 0 # 数据库索引 \ No newline at end of file diff --git a/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap.yml b/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..c3a22ad --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/resources/bootstrap.yml @@ -0,0 +1,16 @@ +spring: + application: + name: txw-sso + + profiles: + active: env + +server: + port: 9301 + servlet: + context-path: /sso + + # 日志文件配置。注意,如果 logging.file.name 不放在 bootstrap.yaml 配置文件,而是放在 application.yaml 中,会导致出现 LOG_FILE_IS_UNDEFINED 文件 +logging: + file: + name: ${user.dir}/logs/${spring.application.name}.log # 日志文件名,全路径 \ No newline at end of file diff --git a/txw-sso/txw-sso-service-biz/src/main/resources/logback-spring.xml b/txw-sso/txw-sso-service-biz/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..d34aa9e --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/resources/logback-spring.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + +       + + ${PATTERN_DEFAULT} + + + + + + + + ${PATTERN_DEFAULT} + + + ${LOG_FILE} + + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + + + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + + + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30} + + + + + + 0 + + 256 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/txw-sso/txw-sso-service-biz/src/main/resources/mapper/readme.md b/txw-sso/txw-sso-service-biz/src/main/resources/mapper/readme.md new file mode 100644 index 0000000..1c34391 --- /dev/null +++ b/txw-sso/txw-sso-service-biz/src/main/resources/mapper/readme.md @@ -0,0 +1 @@ +# mapper \ No newline at end of file