前言
我们平时登录不同的平台,总会有使用到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的鉴权流程,码字不易,希望你喜欢🥰
接下来我将给各位同学划分一张学习计划表!
学习计划
那么问题又来了,作为萌新小白,我应该先学什么,再学什么?
既然你都问的这么直白了,我就告诉你,零基础应该从什么开始学起:
阶段一:初级网络安全工程师
接下来我将给大家安排一个为期1个月的网络安全初级计划,当你学完后,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web渗透、安全服务、安全分析等岗位;其中,如果你等保模块学的好,还可以从事等保工程师。
综合薪资区间6k~15k
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)
2、渗透测试基础(1周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等
3、操作系统基础(1周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)
4、计算机网络基础(1周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现
5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固
6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)
那么,到此为止,已经耗时1个月左右。你已经成功成为了一名“脚本小子”。那么你还想接着往下探索吗?
阶段二:中级or高级网络安全工程师(看自己能力)
综合薪资区间15k~30k
7、脚本编程学习(4周)
在网络安全领域。是否具备编程能力是“脚本小子”和真正网络安全工程师的本质区别。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中,想要高效地使用自制的脚本工具来实现各种目的,更是需要拥有编程能力。
零基础入门的同学,我建议选择脚本语言Python/PHP/Go/Java中的一种,对常用库进行编程学习
搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP,IDE强烈推荐Sublime;
Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,没必要看完
用Python编写漏洞的exp,然后写一个简单的网络爬虫
PHP基本语法学习并书写一个简单的博客系统
熟悉MVC架构,并试着学习一个PHP框架或者Python框架 (可选)
了解Bootstrap的布局或者CSS。
阶段三:顶级网络安全工程师
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
学习资料分享
当然,只给予计划不给予学习资料的行为无异于耍流氓,这里给大家整理了一份【282G】的网络安全工程师从入门到精通的学习资料包,可点击下方二维码链接领取哦。

习资源包免费分享!](https://mp.weixin.qq.com/s/BWb9OzaB-gVGVpkm161PMw)
学习资料分享
当然,只给予计划不给予学习资料的行为无异于耍流氓,这里给大家整理了一份【282G】的网络安全工程师从入门到精通的学习资料包,可点击下方二维码链接领取哦。
