diff --git a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java index 2a134073b111303b3f107cd556df79a39e446616..16d1a50fc2d4bda66937865cfe2a12c447509176 100644 --- a/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java +++ b/src/main/java/me/zhyd/oauth/config/AuthDefaultSource.java @@ -561,7 +561,7 @@ public enum AuthDefaultSource implements AuthSource { }, /** - * 企业微信 + * 企业微信扫描登录 * * @since 1.10.0 */ @@ -582,6 +582,26 @@ public enum AuthDefaultSource implements AuthSource { } }, + /** + * 企业微信网页登录 + */ + WECHAT_ENTERPRISE_WEB { + @Override + public String authorize() { + return "https://open.weixin.qq.com/connect/oauth2/authorize"; + } + + @Override + public String accessToken() { + return "https://qyapi.weixin.qq.com/cgi-bin/gettoken"; + } + + @Override + public String userInfo() { + return "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo"; + } + }, + /** * 酷家乐 * diff --git a/src/main/java/me/zhyd/oauth/enums/scope/AuthWeChatEnterpriseWebScope.java b/src/main/java/me/zhyd/oauth/enums/scope/AuthWeChatEnterpriseWebScope.java new file mode 100644 index 0000000000000000000000000000000000000000..49a5a5e0f54588419c5e3ab1ed7e9d08e08c6fff --- /dev/null +++ b/src/main/java/me/zhyd/oauth/enums/scope/AuthWeChatEnterpriseWebScope.java @@ -0,0 +1,24 @@ +package me.zhyd.oauth.enums.scope; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 企业自建应用授权范围 + * + * @author liguanhua(347826496@qq.com) + * @since 1.15.9 + */ +@Getter +@AllArgsConstructor +public enum AuthWeChatEnterpriseWebScope implements AuthScope { + /** + * {@code scope} 含义,以{@code description} 为准 + */ + SNSAPI_BASE("snsapi_base", "应用授权作用域。企业自建应用固定填写:snsapi_base", true); + + private String scope; + private String description; + private boolean isDefault; + +} diff --git a/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java b/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..73f27b25438c0d69bd8d77594f2feb6db278f731 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java @@ -0,0 +1,136 @@ +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.config.AuthDefaultSource; +import me.zhyd.oauth.config.AuthSource; +import me.zhyd.oauth.enums.AuthResponseStatus; +import me.zhyd.oauth.enums.AuthUserGender; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.utils.HttpUtils; +import me.zhyd.oauth.utils.UrlBuilder; + +/** + *

+ * 企业微信登录父类 + *

+ * + * @author liguanhua(347826496@qq.com) + * @since 1.15.9 + */ +public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultRequest { + + public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source) { + super(config,source); + } + + + public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) { + super(config, source, authStateCache); + } + + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + String response = doGetAuthorizationCode(accessTokenUrl(authCallback.getCode())); + + JSONObject object = this.checkResponse(response); + + return AuthToken.builder() + .accessToken(object.getString("access_token")) + .expireIn(object.getIntValue("expires_in")) + .code(authCallback.getCode()) + .build(); + } + + @Override + protected AuthUser getUserInfo(AuthToken authToken) { + String response = doGetUserInfo(authToken); + JSONObject object = this.checkResponse(response); + + // 返回 OpenId 或其他,均代表非当前企业用户,不支持 + if (!object.containsKey("UserId")) { + throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM, source); + } + String userId = object.getString("UserId"); + String userDetailResponse = getUserDetail(authToken.getAccessToken(), userId); + JSONObject userDetail = this.checkResponse(userDetailResponse); + + return AuthUser.builder() + .rawUserInfo(userDetail) + .username(userDetail.getString("name")) + .nickname(userDetail.getString("alias")) + .avatar(userDetail.getString("avatar")) + .location(userDetail.getString("address")) + .email(userDetail.getString("email")) + .uuid(userId) + .gender(AuthUserGender.getWechatRealGender(userDetail.getString("gender"))) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 校验请求结果 + * + * @param response 请求结果 + * @return 如果请求结果正常,则返回JSONObject + */ + private JSONObject checkResponse(String response) { + JSONObject object = JSONObject.parseObject(response); + + if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) { + throw new AuthException(object.getString("errmsg"), source); + } + + return object; + } + + + /** + * 返回获取accessToken的url + * + * @param code 授权码 + * @return 返回获取accessToken的url + */ + @Override + protected String accessTokenUrl(String code) { + return UrlBuilder.fromBaseUrl(source.accessToken()) + .queryParam("corpid", config.getClientId()) + .queryParam("corpsecret", config.getClientSecret()) + .build(); + } + + /** + * 返回获取userInfo的url + * + * @param authToken 用户授权后的token + * @return 返回获取userInfo的url + */ + @Override + protected String userInfoUrl(AuthToken authToken) { + return UrlBuilder.fromBaseUrl(source.userInfo()) + .queryParam("access_token", authToken.getAccessToken()) + .queryParam("code", authToken.getCode()) + .build(); + } + + /** + * 用户详情 + * + * @param accessToken accessToken + * @param userId 企业内用户id + * @return 用户详情 + */ + private String getUserDetail(String accessToken, String userId) { + String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get") + .queryParam("access_token", accessToken) + .queryParam("userid", userId) + .build(); + return new HttpUtils(config.getHttpConfig()).get(userDetailUrl); + } + +} diff --git a/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java b/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java index 5d615eef45ad95160a7e823d33c8356edcaf4032..d06a230a3fe52f0d22d0b454b3546e763f5c9aa3 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthAlipayRequest.java @@ -43,6 +43,12 @@ public class AuthAlipayRequest extends AuthDefaultRequest { .getAlipayPublicKey(), "RSA2"); } + public AuthAlipayRequest(AuthConfig config, AuthStateCache authStateCache, String proxyHost, Integer proxyPort) { + super(config, AuthDefaultSource.ALIPAY, authStateCache); + this.alipayClient = new DefaultAlipayClient(AuthDefaultSource.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), + "json", "UTF-8", config.getAlipayPublicKey(), "RSA2", proxyHost, proxyPort); + } + @Override protected AuthToken getAccessToken(AuthCallback authCallback) { AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest(); diff --git a/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java b/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java index 799d5a0b9aa88e3100d02c91f04ea796e6632624..f7ed92e380f6dae3071bda280917e61edd533c50 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java @@ -1,27 +1,19 @@ package me.zhyd.oauth.request; -import com.alibaba.fastjson.JSONObject; -import me.zhyd.oauth.utils.HttpUtils; import me.zhyd.oauth.cache.AuthStateCache; import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.config.AuthDefaultSource; -import me.zhyd.oauth.enums.AuthResponseStatus; -import me.zhyd.oauth.enums.AuthUserGender; -import me.zhyd.oauth.exception.AuthException; -import me.zhyd.oauth.model.AuthCallback; -import me.zhyd.oauth.model.AuthToken; -import me.zhyd.oauth.model.AuthUser; import me.zhyd.oauth.utils.UrlBuilder; /** *

- * 企业微信登录 + * 企业微信二维码登录 *

* * @author yangkai.shen (https://xkcoding.com) * @since 1.10.0 */ -public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest { +public class AuthWeChatEnterpriseRequest extends AbstractAuthWeChatEnterpriseRequest { public AuthWeChatEnterpriseRequest(AuthConfig config) { super(config, AuthDefaultSource.WECHAT_ENTERPRISE); } @@ -30,75 +22,6 @@ public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest { super(config, AuthDefaultSource.WECHAT_ENTERPRISE, authStateCache); } - /** - * 微信的特殊性,此时返回的信息同时包含 openid 和 access_token - * - * @param authCallback 回调返回的参数 - * @return 所有信息 - */ - @Override - protected AuthToken getAccessToken(AuthCallback authCallback) { - String response = doGetAuthorizationCode(accessTokenUrl(authCallback.getCode())); - - JSONObject object = this.checkResponse(response); - - return AuthToken.builder() - .accessToken(object.getString("access_token")) - .expireIn(object.getIntValue("expires_in")) - .code(authCallback.getCode()) - .build(); - } - - @Override - protected AuthUser getUserInfo(AuthToken authToken) { - String response = doGetUserInfo(authToken); - JSONObject object = this.checkResponse(response); - - // 返回 OpenId 或其他,均代表非当前企业用户,不支持 - if (!object.containsKey("UserId")) { - throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM, source); - } - String userId = object.getString("UserId"); - String userDetailResponse = getUserDetail(authToken.getAccessToken(), userId); - JSONObject userDetail = this.checkResponse(userDetailResponse); - - return AuthUser.builder() - .rawUserInfo(userDetail) - .username(userDetail.getString("name")) - .nickname(userDetail.getString("alias")) - .avatar(userDetail.getString("avatar")) - .location(userDetail.getString("address")) - .email(userDetail.getString("email")) - .uuid(userId) - .gender(AuthUserGender.getWechatRealGender(userDetail.getString("gender"))) - .token(authToken) - .source(source.toString()) - .build(); - } - - /** - * 校验请求结果 - * - * @param response 请求结果 - * @return 如果请求结果正常,则返回JSONObject - */ - private JSONObject checkResponse(String response) { - JSONObject object = JSONObject.parseObject(response); - - if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) { - throw new AuthException(object.getString("errmsg"), source); - } - - return object; - } - - /** - * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} - * - * @param state state 验证授权流程的参数,可以防止csrf - * @return 返回授权地址 - * @since 1.9.3 - */ @Override public String authorize(String state) { return UrlBuilder.fromBaseUrl(source.authorize()) @@ -108,48 +31,4 @@ public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest { .queryParam("state", getRealState(state)) .build(); } - - /** - * 返回获取accessToken的url - * - * @param code 授权码 - * @return 返回获取accessToken的url - */ - @Override - protected String accessTokenUrl(String code) { - return UrlBuilder.fromBaseUrl(source.accessToken()) - .queryParam("corpid", config.getClientId()) - .queryParam("corpsecret", config.getClientSecret()) - .build(); - } - - /** - * 返回获取userInfo的url - * - * @param authToken 用户授权后的token - * @return 返回获取userInfo的url - */ - @Override - protected String userInfoUrl(AuthToken authToken) { - return UrlBuilder.fromBaseUrl(source.userInfo()) - .queryParam("access_token", authToken.getAccessToken()) - .queryParam("code", authToken.getCode()) - .build(); - } - - /** - * 用户详情 - * - * @param accessToken accessToken - * @param userId 企业内用户id - * @return 用户详情 - */ - private String getUserDetail(String accessToken, String userId) { - String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get") - .queryParam("access_token", accessToken) - .queryParam("userid", userId) - .build(); - return new HttpUtils(config.getHttpConfig()).get(userDetailUrl); - } - } diff --git a/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequest.java b/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..3d0e76e4b165ab666ab5956989359c9090a3033c --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseWebRequest.java @@ -0,0 +1,37 @@ +package me.zhyd.oauth.request; + +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.config.AuthDefaultSource; +import me.zhyd.oauth.enums.scope.AuthWeChatEnterpriseWebScope; +import me.zhyd.oauth.utils.AuthScopeUtils; +import me.zhyd.oauth.utils.UrlBuilder; + +/** + *

+ * 企业微信网页登录 + *

+ * + * @author liguanhua(347826496@qq.com) + * @since 1.15.9 + */ +public class AuthWeChatEnterpriseWebRequest extends AbstractAuthWeChatEnterpriseRequest { + public AuthWeChatEnterpriseWebRequest(AuthConfig config) { + super(config, AuthDefaultSource.WECHAT_ENTERPRISE_WEB); + } + + public AuthWeChatEnterpriseWebRequest(AuthConfig config, AuthStateCache authStateCache) { + super(config, AuthDefaultSource.WECHAT_ENTERPRISE_WEB, authStateCache); + } + + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(source.authorize()) + .queryParam("appid", config.getClientId()) + .queryParam("redirect_uri", config.getRedirectUri()) + .queryParam("response_type", "code") + .queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthWeChatEnterpriseWebScope.values()))) + .queryParam("state", getRealState(state).concat("#wechat_redirect")) + .build(); + } +}