From b56496a6df16de37f62db0064caf260e181365a1 Mon Sep 17 00:00:00 2001 From: MaxKey Date: Sun, 23 Jan 2022 18:18:06 +0800 Subject: [PATCH] SocialSignOn --- .../request/AuthHuaweiWeLinkRequest.java | 172 ++++++++++++++++++ .../request/WeLinkAuthDefaultSource.java | 33 ++++ .../service/SocialSignOnProviderService.java | 4 + .../synchronizer/entity/ResponseData.java | 26 ++- .../main/resources/templates/views/login.ftl | 3 + .../templates/views/loginscanwelink.ftl | 26 +++ 6 files changed, 258 insertions(+), 6 deletions(-) create mode 100644 maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/AuthHuaweiWeLinkRequest.java create mode 100644 maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/WeLinkAuthDefaultSource.java create mode 100644 maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/loginscanwelink.ftl diff --git a/maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/AuthHuaweiWeLinkRequest.java b/maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/AuthHuaweiWeLinkRequest.java new file mode 100644 index 000000000..42f32b565 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/AuthHuaweiWeLinkRequest.java @@ -0,0 +1,172 @@ +package me.zhyd.oauth.request; +import com.alibaba.fastjson.JSONObject; +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.enums.AuthUserGender; +import me.zhyd.oauth.enums.scope.AuthHuaweiScope; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.utils.AuthScopeUtils; +import me.zhyd.oauth.utils.HttpUtils; +import me.zhyd.oauth.utils.UrlBuilder; + +import java.util.HashMap; +import java.util.Map; + +import static me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS; + +public class AuthHuaweiWeLinkRequest extends AuthDefaultRequest { + + public AuthHuaweiWeLinkRequest(AuthConfig config) { + super(config, WeLinkAuthDefaultSource.HUAWEI_WELINK); + } + + public AuthHuaweiWeLinkRequest(AuthConfig config, AuthStateCache authStateCache) { + super(config, WeLinkAuthDefaultSource.HUAWEI_WELINK, authStateCache); + } + + /** + * 获取access token + * + * @param authCallback 授权成功后的回调参数 + * @return token + * @see AuthDefaultRequest#authorize() + * @see AuthDefaultRequest#authorize(String) + */ + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + Map form = new HashMap<>(8); + form.put("grant_type", "authorization_code"); + form.put("code", authCallback.getAuthorization_code()); + form.put("client_id", config.getClientId()); + form.put("client_secret", config.getClientSecret()); + form.put("redirect_uri", config.getRedirectUri()); + + String response = new HttpUtils(config.getHttpConfig()).post(source.accessToken(), form, false).getBody(); + return getAuthToken(response); + } + + /** + * 使用token换取用户信息 + * + * @param authToken token信息 + * @return 用户信息 + * @see AuthDefaultRequest#getAccessToken(AuthCallback) + */ + @Override + protected AuthUser getUserInfo(AuthToken authToken) { + Map form = new HashMap<>(7); + form.put("nsp_ts", System.currentTimeMillis() + ""); + form.put("access_token", authToken.getAccessToken()); + form.put("nsp_fmt", "JS"); + form.put("nsp_svc", "OpenUP.User.getInfo"); + + String response = new HttpUtils(config.getHttpConfig()).post(source.userInfo(), form, false).getBody(); + JSONObject object = JSONObject.parseObject(response); + + this.checkResponse(object); + + AuthUserGender gender = getRealGender(object); + + return AuthUser.builder() + .rawUserInfo(object) + .uuid(object.getString("userId")) + .username(object.getString("userNameCn")) + .nickname(object.getString("userNameCn")) + .gender(gender) + //.avatar(object.getString("headPictureURL")) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 刷新access token (续期) + * + * @param authToken 登录成功后返回的Token信息 + * @return AuthResponse + */ + @Override + public AuthResponse refresh(AuthToken authToken) { + Map form = new HashMap<>(7); + form.put("client_id", config.getClientId()); + form.put("client_secret", config.getClientSecret()); + form.put("refresh_token", authToken.getRefreshToken()); + form.put("grant_type", "refresh_token"); + + String response = new HttpUtils(config.getHttpConfig()).post(source.refresh(), form, false).getBody(); + return AuthResponse.builder().code(SUCCESS.getCode()).data(getAuthToken(response)).build(); + } + + private AuthToken getAuthToken(String response) { + JSONObject object = JSONObject.parseObject(response); + + this.checkResponse(object); + + return AuthToken.builder() + .accessToken(object.getString("access_token")) + .expireIn(object.getIntValue("expires_in")) + .refreshToken(object.getString("refresh_token")) + .build(); + } + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + * @since 1.9.3 + */ + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(super.authorize(state)) + .queryParam("access_type", "offline") + .queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthHuaweiScope.values()))) + .build(); + } + + /** + * 返回获取userInfo的url + * + * @param authToken token + * @return 返回获取userInfo的url + */ + @Override + protected String userInfoUrl(AuthToken authToken) { + return UrlBuilder.fromBaseUrl(source.userInfo()) + .queryParam("nsp_ts", System.currentTimeMillis()) + .queryParam("access_token", authToken.getAccessToken()) + .queryParam("nsp_fmt", "JS") + .queryParam("nsp_svc", "OpenUP.User.getInfo") + .build(); + } + + /** + * 获取用户的实际性别。华为系统中,用户的性别:1表示女,0表示男 + * + * @param object obj + * @return AuthUserGender + */ + private AuthUserGender getRealGender(JSONObject object) { + int genderCodeInt = object.getIntValue("gender"); + String genderCode = genderCodeInt == 1 ? "0" : (genderCodeInt == 0) ? "1" : genderCodeInt + ""; + return AuthUserGender.getRealGender(genderCode); + } + + /** + * 校验响应结果 + * + * @param object 接口返回的结果 + */ + private void checkResponse(JSONObject object) { + if (object.containsKey("NSP_STATUS")) { + throw new AuthException(object.getString("error")); + } + if (object.containsKey("error")) { + throw new AuthException(object.getString("sub_error") + ":" + object.getString("error_description")); + } + } +} diff --git a/maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/WeLinkAuthDefaultSource.java b/maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/WeLinkAuthDefaultSource.java new file mode 100644 index 000000000..5a7cf8a50 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-social/src/main/java/me/zhyd/oauth/request/WeLinkAuthDefaultSource.java @@ -0,0 +1,33 @@ +package me.zhyd.oauth.request; + +import me.zhyd.oauth.config.AuthSource; + +public enum WeLinkAuthDefaultSource implements AuthSource{ + + HUAWEI_WELINK { + @Override + public String authorize() { + return "https://login.welink.huaweicloud.com/connect/oauth2/sns_authorize"; + } + + @Override + public String accessToken() { + return "https://open.welink.huaweicloud.com/api/auth/v2/tickets"; + } + + @Override + public String userInfo() { + return "https://open.welink.huaweicloud.com/api/contact/v1/users"; + } + + @Override + public String refresh() { + return ""; + } + + @Override + public Class getTargetClass() { + return AuthHuaweiWeLinkRequest.class; + } + } +} diff --git a/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProviderService.java b/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProviderService.java index 76fde3f21..9119586c5 100644 --- a/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProviderService.java +++ b/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProviderService.java @@ -123,8 +123,12 @@ public class SocialSignOnProviderService{ authRequest = new AuthWeChatEnterpriseQrcodeRequest(authConfig); }else if(provider.equalsIgnoreCase("workweixin")) { authRequest = new AuthWeChatEnterpriseWebRequest(authConfig); + }else if(provider.equalsIgnoreCase("welink")) { + authRequest = new AuthHuaweiWeLinkRequest(authConfig); } + + return authRequest; } diff --git a/maxkey-synchronizers/maxkey-synchronizer/src/main/java/org/maxkey/synchronizer/entity/ResponseData.java b/maxkey-synchronizers/maxkey-synchronizer/src/main/java/org/maxkey/synchronizer/entity/ResponseData.java index 5b224be61..ed3c28767 100644 --- a/maxkey-synchronizers/maxkey-synchronizer/src/main/java/org/maxkey/synchronizer/entity/ResponseData.java +++ b/maxkey-synchronizers/maxkey-synchronizer/src/main/java/org/maxkey/synchronizer/entity/ResponseData.java @@ -21,39 +21,53 @@ public class ResponseData { protected long errcode; protected long code; - + protected String errmsg; protected String msg; - + protected String message; + public long getErrcode() { return errcode; } + public void setErrcode(long errcode) { this.errcode = errcode; } + public String getErrmsg() { return errmsg; } + public void setErrmsg(String errmsg) { this.errmsg = errmsg; } - - + public long getCode() { return code; } + public void setCode(long code) { this.code = code; } + public String getMsg() { return msg; } + public void setMsg(String msg) { this.msg = msg; } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + public ResponseData() { super(); } - - + } diff --git a/maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/login.ftl b/maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/login.ftl index 350485a36..18e7716d6 100644 --- a/maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/login.ftl +++ b/maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/login.ftl @@ -86,6 +86,9 @@ <#if sspLogin.dingTalkLogin != 'none'> <#include "loginscandingtalk.ftl"> + <#if sspLogin.weLinkLogin != 'none'> + <#include "loginscanwelink.ftl"> + diff --git a/maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/loginscanwelink.ftl b/maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/loginscanwelink.ftl new file mode 100644 index 000000000..6f27d8ba9 --- /dev/null +++ b/maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/loginscanwelink.ftl @@ -0,0 +1,26 @@ + <#if sspLogin.weLinkLogin == 'https'> + + + <#if sspLogin.weLinkLogin == 'http'> + + + \ No newline at end of file