前言
我们平时登录不同的平台,总会有使用到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鉴权的流程介绍。
- 在这里创建你的应用实例创建应用
- 登录后点击
create a client id
,生成一个专用的client_id
和client_secret
。
- 同时设置
Redirect URIs
,这是通过鉴权后重定向的地址,端口上运行你的应用,一定要填写准确。
-
Scopes 权限选择,权限选择在进行授权申请之前,要先确定这个应用需要哪些权限,确定好了再到授权过程中通过后端参数进行声明。
Spotify对权限进行了详细的分类,全部的权限如下:
Authentication 授权
授权的最终目的是获取一个名为access_token
的值,然后用这个access_token
去获取各种个样的API信息。
Spotify为了严格区分不同的用途和权限,把这个access_token
的获取方法分为了三种流程,各自的权限、存活期都不同。
三种流程特点如下:
Authorization Code Flow
: 标准方法,可刷新tokenClient Credentials Flow
: app级token,不可获取用户行为。Implicit Grant Flow
: 临时授权。可获取用户行为,不可刷新。存活期短。
如果没有用户的点击授权,那么只能使用后两者的授权模式。在用户点击授权后,才能拿到用户的行为种子做个性化开发,这也是推荐的开发者授权模式,接下来就是Authorization Code
授权流程:
- 向
/authorize
发送GET请求,请求头包括client_id
和redirect_uri
等 - 经
/authorize,accounts
判断是有效client_id
,redirect_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_id
和client_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环境图在这
临时 (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图在这
这里需要特别注意的是虽然通过 CMD 和 Powershell 都能修改环境变量,在不同终端的环境变量是不能共享的,(笔者在这里踩了很久坑TT)即你在 CMD 可以设置
SPOTIFY_CLIENT_ID="XXX"
,同时也可以在 Powershell 中设置
SPOTIFY_CLIENT_ID="YYY"
如果你只在cmd里设置,Node环境里是读不到的!😰 。并且,上面的环境设置只是临时的,只针对当前运行窗口的环境有效。当终端运行窗口关闭以后,相关设置都会丢失。
设置好后就可以在node中通过process.env.REDIRECT_URI
读取到账户、秘钥信息,接下来就是在后端写鉴权需要的各种接口了
先上一张官网的授权流程图,感受一下授权流程🧐
登录
/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鉴权登录的完整记录啦,(找了蛮多资料发现叙述完整流程的很少,踩了蛮多坑,所以自己动手写了个完整的 ~)
于是乎我们就可以愉快的借助spotify的数据二次开发啦╰(* °▽°* )╯,很感谢看到这里的掘友,希望本文能让你收获些东西,本文以spotify的开放API举例,不玩spotify也没关系,相信大家也能从本文了解一下OAth2的鉴权流程,码字不易,希望你喜欢🥰
今天的文章记一次OAuth2.0用户鉴权分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17942.html