zoukankan      html  css  js  c++  java
  • React + Node 单页应用「二」OAuth 2.0 授权认证 & GitHub 授权实践

    关于项目

    项目地址
    预览地址

    记录最近做的一个 demo,前端使用 React,用 React Router 实现前端路由,Koa 2 搭建 API Server, 最后通过 Nginx 做请求转发。

    文章列表

    第一篇:React + Node 单页应用「一」前端搭建
    React + Node 单页应用「二」OAuth 2.0 授权认证 & GitHub 授权实践

    这是第二篇,介绍下 OAuth 2.0 授权机制,以及 Github App 授权过程,通过获取授权使用 Github API。

    OAuth 2.0

    背景

    传统的 CS(Client-Server) 授权模式下,请求访问受保护资源(用户信息)时,客户端需要向服务器提供资源所属用户的证书,为了让第三方拿到资源,用户就得将证书共享给第三方应用。这种方式会导致以下几个问题:

    • 第三方应用为了长久使用,需要存储用户凭证,特别是明文密码。
    • 服务器需要支持密码验证,虽然密码存在固有的安全缺陷。
    • 第三方应用获得了资源的过度访问权限,导致用户在授权期间无法限制第三方应用的访问权限。
    • 用户不能单独撤销某一个第三方应用的访问权限,必须修改密码来撤销所有授权应用的权限。
    • 任何第三方应用的漏洞都可能会威胁到用户的密码,以及对应密码权限下的用户数据。

    为了解决这个问题,OAuth 协议引入了授权层,并将客户端与服务端的角色区分开,在 OAuth 协议中,客户端请求的 Access_token ,被托管在资源服务器,但受用户控制,并且与用户凭证完全不同。

    举个例子,现在有一家档案馆,档案馆中有很多资料,这些资料有些涉及到机密,有些只是常规资料,
    研究员小李需要进入档案馆查找常规资料,于是就跑去找罗馆长批条子

    “馆长馆长,我要看一些常规资料,请给我批个条子吧”

    罗馆长了解了小李要看的是常规资料,很爽快地批了

    “没问题,给,这是同意的条子

    于是小李拿到条子后,径直去找了档案馆门卫大壮

    “大壮,你看这是馆长给我批的查看常规资料的条子”,

    大壮确认没问题后

    “嗯,既然馆长同意了,来,这是常规文件的钥匙”,

    于是小李就拿着钥匙进入馆内开始找资料,馆内每道房门都有一把锁,如果房间里存放的是常规资料,小李只需要出示钥匙就可以进入,但存放涉密资料的房间,小李的钥匙打不开,必须找馆长批一份查看涉密资料的条子,再拿着条子去大壮那儿换一把新的钥匙。

    角色

    • 资源所有者
      可以授权获取受保护资源的实体,如果这个所有者是人,即常规意义的用户,即档案馆的馆长。

    • 资源服务器
      资源服务器用于存储资源,接收请求,并将资源返回给携带合法 Access_token 的请求,在我们的例子中,档案馆就充当着资源服务器的角色。

    • 客户端
      获取授权后,代表用户请求资源的第三方应用,也就是故事中的研究员小李。

    • 授权服务器
      授权服务器在成功认证资源拥有者并获取到授权后,发放 Access_token 给客户端,故事中大壮的主要工作内容就是发放访问钥匙。

    授权流程

    /**
     * 协议流程
     * 引自 RFC6749
     */
     +--------+                               +---------------+
     |        |--(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 ---|               |
     +--------+                               +---------------+
    

    上面这张图是引自 RFC6749 的 OAuth 2.0 授权流程,概括如下

    • 第三方应用向用户请求授权,用户同意,第三方拿到授权许可
    • 第三方拿着用户的授权许可,请求授权服务器要访问数据的 Access_token
    • 带着 Access_token,第三方应用就可以访问资源服务器中的资源了

    Github APP 授权

    Github 有一套非常完备的 API,在没有获得授权时,仅有部分功能可用,例如获取用户信息、仓库信息等,并且调用次数被限制在了一小时仅允许60次

    授权后,每小时调用次数放开到每小时 5000 次,并且申请授权时,可以选择申请的权限范围,例如申请 star 项目、follow 用户的权限(我们这次用到的)等等,我们通过申请完整的权限,甚至可以写一个 Github 第三方应用。

    注册并配置 APP

    要获取授权,首先需要在 Github 注册一个应用,注册这个应用之前,需要准备好两个东西

    • 用作应用主页访问的 URL,例如 github.lijundong.com(如果是 IP 记得加协议头)
    • 用作接受授权回调的的 URL,例如github.lijundong.com/github/getauth

    可依照以下路径创建一个授权 APP,

    Github > setting > Developer settings > OAuth Apps > New OAuth App

    进入注册界面,需要填写应用名、应用主页地址、应用简介、以及回调 URL,创建完成会跳转到 APP 管理页面,在管理页面可以更新应用信息以及上传应用 Logo,并且你将看到你的应用的 Client IDClient Secret,接下来获取权限需要用到这两个东西。

    授权流程

    GitHub 官方的授权流程:
    第一步:页面跳转到 GitHub 的授权页

    在项目中,我选择了用 <a> 标签链接的方式跳转。

    GET https://github.com/login/oauth/authorize?client_id=xxx&scope=xxx
    /**
     * client_id:注册应用的 client_id 必填
     * scope:申请的权限范围  选填,默认用户权限为空
     */
    

    第二步:回调接口收到 GitHub 的回调请求,获得 code

    我们在注册应用时,设置了授权回调 URL,上一步中,Github 授权页成功获得用户授权后,会带上 code 请求我们设置的回调 URL,在这一步中,我们的 Server 就拿到了用户的授权 code。

    第三步:通过 code 获取 Access_token

    最后一步通过已有 code,加上应用的 client_idclient_secret,我们向 Github 申请 Access_token

    /**
     * code:第二步获取到的 code 必填
     * client_secret:注册应用 client_secret 必填
     * client_id:注册应用的 client_id 必填
     */
    const rp = require('request-promise')
    let option = {
        uri: 'https://github.com/login/oauth/access_token?client_id=' + clientId + '&client_secret=' + clientSecret + '&code=' + code,
        json: true
    } 
    let tokenResp = await rp(option);
    

    第四步:将获得的 Access_token 写入页面的 cookie 中。

    ctx.cookies.set('access_token', tokenResp.access_token, {
        'httpOnly': false
    })
    

    获取用户数据

    因为第三方应用请求 GitHub API 涉及到跨域,第一篇文章已经提到了Github API 只支持 XHR 跨域请求,一些同学如果用 Fetch 跨域请求 API 会导致请求 request type 变成 option ,所以项目中改用 axios 进行网络请求。

    这里以获取登陆用户基本信息为例,

    import Axios from 'axios'
    import Cookie from 'js-cookie'
    const access_token = Cookie.get('access_token');
    
    getLoginInfo() {
        let that = this;
        let url = API.GITHUB.GET_LOGIN_INFO;
        Axios.get(url, {
            params: {
                access_token: access_token
            }
        }).then(function(res) {
            that.setState({
                loginInfo: res.data,
            });
        }).catch(function (error) {
            console.error(error);
          })
    }
    

    参考:

  • 相关阅读:
    70.BOM
    69.捕获错误try catch
    68.键盘事件
    523. Continuous Subarray Sum
    901. Online Stock Span
    547. Friend Circles
    162. Find Peak Element
    1008. Construct Binary Search Tree from Preorder Traversal
    889. Construct Binary Tree from Preorder and Postorder Traversal
    106. Construct Binary Tree from Inorder and Postorder Traversal
  • 原文地址:https://www.cnblogs.com/coderleon/p/7819226.html
Copyright © 2011-2022 走看看