和特朗普吃了顿饭后写下了这篇文章

GitHub OAuth2.0 登录 SpringBoot 原理与实战

1. 前言

最近做了两个梦都很奇葩和大家分享分享快乐.
Dream 1: 第一个梦我梦到了我在和 特朗普 吃饭, 然后告诉 特朗普 他需要向中国学习, 赶紧将防疫作为第一工作, 然后他不听, 还气冲冲的走了, 然后我就去号召大家一起反对他, 后来反对的人越来越多, <<老友记>>中的 钱德勒(我也不知道为啥是他) 也加入了我的阵营, 特朗普 坐不住了, 就当着我的面把 钱德勒 从百层大楼的楼顶扔了下去(Sorry), 死的老惨了(Sorry too…)…

Dream2: 第二个梦是这周周五晚上做的, 梦到回老家, 然后地铁转大巴一个半小时, 下了大巴然后还得转市上的公交三四十分钟才能到家, 觉得好远, 莫名就想起了 <<火影忍者--疾风传>> 里面第四次忍界大战纲手上一线离得太远就用传送物资的时空间忍术将自己传送到了第四次忍界大战的前线, 于是乎, 我就在想, 能不能扩展一下 Java 的 IO 流, 将自己 回老家(这个梦, 很无语)

这两个梦让我每次想起都能开心好久, 希望你也是…哈哈哈哈…好了, 言归正传

正如上文所说, 用户注册是一个让用户极为反感的事情, 因此, 快捷, 方便的用户注册流程能够更好的留住用户, 所以, jfoa: https://github.com/JavaFamilyClub/jfoa 引入了 OAuth 2.0 认证, 目前支持 GitHub 认证实现注册和登录. 如约而至, 今天和大家聊聊 OAuth 2.0 的实现和原理.

OAuth 2.0RFC 6749: https://tools.ietf.org/html/rfc6749 提出的一个授权框架模型, RFC 6749OAuth 2.0 的介绍如下:
The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf. This specification replaces and obsoletes the OAuth 1.0 protocol described in RFC 5849.
OAuth 2.0 授权框架支持第三方应用程序以获取对HTTP服务的有限访问权, 通过协调批准交互来代表资源所有者在资源所有者和HTTP服务之间,或者通过允许第三方应用程序代表自己获取访问权限。这个规范取代并淘汰了 RFC 5849: https://tools.ietf.org/html/rfc5849 中所描述的 OAuth 1.0 协议。

2. OAuth 角色

OAuth 定义了四个角色:

  • 资源所有者

一个能够授予对受保护资源访问权限的实体, 当资源所有者是一个人时, 它被称为最终用户.

  • 资源服务器

托管受保护资源的服务器,能够使用访问令牌接受和响应受保护资源的请求。

  • 客户(client)

代表资源所有者及其授权提出受保护资源请求的应用程序。

  • 授权服务器

成功认证资源所有者并获得授权后,服务器向客户端颁发访问令牌。

一个基本的访问流程如下:

file

  • (A)客户端请求资源所有者的授权。该授权请求可以直接向资源拥有者,或者最好间接通过授权服务器作为中介。

  • (B)客户端收到授权许可,该授权许可是表示资源所有者授权的凭据,使用 OAuth 2.0 提供的四种授权类型之一或扩展授权类型表示。该授权许可类型取决于所使用的方法的客户端,以请求授权并通过支持的类型授权服务器。

  • (C)客户端通过与授权服务器进行身份验证并提供授权许可来请求访问令牌。

  • (D)授权服务器对客户端进行身份验证并验证授权许可,如果有效,则颁发访问令牌。

  • (E)客户端从资源服务器请求受保护的资源,并通过提供访问令牌来进行身份验证。

  • (F)资源服务器验证访问令牌,如果有效,则服务该请求。

3. OAuth 许可

授权许可是客户端使用资源(访问其受保护资源) 的授权来获取访问令牌的凭证, 该规范定义了四种授权方式

  • 授权码
  • 隐式
  • 资源所有者密码
  • 客户端凭据

此外, OAuth 2.0 还提供了用户定义其他类型的可扩展机制.

3.1 授权码

通过使用授权服务器作为客户端和资源所有者之间的中介,可以获取授权代码。客户端不是直接从资源所有者请求授权,而是将资源所有者定向到授权服务器(通过 RFC2616: https://tools.ietf.org/html/rfc2616 中定义的用户代理),后者再将资源所有者与授权代码一起引导回客户端。

在使用授权码将资源所有者引导回客户端之前,授权服务器对资源所有者进行身份验证并获得授权。由于资源所有者仅通过授权服务器进行身份验证,因此资源所有者的凭据永远不会与客户端共享。

授权代码提供了一些重要的安全益处,例如对客户端进行身份验证的能力,以及将访问令牌直接传输到客户端的过程,而无需将其传递给资源所有者的用户代理并可能将其暴露给其他人,包括资源所有者。

3.2 隐式许可

隐式授权是简化的授权代码流程,该流程针对使用JavaScript之类的脚本语言在浏览器中实现的客户端进行了优化。在隐式流程中,不向客户端颁发授权码,而是向客户端直接授予访问令牌(作为资源所有者授权的结果)。授予类型是隐式的,因为没有颁发中间凭证(例如以后用于获取访问令牌的授权码)。

在隐式授予流程中颁发访问令牌时,授权服务器不会对客户端进行身份验证。在某些情况下,可以通过用于将访问令牌传递给客户端的重定向URI验证客户端身份。访问令牌可以向资源所有者或其他具有资源所有者的用户代理应用程序公开访问权限。

隐式授予提高了某些客户端(例如,实现为浏览器内应用程序的客户端)的响应能力和效率,因为它减少了获取访问令牌所需的往返次数。但是,应该权衡这种便利性与使用隐式授权所带来的安全隐患,尤其是在授权码授权类型可用时。

3.3 资源所有者密码

资源所有者的密码(即用户名和密码)可以直接用作获得访问令牌的授权。仅当资源所有者和客户端之间存在高度信任时(例如,客户端是设备操作系统或高度特权的应用程序的一部分),并且其他授权授予类型不可用时(例如授权码),才应使用此授权许可方式。

即使此授予类型要求客户端直接访问资源所有者凭证,资源所有者凭证也用于单个请求,并交换访问令牌。这个授予类型可以通过与长期访问令牌或刷新令牌交换凭据来消除客户端存储资源所有者凭据以供将来使用的需求。

3.4 客户凭证

当授权范围限于在客户端控制下的受保护资源或先前与授权服务器一起安排的受保护资源时,可以将客户端凭据(或其他形式的客户端身份验证)用作授权许可。通常,当客户端代表自己(客户端也是资源所有者)行动或基于先前与授权服务器一起安排的授权请求访问受保护资源时,客户端凭据将用作授权许可。

还有很多概念和细节, 大家感兴趣可以参考 RFC 6749: https://tools.ietf.org/html/rfc6749 .

4. 实战演练

来了来了, 重头戏来了哦, 今天就以 jfoa: https://github.com/JavaFamilyClub/jfoa 来做 Github OAuth2.0 登录 SpringBoot 应用 的演练, 因为jfoa 自支持 Github 注册/登录上线后, 已经经历了一段时间的真实服务器和生产环境的考验.

file

jfoa 使用授权码的方式进行 Github 认证授权. 基本流程如下:

file

4.1 应用登记

一个应用要求 OAuth 授权,必须先到对方网站登记,让对方知道是谁在请求。所以,你要先去 GitHub 登记一下。

  • 进入 GitHub Setting

file

  • 进入开发者设置

file

  • 进入 OAuth Apps, 创建 App

file

上图中已经有两个 App, 一个是 jfoa 生产环境的 App, 一个是 jfoa 开发环境的 App(jfoa 采用生产环境和开发环境分离的开发模式, 否则, 开发调试或者测试会对生产环境有所影响, 比如开发环境注册用户, 删除用户等都会影响开发环境产生脏数据)

  • 填写 App 信息(提示: 本地 localhost 应用也是支持的, 因为仅仅是一个回调地址), 下面是开发模式下jfoa 的 App 配置

file

  • 查看 client_id 并生成 client_secret

file

4.2 在浏览器提供通过 GitHub 登录的界面

file

如上图所示, jfoa 为了安全性考虑, 将 client_idclient_secret 等重要信息都存储在 Server 并进行了加密, 因此这个 UI 并不会直接请求 Github 授权, 而是先访问 jfoa 的服务器获取参数信息, 然后再进行重定向.

   @GetMapping("/public/oauth/github/auth")
   public String auth() {
      String authorizeUrl = githubProvider.getAuthorizeUrl();

      return "redirect:" + authorizeUrl;
   }

并且 jfoa 将密文存储在环境变量中, 并在 build.gradle 中引用环境变量. 如果大家参考 jfoa 项目的话, 需要替换 jfoa/runner/src/main/resources/application.yml 中的 client_idclient_secret 为自己的信息

file

最终, 点击GitHub 登录按钮会重定向到: https://github.com/login/oauth/authorize?scope=user:emailresponse_type=code&redirect_uri=http://localhost/public/oauth/github/callback&state=1&client_id=XXX

4.3 跳转到 GitHub 进行授权

通过上一步的重定向就会进入 GitHub 的授权服务器进行授权, 授权 GitHub 需要你先登录 GitHub

file

登录之后, 第一次访问 OAuth App 时 GitHub 会需要你对 OAuth App 进行授权. 这个页面会罗列 OAuth App 的基本信息以及OAuth App 所请求的资源(这就是 OAuth 2.0 的资源访问限制)

file

4.4 返回授权码

当你对 OAuth App 进行授权后, Github 授权服务器就会携带授权码和状态信息重定向到你请求 GitHub 认证时所携带的 redirect_uri 指定的地址. 即: http://localhost/public/oauth/github/callback?code=YYY&state=1
jfoa 中就会访问下面的 controller:

   @GetMapping("/public/oauth/github/callback")
   public String callback(@RequestParam("code") String code,
                          @RequestParam("state") String state)
   {
      AccessTokenResponse accessTokenResponse
         = githubProvider.queryAccessToken(code, state);

      authentication(accessTokenResponse);

      return "redirect:/";
   }

在这里, 通过 RestTemplate + Apache Http Client 请求 GitHub 授权码, 请求地址如下:

https://github.com/login/oauth/access_token?client_id=XXX&client_secret=***&code=YYY&redirect_uri=http://localhost/public/oauth/github/callbackstate=1

4.5 通过 access_token 访问资源

拿到 access_token 之后就可以访问用户授权的资源的, 比如jfoa目前会请求用户的账户, 用户名, 公司, 简介和邮箱信息.
通过以下请求:

https://api.github.com/user?access_token=*****

access_token 需要以请求头的信息进行传递, 以上 URL 中仅仅只是说明需要携带该信息

   default HttpHeaders queryResourceHeader(AccessTokenResponse token) {
      HttpHeaders headers = new HttpHeaders();
      headers.set(authorizationParamName(),
         token.getToken_type()
            + " " + token.getAccess_token());

      return headers;
   }

Postman 中如下配置:

file

4.6 根据获取到的用户信息和业务对用户进行认证

jfoa 会使用 “GitHub:” + 用户 GitHub 账户作为用户账户对用户进行注册与登录. 大家可以根据自身业务需要灵活变动.

至此, OAuth 2.0 的基本理论和实战就给大家介绍完了, 总体来说 OAuth2.0 思路清晰, 目标明确, 但是如果大家第一次接触还是需要亲自实践一下的, 帅帅实践中也遇到过一些问题记忆深刻, 以后再给大家分享, 大家先自行实践.

file

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值