Spring Security技术栈学习笔记(十三)Spring Social集成第三方登录验证开发流程介绍

开发第三方登录,我们必须首先要了解OAuth协议(本文所讲述的OAuth协议指的是OAuth2协议),本文首先简单介绍OAuth协议,然后基于Spring Social来阐述开发第三方登录需要做哪些准备工作。

一、OAuth协议简介
OAuth协议诞生背景

举一个场景例子,有一个第三方服务提供方,可以给图片提供美化的服务,而你的图片存储在百度云盘上,那么如何做到使第三方服务提供者对你的图片进行美化呢?传统的做法是将自己的百度云盘账户和密码授权给第三方服务提供者,他登录到百度云盘以后对你的图片进行美化,但是这么做有很多隐患,如下列表所示:

  • 第三方服务提供者为了后续的持续服务,会保存自己的百度云盘账号和密码信息;
  • 第三方服务提供者获得了账户和密码,就拥有了和用户相同的权限,用户的百度云盘所有的信息都将暴露给第三方;
  • 第三方服务提供者的服务器一旦被黑客攻破,那么用户将面临信息泄露的危险;
  • 用户只有通过修改百度云盘的密码才能收回对第三方应用的授权,那么这样将所有的第三方的授权全部收回,不利于某些服务的正常进行。

OAuth的诞生就是为了解决上面的问题,它从另一个角度实现了免密授权,大大提高了用户隐私信息的安全性。开放授权(OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。

OAuth的整体设计思路是在“客户端”与“服务提供商”之间,设置了一个授权层(authorization layer)。“客户端”不能直接登录“服务提供商”,只能登录授权层,以此将用户与客户端区分开来。“客户端”登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。“客户端”登录授权层以后,“服务提供商”根据令牌的权限范围和有效期,向“客户端”开放用户储存的部分资料。

OAuth协议中的角色关系

这里贴出一张OAuth协议的角色关系图,我们根据图来描述各个角色之间的关系与执行原理。

  • Third-party application:第三方应用程序,本文中又称“客户端”(client),即上一节例子中的第三方服务提供商。
  • HTTP service providerHTTP服务提供商,本文中简称“服务提供商”,即上一节例子中的百度云盘。
  • Resource Owner:资源所有者,本文中又称“用户”(user)。
  • User Agent:用户代理,本文中就是指浏览器。
  • Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
  • Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。

描述了角色关系之后,我们一起来探讨一下OAuth协议的执行流程:

  • 用户打开客户端以后,客户端要求用户给予授权。
  • 用户同意给予客户端授权。
  • 客户端使用上一步获得的授权,向认证服务器申请令牌。
  • 认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
  • 客户端使用令牌,向资源服务器申请获取资源。
  • 资源服务器确认令牌无误,同意向客户端开放资源。
OAuth授权模式

OAuth2.0协议提供了四种授权模式,它们分别是:

  • 授权码模式(authorization code
  • 简化模式(implicit
  • 密码模式(resource owner password credentials
  • 客户端模式(client credentials

其中,授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与“服务提供商”的认证服务器进行互动。授权码模式流程图如下所示:

授权码模式的基本步骤如下:

  • 用户访问客户端,客户端将用户导向认证服务器。
  • 认证服务器向客户端发送授权询问请求,用户选择是否给予客户端授权。
  • 假设用户给予授权,认证服务器将用户导向客户端事先指定的“重定向URI”(redirection URI),同时附上一个授权码。
  • 客户端收到授权码,附上早先的“重定向URI”,向认证服务器申请令牌。注意:这一步是在客户端的后台的服务器上完成的,对用户不可见,用户是无感的。
  • 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了“授权码”这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
简化模式的基本步骤如下:

  • 客户端将用户导向认证服务器。
  • 用户决定是否给于客户端授权。
  • 假设用户给予授权,认证服务器将用户导向客户端指定的“重定向URI”,并在URIHash部分包含了访问令牌。
  • 浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
  • 资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
  • 浏览器执行上一步获得的脚本,提取出令牌。
  • 浏览器将令牌发给客户端。

密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向“服务商提供商”索要授权。在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。
密码模式的基本步骤如下:

  • 用户向客户端提供用户名和密码。
  • 客户端将用户名和密码发给认证服务器,向后者请求令牌。
  • 认证服务器确认无误后,向客户端提供访问令牌。

客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。
客户端模式的基本步骤如下:

  • 客户端向认证服务器进行身份认证,并要求一个访问令牌。
  • 认证服务器确认无误后,向客户端提供访问令牌。

这里对OAuth协议的简单介绍参考了阮一峰的网络日志中的一篇文章《理解OAuth2.0》,该文章详细介绍了协议中信息传输的相关参数,感兴趣的可以去学习一下,这里仅仅是对OAuth协议进行简要介绍。

二、Spring Social简介

Spring Security认证成功的标准是在SecurityContext中存储了用户相关的Authentication实例对象,也就是说当一个用户提供了正确信息给系统,系统带着用户的信息完成了一系列的校验后,校验通过后,将生成用户信息相关的Authentication存储到SecurityContext中。那么如果使用第三方登录,使用第三方如QQ、微信的用户信息进行登录验证,这是如何做到的呢?道理也是一样的,就是拿到第三方的用户信息,使用第三方的用户信息构建Authentication实例对象,并存储到SecurityContext中,而在获取第三方用户信息的时候,必须遵循OAuth协议,OAuth协议规定的流程,必须严格执行,那么Spring Social的作用就凸显了,它诞生的一个很大作用就是封装了OAuth协议规定的基础流程。

Spring Social已经将前五步封装了,开发者开发特定第三方登录验证,只需要实现第六步和第七步即可,最后组成一个SocialAuthenticationFilter集成到整个验证的过滤器链上,当用户选择第三方登录的时候,就会被该过滤器拦截,在过滤器中获取第三方用户信息,构建用户信息相关的Authentication实例对象并存储到SecurityContext中。

具体的开发原理和流程,我们一起跟着下面的图片,来一步一步讲解。

我们讲解的顺序是从右往左讲,对图片上的每一个类或者接口都进行一个详细的讲解。

  • ServiceProvider:它是一个接口,在包org.springframework.social下,它的存在就是为了适配不同的第三方服务提供商,比如QQ、微信等。如果我们需要开发QQ登录,那么我们就需要为QQ提供一个特定的ServiceProvider,而这个接口下有一个抽象实现AbstractOAuth2ServiceProvider,我们为QQServiceProvider的时候只需要继承AbstractOAuth2ServiceProvider类即可。抽象类AbstractOAuth2ServiceProvider有两个属性OAuth2OperationsApi,接下来分别介绍它们的作用。
  • OAuth2Operations:它是一个接口,在包org.springframework.social.oauth2下,它封装了OAuth协议的前五步,也就是用户授权,直到应用拿到第三方应用(QQ、微信)的访问令牌,该接口有一个实现类OAuth2TemplateOAuth2Template完成了访问第三方应用认证服务器、获取授权码、携带授权码申请令牌、获取令牌等核心步骤,在这里,我们需要做的仅仅是配置一些特定第三方认证服务器的URL即可,因为整个流程是遵循OAuth协议的,所以这些核心步骤需要携带的参数都是公共的,对开发者透明的。
  • Api:在包org.springframework.social下有一个ApiBinding接口,它主要是为了帮助开发者完成第六步的一个接口,由于每一个第三方应用的用户信息都是有区别的,比如用户头像的字段,在QQ里面叫head_image,也许到了微信里面,就叫image了,所以这里是一个个性化的开发区域,需要对每一个第三方服务提供商开发一个特定的类来实现用户数据的获取,这里Spring Social提供了一个抽象类AbstractOAuth2ApiBinding,尽可能地减少我们的开发成本,我们在开发获取用户信息的代码的时候,只需要继承这个抽象类即可。

以上的三段描述将与第三方服务提供商相关类和接口介绍完毕,那么我们与第三方服务提供商交互完毕,拿到了用户数据以后,那么该如何完成认证的操作呢?那么就得继续来介绍上图左边的相关类和接口。

  • Connection:它是一个接口,在包org.springframework.social.connect下,它封装了与用户相关的信息,这些信息,比如DisplayName(显示名称),ProfileUrl(主页地址),ImageUrl(头像地址)等基本信息,这些信息是Spring Social所规定的用户信息(固定字段),它有一个实现类OAuth2Connection,也就是说我们如何将拿到的用户信息转换成OAuth2Connection所封装的用户信息呢?那么就必须得讲解一下生成Connection实现类对象的工厂ConnectionFactory
  • ConnectionFactory:它是一个接口,在包org.springframework.social.connect下,它有一个实现类OAuth2ConnectionFactory,该类就可以完成对Connection的创建,而在OAuth2ConnectionFactory的构造方法中,就用到了ServiceProviderApiAdapterServiceProvider就是我们之前为特定第三方服务提供商编写的代码,它提供了从第三方服务提供商获取用户信息,ApiAdapter是一个适配器,主要是完成了从第三方服务提供商获取到的用户信息到Spring Social规定的用户信息的转换工作,这个适配器也是需要我们自己编写的内容之一。那么有了ServiceProviderApiAdapter,就可以构建OAuth2ConnectionFactory对象,那么就可以来创建Connection的实现类对象了。
  • UserConnection:UserConnectionSpring Social规定的一张数据库表。当用户在登录系统的时候,选择的是QQ登录或者微信登录,那么系统是如何辨别该用户使用QQ号登录的时候,它对应到系统中的一个用户呢?也就是说用户使用的第三方账户A登录,系统是如何做到与业务系统里面的“张三”关联起来的呢?那么这个功劳就应该归属于UserConnection表了,它就是专门用来记录第三方账户和业务系统内的账户之间的关系的一张表。
  • UsersConnectionRepository:它是一个接口,在包org.springframework.social.connect下,它专门封装了UserConnection表的一些基础操作,他有一个默认实现类JdbcUsersConnectionRepository,在该类的包下,有一个JdbcUsersConnectionRepository.sql文件,这个文件中有创建表的语句,需要我们自己拷贝出来创建对应的数据库表,该表名可以是UserConnection,也可以在该名称之前加上一个前缀。这里贴出创建表的语句:
create table UserConnection (userId varchar(255) not null,
	providerId varchar(255) not null,
	providerUserId varchar(255),
	rank int not null,
	displayName varchar(255),
	profileUrl varchar(512),
	imageUrl varchar(512),
	accessToken varchar(512) not null,
	secret varchar(512),
	refreshToken varchar(512),
	expireTime bigint,
	primary key (userId, providerId, providerUserId));
create unique index UserConnectionRank on UserConnection(userId, providerId, rank);

这里就完成了对Spring Social集成第三方登录需要开发内容的基本介绍,我们将在本系列教程的第十四篇文章中介绍Spring Social集成QQ登录验证方式,将在第十五篇文章中介绍Spring Social集成微信登录验证方式,敬请期待。

Spring Security技术栈开发企业级认证与授权系列文章列表:

Spring Security技术栈学习笔记(一)环境搭建
Spring Security技术栈学习笔记(二)RESTful API详解
Spring Security技术栈学习笔记(三)表单校验以及自定义校验注解开发
Spring Security技术栈学习笔记(四)RESTful API服务异常处理
Spring Security技术栈学习笔记(五)使用Filter、Interceptor和AOP拦截REST服务
Spring Security技术栈学习笔记(六)使用REST方式处理文件服务
Spring Security技术栈学习笔记(七)使用Swagger自动生成API文档
Spring Security技术栈学习笔记(八)Spring Security的基本运行原理与个性化登录实现
Spring Security技术栈学习笔记(九)开发图形验证码接口
Spring Security技术栈学习笔记(十)开发记住我功能
Spring Security技术栈学习笔记(十一)开发短信验证码登录
Spring Security技术栈学习笔记(十二)将短信验证码验证方式集成到Spring Security
Spring Security技术栈学习笔记(十三)Spring Social集成第三方登录验证开发流程介绍
Spring Security技术栈学习笔记(十四)使用Spring Social集成QQ登录验证方式
Spring Security技术栈学习笔记(十五)解决Spring Social集成QQ登录后的注册问题
Spring Security技术栈学习笔记(十六)使用Spring Social集成微信登录验证方式

示例代码下载地址:

项目已经上传到码云,欢迎下载,内容所在文件夹为chapter013

更多干货分享,欢迎关注我的微信公众号:爪哇论剑(微信号:itlemon)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值