最新Spring Security实战教程(十四)OAuth2.0精讲 - 四种授权模式与资源服务器搭建

发布于:2025-05-09 ⋅ 阅读:(19) ⋅ 点赞:(0)

在这里插入图片描述

🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志
🎐 个人CSND主页——Micro麦可乐的博客
🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战
🌺《RabbitMQ》专栏19年编写主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战
🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解
🌛《开源项目》本专栏主要介绍目前热门的开源项目,带大家快速了解并轻松上手使用
✨《开发技巧》本专栏包含了各种系统的设计原理以及注意事项,并分享一些日常开发的功能小技巧
💕《Jenkins实战》专栏主要介绍Jenkins+Docker的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程
🌞《Spring Boot》专栏主要介绍我们日常工作项目中经常应用到的功能以及技巧,代码样例完整
🌞《Spring Security》专栏中我们将逐步深入Spring Security的各个技术细节,带你从入门到精通,全面掌握这一安全技术
如果文章能够给大家带来一定的帮助!欢迎关注、评论互动~

回顾链接:
最新Spring Security实战教程(一)初识Spring Security安全框架
最新Spring Security实战教程(二)表单登录定制到处理逻辑的深度改造
最新Spring Security实战教程(三)Spring Security 的底层原理解析
最新Spring Security实战教程(四)基于内存的用户认证
最新Spring Security实战教程(五)基于数据库的动态用户认证传统RBAC角色模型实战开发
最新Spring Security实战教程(六)最新Spring Security实战教程(六)基于数据库的ABAC属性权限模型实战开发
最新Spring Security实战教程(七)方法级安全控制@PreAuthorize注解的灵活运用
最新Spring Security实战教程(八)Remember-Me实现原理 - 持久化令牌与安全存储方案
最新Spring Security实战教程(九)前后端分离认证实战 - JWT+SpringSecurity无缝整合
最新Spring Security实战教程(十)权限表达式进阶 - 在SpEL在安全控制中的高阶魔法
最新Spring Security实战教程(十一)CSRF攻防实战 - 从原理到防护的最佳实践
最新Spring Security实战教程(十二)CORS安全配置 - 跨域请求的安全边界设定
最新Spring Security实战教程(十三)会话管理机制 - 并发控制与会话固定攻击防护

专栏更新完毕后,博主将会上传所有章节代码到CSDN资源免费给大家下载,如你不想等后续章节代码需提前获取,可以私信或留言!

1. 前言(OAuth2简介)

在这里插入图片描述

目前 OAuth2.0 已成为现代应用认证授权的一个标准,从单点登录到微服务架构都依赖其构建安全通道。我们常见的微信、QQ、微博等应用有在使用。OAuth 2.0(Open Authorization)是一种开放的授权框架,允许应用在不暴露用户密码的前提下,安全地代表用户访问第三方服务上的受保护资源。

本章节博主将带着大家深入解析四种核心授权模式,并基于 Spring Security 6 手把手搭建安全的资源服务器,助您掌握分布式系统的认证精髓。

注:博主部分介绍参考了官方文档:https://docs.spring.io/spring-security/reference/servlet/oauth2/index.html 小伙伴们可以自行阅读、并查阅官方DEMO代码


2. OAuth2的角色

根据 RFC 6749 标准,OAuth 2.0 定义了四种角色,每个角色在授权流程中承担不同职责

  • 资源所有者(Resource Owner) :通常是终端用户,拥有受保护资源

  • 客户端(Client):向用户请求授权,代表用户访问资源的应用,通常是一个Web或者无线应用

  • 授权服务器(Authorization Server):认证资源所有者身份并颁发访问令牌

  • 资源服务器(Resource Server):托管资源并根据令牌的有效性和权限范围决定是否允许访问

为了让小伙伴们更好的理解博主用一个现实生活中的场景(小区门禁系统)来类比OAuth2.0的工作原理,帮你直观理解它的角色和四种授权模式。

在这里插入图片描述

场景:小区门禁系统

假设你住在一个需要刷卡或扫码进入的小区,门禁系统要求验证身份后才能开门。以下是 OAuth2.0 的四个角色映射:

  • 资源所有者(Resource Owner)
    住户(你):你是小区住户,拥有进出小区的权限。

  • 客户端(Client)
    快递员或访客:需要临时进入小区的人或设备(比如快递员的小程序)

  • 授权服务器(Authorization Server)
    物业中心:负责验证住户身份,并发放临时通行权限(令牌)

  • 资源服务器(Resource Server)
    小区门禁闸机:接收令牌(如二维码或门禁卡),验证后决定是否放行

OAuth2的核心逻辑

  • 你(住户)不想直接给快递员门禁卡或密码,而是通过物业中心生成一个临时二维码(令牌),限制其权限(例如:仅允许进入1次,或仅在30分钟内有效)。

  • 令牌由物业中心发放,你可以随时在物业系统中吊销它。


3. OAuth 2.0 四种授权模式

不同场景下,OAuth 2.0 定义了四种核心授权模式,各有适用范围和安全考量

3.1 授权码模式(Authorization Code Grant)

授权码(authorization code),指的是第三方应用先申请一个授权码,然后再用该码获取令牌。
这种方式是最常用,最复杂,也是最安全的,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。

场景 :适用于安全的服务端 Web 应用,例如电商网站在结算时需要访问支付平台 API。
流程

1、客户端引导用户浏览器跳转到授权服务器授权端点;
2、用户登录并同意后,授权服务器重定向携带 code 到客户端回调 URL;
3、客户端使用该 code 向令牌端点申请访问令牌,并在请求中携带客户端凭证。

优点 :令牌通过后端交换,避免在浏览器中暴露,安全性较高

在这里插入图片描述
现实生活模拟场景
还是以上述小区门禁系统来举例:快递员通过「小区快递App」申请进入权限

1、快递员点击APP「申请进入小区」,App 跳转到物业的授权页面。
2、你(住户)在物业APP上接收到授权通知,并同意授权(例如:允许快递员在30分钟内进入小区1次)。
3、物业中心生成一个授权码返回给快递员的快递App。
4、快递App 用授权码向物业中心换取一个临时二维码(令牌)。
5、快递员用二维码扫码进入小区门禁(资源服务器)。

3.2 隐式模式(Implicit Grant)

场景:传统的单页应用(SPA)或无法安全存储客户端密钥的纯前端应用。

流程:用户同意授权后,授权服务器直接在重定向 URIfragment 中返回访问令牌,无需第二次交换。

缺点:令牌暴露在浏览器,易受截取攻击,注:Spring Security 6 已移除隐式模式支持,使用PKCE增强的授权码模式

// Spring Security 6已移除隐式模式支持
http.oauth2Client(oauth2 -> oauth2
    .authorizationCodeGrant(Customizer.withDefaults())
)

现实生活模拟场景
访客临时申请进入小区:

1、访客在门禁屏幕上点击「申请临时通行」。
2、直接跳转到物业授权页面,你登录并同意授权。
3、物业中心直接返回一个临时二维码(令牌)到门禁屏幕的URL中。
4、访客用二维码扫码进入,但二维码仅在5分钟内有效。

3.3 客户端凭证模式(Client Credentials Grant)

凭证式(client credentials):也叫客户端模式,适用于没有前端的命令行应用,即在命令行下请求令牌。
这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌

场景:应用间 M2M 通信,如后台微服务之间相互调用私有 API。

流程:客户端直接向令牌端点发送 grant_type=client_credentials 请求,并在 HTTP Basic Auth 中提供客户端 ID/密钥,即可获取访问令牌。

特点:不涉及用户,适合服务账户场景,配置简单且安全性可控

在这里插入图片描述
现实生活模拟场景
物业巡逻车需要进出小区所有区域

1、物业巡逻车用自己的工牌(客户端ID和密钥)向物业中心申请令牌。
2、物业中心验证工牌后,发放一个万能通行令牌(可进入所有区域)。
3、巡逻车凭令牌自由进出小区门禁。

3.4 资源所有者密码凭证模式(Resource Owner Password Credentials Grant)

密码式(Resource Owner Password Credentials):如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌
这种方式需要用户给出自己的用户名/密码,显然风险很大,因此只适用于其他授权方式都无法采用的情况,而且必须是用户高度信任的应用

场景:高度信任的客户端(如官方移动端应用)与授权服务器共属一个安全域时,可直接收集用户名和密码。

流程:客户端将用户名、密码与自身凭证一起 POST 到令牌端点,授权服务器验证后颁发令牌。

风险:客户端需处理用户敏感凭证,安全性最低,通常仅在无法采用授权码模式时作为后备方案
在这里插入图片描述
现实生活模拟场景
你的家人定期出入小区(物业通过你的账号密码给其他人授权)

1、你直接将门禁账号密码授权给到家人。
2、家人用你的账号密码向物业中心申请长期有效的通行令牌。
3、物业返回令牌,家人人凭令牌进出小区。

3.5 总结

模式 门禁场景类比 权限范围 风险
授权码模式 快递员通过App申请临时通行 限时、限次 低(令牌不暴露)
隐式模式 访客直接扫码获得短期通行 超短时效 中(URL暴露令牌)
密码模式 自家家人长期通行 长期有效 高(需信任设备)
客户端凭证模式 物业巡逻车自由通行 全权限 中(客户端可控)

4. 授权服务器与资源服务器搭建

讲解了 OAuth2 四种授权模式,下面我们来实现授权服务器与资源服务器搭建

❶ 引入依赖

Spring Boot 3 + Spring Security 6 项目中,添加以下依赖即可快速构建授权服务器与资源服务器:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

❷ 授权服务器配置

使用 OAuth2AuthorizationServerConfigurer 和基于内存的 RegisteredClientRepository 快速配置:

@Configuration
public class AuthServerConfig {

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
            .clientId("web-client")
            .clientSecret("{noop}web-secret")
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
            .redirectUri("http://localhost:8080/login/oauth2/code/web-client")
            .scope("read")
            .build();
        return new InMemoryRegisteredClientRepository(client);
    }

    @Bean
    public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfigurer<HttpSecurity> configurer = new OAuth2AuthorizationServerConfigurer<>();
        http.apply(configurer).and()
            .exceptionHandling(ex -> ex.authenticationEntryPoint(
                new LoginUrlAuthenticationEntryPoint("/login")
            ));
        return http.build();
    }
}

该配置启用了授权码与客户端凭证模式,并自动暴露 /oauth2/authorize/oauth2/token 等端点

❸ 资源服务器配置

application.yml 中指定授权服务器的 issuer-uri

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:9000

并通过 Lambda DSL 定义安全规则:

@Configuration
public class ResourceServerConfig {

    @Bean
    public SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity http) throws Exception {
        http
          .securityMatcher("/api/**")
          .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/public").permitAll()
            .anyRequest().authenticated()
          )
          .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
        return http.build();
    }
}

此时,所有 /api/** 请求均需携带合法的 JWT 访问令牌,否则会返回 401/403 错误

❹ 完整示例代码

结合上述讲解,我们将完整代码展示如下:

@SpringBootApplication
public class OAuth2ServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(OAuth2ServerApplication.class, args);
    }

    // 注册客户端
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient appClient = RegisteredClient.withId(UUID.randomUUID().toString())
            .clientId("app-client")
            .clientSecret("{noop}app-secret")
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
            .redirectUri("http://localhost:8080/login/oauth2/code/app-client")
            .scope("read")
            .build();
        return new InMemoryRegisteredClientRepository(appClient);
    }

    // 授权服务器安全链
    @Bean
    @Order(1)
    public SecurityFilterChain authServerFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfigurer<HttpSecurity> oauth2Config = new OAuth2AuthorizationServerConfigurer<>();
        http.apply(oauth2Config).and()
            .csrf(csrf -> csrf.ignoringRequestMatchers("/oauth2/token"))
            .exceptionHandling(ex -> ex.authenticationEntryPoint(
                new LoginUrlAuthenticationEntryPoint("/login")
            ));
        return http.build();
    }

    // 资源服务器安全链
    @Bean
    @Order(2)
    public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {
        http
          .securityMatcher("/api/**")
          .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
          .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
        return http.build();
    }
}

在此示例中,同一应用既扮演授权服务器,也作为资源服务器提供 /api/** 保护接口,启动后即可体验完整的 OAuth 2.0 流程。


结语:构建开放安全的认证生态

OAuth2.0 的威力在于其灵活的协议设计,但灵活也意味着责任:

  • 模式选型:根据场景选择最合适的授权流程
  • 密钥管理:采用HSM或KMS保护签名密钥
  • 监控审计:记录所有令牌发放与使用日志
  • 协议演进:跟进OAuth2.1安全增强规范

通过本章节讲解与实战示例,相信小伙伴们已掌握 OAuth 2.0 的核心概念、四大角色、四种授权模式及其在 Spring Security 6 中的配置方式。后续可根据业务需求,引入 PKCE、刷新令牌、动态客户端注册等高级特性,进一步提升系统安全性与可扩展性。

如果你在实践过程中有任何疑问或更好的扩展思路,欢迎在评论区留言,最后希望大家 一键三连 给博主一点点鼓励!


在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到