记一次OAuth2.0用户鉴权

记一次OAuth2.0用户鉴权我们平时登录不同的平台,总会有使用到Token的场景,比如用github账户登录掘金,这个时候我们肯定不会把自己的github账号密码给掘金, OAuth 就是这样一套机制,用于各种免密授权登录场景,在便利的同时保证安全性,其实就是向平台申请token授权。笔者最近在用spot…

前言

我们平时登录不同的平台,总会有使用到Token的场景,比如用github账户登录掘金,这个时候我们肯定不会把自己的github账号密码给掘金, OAuth 就是这样一套机制,用于各种免密授权登录场景,在便利的同时保证安全性,其实就是向平台申请token授权。笔者最近在用spotify API二次开发自己的应用,所以也碰到用户鉴权登录的场景,踩坑蛮多,也学到很多,所以整理下来,希望能帮助到大家🤗。

简要的授权流程

  • 用户打开客户端以后,客户端要求用户给予授权(掘金登录选项:github登录)。
  • 用户同意给予客户端授权(我点击github登录,登录通过)。
  • 客户端使用上一步获得的授权,向认证服务器申请令牌(掘金申请 access_token)。
  • 认证服务器对客户端进行认证以后,确认无误,同意发放令牌(github给他 access_token)。
  • 客户端使用令牌,向资源服务器申请获取资源(掘金拿着access_token 去换资源)。
  • 资源服务器确认令牌无误,同意向客户端开放资源(github校验通行)。

掘金和github简要举例,帮助大家理解,接下来我用Spotify的鉴权详细举例吧

授权码模式(authorization code)

你的网站需要获得服务器的授权,要现在对应的开发者申请账号、秘钥、重定向地址

  • client_id:我们去spotify那里注册的
  • client Secret: soptify给我们的秘钥
  • redirect_uri:认证服务器把客户端重定向去的一个 url(本地开发一般是loacalhost)
  • response_type:需要你给我一个 code
  • state:任意值,规范规请求和返回时都是一样的值

首先示范一下如何在spotify develope申请开发者身份

这里针对申请spotify开发者身份的流程介绍,图文较多,不敢兴趣的可以划过,下面有我对OAth鉴权的流程介绍。

记一次OAuth2.0用户鉴权

  • 登录后点击create a client id,生成一个专用的client_idclient_secret
记一次OAuth2.0用户鉴权

  • 同时设置Redirect URIs,这是通过鉴权后重定向的地址,端口上运行你的应用,一定要填写准确。
记一次OAuth2.0用户鉴权

  • Scopes 权限选择,权限选择在进行授权申请之前,要先确定这个应用需要哪些权限,确定好了再到授权过程中通过后端参数进行声明。

    Spotify对权限进行了详细的分类,全部的权限如下:

记一次OAuth2.0用户鉴权

soptify官网的授权流程

Authentication 授权

授权的最终目的是获取一个名为access_token的值,然后用这个access_token去获取各种个样的API信息。

Spotify为了严格区分不同的用途和权限,把这个access_token的获取方法分为了三种流程,各自的权限、存活期都不同。

三种流程特点如下:

  • Authorization Code Flow: 标准方法,可刷新token
  • Client Credentials Flow: app级token,不可获取用户行为。
  • Implicit Grant Flow: 临时授权。可获取用户行为,不可刷新。存活期短。

如果没有用户的点击授权,那么只能使用后两者的授权模式。在用户点击授权后,才能拿到用户的行为种子做个性化开发,这也是推荐的开发者授权模式,接下来就是Authorization Code授权流程:

  • /authorize发送GET请求,请求头包括client_idredirect_uri
  • /authorize,accounts判断是有效client_idredirect_uri,client_secret(这里注意必须和你申请应用填的完全一致)后,Spotify弹出页面,用户手动登录并点击允许授权
  • Spotify把页面跳转至自己设定的callback网址,并明文传输一个Code
  • Code码向/token发送POST请求,并在header中包括一个动态生成并base64编码的Authorization字符串,格式为Authorization: Basic *<base64 encoded client_id:client_secret>*
  • 从Spotify获得访问令牌access_token和更新令牌refresh_token(双令牌)
  • 拿到授权码acess_token后,每次向浏览器请求资源,请求头都会加上Authorization字段
  • access_token过期后就不能再用了,这时请求服务器会返回401状态码,这个时候就要用refresh token来更新token:用refresh_token/token发送POST请求,获得新的access_token

听起来有些弯弯绕绕😵,其实本质上就是根据OAuth 2.0 标准协议,把服务商的资源,来授权给第三方应用访问,常见于单点登录场景(SSO)

理论差不多是上面这些,还有看不懂的小伙伴可以搜有一下OAuth2.0协议,接下来我们开始写代码了~🤗

编码

首先我们的应用进程要获得client_idclient_secret,肯定不能明文写在代码里,我们可以在本地终端写入临时的环境变量,通过node进程模块process读取到id和秘钥。

process 对象是一个 global (全局变量),提供有关信息,控制当前 Node.js 进程。作为一个对象,它对于 Node.js 应用程序始终是可用的,故无需使用 require()

Node进程怎么写入环境变量呢?这里介绍两种临时环境变量的配置方法

配置Node环境变量

  • Windows配置 临时cmd 查看环境变量,添加环境变量,删除环境变量
#查看环境变量
set SPOTIFY_CLIENT_ID
#如果不存在则添加环境变量 
set SPOTIFY_CLIENT_ID=XXXX
set SPOTIFY_CLIENT_SECRET=YYYY
#环境变量追加值 set 变量名=%变量名%;变量内容 (你的应用路径名)
set path=%path%;C:\web;C:\Tools 
#需要删除环境变量,直接=后面不写入值 
set SPOTIFY_CLIENT_ID=
set SPOTIFY_CLIENT_SECRET=

Window cmd环境图在这

记一次OAuth2.0用户鉴权

临时 (powershell) 查看环境变量,添加环境变量,删除环境变量

#查看是否存在 
$env:SPOTIFY_CLIENT_ID  
#如果不存在则添加环境变量  
$env:SPOTIFY_CLIENT_ID="XXX" 
$env:SPOTIFY_CLIENT_SECRET="YYY" 
#环境变量追加值 
$env:path=$env:path + ";C:\web;C:\Tools"  
#删除环境变量 del env:SPOTIFY_CLIENT_ID  
#显示所有的环境变量 ls env:

powershell图在这

记一次OAuth2.0用户鉴权

这里需要特别注意的是虽然通过 CMD 和 Powershell 都能修改环境变量,在不同终端的环境变量是不能共享的,(笔者在这里踩了很久坑TT)即你在 CMD 可以设置
SPOTIFY_CLIENT_ID="XXX",同时也可以在 Powershell 中设置
SPOTIFY_CLIENT_ID="YYY"如果你只在cmd里设置,Node环境里是读不到的!😰 。并且,上面的环境设置只是临时的,只针对当前运行窗口的环境有效。当终端运行窗口关闭以后,相关设置都会丢失。

设置好后就可以在node中通过process.env.REDIRECT_URI读取到账户、秘钥信息,接下来就是在后端写鉴权需要的各种接口了

先上一张官网的授权流程图,感受一下授权流程🧐

记一次OAuth2.0用户鉴权

登录

  • /login登录
let redirect_uri = 
  process.env.REDIRECT_URI || 
  'http://localhost:8888/callback'

app.get('/login', function(req, res) {
  res.redirect('https://accounts.spotify.com/authorize?' +
    querystring.stringify({
      response_type: 'code',
      client_id: process.env.SPOTIFY_CLIENT_ID,
      scope: 'user-read-private user-read-email user-read-recently-played user-top-read user-follow-read user-follow-modify playlist-read-private playlist-read-collaborative playlist-modify-public',
      expires_in: 3600,
      redirect_uri
    }))
})

这里的scope看着写,需要什么权限就配置什么,权限越多,能访问的用户资源就越多,用户可以点击授权登录后可以在官网链接删除授权。

access_token

  • callback
app.get('/callback', function(req, res) {
  let code = req.query.code || null
  let authOptions = {
    url: 'https://accounts.spotify.com/api/token',
    form: {
      code: code,
      redirect_uri,
      grant_type: 'authorization_code',
      expires_in: 3600
    },
    headers: {
      'Authorization': 'Basic ' + (new Buffer(
        process.env.SPOTIFY_CLIENT_ID + ':' + process.env.SPOTIFY_CLIENT_SECRET
      ).toString('base64'))
    },
    json: true
  }
  request.post(authOptions, function(error, response, body) {
    if(!error && response.statusCode === 200){
      var access_token = body.access_token
      var expires_in = body.expires_in
      let uri = process.env.FRONTEND_URI || 'http://localhost:3000'
      res.redirect(uri + '?access_token=' + access_token +'?expires_in=' + expires_in)
    } else {
      res.redirect(`/#${querystring.stringify({ error: 'invalid_token' })}`);
    }
  })
})

若参数无误,响应数据包格式:

若参数无误,服务器将返回一段JSON文本,包含以下参数:

  • access_token:要获取的Access Token。
  • expires_in:Access Token的有效期,以秒为单位。
  • refresh_token:用于刷新Access Token 的 Refresh Token。
  • scope:Access Token最终的访问范围,即用户实际授予的权限列表(用户在授权页面时,有可能会取消掉某些请求的权限)。
  • cookies: 让浏览器记住客户端,包含session_id

这个时候就会自动跳转到最开始设定好的redirect_url,这个时候这个url上运行着我的React应用,有了鉴权之后就可以愉快的使用用户信息,访问调用spotify的API啦~

超时刷新 refresh_token

refresh_token顾名思义,refresh_token就是起到刷新token的作用,避免用户再次点击授权进行验证,那么refresh_token是怎么和access_token联合起来使用的呢?

如果我们遇到了access_token过期了,那么我们需要使用refresh_token去获取一个新的access_token,听起来很简单,那如果refresh_token也过期了呢?这时用户需要重新登录吗?官网给出的解决方案很简单,使用**refresh_token来扩展access_token的有效性。就是协调双令牌

正确的做法即是两个令牌都有自己的过期时间,因为access_token是需要使用refresh_token刷新获取,所以refresh_token设置的过期时间要比access_token时间长,那么如何避免refresh_token过期呢?办法就是我们使用refresh_token刷新了access_token后,将POST请求发送到Accounts服务/api/token端点,grant_type注明是 refresh_token那么Spotify将返回新的access_token。也返回新的refresh_token,这样两个令牌的时间又得到了延长。这就保证了用户在一个规定时间段只要访问了应用,就可以享受无感知的刷新体验。

app.get('/refresh_token', function(req, res) {
  // requesting access token from refresh token
  const refresh_token = req.query.refresh_token;
  let authOptions = {
    url: 'https://accounts.spotify.com/api/token',
    headers: {
      'Authorization': `Basic` + (new Buffer(
          process.env.SPOTIFY_CLIENT_ID + ':' + process.env.SPOTIFY_CLIENT_SECRET
        ).toString('base64')),
    },
    form: {
      grant_type: 'refresh_token',
      refresh_token,
    },
    json: true,
  };

  request.post(authOptions, function(error, response, body) {
    if (!error && response.statusCode === 200) {
      var access_token = body.access_token;
      res.send({ access_token });
    }
  });
});

axios发具体请求

因为每一次向服务端发请求,请求头都需要带上authorization
所以在我们的前端应用通过
window.location.href.match(/access_token=([^&]*)/)拿到token
window.location.href.match(/expires_in=([\w]*)/)[1]拿到过期时间

… 剩下需要的参数自己匹配 然后axios配置

 headers: {
    'Authorization': `Bearer ${access_token}`,
  }

就可以发请求拿到数据啦~ 🤭

动图演示具体流程👇

记一次OAuth2.0用户鉴权

最后

这就是我的一次OAuth2鉴权登录的完整记录啦,(找了蛮多资料发现叙述完整流程的很少,踩了蛮多坑,所以自己动手写了个完整的 ~)

于是乎我们就可以愉快的借助spotify的数据二次开发啦╰(* °▽°* )╯,很感谢看到这里的掘友,希望本文能让你收获些东西,本文以spotify的开放API举例,不玩spotify也没关系,相信大家也能从本文了解一下OAth2的鉴权流程,码字不易,希望你喜欢🥰

本文部分截图来自

spotify developer官网

今天的文章记一次OAuth2.0用户鉴权分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17942.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注