Spring Security OAuth2 入门,80后程序员感慨中年危机

# 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 浏览器打开后,效果如下:

Spring Security OAuth2 入门

浏览器

  • 输入在 「② 配置登陆账号」 中配置的登陆账号 “yunai” / “1024” 。

  • 实际生产情况下,我们以 QQ 三方登陆作为例子,如下图:

Spring Security OAuth2 入门

  • QQ 示例

4.3 登陆成功,选择允许所有申请的 Scope ,点击【Authorize】按钮,确认授权。如下图:

Spring Security OAuth2 入门

Authorize

4.4 授权完成,回调 redirect_uri 地址。如下图所示:

Spring Security OAuth2 入门

回调地址

  • 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 ,可以参看如下两图:

Spring Security OAuth2 入门

  • 图 1

Spring Security OAuth2 入门

  • 图 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 ,可以参看如下图:

Spring Security OAuth2 入门

4.2 密码模式

Maven 项目结构如下:

Spring Security OAuth2 入门

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 项目结构如下:

Spring Security OAuth2 入门

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 浏览器打开后,效果如下:

Spring Security OAuth2 入门

浏览器

  • 和【授权码模式】基本一致,输入在 「② 配置登陆账号」 中配置的登陆账号 “yunai” / “1024” 。

4.3 登陆成功,直接授权完成,回调 redirect_uri 地址。如下图所示:

Spring Security OAuth2 入门

浏览器

  • 和【授权码模式】基本不一致的有两点

  • 登陆成功后,无需选择允许所有申请的 Scope ,直接授权完成。

  • 返回的不是授权码,而是访问令牌

总的来说,【简化模式】是【授权码模式】的简化模式。

⑤ 调用资源服务器的 API

和【授权码模式】一致。

4.4 客户端模式

Maven 项目结构如下:

Spring Security OAuth2 入门

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 和微服务安全架构》

Spring Security 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 “刷新”访问令牌

因为访问访问令牌会自动过期,通过使用刷新令牌,可以获得新的访问令牌。注意,访问令牌获取到的是新的,不是老的哈。这也是为什么,在标题上,笔者对刷新加了双引号。

curl -i -X POST -u ‘clientapp:112233’ http://localhost:8080/oauth/token -H “accept: application/json” -d ‘grant_type=refresh_token&refresh_token=afeeb083-997f-4ea8-9334-aab6c1696cca’

  • 调用接口还是 “oauth/token” ,差别在于传入的请求参数 grant_type 为 “refresh_token”,使用刷新令牌。

  • 请求参数 refresh_token 为上面获取到的刷新令牌 “afeeb083-997f-4ea8-9334-aab6c1696cca” 。

返回结果示例如下:

{

“access_token”:“507eb761-4b25-4159-b927-ef3eff5e7eff”,

“token_type”:“bearer”,

“refresh_token”:“afeeb083-997f-4ea8-9334-aab6c1696cca”,

“expires_in”:43199,

“scope”:“read_userinfo”

}

  • 获得的访问令牌为 “507eb761-4b25-4159-b927-ef3eff5e7eff” ,是新的。并且,过期时间也变成新的

笔者在看 OAuth2.0 的刷新令牌时,一直有个疑惑:刷新令牌是否有过期时间?答案是,。但是,笔者不太确定,在 Spring Security OAuth2 中,如果不设置刷新令牌的过期时间,刷新时间是否无限长?当然,这个貌似也并不重要。因为,在实际使用中,我们肯定是需要显示( 主动 )设置刷新令牌的过期时间,使用 ClientBuilder#

refreshTokenValiditySeconds(int refreshTokenValiditySeconds) 方法,示例如下:

@Override

public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

clients.inMemory()

.withClient(“clientapp”).secret(“112233”) // Client 账号、密码。

.authorizedGrantTypes(“password”, “refresh_token”) // 密码模式

.scopes(“read_userinfo”, “read_contacts”) // 可授权的 Scope

.refreshTokenValiditySeconds(1200) // 1200 秒过期

// .and().withClient() // 可以继续配置新的 Client

;

}

刷新令牌过期时,返回结果示例如下:

{

“error”:“invalid_token”,

“error_description”:“Invalid refresh token (expired): 7139d075-c4ea-48f0-9dbb-6f65fa6dbeb0”

}

  • 如果胖友要测试这个效果,可以把刷新令牌过期时间设置为 1 秒。

5.3 为什么需要有刷新令牌

出于安全性的考虑,访问令牌的过期时间比较短,刷新令牌的过期时间比较长。这样,如果访问令牌即使被盗用走,那么在一定的时间后,访问令牌也能在较短的时间吼过期。当然,安全也是相对的,如果使用刷新令牌后,获取到新的访问令牌,访问令牌后续可能被盗用。

另外,刷新令牌是可选项,不一定会返回。

笔者整理了下,大家常用开放平台的令牌过期时间,让大家更好的理解:

  • 小米开放平台

  • 《Access Token 生命周期》

  • Access Token :90 天有效期

  • Refresh Token :10 年有效期

  • 微信开放平台

  • 《网站应用微信登录开发指南》

  • Access Token :2 小时有效期

  • Refresh Token :未知有效期

  • 腾讯开放平台

  • 《获取 Access_Token》

  • Access Token :90 天有效期

  • Refresh Token :未知有效期

6. 删除令牌

============

实际在 OAuth2 时,有删除访问令牌和刷新令牌的需求。例如:用户登出系统。虽然说,可以通过客户端本地删除令牌的方式实现。但是,考虑到真正的彻底的实现删除令牌,必然服务端自身需要删除令牌。

在 Spring Security OAuth2 中,并没有提供内置的接口,所以需要自己去实现。笔者参看 《Spring Security OAuth2 – Simple Token Revocation》 文档,实现删除令牌的 API 接口。

因为【密码模式】相对简单,我们直接在原有程序上做改造。对应 GitHub 地址: 。注意,如下仅仅是 Demo ,实际生产环境下需要做改造。

6.1 删除访问令牌

① 新增删除访问令牌的 API 接口

@Autowired

private ConsumerTokenServices tokenServices;

@RequestMapping(method = RequestMethod.POST, value = “api/access_token/revoke”)

public String revokeToken(@RequestParam(“token”) String token) {

tokenServices.revokeToken(token);

return token;

}

  • 使用 ConsumerTokenServices#revokeToken(String tokenValue) 方法,删除访问令牌。

注意,实际生产环境下,授权服务器和资源服务器是不在一起的,所以此处仅仅是示例。主要是为了介绍 ConsumerTokenServices#revokeToken(String tokenValue) 方法的使用。

② 访问删除访问令牌的 API 接口。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

这份《“java高分面试指南”-25分类227页1000+题50w+字解析》同样可分享给有需要的朋友,感兴趣的伙伴们可挑战一下自我,在不看答案解析的情况,测试测试自己的解题水平,这样也能达到事半功倍的效果!(好东西要大家一起看才香)

image

image

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

停滞不前!**

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-vPufm0QS-1712184553734)]
[外链图片转存中…(img-xKsYJiLb-1712184553734)]
[外链图片转存中…(img-lCiR1xTQ-1712184553735)]
[外链图片转存中…(img-6pIfHAcy-1712184553735)]
[外链图片转存中…(img-1s0NE9UI-1712184553735)]
[外链图片转存中…(img-p3QUZspJ-1712184553735)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-9qQSqxNn-1712184553736)]

最后

这份《“java高分面试指南”-25分类227页1000+题50w+字解析》同样可分享给有需要的朋友,感兴趣的伙伴们可挑战一下自我,在不看答案解析的情况,测试测试自己的解题水平,这样也能达到事半功倍的效果!(好东西要大家一起看才香)

[外链图片转存中…(img-oTGyb8Ed-1712184553736)]

[外链图片转存中…(img-hhrBD4oL-1712184553736)]

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 30
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值