当前位置: 首页 > article >正文

基于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的其它高级特性。


http://www.kler.cn/a/555386.html

相关文章:

  • Coze怎么发送消息到飞书
  • stm32hal库寻迹+蓝牙智能车(STM32F103C8T6)
  • HTML/CSS中交集选择器
  • Linux 进程地址空间第二讲动态库地址
  • 每日学习Java之一万个为什么?
  • vue打包
  • Ubuntu编译ZLMediaKit
  • 数据结构——模拟栈例题B3619
  • 使用 Docker 部署 Spark 集群
  • LeetCode热题100——滑动窗口/子串
  • 板块一 Servlet编程:第十节 监听器全解 来自【汤米尼克的JAVAEE全套教程专栏】
  • Go学习-入门
  • 常用电脑,护眼软件推荐 f.lux 3400K | 撰写论文 paper
  • 服务器数据迁移某个目录下的所有文件到另一台服务器
  • Ubuntu上查看端口被哪个进程占用了
  • 如何才能写出好的prompt?
  • 如何使用springboot项目如何实现小程序里面商品的浏览记录功能案例
  • Qt5 C++ TcpSocket 如何判断是服务主动断开tcp socket连接?
  • QPainter绘制3D 饼状图
  • 前端开发中的贪心算法实践:以最小成本解决实际问题