Commit 3c2b2faf20c4e201c2d51ded08207f57948d37ac
1 parent
c7d2e3b7
feat(auth): 实现UAA单点登录功能并集成用户同步
Showing
13 changed files
with
379 additions
and
123 deletions
urbanops-module-system/pom.xml
| @@ -123,7 +123,21 @@ | @@ -123,7 +123,21 @@ | ||
| 123 | <artifactId>pinyin4j</artifactId> | 123 | <artifactId>pinyin4j</artifactId> |
| 124 | <version>2.5.1</version> <!-- 稳定版本 --> | 124 | <version>2.5.1</version> <!-- 稳定版本 --> |
| 125 | </dependency> | 125 | </dependency> |
| 126 | - | 126 | + <dependency> |
| 127 | + <groupId>com.auth0</groupId> | ||
| 128 | + <artifactId>java-jwt</artifactId> | ||
| 129 | + <version>3.8.1</version> | ||
| 130 | + </dependency> | ||
| 131 | + <dependency> | ||
| 132 | + <groupId>io.jsonwebtoken</groupId> | ||
| 133 | + <artifactId>jjwt</artifactId> | ||
| 134 | + <version>0.9.1</version> | ||
| 135 | + </dependency> | ||
| 136 | + <dependency> | ||
| 137 | + <groupId>com.smart.uaa</groupId> | ||
| 138 | + <artifactId>sdk</artifactId> | ||
| 139 | + <version>1.0.1</version> | ||
| 140 | + </dependency> | ||
| 127 | 141 | ||
| 128 | </dependencies> | 142 | </dependencies> |
| 129 | 143 |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/enums/social/SocialTypeEnum.java
| @@ -53,7 +53,11 @@ public enum SocialTypeEnum implements ArrayValuable<Integer> { | @@ -53,7 +53,11 @@ public enum SocialTypeEnum implements ArrayValuable<Integer> { | ||
| 53 | * @see <a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html">接入文档</a> | 53 | * @see <a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html">接入文档</a> |
| 54 | */ | 54 | */ |
| 55 | WECHAT_MINI_PROGRAM(34, "WECHAT_MINI_PROGRAM"), | 55 | WECHAT_MINI_PROGRAM(34, "WECHAT_MINI_PROGRAM"), |
| 56 | - ; | 56 | + |
| 57 | + /** | ||
| 58 | + * UAA | ||
| 59 | + */ | ||
| 60 | + UAA(35, "UAA"); | ||
| 57 | 61 | ||
| 58 | public static final Integer[] ARRAYS = Arrays.stream(values()).map(SocialTypeEnum::getType).toArray(Integer[]::new); | 62 | public static final Integer[] ARRAYS = Arrays.stream(values()).map(SocialTypeEnum::getType).toArray(Integer[]::new); |
| 59 | 63 |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/framework/justauth/core/AuthRequestFactory.java
| @@ -100,9 +100,9 @@ public class AuthRequestFactory { | @@ -100,9 +100,9 @@ public class AuthRequestFactory { | ||
| 100 | AuthRequest authRequest = getDefaultRequest(source); | 100 | AuthRequest authRequest = getDefaultRequest(source); |
| 101 | 101 | ||
| 102 | // 如果获取不到则尝试取自定义的 | 102 | // 如果获取不到则尝试取自定义的 |
| 103 | - if (authRequest == null) { | ||
| 104 | - authRequest = getExtendRequest(properties.getExtend().getEnumClass(), source); | ||
| 105 | - } | 103 | +// if (authRequest == null) { |
| 104 | +// authRequest = getExtendRequest(properties.getExtend().getEnumClass(), source); | ||
| 105 | +// } | ||
| 106 | 106 | ||
| 107 | if (authRequest == null) { | 107 | if (authRequest == null) { |
| 108 | throw new AuthException(AuthResponseStatus.UNSUPPORTED); | 108 | throw new AuthException(AuthResponseStatus.UNSUPPORTED); |
| @@ -159,134 +159,136 @@ public class AuthRequestFactory { | @@ -159,134 +159,136 @@ public class AuthRequestFactory { | ||
| 159 | * @return {@link AuthRequest} | 159 | * @return {@link AuthRequest} |
| 160 | */ | 160 | */ |
| 161 | private AuthRequest getDefaultRequest(String source) { | 161 | private AuthRequest getDefaultRequest(String source) { |
| 162 | - AuthDefaultSource authDefaultSource; | 162 | + AuthSource authDefaultSource; |
| 163 | 163 | ||
| 164 | try { | 164 | try { |
| 165 | authDefaultSource = EnumUtil.fromString(AuthDefaultSource.class, source.toUpperCase()); | 165 | authDefaultSource = EnumUtil.fromString(AuthDefaultSource.class, source.toUpperCase()); |
| 166 | } catch (IllegalArgumentException e) { | 166 | } catch (IllegalArgumentException e) { |
| 167 | // 无自定义匹配 | 167 | // 无自定义匹配 |
| 168 | - return null; | 168 | + authDefaultSource = EnumUtil.fromString(CustomerAuthSource.class, source.toUpperCase()); |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | - AuthConfig config = properties.getType().get(authDefaultSource.name()); | 171 | + AuthConfig config = properties.getType().get(authDefaultSource.getName()); |
| 172 | // 找不到对应关系,直接返回空 | 172 | // 找不到对应关系,直接返回空 |
| 173 | if (config == null) { | 173 | if (config == null) { |
| 174 | return null; | 174 | return null; |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | // 配置 http config | 177 | // 配置 http config |
| 178 | - configureHttpConfig(authDefaultSource.name(), config, properties.getHttpConfig()); | 178 | + configureHttpConfig(authDefaultSource.getName(), config, properties.getHttpConfig()); |
| 179 | 179 | ||
| 180 | - switch (authDefaultSource) { | ||
| 181 | - case GITHUB: | 180 | + switch (authDefaultSource.getName()) { |
| 181 | + case "GITHUB": | ||
| 182 | return new AuthGithubRequest(config, authStateCache); | 182 | return new AuthGithubRequest(config, authStateCache); |
| 183 | - case WEIBO: | 183 | + case "WEIBO": |
| 184 | return new AuthWeiboRequest(config, authStateCache); | 184 | return new AuthWeiboRequest(config, authStateCache); |
| 185 | - case GITEE: | 185 | + case "GITEE": |
| 186 | return new AuthGiteeRequest(config, authStateCache); | 186 | return new AuthGiteeRequest(config, authStateCache); |
| 187 | - case DINGTALK: | 187 | + case "DINGTALK": |
| 188 | return new AuthDingTalkRequest(config, authStateCache); | 188 | return new AuthDingTalkRequest(config, authStateCache); |
| 189 | - case DINGTALK_V2: | 189 | + case "DINGTALK_V2": |
| 190 | return new AuthDingTalkV2Request(config, authStateCache); | 190 | return new AuthDingTalkV2Request(config, authStateCache); |
| 191 | - case DINGTALK_ACCOUNT: | 191 | + case "DINGTALK_ACCOUNT": |
| 192 | return new AuthDingTalkAccountRequest(config, authStateCache); | 192 | return new AuthDingTalkAccountRequest(config, authStateCache); |
| 193 | - case BAIDU: | 193 | + case "BAIDU": |
| 194 | return new AuthBaiduRequest(config, authStateCache); | 194 | return new AuthBaiduRequest(config, authStateCache); |
| 195 | - case CSDN: | 195 | + case "CSDN": |
| 196 | return new AuthCsdnRequest(config, authStateCache); | 196 | return new AuthCsdnRequest(config, authStateCache); |
| 197 | - case CODING: | 197 | + case "CODING": |
| 198 | return new AuthCodingRequest(config, authStateCache); | 198 | return new AuthCodingRequest(config, authStateCache); |
| 199 | - case OSCHINA: | 199 | + case "OSCHINA": |
| 200 | return new AuthOschinaRequest(config, authStateCache); | 200 | return new AuthOschinaRequest(config, authStateCache); |
| 201 | - case ALIPAY: | 201 | + case "ALIPAY": |
| 202 | return new AuthAlipayRequest(config, authStateCache); | 202 | return new AuthAlipayRequest(config, authStateCache); |
| 203 | - case QQ: | 203 | + case "QQ": |
| 204 | return new AuthQqRequest(config, authStateCache); | 204 | return new AuthQqRequest(config, authStateCache); |
| 205 | - case WECHAT_OPEN: | 205 | + case "WECHAT_OPEN": |
| 206 | return new AuthWeChatOpenRequest(config, authStateCache); | 206 | return new AuthWeChatOpenRequest(config, authStateCache); |
| 207 | - case WECHAT_MP: | 207 | + case "WECHAT_MP": |
| 208 | return new AuthWeChatMpRequest(config, authStateCache); | 208 | return new AuthWeChatMpRequest(config, authStateCache); |
| 209 | - case TAOBAO: | 209 | + case "TAOBAO": |
| 210 | return new AuthTaobaoRequest(config, authStateCache); | 210 | return new AuthTaobaoRequest(config, authStateCache); |
| 211 | - case GOOGLE: | 211 | + case "GOOGLE": |
| 212 | return new AuthGoogleRequest(config, authStateCache); | 212 | return new AuthGoogleRequest(config, authStateCache); |
| 213 | - case FACEBOOK: | 213 | + case "FACEBOOK": |
| 214 | return new AuthFacebookRequest(config, authStateCache); | 214 | return new AuthFacebookRequest(config, authStateCache); |
| 215 | - case DOUYIN: | 215 | + case "DOUYIN": |
| 216 | return new AuthDouyinRequest(config, authStateCache); | 216 | return new AuthDouyinRequest(config, authStateCache); |
| 217 | - case LINKEDIN: | 217 | + case "LINKEDIN": |
| 218 | return new AuthLinkedinRequest(config, authStateCache); | 218 | return new AuthLinkedinRequest(config, authStateCache); |
| 219 | - case MICROSOFT: | 219 | + case "MICROSOFT": |
| 220 | return new AuthMicrosoftRequest(config, authStateCache); | 220 | return new AuthMicrosoftRequest(config, authStateCache); |
| 221 | - case MICROSOFT_CN: | 221 | + case "MICROSOFT_CN": |
| 222 | return new AuthMicrosoftCnRequest(config, authStateCache); | 222 | return new AuthMicrosoftCnRequest(config, authStateCache); |
| 223 | 223 | ||
| 224 | - case MI: | 224 | + case "MI": |
| 225 | return new AuthMiRequest(config, authStateCache); | 225 | return new AuthMiRequest(config, authStateCache); |
| 226 | - case TOUTIAO: | 226 | + case "TOUTIAO": |
| 227 | return new AuthToutiaoRequest(config, authStateCache); | 227 | return new AuthToutiaoRequest(config, authStateCache); |
| 228 | - case TEAMBITION: | 228 | + case "TEAMBITION": |
| 229 | return new AuthTeambitionRequest(config, authStateCache); | 229 | return new AuthTeambitionRequest(config, authStateCache); |
| 230 | - case RENREN: | 230 | + case "RENREN": |
| 231 | return new AuthRenrenRequest(config, authStateCache); | 231 | return new AuthRenrenRequest(config, authStateCache); |
| 232 | - case PINTEREST: | 232 | + case "PINTEREST": |
| 233 | return new AuthPinterestRequest(config, authStateCache); | 233 | return new AuthPinterestRequest(config, authStateCache); |
| 234 | - case STACK_OVERFLOW: | 234 | + case "STACK_OVERFLOW": |
| 235 | return new AuthStackOverflowRequest(config, authStateCache); | 235 | return new AuthStackOverflowRequest(config, authStateCache); |
| 236 | - case HUAWEI: | 236 | + case "HUAWEI": |
| 237 | return new AuthHuaweiRequest(config, authStateCache); | 237 | return new AuthHuaweiRequest(config, authStateCache); |
| 238 | - case HUAWEI_V3: | 238 | + case "HUAWEI_V3": |
| 239 | return new AuthHuaweiV3Request(config, authStateCache); | 239 | return new AuthHuaweiV3Request(config, authStateCache); |
| 240 | - case WECHAT_ENTERPRISE: | 240 | + case "WECHAT_ENTERPRISE": |
| 241 | return new AuthWeChatEnterpriseQrcodeRequest(config, authStateCache); | 241 | return new AuthWeChatEnterpriseQrcodeRequest(config, authStateCache); |
| 242 | - case WECHAT_ENTERPRISE_V2: | 242 | + case "WECHAT_ENTERPRISE_V2": |
| 243 | return new AuthWeChatEnterpriseQrcodeV2Request(config, authStateCache); | 243 | return new AuthWeChatEnterpriseQrcodeV2Request(config, authStateCache); |
| 244 | - case WECHAT_ENTERPRISE_QRCODE_THIRD: | 244 | + case "WECHAT_ENTERPRISE_QRCODE_THIRD": |
| 245 | return new AuthWeChatEnterpriseThirdQrcodeRequest(config, authStateCache); | 245 | return new AuthWeChatEnterpriseThirdQrcodeRequest(config, authStateCache); |
| 246 | - case WECHAT_ENTERPRISE_WEB: | 246 | + case "WECHAT_ENTERPRISE_WEB": |
| 247 | return new AuthWeChatEnterpriseWebRequest(config, authStateCache); | 247 | return new AuthWeChatEnterpriseWebRequest(config, authStateCache); |
| 248 | - case KUJIALE: | 248 | + case "KUJIALE": |
| 249 | return new AuthKujialeRequest(config, authStateCache); | 249 | return new AuthKujialeRequest(config, authStateCache); |
| 250 | - case GITLAB: | 250 | + case "GITLAB": |
| 251 | return new AuthGitlabRequest(config, authStateCache); | 251 | return new AuthGitlabRequest(config, authStateCache); |
| 252 | - case MEITUAN: | 252 | + case "MEITUAN": |
| 253 | return new AuthMeituanRequest(config, authStateCache); | 253 | return new AuthMeituanRequest(config, authStateCache); |
| 254 | - case ELEME: | 254 | + case "ELEME": |
| 255 | return new AuthElemeRequest(config, authStateCache); | 255 | return new AuthElemeRequest(config, authStateCache); |
| 256 | - case TWITTER: | 256 | + case "TWITTER": |
| 257 | return new AuthTwitterRequest(config, authStateCache); | 257 | return new AuthTwitterRequest(config, authStateCache); |
| 258 | - case FEISHU: | 258 | + case "FEISHU": |
| 259 | return new AuthFeishuRequest(config, authStateCache); | 259 | return new AuthFeishuRequest(config, authStateCache); |
| 260 | - case JD: | 260 | + case "JD": |
| 261 | return new AuthJdRequest(config, authStateCache); | 261 | return new AuthJdRequest(config, authStateCache); |
| 262 | - case ALIYUN: | 262 | + case "ALIYUN": |
| 263 | return new AuthAliyunRequest(config, authStateCache); | 263 | return new AuthAliyunRequest(config, authStateCache); |
| 264 | - case XMLY: | 264 | + case "XMLY": |
| 265 | return new AuthXmlyRequest(config, authStateCache); | 265 | return new AuthXmlyRequest(config, authStateCache); |
| 266 | - case AMAZON: | 266 | + case "AMAZON": |
| 267 | return new AuthAmazonRequest(config, authStateCache); | 267 | return new AuthAmazonRequest(config, authStateCache); |
| 268 | - case SLACK: | 268 | + case "SLACK": |
| 269 | return new AuthSlackRequest(config, authStateCache); | 269 | return new AuthSlackRequest(config, authStateCache); |
| 270 | - case LINE: | 270 | + case "LINE": |
| 271 | return new AuthLineRequest(config, authStateCache); | 271 | return new AuthLineRequest(config, authStateCache); |
| 272 | - case OKTA: | 272 | + case "OKTA": |
| 273 | return new AuthOktaRequest(config, authStateCache); | 273 | return new AuthOktaRequest(config, authStateCache); |
| 274 | - case PROGINN: | 274 | + case "PROGINN": |
| 275 | return new AuthProginnRequest(config,authStateCache); | 275 | return new AuthProginnRequest(config,authStateCache); |
| 276 | - case AFDIAN: | 276 | + case "AFDIAN": |
| 277 | return new AuthAfDianRequest(config,authStateCache); | 277 | return new AuthAfDianRequest(config,authStateCache); |
| 278 | - case APPLE: | 278 | + case "APPLE": |
| 279 | return new AuthAppleRequest(config,authStateCache); | 279 | return new AuthAppleRequest(config,authStateCache); |
| 280 | - case FIGMA: | 280 | + case "FIGMA": |
| 281 | return new AuthFigmaRequest(config,authStateCache); | 281 | return new AuthFigmaRequest(config,authStateCache); |
| 282 | - case WECHAT_MINI_PROGRAM: | 282 | + case "WECHAT_MINI_PROGRAM": |
| 283 | config.setIgnoreCheckRedirectUri(true); | 283 | config.setIgnoreCheckRedirectUri(true); |
| 284 | config.setIgnoreCheckState(true); | 284 | config.setIgnoreCheckState(true); |
| 285 | return new AuthWechatMiniProgramRequest(config, authStateCache); | 285 | return new AuthWechatMiniProgramRequest(config, authStateCache); |
| 286 | - case QQ_MINI_PROGRAM: | 286 | + case "QQ_MINI_PROGRAM": |
| 287 | config.setIgnoreCheckRedirectUri(true); | 287 | config.setIgnoreCheckRedirectUri(true); |
| 288 | config.setIgnoreCheckState(true); | 288 | config.setIgnoreCheckState(true); |
| 289 | return new AuthQQMiniProgramRequest(config, authStateCache); | 289 | return new AuthQQMiniProgramRequest(config, authStateCache); |
| 290 | + case "UAA": | ||
| 291 | + return new AuthUaaRequest(config, authStateCache); | ||
| 290 | default: | 292 | default: |
| 291 | return null; | 293 | return null; |
| 292 | } | 294 | } |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/framework/justauth/core/AuthUaaRequest.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.framework.justauth.core; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.auth0.jwt.JWT; | ||
| 5 | +import com.auth0.jwt.JWTVerifier; | ||
| 6 | +import com.auth0.jwt.algorithms.Algorithm; | ||
| 7 | +import com.auth0.jwt.interfaces.DecodedJWT; | ||
| 8 | +import me.zhyd.oauth.cache.AuthStateCache; | ||
| 9 | +import me.zhyd.oauth.config.AuthConfig; | ||
| 10 | +import me.zhyd.oauth.enums.AuthUserGender; | ||
| 11 | +import me.zhyd.oauth.exception.AuthException; | ||
| 12 | +import me.zhyd.oauth.model.AuthCallback; | ||
| 13 | +import me.zhyd.oauth.model.AuthToken; | ||
| 14 | +import me.zhyd.oauth.model.AuthUser; | ||
| 15 | +import me.zhyd.oauth.request.AuthDefaultRequest; | ||
| 16 | +import org.springframework.core.io.ClassPathResource; | ||
| 17 | + | ||
| 18 | +import java.security.cert.Certificate; | ||
| 19 | +import java.security.cert.CertificateFactory; | ||
| 20 | +import java.security.interfaces.RSAPublicKey; | ||
| 21 | + | ||
| 22 | +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 23 | +import static com.zteits.urbanops.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_USER_DISABLED; | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +/** | ||
| 27 | + * HRHB SSO 单点登录实现 | ||
| 28 | + * | ||
| 29 | + * @author gelinghu | ||
| 30 | + */ | ||
| 31 | +public class AuthUaaRequest extends AuthDefaultRequest { | ||
| 32 | + | ||
| 33 | + public AuthUaaRequest(AuthConfig config, AuthStateCache authStateCache) { | ||
| 34 | + super(config, CustomerAuthSource.UAA, authStateCache); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @Override | ||
| 38 | + public AuthToken getAccessToken(AuthCallback authCallback) { | ||
| 39 | + try { | ||
| 40 | + // doPostAuthorizationCode 会使用 source 中配置的 accessToken URL | ||
| 41 | + String response = this.doPostAuthorizationCode(authCallback.getCode()); | ||
| 42 | + JSONObject accessTokenObject = JSONObject.parseObject(response); | ||
| 43 | + this.checkResponse(accessTokenObject); | ||
| 44 | + | ||
| 45 | + return AuthToken.builder() | ||
| 46 | + .accessToken(accessTokenObject.getString("access_token")) | ||
| 47 | + .refreshToken(accessTokenObject.getString("refresh_token")) | ||
| 48 | + .scope(accessTokenObject.getString("scope")) | ||
| 49 | + .tokenType(accessTokenObject.getString("token_type")) | ||
| 50 | + .expireIn(accessTokenObject.getIntValue("expires_in")) | ||
| 51 | + .build(); | ||
| 52 | + } catch (Exception e) { | ||
| 53 | + // 记录详细的错误信息,便于排查问题 | ||
| 54 | + throw new AuthException("获取访问令牌失败: " + e.getMessage(), e); | ||
| 55 | + } | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @Override | ||
| 59 | + public AuthUser getUserInfo(AuthToken authToken) { | ||
| 60 | + CertificateFactory certificateFactory = null; | ||
| 61 | + try { | ||
| 62 | + certificateFactory = CertificateFactory.getInstance("X.509"); | ||
| 63 | + // 读取cer公钥证书来配置解码器 | ||
| 64 | + ClassPathResource resource = new ClassPathResource("pub.cer"); | ||
| 65 | + Certificate certificate = certificateFactory.generateCertificate(resource.getInputStream()); | ||
| 66 | + RSAPublicKey publicKey = (RSAPublicKey) certificate.getPublicKey(); | ||
| 67 | + | ||
| 68 | + Algorithm algorithm = Algorithm.RSA256(publicKey, null); | ||
| 69 | + | ||
| 70 | + JWTVerifier verifier = JWT.require(algorithm).acceptLeeway(60) | ||
| 71 | + .build(); | ||
| 72 | + // 解析 JWT | ||
| 73 | + DecodedJWT decodedJWT = verifier.verify(authToken.getAccessToken()); | ||
| 74 | + DecodedJWT jwt = JWT.decode(authToken.getAccessToken()); | ||
| 75 | + String staffNo = jwt.getClaim("account").asString(); | ||
| 76 | + return AuthUser.builder() | ||
| 77 | + .rawUserInfo(null) | ||
| 78 | + .uuid(staffNo) | ||
| 79 | + .username(staffNo) | ||
| 80 | + .avatar("") | ||
| 81 | + .blog("") | ||
| 82 | + .nickname(staffNo) | ||
| 83 | + .company("") | ||
| 84 | + .location("") | ||
| 85 | + .email("") | ||
| 86 | + .remark("") | ||
| 87 | + .gender(AuthUserGender.UNKNOWN) | ||
| 88 | + .token(authToken) | ||
| 89 | + .source(this.source.toString()) | ||
| 90 | + .build(); | ||
| 91 | + } catch (Exception e) { | ||
| 92 | + throw exception(AUTH_LOGIN_USER_DISABLED, "UAA登录失败"); | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + /** | ||
| 97 | + * 检查响应是否有错误 | ||
| 98 | + */ | ||
| 99 | + private void checkResponse(JSONObject object) { | ||
| 100 | + if (object.containsKey("error")) { | ||
| 101 | + throw new AuthException(object.getString("error_description")); | ||
| 102 | + } | ||
| 103 | + } | ||
| 104 | +} | ||
| 0 | \ No newline at end of file | 105 | \ No newline at end of file |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/framework/justauth/core/CustomerAuthSource.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.framework.justauth.core; | ||
| 2 | + | ||
| 3 | +import me.zhyd.oauth.config.AuthSource; | ||
| 4 | + | ||
| 5 | +public enum CustomerAuthSource implements AuthSource { | ||
| 6 | + | ||
| 7 | + UAA { | ||
| 8 | + @Override | ||
| 9 | + public String authorize() { | ||
| 10 | + return "https://test.jichengshanshui.com.cn:28301/oauth2/authorize"; | ||
| 11 | +// return "http://localhost:8080/oauth2/authorize"; | ||
| 12 | +// return "https://uaa.jichengshanshui.com.cn:28201/oauth2/authorize"; | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + @Override | ||
| 16 | + public String accessToken() { | ||
| 17 | + return "https://test.jichengshanshui.com.cn:28301/oauth2/token"; | ||
| 18 | +// return "http://localhost:8080/oauth2/token"; | ||
| 19 | +// return "https://uaa.jichengshanshui.com.cn:28201/oauth2/token"; | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + @Override | ||
| 23 | + public String userInfo() { | ||
| 24 | + return "https://test.jichengshanshui.com.cn:28301/oauth2/userinfo"; | ||
| 25 | +// return "http://localhost:8080/oauth2/userinfo"; | ||
| 26 | +// return "https://uaa.jichengshanshui.com.cn:28201/oauth2/userinfo"; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + public String getName() { | ||
| 31 | + return "UAA"; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + public Class getTargetClass() { | ||
| 37 | + return CustomerAuthSource.class; | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | +} |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/auth/AdminAuthServiceImpl.java
| @@ -175,16 +175,27 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -175,16 +175,27 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 175 | // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 | 175 | // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 |
| 176 | SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), | 176 | SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), |
| 177 | reqVO.getCode(), reqVO.getState()); | 177 | reqVO.getCode(), reqVO.getState()); |
| 178 | - if (socialUser == null || socialUser.getUserId() == null) { | 178 | + if (socialUser == null) { |
| 179 | throw exception(AUTH_THIRD_LOGIN_NOT_BIND); | 179 | throw exception(AUTH_THIRD_LOGIN_NOT_BIND); |
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | // 获得用户 | 182 | // 获得用户 |
| 183 | - AdminUserDO user = userService.getUser(socialUser.getUserId()); | 183 | + AdminUserDO user = null; |
| 184 | + user = userService.getUserByUsername(socialUser.getOpenid()); | ||
| 185 | + if (user == null) { | ||
| 186 | + user = userService.getUserByMobile(socialUser.getOpenid()); | ||
| 187 | + } | ||
| 188 | + | ||
| 184 | if (user == null) { | 189 | if (user == null) { |
| 185 | throw exception(USER_NOT_EXISTS); | 190 | throw exception(USER_NOT_EXISTS); |
| 186 | } | 191 | } |
| 187 | 192 | ||
| 193 | + if (socialUser.getUserId() == null) { | ||
| 194 | + SocialUserBindReqDTO reqDTO = new SocialUserBindReqDTO(user.getId(), UserTypeEnum.ADMIN.getValue(), | ||
| 195 | + reqVO.getType(), reqVO.getCode(), reqVO.getState()); | ||
| 196 | + socialUserService.bindSocialUser(reqDTO); | ||
| 197 | + } | ||
| 198 | + | ||
| 188 | // 创建 Token 令牌,记录登录日志 | 199 | // 创建 Token 令牌,记录登录日志 |
| 189 | return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); | 200 | return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); |
| 190 | } | 201 | } |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/oauth2/SsoProperies.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.oauth2; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | ||
| 5 | +import org.springframework.context.annotation.Configuration; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Description: <p></p> | ||
| 9 | + * <p> | ||
| 10 | + * Author: gelinghu | ||
| 11 | + * Date: 2025年05月14 15:28 | ||
| 12 | + * @author gelinghu | ||
| 13 | + */ | ||
| 14 | + | ||
| 15 | +@Data | ||
| 16 | +@Configuration | ||
| 17 | +@ConfigurationProperties(prefix = "sso.client") | ||
| 18 | +public class SsoProperies { | ||
| 19 | + | ||
| 20 | + private String switchState; | ||
| 21 | + | ||
| 22 | + private String clientId; | ||
| 23 | + | ||
| 24 | + private String clientSecret; | ||
| 25 | + | ||
| 26 | + private String authorizeUrl; | ||
| 27 | + | ||
| 28 | + private String baseUrl; | ||
| 29 | + | ||
| 30 | + private String redirectUri; | ||
| 31 | + | ||
| 32 | + private String authorizationUri; | ||
| 33 | + | ||
| 34 | + private String tokenUri; | ||
| 35 | + | ||
| 36 | + private String jwtPublicKey; | ||
| 37 | + | ||
| 38 | + private String syncUserUri; | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | + |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/oauth2/SsoService.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.oauth2; | ||
| 2 | + | ||
| 3 | +import com.smart.sdk.sso.client.Oauth2Client; | ||
| 4 | +import com.smart.sdk.sso.client.SsoOauth2Client; | ||
| 5 | +import com.smart.sdk.sso.domain.SsoUserSyncVo; | ||
| 6 | +import com.zteits.urbanops.framework.common.enums.CommonStatusEnum; | ||
| 7 | +import com.zteits.urbanops.module.system.dal.dataobject.user.AdminUserDO; | ||
| 8 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
| 9 | +import org.springframework.stereotype.Component; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * Description: <p></p> | ||
| 13 | + * <p> | ||
| 14 | + * Author: gelinghu | ||
| 15 | + * Date: 2025年5月17 17:33 | ||
| 16 | + */ | ||
| 17 | +@Component | ||
| 18 | +@EnableConfigurationProperties(SsoProperies.class) | ||
| 19 | +public class SsoService { | ||
| 20 | + | ||
| 21 | + private SsoProperies ssoProperies; | ||
| 22 | + | ||
| 23 | + private Oauth2Client client; | ||
| 24 | + | ||
| 25 | + public SsoService(SsoProperies ssoProperies) { | ||
| 26 | + this.ssoProperies = ssoProperies; | ||
| 27 | + client = new SsoOauth2Client(ssoProperies.getClientId(), ssoProperies.getClientSecret(), ssoProperies.getBaseUrl(), ssoProperies.getRedirectUri()); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + public Oauth2Client getClient() { | ||
| 31 | + return client; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + public SsoProperies getSsoProperies() { | ||
| 35 | + return ssoProperies; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + public void syncUser(AdminUserDO user) { | ||
| 39 | + SsoUserSyncVo ssoUserSyncVo = new SsoUserSyncVo(); | ||
| 40 | + ssoUserSyncVo.setStaffNo(user.getMobile()) | ||
| 41 | + .setAccount(user.getUsername()) | ||
| 42 | + .setName(user.getNickname()) | ||
| 43 | + .setEmail(user.getEmail()) | ||
| 44 | + .setState(CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()) ? 1 : 0); | ||
| 45 | + getClient().syncUser(ssoUserSyncVo); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | + |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/user/AdminUserServiceImpl.java
| @@ -27,6 +27,7 @@ import com.zteits.urbanops.module.system.dal.mysql.user.AdminUserMapper; | @@ -27,6 +27,7 @@ import com.zteits.urbanops.module.system.dal.mysql.user.AdminUserMapper; | ||
| 27 | import com.zteits.urbanops.module.system.service.dept.DeptService; | 27 | import com.zteits.urbanops.module.system.service.dept.DeptService; |
| 28 | import com.zteits.urbanops.module.system.service.dept.PostService; | 28 | import com.zteits.urbanops.module.system.service.dept.PostService; |
| 29 | import com.zteits.urbanops.module.system.service.dict.DictDataService; | 29 | import com.zteits.urbanops.module.system.service.dict.DictDataService; |
| 30 | +import com.zteits.urbanops.module.system.service.oauth2.SsoService; | ||
| 30 | import com.zteits.urbanops.module.system.service.permission.PermissionService; | 31 | import com.zteits.urbanops.module.system.service.permission.PermissionService; |
| 31 | import com.zteits.urbanops.module.system.service.tenant.TenantService; | 32 | import com.zteits.urbanops.module.system.service.tenant.TenantService; |
| 32 | import com.google.common.annotations.VisibleForTesting; | 33 | import com.google.common.annotations.VisibleForTesting; |
| @@ -87,6 +88,10 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -87,6 +88,10 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 87 | @Resource | 88 | @Resource |
| 88 | private DictDataService dictDataService; | 89 | private DictDataService dictDataService; |
| 89 | 90 | ||
| 91 | + @Resource | ||
| 92 | + private SsoService ssoService; | ||
| 93 | + | ||
| 94 | + | ||
| 90 | @Override | 95 | @Override |
| 91 | @Transactional(rollbackFor = Exception.class) | 96 | @Transactional(rollbackFor = Exception.class) |
| 92 | @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}", | 97 | @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}", |
| @@ -115,6 +120,8 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -115,6 +120,8 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 115 | 120 | ||
| 116 | // 3. 记录操作日志上下文 | 121 | // 3. 记录操作日志上下文 |
| 117 | LogRecordContext.putVariable("user", user); | 122 | LogRecordContext.putVariable("user", user); |
| 123 | + | ||
| 124 | + ssoService.syncUser(user); | ||
| 118 | return user.getId(); | 125 | return user.getId(); |
| 119 | } | 126 | } |
| 120 | 127 | ||
| @@ -232,6 +239,8 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -232,6 +239,8 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 232 | updateObj.setId(id); | 239 | updateObj.setId(id); |
| 233 | updateObj.setStatus(status); | 240 | updateObj.setStatus(status); |
| 234 | userMapper.updateById(updateObj); | 241 | userMapper.updateById(updateObj); |
| 242 | + | ||
| 243 | + ssoService.syncUser(updateObj); | ||
| 235 | } | 244 | } |
| 236 | 245 | ||
| 237 | @Override | 246 | @Override |
| @@ -251,6 +260,8 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -251,6 +260,8 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 251 | 260 | ||
| 252 | // 3. 记录操作日志上下文 | 261 | // 3. 记录操作日志上下文 |
| 253 | LogRecordContext.putVariable("user", user); | 262 | LogRecordContext.putVariable("user", user); |
| 263 | + | ||
| 264 | + ssoService.syncUser(user); | ||
| 254 | } | 265 | } |
| 255 | 266 | ||
| 256 | @Override | 267 | @Override |
urbanops-server/src/main/resources/application-dev.yaml
| @@ -191,30 +191,27 @@ urbanops: | @@ -191,30 +191,27 @@ urbanops: | ||
| 191 | justauth: | 191 | justauth: |
| 192 | enabled: true | 192 | enabled: true |
| 193 | type: | 193 | type: |
| 194 | - DINGTALK: # 钉钉 | ||
| 195 | - client-id: dingvrnreaje3yqvzhxg | ||
| 196 | - client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI | ||
| 197 | - ignore-check-redirect-uri: true | ||
| 198 | - WECHAT_ENTERPRISE: # 企业微信 | ||
| 199 | - client-id: wwd411c69a39ad2e54 | ||
| 200 | - client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw | ||
| 201 | - agent-id: 1000004 | ||
| 202 | - ignore-check-redirect-uri: true | ||
| 203 | - # noinspection SpringBootApplicationYaml | ||
| 204 | - WECHAT_MINI_PROGRAM: # 微信小程序 | ||
| 205 | - client-id: ${wx.miniapp.appid} | ||
| 206 | - client-secret: ${wx.miniapp.secret} | ||
| 207 | - ignore-check-redirect-uri: true | ||
| 208 | - ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验 | ||
| 209 | - WECHAT_MP: # 微信公众号 | ||
| 210 | - client-id: ${wx.mp.app-id} | ||
| 211 | - client-secret: ${wx.mp.secret} | 194 | + UAA: |
| 195 | + client-id: schoms | ||
| 196 | + client-secret: secret | ||
| 197 | + redirect-uri: https://test.jichengshanshui.com.cn:28302/test/social-login?type=35 | ||
| 212 | ignore-check-redirect-uri: true | 198 | ignore-check-redirect-uri: true |
| 213 | cache: | 199 | cache: |
| 214 | type: REDIS | 200 | type: REDIS |
| 215 | prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: | 201 | prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: |
| 216 | timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 | 202 | timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 |
| 217 | 203 | ||
| 204 | +sso: | ||
| 205 | + client: | ||
| 206 | + switchState: on | ||
| 207 | + clientId: schoms | ||
| 208 | + clientSecret: secret | ||
| 209 | + # 前端授权地址(改为justauth,不再这儿配置) | ||
| 210 | + authorizeUrl: | ||
| 211 | + # sso 服务地址 | ||
| 212 | + baseUrl: https://test.jichengshanshui.com.cn:28301 | ||
| 213 | + # 登录成功后跳转地址 (改为justauth,不再这儿配置) | ||
| 214 | + redirectUri: | ||
| 218 | --- #################### iot相关配置 TODO 芋艿【IOT】:再瞅瞅 #################### | 215 | --- #################### iot相关配置 TODO 芋艿【IOT】:再瞅瞅 #################### |
| 219 | pf4j: | 216 | pf4j: |
| 220 | # pluginsDir: /tmp/ | 217 | # pluginsDir: /tmp/ |
| @@ -235,17 +232,7 @@ flow: | @@ -235,17 +232,7 @@ flow: | ||
| 235 | - appid: wy | 232 | - appid: wy |
| 236 | secret: x3z7t6l2g2t2i4w3x5e2e0s5y9e6d1y3 | 233 | secret: x3z7t6l2g2t2i4w3x5e2e0s5y9e6d1y3 |
| 237 | url: https://pms.jichengshanshui.com.cn:9980/prod-api/task/third/report | 234 | url: https://pms.jichengshanshui.com.cn:9980/prod-api/task/third/report |
| 238 | -sso: | ||
| 239 | - client: | ||
| 240 | - switchState: on | ||
| 241 | - clientId: schoms | ||
| 242 | - clientSecret: FVwUBd3ZCrt6mMrZ | ||
| 243 | - # 前端授权地址 | ||
| 244 | - authorizeUrl: https://giomp.jichengshanshui.com.cn:28205/authorize | ||
| 245 | - # sso 服务地址 | ||
| 246 | - baseUrl: https://uaa.jichengshanshui.com.cn:28201 | ||
| 247 | - # 登录成功后跳转地址 | ||
| 248 | - redirectUri: https://giomp.jichengshanshui.com.cn:28205/prod-api/admin-api/sso/callback | 235 | + |
| 249 | # 微信公众号配置 | 236 | # 微信公众号配置 |
| 250 | wechat: | 237 | wechat: |
| 251 | mp: | 238 | mp: |
urbanops-server/src/main/resources/application-prod.yaml
| @@ -228,30 +228,32 @@ global: | @@ -228,30 +228,32 @@ global: | ||
| 228 | justauth: | 228 | justauth: |
| 229 | enabled: true | 229 | enabled: true |
| 230 | type: | 230 | type: |
| 231 | - DINGTALK: # 钉钉 | ||
| 232 | - client-id: dingvrnreaje3yqvzhxg | ||
| 233 | - client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI | ||
| 234 | - ignore-check-redirect-uri: true | ||
| 235 | - WECHAT_ENTERPRISE: # 企业微信 | ||
| 236 | - client-id: wwd411c69a39ad2e54 | ||
| 237 | - client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw | ||
| 238 | - agent-id: 1000004 | ||
| 239 | - ignore-check-redirect-uri: true | ||
| 240 | - # noinspection SpringBootApplicationYaml | ||
| 241 | - WECHAT_MINI_PROGRAM: # 微信小程序 | ||
| 242 | - client-id: ${wx.miniapp.appid} | ||
| 243 | - client-secret: ${wx.miniapp.secret} | ||
| 244 | - ignore-check-redirect-uri: true | ||
| 245 | - ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验 | ||
| 246 | - WECHAT_MP: # 微信公众号 | ||
| 247 | - client-id: ${wx.mp.app-id} | ||
| 248 | - client-secret: ${wx.mp.secret} | 231 | + UAA: |
| 232 | + client-id: schoms | ||
| 233 | + client-secret: FVwUBd3ZCrt6mMrZ | ||
| 234 | + redirect-uri: https://giomp.jichengshanshui.com.cn:4080/social-login?type=35 | ||
| 249 | ignore-check-redirect-uri: true | 235 | ignore-check-redirect-uri: true |
| 250 | cache: | 236 | cache: |
| 251 | type: REDIS | 237 | type: REDIS |
| 252 | prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: | 238 | prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: |
| 253 | timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 | 239 | timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 |
| 254 | 240 | ||
| 241 | +sso: | ||
| 242 | + client: | ||
| 243 | + switchState: on | ||
| 244 | + clientId: schoms | ||
| 245 | + clientSecret: FVwUBd3ZCrt6mMrZ | ||
| 246 | + # 前端授权地址(改为justauth,不再这儿配置) | ||
| 247 | + authorizeUrl: | ||
| 248 | + # sso 服务地址 | ||
| 249 | + baseUrl: https://uaa.jichengshanshui.com.cn:28201 | ||
| 250 | + # 登录成功后跳转地址 (改为justauth,不再这儿配置) | ||
| 251 | + redirectUri: | ||
| 252 | + cache: | ||
| 253 | + type: REDIS | ||
| 254 | + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: | ||
| 255 | + timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 | ||
| 256 | + | ||
| 255 | --- #################### iot相关配置 TODO 芋艿【IOT】:再瞅瞅 #################### | 257 | --- #################### iot相关配置 TODO 芋艿【IOT】:再瞅瞅 #################### |
| 256 | pf4j: | 258 | pf4j: |
| 257 | # pluginsDir: /tmp/ | 259 | # pluginsDir: /tmp/ |
| @@ -272,17 +274,7 @@ flow: | @@ -272,17 +274,7 @@ flow: | ||
| 272 | - appid: wy | 274 | - appid: wy |
| 273 | secret: x3z7t6l2g2t2i4w3x5e2e0s5y9e6d1y3 | 275 | secret: x3z7t6l2g2t2i4w3x5e2e0s5y9e6d1y3 |
| 274 | url: https://pms.jichengshanshui.com.cn:9980/prod-api/task/third/report | 276 | url: https://pms.jichengshanshui.com.cn:9980/prod-api/task/third/report |
| 275 | -sso: | ||
| 276 | - client: | ||
| 277 | - switchState: on | ||
| 278 | - clientId: schoms | ||
| 279 | - clientSecret: FVwUBd3ZCrt6mMrZ | ||
| 280 | - # 前端授权地址 | ||
| 281 | - authorizeUrl: https://giomp.jichengshanshui.com.cn:28205/authorize | ||
| 282 | - # sso 服务地址 | ||
| 283 | - baseUrl: https://uaa.jichengshanshui.com.cn:28201 | ||
| 284 | - # 登录成功后跳转地址 | ||
| 285 | - redirectUri: https://giomp.jichengshanshui.com.cn:28205/prod-api/admin-api/sso/callback | 277 | + |
| 286 | # 微信公众号配置 | 278 | # 微信公众号配置 |
| 287 | wechat: | 279 | wechat: |
| 288 | mp: | 280 | mp: |
urbanops-server/src/main/resources/application.yaml
| @@ -3,7 +3,7 @@ spring: | @@ -3,7 +3,7 @@ spring: | ||
| 3 | name: urbanops-server | 3 | name: urbanops-server |
| 4 | 4 | ||
| 5 | profiles: | 5 | profiles: |
| 6 | - active: prod | 6 | + active: dev |
| 7 | 7 | ||
| 8 | main: | 8 | main: |
| 9 | allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 | 9 | allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 |
urbanops-server/src/main/resources/pub.cer
0 → 100644
No preview for this file type