OAuth2.0 设备授权流程

news2024/10/5 4:26:16

OAuth2.0设备授权流程(Device Authorization Grant)是一种为缺乏输入能力的设备(例如智能电视、游戏机、物联网设备等)设计的授权模式。这些设备通常不具备复杂的键盘或指定输入方式,无法直接进行OAuth2.0标准的交互授权。设备授权的设计流程目标是让用户能够在另一个具备完整输入能力的设备上完成授权操作,从而使不具备输入能力的设备也能获取访问权限保护。

本文将详细介绍 OAuth2.0 设备授权流程的工作原理、步骤及其实现方式。

什么是 OAuth 2.0 设备授权流程?

OAuth 2.0 是一种授权框架,允许应用程序安全地访问用户的资源而无需暴露用户的凭据。设备授权流程专门为输入受限的设备设计,比如智能电视、IoT 设备等。

该流程的核心理念是将用户认证的输入部分迁移到具有更多输入能力的设备上(如手机或电脑),而受限设备只需要展示一个用户代码并等待用户在另一设备上进行授权。

OAuth 2.0 设备授权流程的步骤

设备授权流程大致分为以下几个步骤:

  1. 设备请求授权:设备向授权服务器发送请求,获取一个设备代码和用户代码。
  2. 用户在另一设备上授权:设备将用户代码展示给用户,用户在另一设备上输入该代码并完成授权。
  3. 设备轮询授权服务器:设备定期向授权服务器查询用户是否完成了授权。
  4. 获取访问令牌:授权成功后,设备获取访问令牌,从而可以访问受保护的资源。

在这里插入图片描述

授权流程详解

设备请求授权

设备首先向授权服务器发送请求,获取设备代码和用户代码。该请求通常会包含客户端的身份信息以及所请求的权限范围(scope)。

示例请求:

POST /oauth2/device_authorization HTTP/1.1
Host: auth-server.com
Content-Type: application/x-www-form-urlencoded

response_type=device_code&client_id=client-device-id&scope=read_profile

服务器响应:

{
    "user_code": "DSZQ-FCQZ",
    "device_code": "YAJawG1sR6MH3jzYWa317SpctmshliFIL0tajI21xwCVw2rNDTiu2MLSB3cqJ8SZjyTGT-7QA7v7lVKPoJFwQNwYQ6PLKINnAssOt0F5YwYX0076At2uRh53fIZL_86e",
    "verification_uri_complete": "http://auth-server.com/oauth2/device_verification?user_code=DSZQ-FCQZ",
    "verification_uri": "http://auth-server.com/oauth2/device_verification",
    "expires_in": 1800, 
    "interval": 5
}

用户授权

设备展示 user_code 给用户,用户使用电脑或手机访问 verification_uri,并输入该用户代码进行授权。例如,用户会在浏览器中访问 http://auth-server.com/oauth2/device_verification,并输入 XYZ-9876

设备轮询授权状态

在用户进行授权的同时,设备需要定期向授权服务器轮询授权状态。轮询间隔由授权服务器在最初响应时返回的 interval 字段指定。

示例轮询请求:

POST /oauth2/token HTTP/1.1
Host: auth-server.com
Content-Type: application/x-www-form-urlencoded

client_id=client-device-id&device_code=abc12345&grant_type=urn:ietf:params:oauth:grant-type:device_code

如果用户尚未完成授权,服务器会返回一个错误,表明需要继续等待。

{
  "error": "authorization_pending"
}

获取访问令牌

一旦用户完成授权,设备的轮询请求将返回一个访问令牌,设备即可使用该令牌访问受保护资源。

成功响应:

{
  "access_token": "access-token-xyz",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read_profile"
}

如何实现 OAuth 2.0 设备授权流程

在实际开发中,可以使用多种技术栈来实现 OAuth 2.0 设备授权流程。下面简要介绍使用 Spring Security 和 Spring Authorization Server 实现该流程的步骤:

项目依赖

确保项目中包含以下必要的依赖项:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-authorization-server</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
配置授权服务器

在 Spring Boot 项目中配置 AuthorizationServerConfig,以启用设备授权端点:

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
   
    //设置设备授权验证成功后处理逻辑,当前逻辑转发到/success
    authorizationServerConfigurer.deviceAuthorizationEndpoint(Customizer.withDefaults())
            .deviceVerificationEndpoint(deviceVerification ->
                    deviceVerification.deviceVerificationResponseHandler(new SimpleUrlAuthenticationSuccessHandler("/success")));

    RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();

    // @formatter:off
    http
        .securityMatcher(endpointsMatcher).authorizeHttpRequests((authorize) -> {
            authorize.anyRequest().authenticated();
        }).csrf((csrf) -> {
            csrf.ignoringRequestMatchers(endpointsMatcher);
        })
        .apply(authorizationServerConfigurer);
    // @formatter:on


    //设备授权相关配置
    DeviceClientAuthenticationConfigurer deviceClientAuthenticationConfigurer = new DeviceClientAuthenticationConfigurer();
    deviceClientAuthenticationConfigurer.configure(http);

    http.exceptionHandling(exceptions -> exceptions.
            authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));

    return http.build();
}

@Bean
public RegisteredClientRepository registeredClientRepository() {
    RegisteredClient registeredClient = RegisteredClient.withId("1")
            .clientId("relive-device-client")
            .clientAuthenticationMethods(s -> {
                s.add(ClientAuthenticationMethod.NONE);
            })
            .authorizationGrantTypes(a -> {
                a.add(AuthorizationGrantType.DEVICE_CODE);
                a.add(AuthorizationGrantType.REFRESH_TOKEN);
            })
            .scope("message.read")
            .clientSettings(ClientSettings.builder()
                    .requireAuthorizationConsent(true)
                    .requireProofKey(false)
                    .build())
            .tokenSettings(TokenSettings.builder()
                    .accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
                    .accessTokenTimeToLive(Duration.ofSeconds(30 * 60))
                    .refreshTokenTimeToLive(Duration.ofSeconds(60 * 60))
                    .deviceCodeTimeToLive(Duration.ofSeconds(30 * 60))
                    .reuseRefreshTokens(true)
                    .build())
            .build();
    return new InMemoryRegisteredClientRepository(registeredClient);
}

由于 Spring Authorization Server 对于设备授权客户端认证并没有支持,所以需要自定义 DeviceClientAuthenticationConverterDeviceClientAuthenticationProvider,通过创建DeviceClientAuthenticationConfigurer配置类,注册设备授权客户端认证。

public class DeviceClientAuthenticationConfigurer extends AbstractHttpConfigurer<DeviceClientAuthenticationConfigurer, HttpSecurity> {

    private AuthenticationConverter deviceClientAuthenticationConverter;

    private AuthenticationProvider deviceClientAuthenticationProvider;


    public DeviceClientAuthenticationConfigurer deviceClientAuthenticationConverter(AuthenticationConverter deviceClientAuthenticationConverter) {
        Assert.notNull(deviceClientAuthenticationConverter, "deviceClientAuthenticationConverter can not be null");
        this.deviceClientAuthenticationConverter = deviceClientAuthenticationConverter;
        return this;
    }

    public DeviceClientAuthenticationConfigurer deviceClientAuthenticationProvider(AuthenticationProvider deviceClientAuthenticationProvider) {
        Assert.notNull(deviceClientAuthenticationProvider, "deviceClientAuthenticationProvider can not be null");
        this.deviceClientAuthenticationProvider = deviceClientAuthenticationProvider;
        return this;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        AuthorizationServerSettings authorizationServerSettings = getAuthorizationServerSettings(http);

        if (this.deviceClientAuthenticationConverter == null) {
            this.deviceClientAuthenticationConverter = new DeviceClientAuthenticationConverter(
                    authorizationServerSettings.getDeviceAuthorizationEndpoint());
        }

        if (this.deviceClientAuthenticationProvider == null) {
            RegisteredClientRepository registeredClientRepository = getRegisteredClientRepository(http);
            this.deviceClientAuthenticationProvider = new DeviceClientAuthenticationProvider(registeredClientRepository);
        }

        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .clientAuthentication(clientAuthentication ->
                        clientAuthentication
                                .authenticationConverter(deviceClientAuthenticationConverter)
                                .authenticationProvider(deviceClientAuthenticationProvider)
                );
    }

    private static AuthorizationServerSettings getAuthorizationServerSettings(HttpSecurity httpSecurity) {
        AuthorizationServerSettings authorizationServerSettings = httpSecurity.getSharedObject(AuthorizationServerSettings.class);
        if (authorizationServerSettings == null) {
            authorizationServerSettings = getOptionalBean(httpSecurity, AuthorizationServerSettings.class);
            if (authorizationServerSettings == null) {
                authorizationServerSettings = AuthorizationServerSettings.builder().build();
            }
        }
        return authorizationServerSettings;
    }

    private static RegisteredClientRepository getRegisteredClientRepository(HttpSecurity httpSecurity) {
        RegisteredClientRepository registeredClientRepository = httpSecurity.getSharedObject(RegisteredClientRepository.class);
        if (registeredClientRepository == null) {
            registeredClientRepository = getOptionalBean(httpSecurity, RegisteredClientRepository.class);
            if (registeredClientRepository == null) {
                registeredClientRepository = new InMemoryRegisteredClientRepository();
            }
        }
        return registeredClientRepository;
    }

    public static <T> T getOptionalBean(HttpSecurity httpSecurity, Class<T> type) {
        Map<String, T> beansMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                httpSecurity.getSharedObject(ApplicationContext.class), type);
        if (beansMap.size() > 1) {
            throw new NoUniqueBeanDefinitionException(type, beansMap.size(),
                    "Expected single matching bean of type '" + type.getName() + "' but found " +
                            beansMap.size() + ": " + StringUtils.collectionToCommaDelimitedString(beansMap.keySet()));
        }
        return (!beansMap.isEmpty() ? beansMap.values().iterator().next() : null);
    }
}

如果你希望进一步了解 OAuth 2.0 设备授权流程的实现,可以参考我在 GitHub 上的示例项目:OAuth2.0 设备授权流程 - Spring Security & Spring Authorization Server 示例。

该项目详细演示了如何使用 Spring Security 和 Spring Authorization Server 实现设备授权流程,包含完整的代码和配置示例,帮助你快速上手。

演示

在这里插入图片描述

一起学习的小伙伴可以关注下我的公众号,我会经常更新技术实践类文章,所有文章都会有相应源码供大家学习交流!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2189223.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

8c语言基础文件

关于文件你必须了解的一些基本概念 什么是文件&#xff1f; 文件是计算机文件&#xff0c;属于文件的一种&#xff0c;与普通文件的载体不同&#xff0c;计算机文件是以计算机硬盘为载体存储在计算机上的信息集合。 在程序设计中&#xff0c;我们一般关注的文件有两类&#x…

【C++】空指针和野指针

文章目录 1.空指针2.野指针总结 1.空指针 概念&#xff1a;指针变量指向内存中编号为0的空间。 用途&#xff1a;初始化指针变量。 注意&#xff1a;空指针指向的内存是不可以访问的。 示例&#xff1a; int main(){//指针变量p指向内存地址编号为0的空间int *PNULL&#…

从零开始学cv-15:图像分割

文章目录 前言一、全局阈值分割&#xff1a;二、自适应阈值分割&#xff1a;三、分水岭算法&#xff1a; 前言 在当代计算机视觉领域&#xff0c;图像分割技术扮演着至关重要的角色&#xff0c;它为图像理解、目标识别和场景解析等高级视觉任务提供了基础。OpenCV&#xff0c;…

Redis:hash类型

Redis&#xff1a;hash类型 hash命令设置与读取HSETHGETHMGET 哈希操作HEXISTSHDELHKEYSHVALSHGETALLHLENHSETNXHINCRBYHINCRBYFLOAT 内部编码ziplisthashtable 目前主流的编程语言中&#xff0c;几乎都提供了哈希表相关的容器&#xff0c;Redis自然也会支持对应的内容&#xf…

李宏毅深度学习-循环神经网络RNN

Recurrent Neural Network 这个问题可以使用一个前馈神经网络&#xff08;feedforward neural network&#xff09;来解&#xff0c;如图5.2 所示&#xff0c; 输入是一个单词&#xff0c;把“上海”变成一个向量&#xff0c;“丢”到这个神经网络里面。输入是一个单词&#x…

平衡二叉搜索树---java---黑马

平衡二叉搜索树 AVL树的实现 二叉搜索树在插入和删除时&#xff0c;节点可能发生失衡&#xff1b;如果在插入和删除时通过旋转&#xff0c;始终让二叉搜索树保持平衡&#xff0c;称之为平衡二叉搜索树&#xff1b;AVL树是自平衡二叉搜索树的实现之一 LL - 失衡节点(图中5红…

项目-坦克大战学习笔记-地图完善

之前我们详细讲解了怎么在地图上绘制墙&#xff0c;这次我们来完善整个地图&#xff0c; 地图的静态物体构成分为可破坏的墙体&#xff0c;不可破坏的铁块&#xff0c;以及最终boos 那我们为了方便存储将三个对象分开放为3个列表 private static List<gudin> walllist…

vscode有问题

开始给我报错&#xff0c;说命名不规范 然后我就改&#xff0c;改了好几遍之后还是报错。问了ai&#xff0c;也用它的方法改了&#xff0c;还是报错。。。 然后关掉vscode&#xff0c;重启&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c;运行成功

【算法系列-链表】删除链表的倒数第N个结点

【算法系列-链表】删除链表的倒数第N个结点 文章目录 【算法系列-链表】删除链表的倒数第N个结点1. 算法分析&#x1f6f8;2. 模拟解决问题2.1 思路分析&#x1f3af;2.2 代码示例&#x1f330; 3. 双指针(快慢指针)解决问题3.1 思路分析&#x1f3af;3.2 代码示例&#x1f330…

Web-Machine-N7解题过程

1.主机探测 arp-scan -lnmap -sn 192.168.1.0/24sudo netdiscover -r 192.168.1.0/24masscan -p0-65535 192.168.1.0/24 2.端口扫描 nmap -A -sC -sT -sV 192.168.1.188 --min-rate 10000 &#xff08;简略扫描&#xff09;nmap -sS 192.168.1.188 -A&#xff1a; 启用操作系…

vue的图片显示

通过参数 调用方法 进行显示图片 方法一: 方法二:

使用TM1618控制LED了解P-MOS和N-MOS的开漏输出的不同

数据手册上的截取内容 推荐的共阴/阳极电路 可以发现GRID总接LED的负极&#xff0c;SEG引脚接的是LED 正极 分析输出的MOS管类型可以很好的知道原因 图片来源 通过都是开漏输出可以看出&#xff0c;引脚引出的内部电路是不同的。P-mos引出的是漏极&#xff0c;导通时&#…

平面电磁波的能量传输速度——就记平均坡印廷矢量/平均能量密度(物理意义,坡印廷是单位时间该点流过的能量,能量密度表示该店有多少能量。如果能量密度不变,经过流动速度是2/s,则1s流过的能量就是2*密度

能量传输速度 平均坡印廷矢量的模&#xff08;功率流密度&#xff09;/ 电磁波平均能量密度&#xff08;电场和磁场的都算进去&#xff09; 能量传输速度等于相速度 但是文心给出的能量流动速度的定义是

2. 术语解释(类、成员变量、行为、属性、this)以及注销账号和空指针异常

文章目录 1. 类2. 成员变量3. 行为4. 属性5. zhangDog.name 以及 zhangDog.sleep()解析6. this① 提出问题② 解决方案③ 进一步完善&#xff1a;引入this 7. 注销账号8. 空指针异常9. 内容出处 1. 类 我们的目的是不是要给狗写一个程序&#xff0c;而且要让所有狗都能用&#…

20241004给荣品RD-RK3588-AHD开发板刷Rockchip原厂的Android12【HDMI0显示】

20241004给荣品RD-RK3588-AHD开发板刷Rockchip原厂的Android12【HDMI0显示】 2024/10/4 19:40 1、配置RK3588S的默认DTS为&#xff1a;rk3588s-evb4-lp4x-v10.dts D:\Android\rk3588s4_3588a12\device\rockchip\rk3588\rk3588s_s\BoardConfig.mk Z:\rk3588s4_3588a12\device\ro…

【论文笔记】Efficient LoFTR: Semi-Dense Local Feature Matching with Sparse-Like Speed

【引用格式】&#xff1a;Wang Y, He X, Peng S, et al. Efficient LoFTR: Semi-dense local feature matching with sparse-like speed[C]//Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. 2024: 21666-21675. 【网址】&#xff1a;ht…

PostgreSQL Docker Error – 5432: 地址已被占用

PostgreSQL Docker Error – 5432: 地址已被占用 今天在学习【Spring Boot React】价值79.9美元&#xff0c;全栈开发&#xff0c;搭建个人网站、做毕业设计、试试这套课程第17~21节视频的时候&#xff0c;发现运行docker run --name demo-postgres -e POSTGRES_PASSWORDpass…

数据集-目标检测系列- 货船 检测数据集 freighter>> DataBall

数据集-目标检测系列- 货船 检测数据集 freighter>> DataBall 数据集-目标检测系列- 货船 检测数据集 freighter>> DataBall 数据量&#xff1a;3k 想要进一步了解&#xff0c;请联系。 DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种…

高效参数微调

一、增加额外参数的方法 这些方法通过向模型中添加额外的可训练参数&#xff0c;而不修改原有的预训练模型参数。 1. Prefix Tuning&#xff08;前缀微调&#xff09; 原理&#xff1a;在每一层的输入添加一组可训练的“前缀”向量&#xff0c;这些前缀向量会影响模型的输出…

使用PL/SQL Deverloper过程遇见的问题

目录 背景: ORA-01031权限问题&#xff1a; PL/SQL Deverloper显示Oravle中存在的所有表&#xff1a; PL/SQL Deverloper优点: 背景: PL/SQL Developer是由Allround Automations公司开发的一款集成开发环境(IDE),它专门面向Oracle数据库存储的程序单元的开发。随着越来越多…