基于Spring Security 6的OAuth2 系列之十七 - 高级特性--设备授权码模式
之所以想写这一系列,是因为之前工作过程中使用Spring Security OAuth2搭建了网关和授权服务器,但当时基于spring-boot 2.3.x,其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0,结果一看Spring Security也升级为6.3.0。无论是Spring Security的风格和以及OAuth2都做了较大改动,里面甚至将授权服务器模块都移除了,导致在配置同样功能时,花费了些时间研究新版本的底层原理,这里将一些学习经验分享给大家。
注意:由于框架不同版本改造会有些使用的不同,因此本次系列中使用基本框架是 spring-boo-3.3.0(默认引入的Spring Security是6.3.0),JDK版本使用的是19,本系列OAuth2的代码采用Spring Security6.3.0框架,所有代码都在oauth2-study项目上:https://github.com/forever1986/oauth2-study.git
目录
- 1 设备授权码模式有什么作用
- 2 设备授权码模式流程
- 3 代码实例
- 4 底层原理
上一章,我们介绍了PKCE增强版,这一章我们继续来了解OAuth2.1新增特性的设备授权码模式。
1 设备授权码模式有什么作用
我们从官方OAuth2协议文档RFC8628中截取一段文字
翻译过来就是在一些输入受限的设备上,比如智能电视、媒体控制台、相框和打印机等,规范了一种“设备授权码模式”。随着现在社会上越来越多的各种各样设备,OAuth2也是与时俱进,新增了支持这些设备的授权。下面我们就来看看其流程是怎么样的。
2 设备授权码模式流程
从官方文档RFC8628中截取流程图如下:
- A步骤:设备客户端向认证服务器(/oauth2/device_authorization)发起授权访问,这时候会带上客户端信息
- B步骤:授权服务器会给设备客户端返回设备码、用户码及验证的URI
- C步骤:设备客户端会让用户需要在另一设备(比如智能手机)进行访问验证的URI
- D步骤:用户根据验证的URI在另一台设备(比如智能手机)打开页面,输入用户码,并确认授权;这是会向授权服务器发起认证请求
- 步骤E:设备客户端在完成C步骤之后,就开始轮询的方式向授权服务器获取令牌信息。这时候会带上客户端信息和设备码
- 步骤F:授权服务器收到设备客户端使用设备码获取令牌信息的请求后,检查D步骤是否已提交授权确认,如果已提交授权确认,则返回令牌信息
从上面步骤,其实我们整理一下与原先授权码模式唯一不同之处在于,由于设备客户端无输入口,无法确认授权,因此使用一个URL在可以输入确认授权的设备(比如智能手机或者电脑)去确认授权。
3 代码实例
代码参考lesson11子模块
1)新建lesson11子模块,其pom引入如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
</dependency>
</dependencies>
2)在resources目录下,配置yaml文件,配置端口和Spring Security的账号密码
server:
port: 9000
logging:
level:
org.springframework.security: trace
spring:
security:
# 使用security配置授权服务器的登录用户和密码
user:
name: user
password: 1234
3)在config包下,配置SecurityConfig:
@Configuration
public class SecurityConfig {
// 自定义授权服务器的Filter链
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
// oidc配置
.oidc(withDefaults());
// 异常处理
http.exceptionHandling((exceptions) -> exceptions.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login")));
return http.build();
}
// 自定义Spring Security的链路。如果自定义授权服务器的Filter链,则原先自动化配置将会失效,因此也要配置Spring Security
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()).formLogin(withDefaults());
return http.build();
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient3 = RegisteredClient.withId(UUID.randomUUID().toString())
// 客户端id
.clientId("oidc-client")
// 客户端密码
.clientSecret("{noop}secret")
// 客户端认证方式
.clientAuthenticationMethods(methods ->{
methods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
})
// 配置设备授权码模式
.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)
// 回调地址
.redirectUri("http://localhost:8080/login/oauth2/code/oidc-client")
.postLogoutRedirectUri("http://localhost:8080/")
// 授权范围
.scopes(scopes->{
scopes.add(OidcScopes.OPENID);
scopes.add(OidcScopes.PROFILE);
})
.build();
return new InMemoryRegisteredClientRepository(registeredClient3 );
}
}
4)配置启动类Oauth2Lesson11ServerApplication,并启动项目
@SpringBootApplication
public class Oauth2Lesson11ServerApplication {
public static void main(String[] args) {
SpringApplication.run(Oauth2Lesson11ServerApplication.class, args);
}
}
5)测试获得用户码和验证URI
请求/oauth2/device_authorization,配置Authorization和Body
user_code:用户码
device_code:设备码
verification_uri_complete:验证的完整URI
verification_uri:验证的URI(没带用户码)
expires_in:有效期
6)从上面返回的数据中,把verification_uri_complete的完整URI通过浏览器确认授权,这时候需要登录,然后进入授权界面,点击确认
7)访问:/oauth2/token ,将步骤5)中获得的device_code(设备码)放入参数中,如下图:
4 底层原理
我们知道授权服务器是通过过滤器链实现的,因此我们只需要找到/oauth2/device_authorization、/oauth2/device_verification和/oauth2/token三个过滤器,就能够理清楚整个底层原理:
1)先来看请求/oauth2/device_authorization接口,获得用户码、验证URI和设备码的过滤器OAuth2DeviceAuthorizationEndpointFilter
2)其中authenticationManager是调用OAuth2DeviceAuthorizationRequestAuthenticationProvider,其逻辑如下
3)OAuth2DeviceAuthorizationEndpointFilter最后通过authenticationSuccessHandler进行返回信息
4)再来看看请求/oauth2/device_verification,使用OAuth2DeviceVerificationEndpointFilter过滤器进行验证
5)最后看看/oauth2/token接口,这个就是熟悉的OAuth2TokenEndpointFilter过滤器,其认证使用的是OAuth2DeviceCodeAuthenticationProvider。
6)整体流程如下:
结语:本章我们对设备授权码模式进行了详细的讲解。到这里,我们已经将OAuth2.1的主要新特性都讲完了。下一章我们继续讲解OAuth2的其它高级特性。