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);
          })
    }
    

    参考:

  • 相关阅读:
    CrawlSpiders
    从抓取Tencent中学习Scrapy
    对象返回规范的url的两种方式的两种方式
    多对多关系的额外字段
    Django定时任务
    Scripy学习(一)
    Django开发博客一(搭建模型和准备数据)
    求并集
    求子集、交集
    java数学函数Math类中常用的方法
  • 原文地址:https://www.cnblogs.com/coderleon/p/7819226.html
Copyright © 2011-2022 走看看