一、简介
OAuth 是一个开放标准,该标准允许用户让第三方应用访问该用户在某一网站上存储的私密资源(如头像、照片、视频等),并且在这个过租中无须将用户名和密码提供给第三方应用。通过令牌(token)可以实现这一功能,每一个令牌授权一个特定的网站在特定的时段内允许可特定的资源。0Auth让用户可以授权第三方网站灵活访问它们存储在另外一些资源服务器上的特定信息,而非所有内容。对于用户而言,我们在互联网应用中最常见的 OAuth应用就是各种第三方登录,例如QQ授权登录、微信授权登录、微博授权登录、GitHub 授权登录等。
例如用户想登录 Ruby China,传统方式是使用用户名密码但是这样并不安全,因为网站会存储你的用户名密码,这样可能会导致密码泄露。这种授权方式安全隐患很大,如果使用 0Auth 协议就能很好地解决这一问题。
注意:OAuth2 是0Auth 协议的下一版本,但不兼容 Auth 1.0。 OAuth2 关注客户端开发者的简易性,同时为 web 应用、桌面应用、移动设备IoT 设备提供专门的认证流程。
二、OAuth2 四种授权模式
0Auth2 协议一共支持四种不同的授权模式, 无论哪种授权模式,其授权流程都是相似的,只不过在个别步骤上有一些差异而已;
- 授权码模式:常见的第三方平台登录功能基本都是使用这种模式;
- 简化横式:简化模式是不需要第三方服务端参与,直接在浏览器中向授权服务器申请令牌 (token),如果网站是纯静态页面,则可以采用这种方式。
- 密码模式:密码模式是用户把用户名/密码直接告诉客户端,客户端使用这些信息后授权服务器申请令牌(token)。这需要用户对客户端高度信任,例如3客户端应用和服务提供商就是同一家公司。
- 客户端模式:客户端模式是指客户端使用自己的名义而不是用户的名义向服务提估者申请授权。严格来说,客户诺模式并不能算作0Auth协议解决问题的种解决方案,但是对于开发者而言,在一些为移动端提供的授权服务器上使用这种模式还是非常方便的。
-(A) 用户打开客户端以后,客户端要求用户给予授权。
-(B) 用户同意给予客户端授权。
-(C) 客户端使用上一步获得的授权,向认证服务器申请令牌。
-(D) 认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
-(E) 客户端使用令牌,向资源服务器申请获取资源。
-(F) 资源服务器确认令牌无误,同意向客户端开放资源。
从上图中我们可以看出六个步骤之中,B是关键,即用户怎样才能给于客户端授权。同时会发现 OAuth2 中包含四种不同的角色:
。 client : 第三方应用。
。 Resource 0wner: 资源所有者。
。 Authorization Server: 授权服务器。
。 Resource Server: 资源服务器。
2.1 授权码模式
授权码模式(Authorization code) 是功能最完整、流程最严密、最安全并且使用最广泛的一种0Auth2 授权模式。同时也是最复杂的一种授权模式,它的特点就是通过客户端的后台服务器,与服务提供商的认证服务器进行互动;
Third-party application:第三方应用程序,简称"客户端”(client);
。Resource 0wner:资源所有者,简称"用户” (user) ;
。User Aqent:用户代理,是指浏览器;
。Authorization Server:认证服务器,即服务端专门用来处理认证的服务器;
。Resource Server;资源服务器,即服务端存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器
流程图连接:RFC 6749 - The OAuth 2.0 Authorization Framework
具体流程如下:
(A) 用户访问第三方应用,第三方应用通过浏览器导向认证服务器。
(B) 用户选择是否给予客户端授权。
(C) 假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI”(redirection URI),同时附上一个授权码。
(D) 客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
(E) 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌 (access token)和更新牌(refresh token)
2.1.1 核心参数
https://wx.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=http://www.baidu.com&scope=read
2.1.2 参数详解
client_id | 授权服务器注册应用后的唯一标识 |
response_type | 必须 固定值 在授权码中必须为 code |
redirect_uri | 必须 通过客户端注册的重定向URL |
scope | 必须 令牌可以访问资源权限 read 只读 all 读写 |
state | 可选 存在原样返回客户端 用来防止 CSRF跨站攻击 |
2.2 简化模式
简化模式(simple grant type) 不通过第三方应用程序的服务器,直接在测览器中向认证服务器申请令牌,跳过了"授权码"这个步,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
其具体的授权流程如图所示:
(A) 第三方应用将用户导向认证服务器
(B) 用户决定是否给于客户端授权。.
(C) 假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。#token.
(D) 浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。.
(E) 资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
(F) 浏览器执行上一步获得的脚本,提取出令牌。
(G) 浏览器将令牌发给客户端。
2.2.1 核心参数
https://wx.com/oauth/authorize?response_type=token&client_id=CLIENT_ID&redirect_uri=http://wmw.baidu.com&scope=read
2.2.2 参数详解
字段 | 描述 |
---|---|
client_id | 授权服务器注册应用后的唯一标识 |
response_type | 必须 固定值 在授权码中必须为token |
redirect_uri | 必须 通过客户端注册的重定向URL |
scope | 必须 令牌可以访问资源权限 read 只读 all 读写 |
state | 可选 存在原样返回客户端 用来防止 CSRF跨站攻击 |
2.3 密码模式
密码模式(Resource 0wner Password Credentias grant)中,用户向客户端提供自己的用户各和密码。客户端使用这些信息,向“服务商提供两"索要授权。在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情下,比如客户端是操作系统的一部分,或者由一个相同公司出品。而认证服务图只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。
其具体的授权流程如图所示
具体步骤如下:
(A) 用户向客户端提供用户名和密码。
(B) 客户端将用户名和密码发给认证服务器,向后者请求令牌。
(C) 认证服务器确认无误后,向客户端提供访间令牌。
2.3.1 核心参数
https://wx.com/token?grant_type=password&username-USERNAME&password=PASSWORD&client_id=CLIENT_ID
2.4 客户端模式
客户端模式(Client Credentials Grant) 指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于0Auth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求”服务提供商“提供服务,其实不存在授权问题。
授权流程图如下
具体步骤如下:
(A) 客户端向认证服务器进行身份认证,并要求一个访问令牌。
(B) 认证服务器确认无误后,向客户端提供访问令牌。
2.4.1 核心参数
https://wx.com/token?grant_type=client_credentials&client_secret=CLIENT_SECRET&client_id=CLIENT_ID
三、OAuth2 标准接口
。 /oauth/authorize: 授权端点
。 /oauth/token: 获取令牌端点
。/oauth/confirm_access: 用户确认授权提交端点
。/oauth/error: 授权服务错误信息端点
。/oauth/check_token: 用于资源服务访问的令牌解析端点
。/oauth/token_key: 提供公有密匙的端点,如果使用JWT令牌的话
四、案例实践
使用 gitee 授权登录,登录成功后访问 hello接口,获取授权后的信息
4.1 OAuth2结合Gitee授权案例
打开引用设置:https://gitee.com/oauth/applications
1、进入第三方应用
2、创建应用,填写回调的地址以及主页地址
4.2 后端配置
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login();// 使用oauth2 认证,配置从配置文件中读取
}
}
导入依赖
--所需的依赖
<!-- oauth2 客户端 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
配置 yml 文件将gitee 加入provider 中
如果不加的话,启动会报 provider Id not found 相当于要明确指出来
spring:
security:
oauth2:
client:
registration:
gitee:
provider: gitee
client-id: 83f45xxxf29198ff68ada42f8
client-secret: 61e7fe7502a0fd162exx429542f739c9ba6322
# 重定向的url地址,这个地址为默认的
redirect-uri: http://localhost:8082/login/oauth2/code/gitee
authorization-grant-type: "authorization_code"
client-name: gitee
#不配置这个会报 gitee找不到
provider:
gitee:
authorization-uri: https://gitee.com/oauth/authorize
token-uri: https://gitee.com/oauth/token
user-info-uri: https://gitee.com/api/v5/user
user-name-attribute: "name"
4.3 测试controller
用于授权成功后,获取oauth2的用户信息
@RestController
public class HelloController {
/**
* 获取认证后的用户信息
* @return
*/
@RequestMapping("hello")
public DefaultOAuth2User hello() {
System.out.println("oauth2用户信息获取");
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return (DefaultOAuth2User)authentication.getPrincipal();
}
}
4.4 授权效果
当我们启动项目后,访问 8082接口,自动给我们定向到 gitee授权页面,我们同意后,就会跳到我们配置的主页面了;
4.5 同时开启表单登录
当我们启动项目后,会自动进入到授权页面了,但是有时候我们需要在登录页面加上 gitee的入口,当用户点击后,我们才开始进行授权的操作,这个该怎么做呢,其实我们开启我们的表单认证就好了;
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()//开启表单登录
.and()
.oauth2Login();// 使用oauth2 认证,配置从配置文件中读取
五、授权过滤器源码查看
我们肯定在疑问,我们项目里面并没有/login/oauth2/code 这个接口,为什么能访问呢,我们自己加上了 gitee如何就能收到对应的授权呢;
接下来我们来看 OAuth2LoginAuthenticationFilter 源码
5.1 源码入口
org.springframework.security.config.annotation.web.builders.HttpSecurity#oauth2Login()
org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer
org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter
DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";
5.2 attemptAuthentication() 方法
attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
org.springframework.security.authentication.AuthenticationManager#authenticate 这一步就是请求 gitee的认证服务器
5.3 OAuth2AuthenticationToken
当我们通过Oauth2认证成功后,系统会返回一个OAuth2AuthenticationToken对象,同事会将用户信息包装一个 OAuth2User ,对应的实现为DefaultOAuth2User
六、spring security OAuth2 杂谈
该文献参考 哔站 编程不良人 ,有需求可以关注他
Spring Security 对 0Ath2 提供了很好的支持,这使得我们在 Spring Security中使用 0Auth2 非常地方便。然而由于历史原因,SoringSeaurity对0Auth2的支持比较混乱,这里简单梳理一下。
大约十年前,Spring 引入了一个社区驱动的开源项目 Spring Security 0Auth,并将其纳入 Spring 项目组合中到今天为止,这个项目己经发展成为一个成熟的项目,可以支持大部分0Auth 规范,包括资源服务器、 客户端和授权服务器等。
然而早期的项目存在一些问题,例如:
0Auth 是在早期完成的,开发者无法预料未来的变化以及这些代码到底要被怎么使用.这导致很多 Spring 项目提供了自己的 0Auth 支持,也就带来了 0Auth 支持的碎片化。最早的0Auth项目同时支特 0Auth1. 和 0Auth2.0,而现在0Autb1 早已经不再使用可以放弃了。
现在我们有更多的库可以选择,可以在这些库的基础上去开发,以便更好地支持JWT等新技术。基于以上这些原因,官方决定重写 Spring ecurity 0Auth, 以便更好地协调 Spring 和Auth,并简化代码库,使Spring 的 0Auth 支持更加灵活。然而,在重写的过程中,发生了不少波折。
2018年1月30日,Sprng 官方发了一个通知,表示要逐渐停止现有的 0Ath2支持,然后在 Spring Security 5中构建下一代 Auth2.0 支持。这么做的原因是因为当时 0Auth2 的落地方案比较混乱,在 Spring Security 0Auth、Spring cloud Security、Spring Boot 1.5.x 以及当时最新的Spring Security 5.x 中都提供了对 Auth2 的实现。以至于当开发者需要使用 0Auth2 时,不得不问,到底选哪一个依赖合适呢?所以Spring 官方决定有必要将 Auth2.0 的支持统一到一个项目中,以便为用户提供明确的选择,并避免何潜在的混乱,同时 OAuth20的开发文档也要重新编写,以方便开发人员学习。所有的决定将在 pring Security 5 中开始,构建下一代 Ath2的支持。从那个时候起,Spring Security0Auth项目就正式处于维护模式。官方将提供至少一年的错识/安全修复程序,并且会考虑添加次要功能,但不会添加主要功能。同时将 Spring Security 0Auth中的所有功能重构到 Spring Security 5x中
到了2019年11月14日,Spring 官方又发布-个通知,这次的通知首先表示 Spring Security 0Auth 在往 Spring Security 5.x 的过程非常顺利,大都分迁程工作已经完成了,剩下的将在5.3 版本中完成迁移,在迁移的过程中还添加了许多新功能。包括对 openId Connect1.0 的支持。同时还宣布将不再支持授权服务器,不支持的原因有两个:
1、在2019年,已经有大量的商业和开源投权服务器可用,
2、授权服务器是使用一个库来构建产品,而 Spring Security 作为框架,并不适合做这件事情
一石激起千层浪,许多开发者表示对此难以接受。这件事也在Spring 社区引发了激烈的讨论,好在 Spring 官方愿意倾听来自社区的声音。
到了2020年4月15日,Spring 官方宣布启动 Spring Authorization server 项目。这是-个由 Spring ecurity 团队领导的社区驱动的项目致力于向 Spring 社区提供 Authorization Server支持,也就是说,Spring 又重新支持授权服务器了。2020年8月21日,Spring Authorization Server 0.0.1正式发布!
这就是 Auth2在Spring 家族中的发展历程了。在后面章节中,客户端和资源服务器都将采用最新的方式来构建,授权服务器依然采用旧的方式来构建因为目前的 Spring Authorization Server 0.0.1功能较少且 BUG 较多。