0

0

Spring Boot应用中的用户认证策略:JWT与OAuth2的协同

碧海醫心

碧海醫心

发布时间:2025-09-29 13:27:13

|

839人浏览过

|

来源于php中文网

原创

Spring Boot应用中的用户认证策略:JWT与OAuth2的协同

在Spring Boot应用中整合用户注册与多种认证方式(如自定义凭证和社交媒体登录)时,最佳实践是利用成熟的OAuth2/OpenID Connect授权服务器(如Keycloak、Auth0),而非自行实现JWT认证。本文将阐述如何将REST API作为资源服务器,UI作为客户端,并探讨OAuth2客户端库的使用及BFF模式,以构建安全、可扩展的认证系统。

核心理念:授权服务器与角色划分

在构建现代web应用,特别是需要支持多种认证方式(如传统用户名/密码和社交媒体登录)的spring boot应用时,一个常见的误区是试图从零开始实现一个完整的用户管理和认证系统。最佳实践是采纳行业标准oauth2和openid connect,并利用成熟的授权服务器

为何不应自建授权服务器? 自行构建授权服务器不仅复杂,而且极易引入安全漏洞。一个完整的授权服务器需要处理用户注册、密码管理、多因素认证、社交媒体身份联邦、令牌颁发与刷新、会话管理等诸多功能,这些都需要深厚的安全专业知识。

推荐的授权服务器 市面上已有许多功能强大、安全可靠的OAuth2/OpenID Connect授权服务器可供选择:

  • 自托管解决方案: 如Keycloak,提供丰富的用户管理、身份联邦、MFA等功能,可部署在自己的基础设施上。
  • 云服务: 如Auth0、Amazon Cognito、Okta等,提供开箱即用的认证和授权服务,大大简化了集成难度。

这些授权服务器通常内置了社交媒体登录(Google、Facebook等)的集成能力,极大地简化了多认证源的管理。

OAuth2体系中的角色 在OAuth2/OpenID Connect框架下,您的Spring Boot应用的不同部分将扮演不同的角色:

  1. 授权服务器 (Authorization Server):负责用户身份验证、颁发访问令牌(Access Token)和刷新令牌(Refresh Token)。这是您应该选择现有解决方案的部分。
  2. 资源服务器 (Resource Server):您的Spring Boot REST API。它不处理用户凭证,只负责验证传入请求中的访问令牌,并根据令牌的有效性和权限来决定是否允许访问受保护的资源。
  3. 客户端 (Client):您的用户界面(UI),无论是单页应用(SPA)、移动应用还是传统的服务器端渲染应用。它代表用户向授权服务器请求令牌,并使用这些令牌访问资源服务器。

资源服务器的实现:保护您的Spring Boot REST API

您的Spring Boot REST API作为资源服务器,其主要职责是验证来自客户端的请求中包含的访问令牌。Spring Security提供了强大的支持来简化这一过程。

配置Spring Security作为资源服务器 使用spring-boot-starter-oauth2-resource-server依赖可以轻松地将您的Spring Boot应用配置为OAuth2资源服务器。它能够解析JWT(JSON Web Token)格式的访问令牌,并根据授权服务器提供的公钥进行验证。

示例配置:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class ResourceServerConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                // 允许特定路径公开访问,例如健康检查、文档等
                .requestMatchers("/api/public/**").permitAll()
                // 所有其他请求都需要认证
                .anyRequest().authenticated()
            )
            // 配置OAuth2资源服务器,默认使用JWT验证
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(Customizer.withDefaults())
            );
        // 如果需要,可以禁用CSRF,因为JWT是无状态的
        // http.csrf(csrf -> csrf.disable());
        return http.build();
    }

    // Spring Boot会自动配置JwtDecoder,通过 'spring.security.oauth2.resourceserver.jwt.issuer-uri' 或 'jwk-set-uri'
    // 如果需要更复杂的JWT解析,可以自定义JwtDecoder Bean
    // @Bean
    // public JwtDecoder jwtDecoder() {
    //     // 例如,从特定的JWK Set URI加载
    //     return NimbusJwtDecoder.withJwkSetUri("YOUR_AUTHORIZATION_SERVER_JWKS_URI").build();
    // }
}

在application.properties或application.yml中,您需要指定授权服务器的URI,以便资源服务器能够获取验证JWT所需的公钥(JWK Set URI):

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/your-realm # Keycloak示例
# 或者
# spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:8080/realms/your-realm/protocol/openid-connect/certs

通过上述配置,您的Spring Boot REST API将能够自动验证传入请求的Authorization: Bearer <token>头中的JWT,并根据令牌的有效性(签名、过期时间、颁发者等)来决定是否允许访问。

客户端的实现:用户界面与OAuth2流程

客户端(UI)负责引导用户完成认证流程,并获取访问令牌以访问资源服务器。根据UI的类型,实现方式有所不同。

1. 单页应用 (SPA) 或移动应用 对于Angular、React、Vue等前端框架构建的SPA或原生移动应用,它们通常直接与授权服务器交互,采用授权码流 (Authorization Code Flow) 配合PKCE (Proof Key for Code Exchange)

  • 流程:
    1. 用户在SPA中点击登录。
    2. SPA将用户重定向到授权服务器的登录页面。
    3. 用户在授权服务器完成认证(用户名/密码或社交登录)。
    4. 授权服务器将用户重定向回SPA,并带上一个授权码。
    5. SPA使用授权码和PKCE密钥向授权服务器的令牌端点交换访问令牌和刷新令牌。
    6. SPA将访问令牌存储在本地(如LocalStorage),并在后续请求中将其添加到Authorization头中发送给资源服务器。
  • 客户端库: 建议使用成熟的OAuth2/OpenID Connect客户端库,如oidc-client-js (JavaScript/TypeScript)、AppAuth-JS、或特定框架的库。
  • 注意事项: 令牌存储在浏览器端存在XSS攻击风险,需谨慎处理。

2. 服务器端渲染 (SSR) 或后端服务前端 (BFF) 如果您的UI是由Spring Boot应用本身渲染(如使用Thymeleaf)或者您采用了后端服务前端(BFF)模式,那么您的Spring Boot应用将扮演OAuth2客户端的角色。

使用spring-boot-starter-oauth2-client Spring Security提供了spring-boot-starter-oauth2-client,用于简化Spring Boot应用作为OAuth2客户端的实现。

示例配置 (application.yml):

B12
B12

B12是一个由AI驱动的一体化网站建设平台

下载
spring:
  security:
    oauth2:
      client:
        registration:
          google: # 注册一个名为 'google' 的OAuth2客户端
            client-id: your-google-client-id
            client-secret: your-google-client-secret
            scope: openid,profile,email # 请求的权限范围
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" # 回调URI
            client-name: Google
            authorization-grant-type: authorization_code # 授权码流
          # 您也可以配置自定义授权服务器
          my-auth-server:
            client-id: my-client-id
            client-secret: my-client-secret
            scope: openid,api.read,api.write
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            client-name: My Auth Server
            authorization-grant-type: authorization_code
        provider:
          google: # 配置Google授权服务器的端点
            authorization-uri: https://accounts.google.com/o/oauth2/v2/auth
            token-uri: https://oauth2.googleapis.com/token
            user-info-uri: https://openidconnect.googleapis.com/v1/userinfo
            jwk-set-uri: https://www.googleapis.com/oauth2/v3/certs
            user-name-attribute: sub # 用于获取用户名的属性
          my-auth-server: # 配置自定义授权服务器的端点
            issuer-uri: http://localhost:8080/realms/your-realm # OpenID Connect Issuer URI

通过上述配置,Spring Security会自动处理OAuth2授权码流。用户访问受保护的页面时,会被重定向到授权服务器进行登录。成功登录后,Spring Boot客户端会获取访问令牌,并将其存储在会话中。

获取用户信息: 在控制器中,您可以轻松获取已认证的用户信息:

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.Map;

@Controller
public class HomeController {

    @GetMapping("/")
    public String home(@AuthenticationPrincipal OAuth2User oauth2User, Model model) {
        if (oauth2User != null) {
            model.addAttribute("userName", oauth2User.getAttribute("name"));
            model.addAttribute("userAttributes", oauth2User.getAttributes());
        } else {
            model.addAttribute("userName", "Guest");
        }
        return "index"; // 返回一个Thymeleaf模板
    }

    @GetMapping("/userinfo")
    public Map<String, Object> getUserInfo(@AuthenticationPrincipal OAuth2User oauth2User) {
        return oauth2User.getAttributes();
    }
}

后端服务前端(BFF)模式:提升安全性与体验

当您拥有一个SPA前端和一个Spring Boot后端API时,BFF模式是一个非常推荐的架构选择,尤其是在安全性方面。

什么是BFF模式? BFF(Backend For Frontend)模式是指为特定前端应用(如SPA)提供定制化API服务的后端层。在这个场景中,BFF充当了OAuth2客户端,代表浏览器与授权服务器和资源服务器进行交互。

BFF模式的优势:

  • 隐藏令牌: 访问令牌和刷新令牌存储在BFF的服务器端,不会暴露给浏览器端的JavaScript。这大大降低了跨站脚本(XSS)攻击获取令牌的风险。
  • 会话管理: BFF可以与浏览器建立传统的、基于会话的认证机制(如HTTP Only Cookie),简化前端的认证逻辑,前端无需直接处理OAuth2令牌。
  • 协议转换: BFF将浏览器端的会话请求转换为带有访问令牌的请求,转发给资源服务器。
  • 简化前端: 前端无需关心复杂的OAuth2流程和令牌管理,只需与BFF进行简单的会话通信。

BFF的实现 您可以构建一个独立的Spring Boot应用作为BFF。这个BFF应用将同时扮演OAuth2客户端的角色(使用spring-boot-starter-oauth2-client与授权服务器交互)和代理的角色(将请求转发给资源服务器)。

示例架构:

  1. 浏览器 (SPA) <--- (基于Cookie的会话) ---> BFF (Spring Boot)
  2. BFF (Spring Boot) <--- (OAuth2授权码流) ---> 授权服务器
  3. BFF (Spring Boot) <--- (带有访问令牌的请求) ---> 资源服务器 (Spring Boot REST API)

BFF中的配置与转发逻辑: BFF应用需要配置为OAuth2客户端,如上文所示。当用户通过BFF登录后,BFF会持有用户的访问令牌。在转发请求到资源服务器时,BFF需要将这个访问令牌添加到请求的Authorization头中。

您可以使用Spring Cloud Gateway或自定义的WebClient来实现请求转发:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {

    @Bean
    WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        return WebClient.builder()
                .filter(oauth2ClientFilter(authorizedClientManager)) // 添加OAuth2客户端过滤器
                .build();
    }

    // 配置OAuth2AuthorizedClientManager以获取和刷新令牌
    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientRepository authorizedClientRepository) {
        OAuth2AuthorizedClientProvider authorizedClientProvider =
                OAuth2AuthorizedClientProviderBuilder.builder()
                        .authorizationCode()
                        .refreshToken()
                        .clientCredentials()
                        .password()
                        .build();
        DefaultOAuth2AuthorizedClientManager authorizedClientManager =
                new DefaultOAuth2AuthorizedClientManager(
                        clientRegistrationRepository, authorizedClientRepository);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
        return authorizedClientManager;
    }

    // WebClient过滤器,用于将访问令牌添加到请求头
    private org.springframework.web.reactive.function.client.ExchangeFilterFunction oauth2ClientFilter(
            OAuth2AuthorizedClientManager authorizedClientManager) {
        return (request, next) -> {
            // 这里需要根据实际情况获取当前用户的OAuth2AuthorizedClient
            // 通常可以通过SecurityContextHolder获取当前认证用户的Principal
            // 并使用authorizedClientManager.authorize(OAuth2AuthorizeRequest)来获取或刷新令牌
            // 简化示例:假设我们总是使用某个注册ID的令牌
            // 实际应用中需要更复杂的逻辑来关联用户和其OAuth2AuthorizedClient
            // 例如,从SecurityContext中获取OAuth2AuthenticationToken
            // String clientRegistrationId = "google"; // 或从请求中动态获取
            // OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(
            //     OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)
            //         .principal(SecurityContextHolder.getContext().getAuthentication())
            //         .build());
            // if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
            //     request = WebClient.RequestHeadersSpec::header(HttpHeaders.AUTHORIZATION,
            //         "Bearer " + authorizedClient.getAccessToken().getTokenValue());
            // }
            return next.exchange(request);
        };
    }
}

注意: 上述BFF的oauth2ClientFilter是一个概念性示例,实际实现中,如何将当前用户的OAuth2AuthorizedClient与WebClient请求关联,需要更精细的逻辑,通常涉及从SecurityContextHolder中获取OAuth2AuthenticationToken并构建OAuth2AuthorizeRequest。

注意事项与最佳实践

  • 选择合适的授权服务器: 根据项目规模、安全性要求、团队熟悉度及预算,选择最适合的授权服务器。
  • 始终使用HTTPS: 所有的认证和授权通信都必须通过HTTPS进行,以防止中间

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

49

2026.02.13

TypeScript全栈项目架构与接口规范设计
TypeScript全栈项目架构与接口规范设计

本专题面向全栈开发者,系统讲解基于 TypeScript 构建前后端统一技术栈的工程化实践。内容涵盖项目分层设计、接口协议规范、类型共享机制、错误码体系设计、接口自动化生成与文档维护方案。通过完整项目示例,帮助开发者构建结构清晰、类型安全、易维护的现代全栈应用架构。

198

2026.02.25

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

79

2026.03.13

spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

161

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

89

2026.01.26

spring boot框架优点
spring boot框架优点

spring boot框架的优点有简化配置、快速开发、内嵌服务器、微服务支持、自动化测试和生态系统支持。本专题为大家提供spring boot相关的文章、下载、课程内容,供大家免费下载体验。

139

2023.09.05

spring框架有哪些
spring框架有哪些

spring框架有Spring Core、Spring MVC、Spring Data、Spring Security、Spring AOP和Spring Boot。详细介绍:1、Spring Core,通过将对象的创建和依赖关系的管理交给容器来实现,从而降低了组件之间的耦合度;2、Spring MVC,提供基于模型-视图-控制器的架构,用于开发灵活和可扩展的Web应用程序等。

410

2023.10.12

Java Spring Boot开发
Java Spring Boot开发

本专题围绕 Java 主流开发框架 Spring Boot 展开,系统讲解依赖注入、配置管理、数据访问、RESTful API、微服务架构与安全认证等核心知识,并通过电商平台、博客系统与企业管理系统等项目实战,帮助学员掌握使用 Spring Boot 快速开发高效、稳定的企业级应用。

73

2025.08.19

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

69

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Vue 教程
Vue 教程

共42课时 | 9.6万人学习

Vue3.x 工具篇--十天技能课堂
Vue3.x 工具篇--十天技能课堂

共26课时 | 1.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号