今天在做自己的一个自用项目,由于不想记忆太多账号密码,也不想设置一个简单的密码并不安全,为了防止了密码忘记了每次都找,自建了 Gitea 私服,就打算直接接入第三方授权登录,Java 已经有这方面比较全的开箱即用的整合第三方登录的开源组件,就是 JustAuth,直接引入组件简单配置就能完成Github、Gitee、支付宝、新浪微博、微信、Google、Facebook、Twitter、StackOverflow等国内外数大多数主流的第三方平台授权登录策略。
由于自己是要集成 Gitea 私服,现在最新版都还没有支持,不知道是基于什么考虑,官方仓库有 issues177 就有人提过,但是一直处于打开状态。所以今天直接按照官方把这块代码完善了。
做之前还是先大概了解一下 oauth2 授权协议,OAuth 2.0 协议是一种三方授权协议,目前大部分的第三方登录与授权都是基于该协议的标准或改进实现。网上一大把资源都能查到,我这里直接用 AI 翻译的大白话介绍一下:
Auth 2.0 是一种授权机制,简单来说,它能让你在不把自己账号密码告诉其他应用的情况下,允许该应用访问你在另一个服务中的某些信息或功能。
比如说,你想在某个健身应用里分享你的运动数据到微信朋友圈。如果你直接把微信账号密码给健身应用,那太不安全了,万一应用有漏洞或者被黑客攻击,你的微信账号就危险了。这时候 OAuth 2.0 就派上用场了,你点击健身应用的分享按钮后,会跳转到微信的授权页面,你可以选择授权健身应用访问你的哪些信息,比如运动步数等,然后健身应用就可以根据你授权的范围,把相应数据分享到朋友圈,而它并不知道你的微信账号密码,也无法获取你其他未授权的信息。这样既方便又安全,能让不同的应用之间安全地进行信息交互和功能授权。
假设整个流程开始之前,用户已经登录,那么整个授权流程如下:
整个流程中,客户端都无法接触到用户的登录凭证信息,客户端通过访问令牌请求受保护资源,用户可以通过对授权操作的控制来间接控制客户端对于受保护资源的访问权限范围和时效。
客户端是 OAuth 服务的接入方,其目的是请求用户存储在资源服务器上的受保护资源,客户端可以移动应用、网页应用,以及电视应用等等。
用户代理是用户参与互联网的工具,一般可以理解为浏览器。
受保护资源所属的实体,比如资源的持有人等,下文的用户即资源所有者。
授权服务器的主要职责是验证资源所有者的身份,并依据资源所有者的许可对第三方应用下发令牌。
托管资源的服务器,能够接收和响应持有令牌的资源访问请求,可以与授权服务器是同一台服务器,也可以分开。
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
流程解析
OAuth 2.0 的授权流程可以用一个生活场景来简单理解,比如你去餐厅吃饭需要寄存外套:
参考官方文档自定义第三方平台的OAuth 只需要实现实现AuthSource接口以及自定义一个Request。
下面给出 Gitea 的实现类,Gitea 的授权断点可以再这里找到:https://docs.gitea.com/zh-cn/development/oauth2-provider
Authorization Endpoint | /login/oauth/authorize |
---|---|
Access Token Endpoint | /login/oauth/access_token |
OpenID Connect UserInfo | /login/oauth/userinfo |
JSON Web Key Set | /login/oauth/keys |
Oauth2 一共有四种授权模式:1)授权码模式(Authorization Code Grant);2)隐式授权模式(Implicit Grant);3)资源所有者密码凭证模式(Resource Owner Password Credentials Grant);4)以及客户端凭证模式(Client Credentials Grant),目前 Gitea 仅支持Authorization Code Grant)标准。
AuthGiteaSource.class:
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.request.AuthDefaultRequest;
/**
* gitea Oauth2 默认接口说明
* * https://www.51it.wang
*
* @author lcry
*/
public enum AuthGiteaSource implements AuthSource {
/**
* 自己搭建的 gitea 私服
*/
GITEA {
/**
* 授权的api
*/
@Override
public String authorize() {
return AuthGiteaRequest.SERVER_URL + "/login/oauth/authorize";
}
/**
* 获取accessToken的api
*/
@Override
public String accessToken() {
return AuthGiteaRequest.SERVER_URL + "/login/oauth/access_token";
}
/**
* 获取用户信息的api
*/
@Override
public String userInfo() {
return AuthGiteaRequest.SERVER_URL + "/login/oauth/userinfo";
}
/**
* 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest}
*/
@Override
public Class<? extends AuthDefaultRequest> getTargetClass() {
return AuthGiteaRequest.class;
}
}
}
AuthGiteaRequest.class:
import cn.hutool.core.lang.Dict;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
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.request.AuthDefaultRequest;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.json.utils.JsonUtils;
/**
* Gitea授权登录请求实现
* https://www.51it.wang
* @author lcry
*/
@Slf4j
public class AuthGiteaRequest extends AuthDefaultRequest {
public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.gitea.server-url");
/**
* 设定归属域
*/
public AuthGiteaRequest(AuthConfig config) {
super(config, AuthGiteaSource.GITEA);
}
public AuthGiteaRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthGiteaSource.GITEA, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String body = doPostAuthorizationCode(authCallback.getCode());
Dict object = JsonUtils.parseMap(body);
// oauth/token 验证异常
if (object.containsKey("error")) {
throw new AuthException(object.getStr("error_description"));
}
// user 验证异常
if (object.containsKey("message")) {
throw new AuthException(object.getStr("message"));
}
return AuthToken.builder()
.accessToken(object.getStr("access_token"))
.refreshToken(object.getStr("refresh_token"))
.idToken(object.getStr("id_token"))
.tokenType(object.getStr("token_type"))
.scope(object.getStr("scope"))
.build();
}
@Override
protected String doPostAuthorizationCode(String code) {
HttpRequest request = HttpRequest.post(source.accessToken())
.form("client_id", config.getClientId())
.form("client_secret", config.getClientSecret())
.form("grant_type", "authorization_code")
.form("code", code)
.form("redirect_uri", config.getRedirectUri());
HttpResponse response = request.execute();
return response.body();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String body = doGetUserInfo(authToken);
Dict object = JsonUtils.parseMap(body);
// oauth/token 验证异常
if (object.containsKey("error")) {
throw new AuthException(object.getStr("error_description"));
}
// user 验证异常
if (object.containsKey("message")) {
throw new AuthException(object.getStr("message"));
}
return AuthUser.builder()
.uuid(object.getStr("sub"))
.username(object.getStr("name"))
.nickname(object.getStr("preferred_username"))
.avatar(object.getStr("picture"))
.email(object.getStr("email"))
.token(authToken)
.source(source.toString())
.build();
}
}
配置文件格式:
justauth:
type:
gitea:
server-url: https://demo.gitea.com
client-id: b3c5*********8f13910
client-secret: gto_5t*********wiyykoa
redirect-uri: ${justauth.address}/social-callback?source=gitea
所有参数信息直接通过登录 Gitea 个人账号-用户设置-应用-创建新的 OAuth2 应用程序。
验证流程:
通过本文你可以通过扩展 JustAuth 组件实现所有支持 Oauth2 授权登录,本文代码本来打算直接提交给 JustAuth,但是看了了仓库还有很多 pull request 作者没有合并,并且最近更新也是 5 个月前,可能作者是觉得已经开放了自定义扩展,可以自己内部扩展就行了,没必要都实现,所以就先这样吧。
OAuth2.0 协议原理 OAuth流程 JustAuth-demo 示例仓库 justauth-spring-boot-starter 仓库
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有