《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
-
4.4 客户端模式
-
4.5 如何选择?
-
4.6 为什么有 Client 编号和密码
-
5. 刷新令牌
-
5.1 获取刷新令牌
-
5.2 “刷新”访问令牌
-
5.3 为什么需要有刷新令牌
-
6. 删除令牌
-
6.1 删除访问令牌
-
6.2 删除刷新令牌
-
6.3 RFC7009 - OAuth2 Token Revocation
-
7. 令牌元数据
-
666. 彩蛋
1. 概述
==========
本文,我们来入门 Spring Security OAuth2.0 的使用。通过本文,希望你对 OAuth2.0 有一次身临其境的感受。
另外,这是一篇入门的文章,所以实际场景下,需要做一些微调。当然,需要微调的地方,笔者会在示例中说明,以免误导。
如果你是 OAuth2.0 的萌新,建议先通读阮一峰大神的 《理解OAuth 2.0》。因为,本文不会去阐述 OAuth2.0 概念部分的内容。或者,也可以看看 《OAuth 2.0最简向导》 ,比较生动形象。
阅读完本文后,你想要更加深入的理解 OAuth2.0 ,可以阅读如下两本书籍:
-
《OAuth2 in Action》 重原理
-
《OAuth2 2.0 Cookbook》 重实践,基于 Spring Security OAuth2 。
阅读完本文后,你想要了解源码,可以阅读老徐的两篇文章:
-
《Re:从零开始的Spring Security OAuth2(二)》
-
《Re:从零开始的Spring Security OAuth2(三)》
OK,一波安利之后,我们来一起进入正文。对于 Spring Security OAuth2 的配置,大体来说,就是两步:
-
配置授权服务器( AuthorizationServer )
-
配置资源服务器( ResourceServer )
2. 引入 Spring Security OAuth2 依赖
====================================
在 pom.xml 文件中,引入如下:
org.springframework.boot
spring-boot-starter-parent
1.5.16.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
org.springframework.security.oauth
spring-security-oauth2
因为,我们使用的是 SpringBoot 的版本为 1.5.16.RELEASE ,所以使用的 Spring Security 的版本为 4.2.8.RELEASE ,Spring Security OAuth2 的版本为 2.2.0.15.RELEASE 。
3. 配置资源服务器
===============
一般情况下,资源服务器指的是,我们提供 API 的应用或服务。例如,订单服务、商品服务。考虑到让整个示例更加简单,本文先将它和授权服务器放在一个 Maven 项目中。
① 创建一个 Controller 类
/**
* 示例模块 Controller
*/
@RestController
@RequestMapping(“/api/example”)
public class ExampleController {
@RequestMapping(“/hello”)
public String hello() {
return “world”;
}
}
- 非常简单,这是一个示例模块的 Controller ,提供 /api/example/hello 接口。
② 配置资源服务器
// 资源服务配置
@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 对 “/api/**” 开启认证
.anyRequest()
.authenticated()
.and()
.requestMatchers()
.antMatchers(“/api/**”);
}
}
-
@Configuration 注解,保证 OAuth2ResourceServer 能够被 SpringBoot 扫描到配置。
-
@EnableResourceServer 注解,开启资源服务器。
-
继承( extends ) ResourceServerConfigurerAdapter 类,并覆写 #configure(HttpSecurity http) 方法,配置对 HTTP 请求中,匹配 /api/**" 路径,开启认证的验证。
4. 配置授权服务器
===============
在 OAuth2.0 中,定义了四种授权模式:
-
授权码模式( authorization code )
-
密码模式( resource owner password credentials )
-
简化模式( implicit )
-
客户端模式( client credentials )
所以,笔者在 SpringBoot-Labs/lab-02 目录下,每一种方式,都提供了一个 Maven 项目示例。
4.1 授权码模式
Maven 项目结构如下:
Maven 项目结构
对应 GitHub 地址:
https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-02/authorization-code-server
① 配置授权服务器
// 授权服务器配置
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory() // <1>
// <2> begin …
.withClient(“clientapp”).secret(“112233”) // Client 账号、密码。
.redirectUris(“http://localhost:9001/callback”) // 配置回调地址,选填。
.authorizedGrantTypes(“authorization_code”) // 授权码模式
.scopes(“read_userinfo”, “read_contacts”) // 可授权的 Scope
// <2> end …
// .and().withClient() // 可以继续配置新的 Client // <3>
;
}
}
-
@Configuration 注解,保证 OAuth2AuthorizationServer 能够被 SpringBoot 扫描到配置。
-
@EnableAuthorizationServer 注解,开启授权服务器。
-
<1> 处,基于内存,为了方便测试。实际情况下,最好放入数据库中,方便管理。
-
<2> 处,创建一个 Client 配置。
-
<3> 处,可以使用 #and() 方法,继续添加另外的 Client 配置。
② 配置登陆账号
创建 application.properties 文件,并配置如下:
# Spring Security Setting
security.user.name=yunai
security.user.password=1024
-
这里配置了一个账号为 “yunai” ,密码为 “1024” 的登陆账户。
-
实际生产环境下,登陆账号的数据,肯定是放在数据库中。
③ 启动项目
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
启动项目
④ 获取授权码
4.1 浏览器打开
http://localhost:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://localhost:9001/callback&response_type=code&scope=read_userinfo
-
client_id 参数,必传,为我们在 OAuth2AuthorizationServer 中配置的 Client 的编号。
-
redirect_url 参数,可选,回调地址。当然,如果 client_id 对应的 Client 未配置 redirectUris 属性,会报错。
-
response_type 参数,必传,返回结果为授权码。
-
scope 参数,可选,申请授权的 Scope 。如果多个,使用逗号分隔。
-
state 参数,可选,表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。
-
未在上述 URL 中体现出来。
4.2 浏览器打开后,效果如下:
浏览器
-
输入在 「② 配置登陆账号」 中配置的登陆账号 “yunai” / “1024” 。
-
实际生产情况下,我们以 QQ 三方登陆作为例子,如下图:
- QQ 示例
4.3 登陆成功,选择允许所有申请的 Scope ,点击【Authorize】按钮,确认授权。如下图:
Authorize
4.4 授权完成,回调 redirect_uri 地址。如下图所示:
回调地址
- code 参数,就是返回的授权码。
⑤ 获取访问令牌
curl -X POST --user clientapp:112233 http://localhost:8080/oauth/token -H “content-type: application/x-www-form-urlencoded” -d “code=UydkmV&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A9001%2Fcallback&scope=read_userinfo”
-
–user clientapp:112233 处,填写我们在 OAuth2AuthorizationServer 中配置的 Client 的编号和密码。
-
code=UydkmV 处,填写在 「④ 获取授权码」 中获取的授权码( code ) 。
返回结果示例如下:
{
“access_token”: “e60e41f2-2ad0-4c79-97d5-49af38e5c2e8”,
“token_type”: “bearer”,
“expires_in”: 43199,
“scope”: “read_userinfo”
}
-
access_token 属性,访问令牌。非空。
-
token_type 属性,令牌类型,可以是 “bearer” 或 “mac” 类型。非空。
-
expires_in 属性,过期时间,单位为秒。一般情况下,非空。
-
scope 属性,权限范围。如果与 Client 申请的范围一致,此项可省略。
-
refresh_token 属性,刷新令牌,用来获取下一次的访问令牌。
-
在授权码模式下,允许为空。
可能有部分胖友是 Windows 电脑,可以参考 《windows(64位)下使用 curl 命令》 来安装一个 curl 命令。
当然,如果胖友使用 Postman ,可以参看如下两图:
- 图 1
- 图 2
⑥ 调用资源服务器的 API
curl -X GET http://localhost:8080/api/example/hello -H “authorization: Bearer e60e41f2-2ad0-4c79-97d5-49af38e5c2e8”
- authorization: Bearer e60e41f2-2ad0-4c79-97d5-49af38e5c2e8 处,填写指定的访问令牌类型和访问令牌。例如此处分别为,“Bearer”、“e60e41f2-2ad0-4c79-97d5-49af38e5c2e8” 。
如果胖友使用 Postman ,可以参看如下图:
- 图
4.2 密码模式
Maven 项目结构如下:
Maven 项目结构
对应 GitHub 地址:
https://github.com/YunaiV/SpringBoot-Labs/tree/f8d701cbd9b2a4f2cee3a7f2186148bcdf859895/lab-02/resource-owner-password-credentials-server
① 配置授权服务器
// 授权服务器配置
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
// 用户认证
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(“clientapp”).secret(“112233”) // Client 账号、密码。
.authorizedGrantTypes(“password”) // 密码模式
.scopes(“read_userinfo”, “read_contacts”) // 可授权的 Scope
// .and().withClient() // 可以继续配置新的 Client
;
}
}
-
配置 Client 的方式,和【授权码模式】基本一致。差别在于:
-
无需配置 `redirectUris` 属性,因为不需要回调地址。
-
配置授权模式为【密码模式】。
-
另外,需要引入 AuthenticationManager 来支持【密码模式】,否则会报 “Resolved [error=“unsupported_grant_type”, error_description=“Unsupported grant type: password”]” 异常。
② 配置登陆账号
和【授权码模式】一致。
③ 启动项目
和【授权码模式】一致。
④ 获取访问令牌
curl -X POST --user clientapp:112233 http://localhost:8080/oauth/token -H “accept: application/json” -H “content-type: application/x-www-form-urlencoded” -d “grant_type=password&username=yunai&password=1024&scope=read_userinfo”
-
和【授权码模式】差异比较大。
-
直接请求 oauth/token 接口,获得访问令牌。
-
请求参数带上了 username 和 password ,就用户的登陆账号和密码。
-
请求参数 grant_type 为 password ,表示【密码模式】。
返回结果示例如下:
{
“access_token”: “68de6eb9-5672-4e47-a3e6-110404285ba9”,
“token_type”: “bearer”,
“expires_in”: 43199,
“scope”: “read_userinfo”
}
- 和【授权码模式】一致。
⑤ 调用资源服务器的 API
和【授权码模式】一致。
4.3 简化模式
Maven 项目结构如下:
Maven 项目结构
对应 GitHub 地址:
https://github.com/YunaiV/SpringBoot-Labs/tree/f8d701cbd9b2a4f2cee3a7f2186148bcdf859895/lab-02/implicit-server
① 配置授权服务器
// 授权服务器配置
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(“clientapp”).secret(“112233”) // Client 账号、密码。
.redirectUris(“http://localhost:9001/callback”) // 配置回调地址,选填。
.authorizedGrantTypes(“implicit”) // 授权码模式
.scopes(“read_userinfo”, “read_contacts”) // 可授权的 Scope
// .and().withClient() // 可以继续配置新的 Client
;
}
}
- 和【授权码模式】基本一致。差别仅仅在于:配置授权模式为【简化模式】。
FROM 《理解 OAuth 2.0》
简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
② 配置登陆账号
和【授权码模式】一致。
③ 启动项目
和【授权码模式】一致。
④ 获取授权码
4.1 浏览器打开
http://localhost:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://localhost:9001/callback&response_type=implicit&scope=read_userinfo
- 和【授权码模式】基本一致。差别仅仅在于:请求参数 response_type 为 “implicit” 简化模式。
4.2 浏览器打开后,效果如下:
浏览器
- 和【授权码模式】基本一致,输入在 「② 配置登陆账号」 中配置的登陆账号 “yunai” / “1024” 。
4.3 登陆成功,直接授权完成,回调 redirect_uri 地址。如下图所示:
浏览器
-
和【授权码模式】基本不一致的有两点:
-
登陆成功后,无需选择允许所有申请的 Scope ,直接授权完成。
-
返回的不是授权码,而是访问令牌。
总的来说,【简化模式】是【授权码模式】的简化模式。
⑤ 调用资源服务器的 API
和【授权码模式】一致。
4.4 客户端模式
Maven 项目结构如下:
Maven 项目结构
对应 GitHub 地址:
https://github.com/YunaiV/SpringBoot-Labs/tree/f8d701cbd9b2a4f2cee3a7f2186148bcdf859895/lab-02/client-credentials-server
① 配置授权服务器
和【密码模式】一致。
② 配置登陆账号
它无需配置登陆账号。因为它没有用户的概念,直接与授权服务器交互,通过 Client 的编号( client_id )和密码( client_secret )来保证安全性。
③ 启动项目
和【密码模式】一致。
④ 获取访问令牌
curl -X POST “http://localhost:8080/oauth/token” --user clientapp:112233 -d “grant_type=client_credentials&scope=read_contacts”
-
和【密码模式】基本一致,差别如下:
-
请求参数无需带上了 `username` 和 `password` 。
-
请求参数 `grant_type` 为 `client_credentials` ,表示【密码模式】。
返回结果示例如下:
{
“access_token”:“cb2bdfd8-18fa-4b8f-b525-10587bd672e8”,
“token_type”:“bearer”,
“expires_in”:43199,
“scope”:“read_contacts”
}
- 和【密码模式】一致。
⑤ 调用资源服务器的 API
和【密码模式】一致。
总的来说,【客户端模式】是【密码模式】的简化模式。
4.5 如何选择?
可能很多胖友,有跟笔者一样的困惑。下面笔者引用杨波老师的一张图,相信能解决我们的困扰。如下图所示:
FROM 《深度剖析 OAuth2 和微服务安全架构》
授权类型选择
当然,对于黄框部分,对于笔者还是比较困惑的。笔者认为,第三方的单页应用 SPA ,也是适合采用 Authorization Code Grant 授权模式的。例如,《微信网页授权》 :
具体而言,网页授权流程分为四步:
1、引导用户进入授权页面同意授权,获取code
2、通过code换取网页授权access_token(与基础支持中的access_token不同)
3、如果需要,开发者可以刷新网页授权access_token,避免过期
4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
所以,笔者猜测,之所以图中画的是 Implicit Grant 的原因是,受 Google 的 《OAuth 2.0 for Client-side Web Applications》 一文中,推荐使用了 Implicit Grant 。
当然,具体使用 Implicit Grant 还是 Authorization Code Grant 授权模式,没有定论。笔者,偏向于使用 Authorization Code Grant,对于第三方客户端的场景。
4.6 为什么有 Client 编号和密码
我们看到上述四种授权模式,无论是哪一种,最终调用授权服务器时,都会传递 Client 编号和密码,这是为什么呢?通过 Client 编号和密码,授权服务器可以知道调用的来源以及正确性。这样,即使“坏人”拿到 Access Token ,但是没有 Client 编号和密码,也不能和授权服务器发生有效的交互。
5. 刷新令牌
============
在 「4. 配置授权服务器」 中,我们一直没有看到我们期盼的刷新令牌( refresh token )的身影。这是为什么呢?因为我们在配置 Spring Security OAuth2 并未配置,获取访问令牌的同时,获取刷新令牌。
那么,怎么配置开启获取刷新令牌的功能呢?我们来看看 「5.1 获取刷新令牌」 。
5.1 获取刷新令牌
因为【密码模式】相对简单,我们直接在原有程序上做改造。对应 GitHub 地址:
https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-02/authorization-code-server-with-refresh-token 。
在步骤上,如果和原有【密码模式】保持一致的地方,下文会进行省略,并标注“和原有一致”。
① 配置授权服务器
// 授权服务器配置
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
// 用户认证
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(“clientapp”).secret(“112233”) // Client 账号、密码。
.authorizedGrantTypes(“password”, “refresh_token”) // 密码模式 // <1>
.scopes(“read_userinfo”, “read_contacts”) // 可授权的 Scope
// .and().withClient() // 可以继续配置新的 Client
;
}
}
- 在 <1> 处,我们很神奇的多配置了一个 “refresh_token” ,用于开启获取刷新令牌的功能。但是但是但是,OAuth2 的授权模式说好的是四种的么,怎么又出现了 “refresh_token” 这种授权模式?淡定,在 Spring Security OAtuh2 中,“refresh_token” 作为一种特殊的授权模式配置,用于开启获取刷新令牌的功能。所以,其它授权模式如果开启获取刷新令牌的功能,需要在 #authorizedGrantTypes(…) 设置时,多传入 “refresh_token” 方法参数。
② 配置登陆账号
和原有一致。
③ 启动项目
和原有一致。
④ 获取访问令牌
curl -X POST --user clientapp:112233 http://localhost:8080/oauth/token -H “accept: application/json” -H “content-type: application/x-www-form-urlencoded” -d “grant_type=password&username=yunai&password=1024&scope=read_userinfo”
- 和原有一致。
返回结果示例如下:
{
“access_token”:“092a2286-04e7-4e7d-8c20-19fbe25865ff”,
“token_type”:“bearer”,
“refresh_token”:“afeeb083-997f-4ea8-9334-aab6c1696cca”,
“expires_in”:43199,
“scope”:“read_userinfo”
}
- 在原有的基础上,多返回了 “refresh_token” 刷新令牌。美滋滋。
⑤ 调用资源服务器的 API
和原有一致。
5.2 “刷新”访问令牌
总结
本文从基础到高级再到实战,由浅入深,把MySQL讲的清清楚楚,明明白白,这应该是我目前为止看到过最好的有关MySQL的学习笔记了,我相信如果你把这份笔记认真看完后,无论是工作中碰到的问题还是被面试官问到的问题都能迎刃而解!
MySQL50道高频面试题整理:
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
_token" ,用于开启获取刷新令牌的功能。但是但是但是,OAuth2 的授权模式说好的是四种的么,怎么又出现了 “refresh_token” 这种授权模式?淡定,在 Spring Security OAtuh2 中,“refresh_token” 作为一种特殊的授权模式配置,用于开启获取刷新令牌的功能。所以,其它授权模式如果开启获取刷新令牌的功能,需要在 #authorizedGrantTypes(…) 设置时,多传入 “refresh_token” 方法参数。
② 配置登陆账号
和原有一致。
③ 启动项目
和原有一致。
④ 获取访问令牌
curl -X POST --user clientapp:112233 http://localhost:8080/oauth/token -H “accept: application/json” -H “content-type: application/x-www-form-urlencoded” -d “grant_type=password&username=yunai&password=1024&scope=read_userinfo”
- 和原有一致。
返回结果示例如下:
{
“access_token”:“092a2286-04e7-4e7d-8c20-19fbe25865ff”,
“token_type”:“bearer”,
“refresh_token”:“afeeb083-997f-4ea8-9334-aab6c1696cca”,
“expires_in”:43199,
“scope”:“read_userinfo”
}
- 在原有的基础上,多返回了 “refresh_token” 刷新令牌。美滋滋。
⑤ 调用资源服务器的 API
和原有一致。
5.2 “刷新”访问令牌
总结
本文从基础到高级再到实战,由浅入深,把MySQL讲的清清楚楚,明明白白,这应该是我目前为止看到过最好的有关MySQL的学习笔记了,我相信如果你把这份笔记认真看完后,无论是工作中碰到的问题还是被面试官问到的问题都能迎刃而解!
MySQL50道高频面试题整理:
[外链图片转存中…(img-IYhmCIa4-1714752074769)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!