基于OAuth2.0的第三方登录

第三方登录的实质

第三方登录的实质,就是在OAuth2.0协议的基础上,利用外部的标识来获取帐号的内部标识然后完成登录的过程。
具体的讲,就是将三方的帐号绑定到产品自身的帐号上,当查询到用户第三方的帐号已经绑定了平台的某个user_id时,直接登录对应的帐号。例如利用微信QQ快捷登陆,可以不用注册APP账号,先点击跳转到微信,再点击授权进去APP中。

几个重要概念

1)外部标识
用来使用用户身份的标志,可以是用户名,手机号,邮箱等,每一个外部标识一定和一个内部标识相关联用以确定一个用户。
外部标识的作用有两个

  1. 让用户通过自己熟知且占有的外部标识来登录产品
  2. 可以通过校验外部标识来实现找回或转移数据资产

2)内部标识
即产品中用于标识用户唯一性的标志,例如user_id,必须有,不可更改且唯一,用户一般接触不到内部标识。
当一个内部标识建立后,用户所有的数据资产都会绑定到这个内部标识上。

3)user_id
一个常用的内部标识,类似你的18位身份证ID

4)app_id
用于区别不同APP的ID,具有唯一性。

5)open_id
第三方平台为了用户信息的安全,一般不会直接将用户的内部标识给到其他产品,而是选择了给一个外部标识,这个open_id就是微信给各个APP用以区分微信用户身份的外部标识。

6)union_id
不同的产品的可以使用同一个union_id来确认用户的身份。

7)access_token
可以理解为通行证,有了这个通行证,就能获取到第三方平台指定用户的有限信息。

8)OAuth 2.0
OAuth2.0就是客户端和认证服务器之间由于相互不信任而产生的一个授权协议,只要授权方和被授权方遵守这个协议去写代码提供服务,那双方就是实现了OAuth2.0模式。

为什么需要加入第三方登录

  1. 提高登录转化率,登录更加快捷,不需要输入密码
  2. 提高注册转化率,注册更加快速,方便获取用户信息
  3. 信赖感(让用户觉得这个产品和大厂是有合作的,提高对产品的信赖感)

OAuth2.0协议规范流程

在这里插入图片描述
(1) Client请求RO的授权,请求中一般包含:要访问的资源路径,操作类型,Client的身份等信息。
(2) RO批准授权,并将“授权证据”发送给Client。至于RO如何批准,这个是协议之外的事情。典型的做法是,AS提供授权审批界面,让RO显式批准。这个可以参考下一节实例化分析中的描述。
(3) Client向AS请求“访问令牌(Access Token)”。此时,Client需向AS提供RO的“授权证据”,以及Client自己身份的凭证。
(4) AS验证通过后,向Client返回“访问令牌”。访问令牌也有多种类型,若为bearer类型,那么谁持有访问令牌,谁就能访问资源。
(5) Client携带“访问令牌”访问RS上的资源。在令牌的有效期内,Client可以多次携带令牌去访问资源。
(6) RS验证令牌的有效性,比如是否伪造、是否越权、是否过期,验证通过后,才能提供服务。

最典型的Authorization Code 授权模式

在这里插入图片描述
核心思想:
oauth 的核心思想就是要让第三方在不知道用户名密码的情况下完成鉴权,但是没有密码用户名组合根本不可能有效鉴权, oauth 实际的过程是一个李代桃疆的手法。在第一方用你的原始用户名和密码组合,生成另外一对名称密码组合,这个阶段叫做获取 code 和 state,这对组合送到第二方也就是你的资源所在地,同样较验一遍,如果合格,给你生成一个带有时效性的 access token, 第三方在有效期内拿着这个 access token 跳过第一方直接请求第二方的资源,至于为什么不直接返回 access token?
是因为如果使用 code 方式的话,服务器获得用户授权后通过 302 跳转到你的 callback URI 上,并在 url query 上带上用于交换 accesd token 的 code ,你在浏览器地址栏就可以看到这个code ,已经暴露有可能被不法应用,所以在 url 上直接返回 access token 是不安全的,而client拿到code以后换取access token是client后台对认证服务器的访问,并且需要clientID和client secret,不依赖浏览器,access token不会暴露出去。

为何引入authorization_code?

因为单从OAuth2.0的授权过程来看,如果直接返回access_token,协议将变得更加简洁,而且少一次Client与AS之间的交互,性能也更优,其实不然。引入authorization_code有很多妙处,主要原因如下:

  1. 浏览器的redirect_uri是一个不安全信道,此方式不适合于传递敏感数据(如access_token),会显著扩大access_token被泄露的风险。
    但authorization_code可以通过redirect_uri方式来传递,是因为authorization_code并不像access_token一样敏感。
    即使authorization_code被泄露,攻击者也无法直接拿到access_token,因为拿authorization_code去交换access_token是需要验证Client的真实身份。

  2. 由于协议需要验证Client的身份,如果不引入authorization_code,这个Client的身份认证只能通过第1步的redirect_uri来传递。同样由于redirect_uri是一个不安全信道,这就额外要求Client必须使用数字签名技术来进行身份认证,而不能用简单的密码或口令认证方式。
    引入authorization_code之后,AS可以直接对Client进行身份认证(见步骤4和5),而且可以支持任意的Client认证方式(比如,简单地直接将Client端密钥发送给AS)。

OAuth 协议设计不同于简单的网络安全协议的设计,因为OAuth需要考虑各种Web攻击,比如CSRF (Cross-Site Request Forgery), XSS (Cross Site Script), Clickjacking。
在redirect_uri中引入state参数就是从浏览器安全角度考虑的,有了它就可以抵制CSRF攻击。

第三方登录之GitHub实践

开发之前,需要前往第三方登录的开发者平台QQ、新浪微博、Github,注册账号并填写信息申请接入,成功后会给你一个ID和秘钥,以后你就通过该ID和秘钥来获取令牌,从而实现第三方登录。申请ID和秘钥时Github不需要审核,所以本文主要介绍GitHub实践。QQ、微信、微博等需要审核,但流程和原理都是一样的。

Register a new OAuth application

登陆个人GitHub账号,进入【Settings】->选择【applications】->选择【Developer applications】-> 【Register a new OAuth application】

填写必要信息

注册之后会得到 github提供的client id和client secret

以Github为例,其相应的API地址分别为:
https://github.com/login/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&state={state}
http://localhost:8888/oauth/github/callback?code={code}&state={state}
https://github.com/login/oauth/access_token
https://api.github.com/user?access_token={access_token}
如果要写

现在,我们需要做下列事情:

  • 在首页显示Github的授权链接,使用户能够访问authorizationUrl
  • 编写一个Controller,处理http://localhost:8888/oauth/github/callback?code={code}&state={state}的请求,主要是拿到code
  • 用code访问http://localhost:8888/oauth/github/callback?code={code}&state={state}access
    token
  • 然后利用access token访问https://api.github.com/user?access_token={access_token},拿到用户信息

引入必要的依赖,就可以开发了,前期代码写的有些繁琐,后来发现巧妙使用配置文件application.yml中会更加简洁,因为Spring已经整合好了整个Oauth2.0流程,可以减少了很多代码。
逻辑代码就不具体展示了,重中之重的配置文件application.yml内容如下

github-base-url: https://github.com/login

spring:
	security:
		oauth2:
			client:
				registration:
					github:
						client-id: 4255eebca50558bd0579(Your client-id)
						client-secret: XXXXX(Your client-secret)
						authorizationGrantType: authorization_code
						redirect_uri_template: "{baseUrl}/login/oauth2/code/{registrationId}"
						clientName: github-client
				provider:
					github:
						token-uri: ${github-base-url}/oauth/access_token
						authorization-uri: ${github-base-url}/oauth/authorize
						user-info-uri: https://api.github.com/user
server:
port: 8888

GitHub完成授权验证的大致流程

在这里插入图片描述
1.用户点击github登录本地应用引导用户跳转到第三方授权页
跳转地址:https://github.com/login/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&state={state}

client_id,client_secret是注册好Oauth APP后github提供的,需要写在本地代码或者配置文件中,state也是在本地生成的。redirect_uri 就是在GitHub官网填的Authorization callback URL。

此时带着state等参数去申请授权,但此时尚未登陆,未能通过authorize,GitHub返回code参数。

2.授权成功后会重定向带参数访问上面的redirect_uri,并多了一个code参数
后台接收code这个参数,我们带着这个code再次访问github 地址
https://github.com/login/oauth/access_token?client_id=xxx&client_secret=xxx&code=xxx&redirect_uri=http://127.0.0.1:8080/cqput-bbs/User/RegisteredByGithub.do

3.通过state参数和code参数,成功获取access_token
有了access_token,只需要把access_token参数放在URL后面即可,就可以换取用户信息了。
访问地址:https://api.github.com/user?access_token=xxx;

4.得到github授权用户的个人信息,就表明授权成功

整个流程类似于下图,放在这里方便大家理解。
在这里插入图片描述

总结

本文主要是结合自己所学知识,和众多参考资料,探讨了自己对OAuth2.0原理和第三方登录实质的一些理解,完成了第三方登录之GitHub的实践。
希望能对大家有所帮助~ 有问题欢迎留言交流,不足之处还请多多指正。