OAuth2——鉴权码模式
背景介绍
试想有这样一个场景,作为一款App开发者,你想要通过github或google等账号登录,要如何实现?
首先,你不可能直接拿到用户github的账号和密码,因为这会存在密码泄露的问题。即使用户信任你的应用,你的App与第三方账号用户密码的一致性也很难保证
其次,假设你的App在登录时,通过github进行密码验证后,直接返回用户信息可以吗?这样第一次登录是没问题的,但很显然无法保证登陆的时效性。你的App不能一直保存用户信息,长时间不操作应该能自动将用户退出;一次登录,永久有效显然也不够安全
所以,需要这样一个机制:安全地使用第三方应用的用户信息,并能够实时校验用户的有效性
OAuth2协议原理
OAuth2是OAuth协议的2.0版本,它就是一种安全的方式去授权第三方应用(你自己的应用)去获取其他应用(github)的资源(用户信息)
角色
OAuth2协议定义了以下几种角色:
User Agent:浏览器
Client:第三方应用
Resource Owner:资源所有者,即用户
Authorization Server:授权服务器,即提供第三方登录服务的服务器,如github
Resource Server:用户资源信息的服务器
流程
授权码模式是OAuth2协议流程最严密、功能最完整实现方式,其流程如下所示:

(A):用户访问客户端,客户端跳转到授权服务器提供的授权页面上
授权服务器提供的重定向URL格式如下:
https://ip:port/xxx?client_id=client_id&redict_url=redict_url
其中:redirect_uri:表示重定向URI,必选项,表示授权完成后会跳转的地址client_id:表示客户端ID,必选项,在授权服务器上进行申请
(B):用户选择是否给客户端授权
(C):用户给予授权,授权服务器将用户导向客户端事先指定的重定向地址,同时附上一个授权码(一般是重定向地址后跟上一个code参数),例如:
redirect_url&code=xxx
(D):客户端附上授权码和重定向地址,去请求Access Token(令牌)
这里如果是前后端分离项目,一般通过后端去请求令牌,因为后端往往也需要保存令牌。前端访问后端的接口,后端去请求令牌后再返回给前端,这样前后端都保存了令牌
请求令牌的接口格式如下:
https://ip:port/xxx?client_id=client_id&redict_url=redict_url&code=code&grant_type=authorization_code&secret=secret
其中:grant_type:表示使用的授权模式,必选项,此处的值固定为authorization_codecode:表示上一步获得的授权码,必选项redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致client_id:表示客户端ID,必选项secret:密钥,在授权服务器上获取
(E):授权服务器返回Access Token、Refresh Token(可选)和expire time(可选)Access Token:访问令牌,通过此令牌可以获取用户资源Refresh Token:更新令牌,通过接口调用,可以在不重新授权的情况下,获取下一个令牌expire time:表示令牌的有效时间
最安全的模式——授权码
授权码模式是OAuth2协议流程最严密安全的一种实现方式,主要体现在以下几点:
授权服务器直接提供重定向请求,用户可以在该网页上进行授权,没有密码泄露的风险
授权服务器先返回授权码,而不是令牌,可以避免令牌被窃取
即使授权码被截胡,攻击者因为没有拿不到密钥,所以无法获取令牌(密钥只在后端保存)
客户端去请求令牌时,要传入上一步的授权码和重定向地址,授权服务器会与请求授权码传入的重定向地址进行比对。当二者一致时授权服务器才认为此次请求是合法的,并传回令牌
资源服务器
资源访问
客户端根据令牌向资源服务器安全地访问资源
例如,在你的个人App中,你想要用户完成登录以后才能进行其他操作(资源的访问)。那么你个人的App后端就相当于是资源服务器,前端是客户端
在完成上述令牌的申请流程后,前端访问后端的所有接口,都需要附上令牌(Header方式)。后端通过校验令牌的合法性,从而安全地访问后端资源
令牌校验
访问资源需要校验令牌的合法性,可以想到有两种实现方式:
通过授权服务器的接口(端点)进行校验
资源服务器自己维护每个用户的令牌,每次接口调用时去判断是否有效
通过授权服务器校验
授权服务器提供接口,每次访问资源时,去调用该接口并附上令牌(Header方式)。该接口会返回令牌是否有效 通过这种方式可以很好的实现资源的安全访问(前提是要授权服务器能提供接口)
如果后端是SpringBoot开发,可以通过配置资源服务器,并设置filter chain来指定哪些接口需要校验,这里不做展开
通过资源服务器校验
当授权服务器不提供接口时,资源服务器需要自行保存令牌,并在每次必要请求时,判断令牌是否有效。常用的方式是通过redis来保存令牌