diff --git a/docs/update.md b/docs/update.md index e592230674a1a0950011bdc84ebbb22df34cc74a..685306fcef6359d433b6bfccef4c4fc3505487bb 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,7 +1,10 @@ ## v1.1x(未发布) - 修复抖音登录取值取错层级的问题([issue#I15SIG@Gitee](https://gitee.com/yadong.zhang/JustAuth/issues/I15SIG)) - 升级相关依赖:lombok@v1.18.10,hutool@5.0.5,fastjson@1.2.62,alipay@4.8.10.ALL([PR#11@Gitee](https://gitee.com/yadong.zhang/JustAuth/pulls/11)) - +- 完善异常提示的逻辑,支持传入Source(平台),发生异常时显示对应的source(平台) +- `checkState`方法从`AuthDefaultRequest`中提出到`AuthChecker`中 +- `AuthResponseStatus`枚举类中增加`ILLEGAL_STATUS`、`REQUIRED_REFRESH_TOKEN`两个枚举值 +- `AuthSource`接口中增加`getName`方法,用来对外提供实际`source`的字符串值 ## v1.13.1 ### 2019/11/12 diff --git a/src/main/java/me/zhyd/oauth/config/AuthSource.java b/src/main/java/me/zhyd/oauth/config/AuthSource.java index f42949a5123ee2a2e0900fc729ee5216b577ff04..a6cde6460d2e6f2a429cc26110a09de9a0ed9b15 100644 --- a/src/main/java/me/zhyd/oauth/config/AuthSource.java +++ b/src/main/java/me/zhyd/oauth/config/AuthSource.java @@ -61,4 +61,16 @@ public interface AuthSource { default String refresh() { throw new AuthException(AuthResponseStatus.UNSUPPORTED); } + + /** + * 获取Source的字符串名字 + * + * @return name + */ + default String getName() { + if (this instanceof Enum) { + return String.valueOf(this); + } + return this.getClass().getSimpleName(); + } } diff --git a/src/main/java/me/zhyd/oauth/enums/AuthResponseStatus.java b/src/main/java/me/zhyd/oauth/enums/AuthResponseStatus.java index 9e9155f6cf358b075f0c18ea00a7045456f2db43..9865a02ca2cd79230a0757ca630122dda9192e0d 100644 --- a/src/main/java/me/zhyd/oauth/enums/AuthResponseStatus.java +++ b/src/main/java/me/zhyd/oauth/enums/AuthResponseStatus.java @@ -26,6 +26,8 @@ public enum AuthResponseStatus { ILLEGAL_REDIRECT_URI(5006, "Illegal redirect uri"), ILLEGAL_REQUEST(5007, "Illegal request"), ILLEGAL_CODE(5008, "Illegal code"), + ILLEGAL_STATUS(5009, "Illegal state"), + REQUIRED_REFRESH_TOKEN(5010, "The refresh token is required; it must not be null"), ; private int code; diff --git a/src/main/java/me/zhyd/oauth/exception/AuthException.java b/src/main/java/me/zhyd/oauth/exception/AuthException.java index e5d3bc3b262da2f80d6358031b47b835a402ef9f..67cdd6376562cf8e931eb26ab9078659cb448ad5 100644 --- a/src/main/java/me/zhyd/oauth/exception/AuthException.java +++ b/src/main/java/me/zhyd/oauth/exception/AuthException.java @@ -1,5 +1,6 @@ package me.zhyd.oauth.exception; +import me.zhyd.oauth.config.AuthSource; import me.zhyd.oauth.enums.AuthResponseStatus; /** @@ -17,6 +18,10 @@ public class AuthException extends RuntimeException { this(AuthResponseStatus.FAILURE.getCode(), errorMsg); } + public AuthException(String errorMsg, AuthSource source) { + this(AuthResponseStatus.FAILURE.getCode(), errorMsg, source); + } + public AuthException(int errorCode, String errorMsg) { super(errorMsg); this.errorCode = errorCode; @@ -24,7 +29,15 @@ public class AuthException extends RuntimeException { } public AuthException(AuthResponseStatus status) { - super(status.getMsg()); + this(status.getCode(), status.getMsg()); + } + + public AuthException(int errorCode, String errorMsg, AuthSource source) { + this(errorCode, String.format("%s [%s]", errorMsg, source.getName())); + } + + public AuthException(AuthResponseStatus status, AuthSource source) { + this(status.getCode(), status.getMsg(), source); } public AuthException(String message, Throwable cause) { diff --git a/src/main/java/me/zhyd/oauth/request/AuthDefaultRequest.java b/src/main/java/me/zhyd/oauth/request/AuthDefaultRequest.java index eb44f3996ca19a33771ed3e21120c797a87f865a..c02c571265c8891664487c5d5b48daa1a03d509a 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthDefaultRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthDefaultRequest.java @@ -39,7 +39,7 @@ public abstract class AuthDefaultRequest implements AuthRequest { this.source = source; this.authStateCache = authStateCache; if (!AuthChecker.isSupportedAuth(config, source)) { - throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE); + throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE, source); } // 校验配置合法性 AuthChecker.checkConfig(config, source); @@ -75,7 +75,7 @@ public abstract class AuthDefaultRequest implements AuthRequest { public AuthResponse login(AuthCallback authCallback) { try { AuthChecker.checkCode(source, authCallback); - this.checkState(authCallback.getState()); + AuthChecker.checkState(authCallback.getState(), source, authStateCache); AuthToken authToken = this.getAccessToken(authCallback); AuthUser user = this.getUserInfo(authToken); @@ -266,15 +266,4 @@ public abstract class AuthDefaultRequest implements AuthRequest { return HttpRequest.get(revokeUrl(authToken)).execute(); } - - /** - * 校验回调传回的state - * - * @param state {@code state}一定不为空 - */ - protected void checkState(String state) { - if (StringUtils.isEmpty(state) || !authStateCache.containsKey(state)) { - throw new AuthException(AuthResponseStatus.ILLEGAL_REQUEST); - } - } } diff --git a/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java b/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java index 3d6a2f777f981c9cd358cd7366f4101fa843988f..886ad0983b11f8b43a8b91458ab367a4c7586104 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthLinkedinRequest.java @@ -145,7 +145,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest { public AuthResponse refresh(AuthToken oldToken) { String refreshToken = oldToken.getRefreshToken(); if (StringUtils.isEmpty(refreshToken)) { - throw new AuthException(AuthResponseStatus.UNSUPPORTED); + throw new AuthException(AuthResponseStatus.REQUIRED_REFRESH_TOKEN, source); } String refreshTokenUrl = refreshTokenUrl(refreshToken); return AuthResponse.builder() @@ -161,7 +161,7 @@ public class AuthLinkedinRequest extends AuthDefaultRequest { */ private void checkResponse(JSONObject object) { if (object.containsKey("error")) { - throw new AuthException(object.getString("error_description")); + throw new AuthException(object.getString("error_description"), source); } } diff --git a/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java b/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java index e41d4220fcc15a7da76fa5ec9dfb6f8615e20559..f2e2bcb36f7435deafa5e47a8e26212292474ddf 100644 --- a/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java +++ b/src/main/java/me/zhyd/oauth/request/AuthWeChatEnterpriseRequest.java @@ -57,7 +57,7 @@ public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest { // 返回 OpenId 或其他,均代表非当前企业用户,不支持 if (!object.containsKey("UserId")) { - throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM); + throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM, source); } String userId = object.getString("UserId"); HttpResponse userDetailResponse = getUserDetail(authToken.getAccessToken(), userId); @@ -88,7 +88,7 @@ public class AuthWeChatEnterpriseRequest extends AuthDefaultRequest { JSONObject object = JSONObject.parseObject(response.body()); if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) { - throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg")); + throw new AuthException(object.getString("errmsg"), source); } return object; diff --git a/src/main/java/me/zhyd/oauth/utils/AuthChecker.java b/src/main/java/me/zhyd/oauth/utils/AuthChecker.java index bf7146fd5a705d56fecf24696e3b04d546f6b912..802098f30f931d587cde8b49d2759a53d16f7be0 100644 --- a/src/main/java/me/zhyd/oauth/utils/AuthChecker.java +++ b/src/main/java/me/zhyd/oauth/utils/AuthChecker.java @@ -1,5 +1,6 @@ package me.zhyd.oauth.utils; +import me.zhyd.oauth.cache.AuthStateCache; import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.config.AuthDefaultSource; import me.zhyd.oauth.config.AuthSource; @@ -31,7 +32,7 @@ public class AuthChecker { if (isSupported && AuthDefaultSource.STACK_OVERFLOW == source) { isSupported = StringUtils.isNotEmpty(config.getStackOverflowKey()); } - if (isSupported && AuthDefaultSource.WECHAT_ENTERPRISE == source){ + if (isSupported && AuthDefaultSource.WECHAT_ENTERPRISE == source) { isSupported = StringUtils.isNotEmpty(config.getAgentId()); } return isSupported; @@ -47,15 +48,17 @@ public class AuthChecker { public static void checkConfig(AuthConfig config, AuthSource source) { String redirectUri = config.getRedirectUri(); if (!GlobalAuthUtil.isHttpProtocol(redirectUri) && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) { - throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI); + throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source); } // facebook的回调地址必须为https的链接 if (AuthDefaultSource.FACEBOOK == source && !GlobalAuthUtil.isHttpsProtocol(redirectUri)) { - throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI); + // Facebook's redirect uri must use the HTTPS protocol + throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source); } // 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1 if (AuthDefaultSource.ALIPAY == source && GlobalAuthUtil.isLocalHost(redirectUri)) { - throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI); + // The redirect uri of alipay is forbidden to use localhost or 127.0.0.1 + throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source); } } @@ -76,7 +79,23 @@ public class AuthChecker { code = callback.getAuthorization_code(); } if (StringUtils.isEmpty(code)) { - throw new AuthException(AuthResponseStatus.ILLEGAL_CODE); + throw new AuthException(AuthResponseStatus.ILLEGAL_CODE, source); + } + } + + /** + * 校验回调传回的{@code state},为空或者不存在 + *

+ * {@code state}不存在的情况只有两种: + * 1. {@code state}已使用,被正常清除 + * 2. {@code state}为前端伪造,本身就不存在 + * + * @param state {@code state}一定不为空 + * @param authStateCache {@code authStateCache} state缓存实现 + */ + public static void checkState(String state, AuthSource source, AuthStateCache authStateCache) { + if (StringUtils.isEmpty(state) || !authStateCache.containsKey(state)) { + throw new AuthException(AuthResponseStatus.ILLEGAL_STATUS, source); } } }