本文章的笔记整理来自黑马视频https://www.bilibili.com/video/BV1vt4y1i7zA?p=52,相关资料可以在该视频的评论区获取。
注:如果对 Spring Security 不是很了解的读者,可以先去阅读Spring Security——入门介绍这篇文章。
1.概念说明
OAuth 是 Open Authorization
的简写。OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 OAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 OAuth 是安全的。OAuth2.0 是 OAuth 协议的延续版本,但不向前兼容(即完全废止了 OAuth1.0)。
2.使用场景
(1)假设,A 网站是一个打印照片的网站,B 网站是一个存储照片的网站,二者原本毫无关联。如果一个用户想使用 A 网站打印自己存储在 B 网站的照片,那么 A 网站就需要使用 B 网站的照片资源才行。按照传统的思考模式,我们需要 A 网站具有登录 B 网站的用户名和密码才行,但是,现在有了 OAuth2,只需要 A 网站获取到使用 B 网站照片资源的一个通行令牌即可!这个令牌无需具备操作 B 网站所有资源的权限,也无需永久有效,只要满足 A 网站打印照片需求即可。
(2)不过 OAuth2 与单点登录又有所区别,单点登录是用户一次登录,自己可以操作其他关联的服务资源。OAuth2 则是用户给一个系统授权,可以直接操作其他系统资源的一种方式。但 SpringSecurity 的 OAuth2 也是可以实现单点登录的!
(3)总结:SpringSecurity 的 OAuth2 既可以做服务之间资源共享,也可以实现单点登录。
3.Oauth2.0 的认证流程是什么?其中包括哪些角色?
(1)Oauth2.0 的认证流程如下图所示:
(2)OAuth2 协议主要包括以下角色:
- 资源所有者 (Resource Owner):资源所有者是指拥有受保护资源的用户,即数据或服务的真正拥有者。资源所有者通过授权将访问权限委派给客户端。
- 客户端 (Client):客户端是指请求访问受保护资源的应用程序、网站或设备。它可以是第三方应用、移动应用、桌面应用等。
- 授权服务器 (Authorization Server):也称为认证服务器,授权服务器负责认证资源所有者,并颁发访问令牌给客户端。它验证客户端的身份和授权请求,并提供资源所有者进行授权的界面。
- 资源服务器 (Resource Server):资源服务器存储和管理受保护的资源。它接收来自客户端的访问请求,并根据访问令牌的有效性来判断是否授权访问资源。
- 授权许可 (Authorization Grant):授权许可是资源所有者授权给客户端访问受保护资源的凭证。OAuth2定义了多种授权许可类型,如授权码、简化授权、密码授权和客户端凭证等。
- 访问令牌 (Access Token):访问令牌是由授权服务器颁发给客户端的凭证,表示客户端被授权访问受保护资源的权限。客户端使用访问令牌来请求资源服务器获取受保护资源。
- 刷新令牌 (Refresh Token):刷新令牌是可选的,用于在访问令牌过期后获取新的访问令牌。客户端可以使用刷新令牌向授权服务器请求刷新访问令牌,以延长访问权限的有效期。
(3)这些角色在 OAuth2 协议中相互交互,实现了资源所有者的授权和客户端的受保护资源访问。不同的应用场景和授权类型可能会涉及到其中的一部分或全部角色。
4.OAuth2.0 中的四种授权方式
可以参照下面这张图来理解 OAuth2.0 中的四种授权方式。
4.1.✨授权码模式 (authorization code)
(1)流程:【A 服务客户端】需要用到【B 服务资源服务】中的资源
- 第一步:【A 服务客户端】将用户自动导航到【B 服务认证服务】,这一步用户需要提供一个回调地址,以备【B 服务认证服务】返回授权码使用。
- 第二步: 用户点击授权按钮表示让【A 服务客户端】使用【B 服务资源服务】,这一步需要用户登录 B 服务,也就是说用户要事先具有 B 服务的使用权限。
- 第三步:【B 服务认证服务】生成授权码,授权码将通过第一步提供的回调地址,返回给【A 服务客户端】。注意这个授权码并非通行【B 服务资源服务】的通行凭证。
- 第四步:【A 服务认证服务】携带上一步得到的授权码向【B 服务认证服务】发送请求,获取通行凭证 token。
- 第五步:【B 服务认证服务】给【A 服务认证服务】返回令牌 token 和更新令牌 refresh token。
(2)使用场景:授权码模式是 OAuth2 中最安全最完善的一种模式,应用场景最广泛,可以实现服务之间的调用,常见的微信,QQ 等第三方登录也可采用这种方式实现。
4.2.简化模式 (implicit)
(1)流程
说明:简化模式中没有【A 服务认证服务】这一部分,全部有【A 服务客户端】与 B 服务交互,整个过程不再有
授权码,token 直接暴露在浏览器。
- 第一步:【A 服务客户端】将用户自动导航到【B 服务认证服务】,这一步用户需要提供一个回调地址,以备【B 服务认证服务】返回token使用,还会携带一个【A 服务客户端】的状态标识state。
- 第二步: 用户点击授权按钮表示让【A 服务客户端】使用【B 服务资源服务】,这一步需要用户登录B服务,也就是说用户要事先具有B服务的使用权限。
- 第三步:【B 服务认证服务】生成通行令牌token,token将通过第一步提供的回调地址,返回给【A 服务客户端】。
(2)使用场景:适用于 A 服务没有服务器的情况。比如:纯手机小程序,JavaScript语言实现的网页插件等。
4.3.密码模式 (resource owner password credentials)
(1)流程
- 第一步: 直接告诉【A 服务客户端】自己的【B 服务认证服务】的用户名和密码
- 第二步:【A 服务客户端】携带【B 服务认证服务】的用户名和密码向【B 服务认证服务】发起请求获取 token。
- 第三步:【B 服务认证服务】给【A 服务客户端】颁发 token。
(2)使用场景
此种模式虽然简单,但是用户将 B 服务的用户名和密码暴露给了 A 服务,需要两个服务信任度非常高才能使用。
4.4.客户端模式 (client credentials)
(1)流程
说明:这种模式其实已经不太属于 OAuth2 的范畴了。A 服务完全脱离用户,以自己的身份去向 B 服务索取 token。换言之,用户无需具备 B 服务的使用权也可以。完全是A服务与B服务内部的交互,与用户无关了。
- 第一步: A 服务向 B 服务索取 token。
- 第二步: B 服务返回 token 给 A 服务。
(2)使用场景:A 服务本身需要 B 服务资源,与用户无关。
5.OAuth2.0 中表结构说明
如果只是写个测试案例,完全可以不用连接数据库,直接将用户等信息写在项目中就行。但是,如果在企业开发中,试想,我们自己做的一个软件,想使用微信第三方登录。难道你还指望微信去修改他们的代码,让我们去访问?想都别想!那么微信会怎么做呢?微信会提供好一个接入的入口,让我们自己去申请访问权限。这些数据自然而然需要保存在数据库中!所以,下面将直接讲解数据库版实现方式!
5.1.建表语句
/*
SQLyog Ultimate v12.08 (64 bit)
MySQL - 8.0.16 : Database - security_authority
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*Table structure for table `oauth_access_token` */
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
`token_id` varchar(255) DEFAULT NULL,
`token` longblob,
`authentication_id` varchar(255) DEFAULT NULL,
`user_name` varchar(255) DEFAULT NULL,
`client_id` varchar(255) DEFAULT NULL,
`authentication` longblob,
`refresh_token` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `oauth_access_token` */
/*Table structure for table `oauth_approvals` */
DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals` (
`userId` varchar(255) DEFAULT NULL,
`clientId` varchar(255) DEFAULT NULL,
`scope` varchar(255) DEFAULT NULL,
`status` varchar(10) DEFAULT NULL,
`expiresAt` datetime DEFAULT NULL,
`lastModifiedAt` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `oauth_approvals` */
/*Table structure for table `oauth_client_details` */
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` varchar(255) NOT NULL,
`resource_ids` varchar(255) DEFAULT NULL,
`client_secret` varchar(255) DEFAULT NULL,
`scope` varchar(255) DEFAULT NULL,
`authorized_grant_types` varchar(255) DEFAULT NULL,
`web_server_redirect_uri` varchar(255) DEFAULT NULL,
`authorities` varchar(255) DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` varchar(255) DEFAULT NULL,
`autoapprove` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `oauth_client_details` */
/*Table structure for table `oauth_client_token` */
DROP TABLE IF EXISTS `oauth_client_token`;
CREATE TABLE `oauth_client_token` (
`token_id` varchar(255) DEFAULT NULL,
`token` longblob,
`authentication_id` varchar(255) DEFAULT NULL,
`user_name` varchar(255) DEFAULT NULL,
`client_id` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `oauth_client_token` */
/*Table structure for table `oauth_code` */
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
`code` varchar(255) DEFAULT NULL,
`authentication` varbinary(2550) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `oauth_code` */
/*Table structure for table `oauth_refresh_token` */
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(255) DEFAULT NULL,
`token` longblob,
`authentication` longblob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `oauth_refresh_token` */
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
5.2.表字段说明
5.2.1.oauth_client_details【核心表】
5.2.2.oauth_client_token
该表用于在客户端系统中存储从服务端获取的token数据,在spring-oauth-server项目中未使用到,对oauth_client_token表的主要操作在JdbcClientTokenServices.java类中,更多的细节请参考该类。
5.2.3.oauth_access_token
5.2.4.oauth_refresh_token
在项目中,主要操作oauth_refresh_token表的对象是JdbcTokenStore.java.。(与操作oauth_access_token表的对象一样);更多的细节请参考该类。如果客户端的grant_type不支持refresh_token,则不会使用该表。
5.2.5.oauth_code
在项目中,主要操作 oauth_code 表的对象是 JdbcAuthorizationCodeServices.java,更多的细节请参考该类。 只有当 grant_type为 “authorization_code” 时,该表中才会有数据产生;其他的 grant_type 没有使用该表。