diff --git a/src/main/java/me/zhyd/oauth/authorization/AuthorizationFactory.java b/src/main/java/me/zhyd/oauth/authorization/AuthorizationFactory.java index 7d2c65cd01147ac855258ff6c04f5a0cf6557c6f..386891705e4e6d3b43ce9d054e34e347531f9fe3 100644 --- a/src/main/java/me/zhyd/oauth/authorization/AuthorizationFactory.java +++ b/src/main/java/me/zhyd/oauth/authorization/AuthorizationFactory.java @@ -59,6 +59,7 @@ public class AuthorizationFactory { AuthorizationFactory.register(AuthSource.DINGTALK, new DingTalkAuthorization()); AuthorizationFactory.register(AuthSource.GITEE, new GiteeAuthorization()); AuthorizationFactory.register(AuthSource.GITHUB, new GithubAuthorization()); + AuthorizationFactory.register(AuthSource.GOOGLE, new GoogleAuthorization()); AuthorizationFactory.register(AuthSource.OSCHINA, new OschinaAuthorization()); AuthorizationFactory.register(AuthSource.QQ, new QqAuthorization()); AuthorizationFactory.register(AuthSource.TAOBAO, new TaobaoAuthorization()); diff --git a/src/main/java/me/zhyd/oauth/authorization/GoogleAuthorization.java b/src/main/java/me/zhyd/oauth/authorization/GoogleAuthorization.java new file mode 100644 index 0000000000000000000000000000000000000000..6a6b318f1b5095e4796721bf3d73bb2fe48d7524 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/authorization/GoogleAuthorization.java @@ -0,0 +1,19 @@ +package me.zhyd.oauth.authorization; + +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.utils.UrlBuilder; + +/** + * Google授权 + * + * @author yangkai.shen (https://xkcoding.com) + * @version 1.3 + * @since 1.3 + */ +public class GoogleAuthorization implements Authorization { + + @Override + public String getAuthorizeUrl(AuthConfig config) { + return UrlBuilder.getGoogleAuthorizeUrl(config.getClientId(), config.getRedirectUri()); + } +} diff --git a/src/main/java/me/zhyd/oauth/consts/ApiUrl.java b/src/main/java/me/zhyd/oauth/consts/ApiUrl.java index a99eb02ef1bab1bcc6bb4044d8fa0103fd633b8d..377475fb98dbcaee95619a49e179cbaaa07ca59d 100644 --- a/src/main/java/me/zhyd/oauth/consts/ApiUrl.java +++ b/src/main/java/me/zhyd/oauth/consts/ApiUrl.java @@ -383,6 +383,35 @@ public enum ApiUrl { throw new AuthException(ResponseStatus.UNSUPPORTED); } + @Override + public String refresh() { + throw new AuthException(ResponseStatus.UNSUPPORTED); + } + }, + /** + * Google + */ + GOOGLE { + @Override + public String authorize() { + return "https://accounts.google.com/o/oauth2/v2/auth"; + } + + @Override + public String accessToken() { + return "https://www.googleapis.com/oauth2/v4/token"; + } + + @Override + public String userInfo() { + return "https://oauth2.googleapis.com/tokeninfo"; + } + + @Override + public String revoke() { + throw new AuthException(ResponseStatus.UNSUPPORTED); + } + @Override public String refresh() { throw new AuthException(ResponseStatus.UNSUPPORTED); diff --git a/src/main/java/me/zhyd/oauth/model/AuthSource.java b/src/main/java/me/zhyd/oauth/model/AuthSource.java index 55631b687452a2fb6eb0f12bed611c717d101fba..f4f964dfa91ddaa5f03d0e3472ef7bfe0f26b316 100644 --- a/src/main/java/me/zhyd/oauth/model/AuthSource.java +++ b/src/main/java/me/zhyd/oauth/model/AuthSource.java @@ -21,5 +21,8 @@ public enum AuthSource { TAOBAO, QQ, WECHAT, + /** + * 谷歌登录,参考文档:https://developers.google.com/identity/protocols/OpenIDConnect + */ GOOGLE, } diff --git a/src/main/java/me/zhyd/oauth/model/AuthToken.java b/src/main/java/me/zhyd/oauth/model/AuthToken.java index 011ece89aa2efb3b141bf8befc4b0c7b6465809f..ebadf0c61c9b57c8fadfcc701b15d124db81cc13 100644 --- a/src/main/java/me/zhyd/oauth/model/AuthToken.java +++ b/src/main/java/me/zhyd/oauth/model/AuthToken.java @@ -17,4 +17,12 @@ public class AuthToken { private String uid; private String openId; private String accessCode; + + /** + * Google附带属性 + */ + private String scope; + private String tokenType; + private String idToken; + } diff --git a/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java b/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..52b2bd8fa8e888ce7c96dfcec596336920ea0f7d --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AuthGoogleRequest.java @@ -0,0 +1,63 @@ +package me.zhyd.oauth.request; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.alibaba.fastjson.JSONObject; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.model.AuthSource; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.utils.UrlBuilder; + +/** + * Google登录 + * + * @author yangkai.shen (https://xkcoding.com) + * @version 1.3 + * @since 1.3 + */ +public class AuthGoogleRequest extends BaseAuthRequest { + + public AuthGoogleRequest(AuthConfig config) { + super(config, AuthSource.GOOGLE); + } + + @Override + protected AuthToken getAccessToken(String code) { + String accessTokenUrl = UrlBuilder.getGoogleAccessTokenUrl(config.getClientId(), config.getClientSecret(), code, config + .getRedirectUri()); + HttpResponse response = HttpRequest.post(accessTokenUrl).execute(); + JSONObject object = JSONObject.parseObject(response.body()); + + if (object.containsKey("error") || object.containsKey("error_description")) { + throw new AuthException("get google access_token has error:[" + object.getString("error") + "], error_description:[" + object + .getString("error_description") + "]"); + } + + return AuthToken.builder() + .accessToken(object.getString("access_token")) + .expireIn(object.getIntValue("expires_in")) + .scope(object.getString("scope")) + .tokenType(object.getString("token_type")) + .idToken(object.getString("id_token")) + .build(); + } + + @Override + protected AuthUser getUserInfo(AuthToken authToken) { + String accessToken = authToken.getIdToken(); + HttpResponse response = HttpRequest.get(UrlBuilder.getGoogleUserInfoUrl(accessToken)).execute(); + String userInfo = response.body(); + JSONObject object = JSONObject.parseObject(userInfo); + return AuthUser.builder() + .username(object.getString("name")) + .avatar(object.getString("picture")) + .nickname(object.getString("name")) + .location(object.getString("locale")) + .email(object.getString("email")) + .token(authToken) + .source(AuthSource.GOOGLE) + .build(); + } +} diff --git a/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java b/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java index 1e0feab4b0561505d05bf541517313e1a6c40d94..5467739f2426444bfebdaaefc2b14c9ed6e0a21b 100644 --- a/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java +++ b/src/main/java/me/zhyd/oauth/utils/UrlBuilder.java @@ -17,6 +17,10 @@ public class UrlBuilder { private static final String GITHUB_USER_INFO_PATTERN = "{0}?access_token={1}"; private static final String GITHUB_AUTHORIZE_PATTERN = "{0}?client_id={1}&state=1&redirect_uri={2}"; + private static final String GOOGLE_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&scope=openid%20email%20profile&redirect_uri={2}&state={3}"; + private static final String GOOGLE_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&code={3}&redirect_uri={4}&grant_type=authorization_code"; + private static final String GOOGLE_USER_INFO_PATTERN = "{0}?id_token={1}"; + private static final String WEIBO_ACCESS_TOKEN_PATTERN = "{0}?client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}&redirect_uri={4}"; private static final String WEIBO_USER_INFO_PATTERN = "{0}?{1}"; private static final String WEIBO_AUTHORIZE_PATTERN = "{0}?client_id={1}&response_type=code&redirect_uri={2}"; @@ -492,4 +496,38 @@ public class UrlBuilder { public static String getTaobaoAuthorizeUrl(String clientId, String redirectUrl) { return MessageFormat.format(TAOBAO_AUTHORIZE_PATTERN, ApiUrl.TAOBAO.authorize(), clientId, redirectUrl); } + + /** + * 获取Google授权地址 + * + * @param clientId google 应用的Client ID + * @param redirectUrl google 应用授权成功后的回调地址 + * @return full url + */ + public static String getGoogleAuthorizeUrl(String clientId, String redirectUrl) { + return MessageFormat.format(GOOGLE_AUTHORIZE_PATTERN, ApiUrl.GOOGLE.authorize(), clientId, redirectUrl, System.currentTimeMillis()); + } + + /** + * 获取Google token的接口地址 + * + * @param clientId google应用的Client ID + * @param clientSecret google应用的Client Secret + * @param code google授权前的code,用来换token + * @param redirectUri 待跳转的页面 + * @return full url + */ + public static String getGoogleAccessTokenUrl(String clientId, String clientSecret, String code, String redirectUri) { + return MessageFormat.format(GOOGLE_ACCESS_TOKEN_PATTERN, ApiUrl.GOOGLE.accessToken(), clientId, clientSecret, code, redirectUri); + } + + /** + * 获取Google用户详情的接口地址 + * + * @param token google 应用的token + * @return full url + */ + public static String getGoogleUserInfoUrl(String token) { + return MessageFormat.format(GOOGLE_USER_INFO_PATTERN, ApiUrl.GOOGLE.userInfo(), token); + } } diff --git a/src/test/java/me/zhyd/oauth/AuthRequestTest.java b/src/test/java/me/zhyd/oauth/AuthRequestTest.java index 8e32bf70418bd96f4cff3ff6730e261a161a9c0a..de29fbdaa31118f30c31418ddd43782aa6efaa66 100644 --- a/src/test/java/me/zhyd/oauth/AuthRequestTest.java +++ b/src/test/java/me/zhyd/oauth/AuthRequestTest.java @@ -141,4 +141,17 @@ public class AuthRequestTest { // 授权登录后会返回一个code,用这个code进行登录 AuthResponse login = authRequest.login("code"); } + + @Test + public void googleTest() { + AuthRequest authRequest = new AuthGoogleRequest(AuthConfig.builder() + .clientId("clientId") + .clientSecret("clientSecret") + .redirectUri("redirectUri") + .build()); + // 返回授权页面,可自行调整 + String url = authRequest.authorize(); + // 授权登录后会返回一个code,用这个code进行登录 + AuthResponse login = authRequest.login("code"); + } }