之所以想写这一系列,是因为之前工作过程中使用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: 9000logging:level:org.springframework.security: tracespring:security:# 使用security配置授权服务器的登录用户和密码user:name: userpassword: 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();}@Beanpublic 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的其它高级特性。