首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >微服务安全之OAuth2协议深度解析:从原理到实战落地

微服务安全之OAuth2协议深度解析:从原理到实战落地

作者头像
果酱带你啃java
发布2026-04-14 13:54:57
发布2026-04-14 13:54:57
480
举报

一、OAuth2协议核心概念与应用场景

1.1 为什么需要OAuth2?

在微服务架构中,服务间通信的安全性至关重要。传统的用户名密码直接传递方式存在严重安全隐患:第三方应用可能存储用户凭证、用户无法限制第三方应用的访问权限、一旦凭证泄露后果不堪设想。OAuth2协议通过授权层分离用户凭证与第三方应用的访问权限,解决了这些痛点。

OAuth2的核心价值在于:**"在不分享用户密码的前提下,允许第三方应用获取有限的访问权限"**。例如:

  • 微信登录第三方网站时,无需提供微信密码
  • 第三方应用获取用户GitHub仓库权限时,可限制只读/读写范围
  • 微服务架构中,服务A访问服务B的受保护资源时的权限控制

1.2 OAuth2核心角色

  1. 资源所有者(Resource Owner):能够授予对受保护资源访问权限的实体,通常指用户
  2. 授权服务器(Authorization Server):验证资源所有者身份并颁发访问令牌的服务器
  3. 资源服务器(Resource Server):存储受保护资源并接受/响应令牌访问的服务器
  4. 客户端(Client):代表资源所有者请求访问受保护资源的应用程序

1.3 OAuth2协议流程

二、OAuth2的四种授权模式

2.1 授权码模式(Authorization Code)

适用场景:服务端应用、有后端的Web应用 核心特点:最安全的模式,授权码通过前端传输,令牌通过后端传输

2.1.1 授权码模式流程
2.1.2 代码实现:授权服务器配置

pom.xml依赖配置

代码语言:javascript
复制
<dependencies>
    <!-- Spring Security OAuth2 Authorization Server -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-authorization-server</artifactId>
        <version>1.2.3</version>
    </dependency>
    <!-- Spring Security Web -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>6.2.1</version>
    </dependency>
    <!-- Spring Security Config -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>6.2.1</version>
    </dependency>
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.30</version>
        <scope>provided</scope>
    </dependency>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>3.2.2</version>
    </dependency>
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.3.0</version>
        <scope>runtime</scope>
    </dependency>
    <!-- MyBatis Plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.5</version>
    </dependency>
    <!-- Swagger3 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>
    <!-- Fastjson2 -->
    <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>2.0.32</version>
    </dependency>
    <!-- Guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>32.1.3-jre</version>
    </dependency>
</dependencies>

授权服务器配置类

代码语言:javascript
复制
package com.jam.demo.config;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;

/**
 * OAuth2授权服务器配置类
 * @author ken
 */
@Configuration
@EnableWebSecurity
@Slf4j
publicclass AuthorizationServerConfig {

    /**
     * 配置授权服务器安全过滤器链
     */
    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults()); // 启用OpenID Connect 1.0
        
        http.exceptionHandling(exceptions -> exceptions
                .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));
        
        return http.build();
    }

    /**
     * 配置默认安全过滤器链
     */
    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -> authorize
                        .anyRequest().authenticated())
                .formLogin(Customizer.withDefaults());
        
        return http.build();
    }

    /**
     * 配置用户详情服务
     */
    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
                .username("jam")
                .password("123456")
                .roles("USER")
                .build();
        
        returnnew InMemoryUserDetailsManager(userDetails);
    }

    /**
     * 配置客户端信息
     */
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("demo-client")
                .clientSecret("{noop}demo-secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri("http://localhost:8080/login/oauth2/code/demo-client")
                .scope(OidcScopes.OPENID)
                .scope("demo.read")
                .scope("demo.write")
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .build();
        
        returnnew InMemoryRegisteredClientRepository(registeredClient);
    }

    /**
     * 配置JWK源
     */
    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        returnnew ImmutableJWKSet<>(jwkSet);
    }

    /**
     * 生成RSA密钥对
     */
    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        } catch (Exception ex) {
            log.error("生成RSA密钥对失败", ex);
            thrownew IllegalStateException(ex);
        }
        return keyPair;
    }

    /**
     * 配置JWT解码器
     */
    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

    /**
     * 配置授权服务器设置
     */
    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }
}
2.1.3 测试授权码流程
  1. 请求授权码
代码语言:javascript
复制
GET http://localhost:8080/oauth2/authorize?response_type=code&client_id=demo-client&redirect_uri=http://localhost:8080/login/oauth2/code/demo-client&scope=openid%20demo.read&state=abc123
  1. 用户认证授权后,获取授权码
代码语言:javascript
复制
http://localhost:8080/login/oauth2/code/demo-client?code=V8D6BfE9...&state=abc123
  1. 使用授权码获取令牌
代码语言:javascript
复制
POST http://localhost:8080/oauth2/token
Authorization: Basic ZGVtby1jbGllbnQ6ZGVtby1zZWNyZXQ=
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=V8D6BfE9...&redirect_uri=http://localhost:8080/login/oauth2/code/demo-client
  1. 令牌响应
代码语言:javascript
复制
{
    "access_token": "eyJraWQiOiI...",
    "refresh_token": "eyJraWQiOiJ...",
    "scope": "openid demo.read",
    "id_token": "eyJraWQiOiJ...",
    "token_type": "Bearer",
    "expires_in": 3599
}

2.2 简化模式(Implicit)

适用场景:纯前端应用、无后端的单页应用 核心特点:直接返回令牌,不经过后端,安全性较低

2.2.1 简化模式流程
2.2.2 配置简化模式客户端
代码语言:javascript
复制
RegisteredClient implicitClient = RegisteredClient.withId(UUID.randomUUID().toString())
        .clientId("implicit-client")
        .clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
        .authorizationGrantType(AuthorizationGrantType.IMPLICIT)
        .redirectUri("http://localhost:8080/implicit/callback")
        .scope("demo.read")
        .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
        .build();

2.3 密码模式(Resource Owner Password Credentials)

适用场景:高度信任的应用、用户与客户端属于同一组织 核心特点:用户直接向客户端提供用户名密码,客户端使用这些凭证获取令牌

2.3.1 密码模式流程
2.3.2 密码模式请求示例
代码语言:javascript
复制
POST http://localhost:8080/oauth2/token
Authorization: Basic ZGVtby1jbGllbnQ6ZGVtby1zZWNyZXQ=
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=jam&password=123456&scope=demo.read

2.4 客户端凭证模式(Client Credentials)

适用场景:机器对机器的通信、后台服务间的通信 核心特点:客户端使用自身凭证获取令牌,不涉及用户授权

2.4.1 客户端凭证模式流程
2.4.2 客户端凭证模式配置
代码语言:javascript
复制
RegisteredClient clientCredentialsClient = RegisteredClient.withId(UUID.randomUUID().toString())
        .clientId("client-credentials-client")
        .clientSecret("{noop}client-secret")
        .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
        .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
        .scope("demo.admin")
        .build();
2.4.3 客户端凭证模式请求示例
代码语言:javascript
复制
POST http://localhost:8080/oauth2/token
Authorization: Basic Y2xpZW50LWNyZWRlbnRpYWxzLWNsaWVudDpjbGllbnQtc2VjcmV0
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=demo.admin

三、OAuth2令牌详解

3.1 令牌类型

  1. 访问令牌(Access Token):用于访问受保护资源的令牌,通常有效期较短(如1小时)
  2. 刷新令牌(Refresh Token):用于在访问令牌过期后获取新的访问令牌,有效期较长(如30天)
  3. ID令牌(ID Token):OpenID Connect扩展的令牌,包含用户身份信息

3.2 JWT令牌结构

JWT(JSON Web Token)是OAuth2中常用的令牌格式,由三部分组成:

  1. Header:指定令牌类型和签名算法
代码语言:javascript
复制
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "123456"
}
  1. Payload:包含令牌的声明信息
代码语言:javascript
复制
{
  "sub": "1234567890",
  "name": "jam",
  "iat": 1516239022,
  "exp": 1516242622,
  "scope": "demo.read",
  "client_id": "demo-client"
}
  1. Signature:使用私钥对前两部分的签名
代码语言:javascript
复制
RSASHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  privateKey
)

3.3 令牌存储与管理

3.3.1 令牌存储方案对比

存储方案

优点

缺点

适用场景

内存存储

速度快、实现简单

不持久化、重启丢失

开发环境、测试环境

数据库存储

持久化、可扩展

性能开销、需要管理

生产环境、中小型应用

Redis存储

高性能、支持过期

需要Redis服务、网络开销

高并发生产环境

3.3.2 MySQL令牌存储实现

数据库表结构

代码语言:javascript
复制
CREATE TABLE oauth2_authorization (
    idVARCHAR(100) NOTNULL PRIMARY KEY,
    registered_client_id VARCHAR(100) NOTNULL,
    principal_name VARCHAR(200) NOTNULL,
    authorization_grant_type VARCHAR(100) NOTNULL,
    authorized_scopes VARCHAR(1000) DEFAULTNULL,
    attributesTEXTDEFAULTNULL,
    state VARCHAR(500) DEFAULTNULL,
    authorization_code_value TEXTDEFAULTNULL,
    authorization_code_issued_at TIMESTAMPDEFAULTNULL,
    authorization_code_expires_at TIMESTAMPDEFAULTNULL,
    authorization_code_metadata TEXTDEFAULTNULL,
    access_token_value TEXTDEFAULTNULL,
    access_token_issued_at TIMESTAMPDEFAULTNULL,
    access_token_expires_at TIMESTAMPDEFAULTNULL,
    access_token_metadata TEXTDEFAULTNULL,
    access_token_type VARCHAR(100) DEFAULTNULL,
    access_token_scopes VARCHAR(1000) DEFAULTNULL,
    oidc_id_token_value TEXTDEFAULTNULL,
    oidc_id_token_issued_at TIMESTAMPDEFAULTNULL,
    oidc_id_token_expires_at TIMESTAMPDEFAULTNULL,
    oidc_id_token_metadata TEXTDEFAULTNULL,
    refresh_token_value TEXTDEFAULTNULL,
    refresh_token_issued_at TIMESTAMPDEFAULTNULL,
    refresh_token_expires_at TIMESTAMPDEFAULTNULL,
    refresh_token_metadata TEXTDEFAULTNULL,
    user_code_value TEXTDEFAULTNULL,
    user_code_issued_at TIMESTAMPDEFAULTNULL,
    user_code_expires_at TIMESTAMPDEFAULTNULL,
    user_code_metadata TEXTDEFAULTNULL,
    device_code_value TEXTDEFAULTNULL,
    device_code_issued_at TIMESTAMPDEFAULTNULL,
    device_code_expires_at TIMESTAMPDEFAULTNULL,
    device_code_metadata TEXTDEFAULTNULL,
    INDEX idx_registered_client_id (registered_client_id),
    INDEX idx_principal_name (principal_name),
    INDEX idx_authorization_code_value (authorization_code_value(255)),
    INDEX idx_access_token_value (access_token_value(255)),
    INDEX idx_refresh_token_value (refresh_token_value(255)),
    INDEX idx_user_code_value (user_code_value(255)),
    INDEX idx_device_code_value (device_code_value(255))
) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATETABLE oauth2_registered_client (
    idVARCHAR(100) NOTNULL PRIMARY KEY,
    client_id VARCHAR(100) NOTNULL,
    client_id_issued_at TIMESTAMPDEFAULTCURRENT_TIMESTAMPNOTNULL,
    client_secret VARCHAR(200) DEFAULTNULL,
    client_secret_expires_at TIMESTAMPDEFAULTNULL,
    client_name VARCHAR(200) NOTNULL,
    client_authentication_methods VARCHAR(1000) NOTNULL,
    authorization_grant_types VARCHAR(1000) NOTNULL,
    redirect_uris VARCHAR(1000) DEFAULTNULL,
    post_logout_redirect_uris VARCHAR(1000) DEFAULTNULL,
    scopes VARCHAR(1000) NOTNULL,
    client_settings TEXTNOTNULL,
    token_settings TEXTNOTNULL,
    UNIQUEKEY uk_client_id (client_id)
) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

配置JDBC令牌存储

代码语言:javascript
复制
@Bean
public RegisteredClientRepository registeredClientRepository(DataSource dataSource) {
    returnnew JdbcRegisteredClientRepository(dataSource);
}

@Bean
public OAuth2AuthorizationService authorizationService(DataSource dataSource, RegisteredClientRepository registeredClientRepository) {
    returnnew JdbcOAuth2AuthorizationService(dataSource, registeredClientRepository);
}

@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(DataSource dataSource, RegisteredClientRepository registeredClientRepository) {
    returnnew JdbcOAuth2AuthorizationConsentService(dataSource, registeredClientRepository);
}

四、资源服务器实现

4.1 资源服务器配置

代码语言:javascript
复制
package com.jam.demo.resource.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;

import java.security.interfaces.RSAPublicKey;

/**
 * 资源服务器配置类
 * @author ken
 */
@Configuration
@EnableWebSecurity
@Slf4j
publicclass ResourceServerConfig {

    /**
     * 配置资源服务器安全过滤器链
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/public/**").permitAll()
                        .requestMatchers("/api/**").authenticated()
                        .requestMatchers("/api/admin/**").hasAuthority("SCOPE_demo.admin")
                        .requestMatchers("/api/write/**").hasAuthority("SCOPE_demo.write")
                        .requestMatchers("/api/read/**").hasAuthority("SCOPE_demo.read")
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(Customizer.withDefaults()));
        
        return http.build();
    }

    /**
     * 配置JWT解码器
     */
    @Bean
    public JwtDecoder jwtDecoder(RSAPublicKey publicKey) {
        return NimbusJwtDecoder.withPublicKey(publicKey).build();
    }
}

4.2 受保护资源实现

代码语言:javascript
复制
package com.jam.demo.resource.controller;

import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Maps;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * 受保护资源控制器
 * @author ken
 */
@RestController
@RequestMapping("/api")
@Api(tags = "受保护资源接口")
@Slf4j
publicclass ProtectedResourceController {

    /**
     * 获取公共信息
     */
    @GetMapping("/public/info")
    @ApiOperation("获取公共信息")
    public JSONObject getPublicInfo() {
        JSONObject result = new JSONObject();
        result.put("message", "这是公共信息,无需认证");
        result.put("status", "success");
        return result;
    }

    /**
     * 获取用户信息
     */
    @GetMapping("/read/userinfo")
    @ApiOperation("获取用户信息")
    public Map<String, Object> getUserInfo(Authentication authentication) {
        Map<String, Object> result = Maps.newHashMap();
        
        Jwt jwt = (Jwt) authentication.getPrincipal();
        result.put("username", jwt.getClaim("sub"));
        result.put("scopes", jwt.getClaim("scope"));
        result.put("authorities", authentication.getAuthorities());
        result.put("token", jwt.getTokenValue());
        
        return result;
    }

    /**
     * 管理员操作
     */
    @GetMapping("/admin/operation")
    @ApiOperation("管理员操作")
    public JSONObject adminOperation() {
        JSONObject result = new JSONObject();
        result.put("message", "管理员操作成功");
        result.put("status", "success");
        return result;
    }
}

4.3 资源访问测试

代码语言:javascript
复制
GET http://localhost:8081/api/read/userinfo
Authorization: Bearer eyJraWQiOiI...

响应结果:

代码语言:javascript
复制
{
    "username": "jam",
    "scopes": "demo.read",
    "authorities": [
        {
            "authority": "SCOPE_demo.read"
        }
    ],
    "token": "eyJraWQiOiI..."
}

五、OAuth2最佳实践与安全防护

5.1 安全最佳实践

  1. 使用HTTPS:所有OAuth2相关通信必须使用HTTPS,防止中间人攻击
  2. 令牌有效期设置:访问令牌有效期不宜过长(建议1小时内),刷新令牌可适当延长
  3. 授权码的一次性使用:授权码只能使用一次,使用后立即失效
  4. 客户端认证:机密客户端必须进行客户端认证,使用client_secret或JWT客户端断言
  5. 作用域精细化:根据实际需求定义精细的作用域,遵循最小权限原则

5.2 常见安全风险与防护

安全风险

防护措施

授权码拦截

使用PKCE(Proof Key for Code Exchange)、HTTPS、短有效期

令牌泄露

存储在安全位置、使用HTTPS、实现令牌撤销机制

CSRF攻击

使用state参数、验证redirect_uri

客户端伪装

验证client_id、使用客户端认证、限制redirect_uri

5.3 PKCE实现(授权码模式增强)

代码语言:javascript
复制
RegisteredClient pkceClient = RegisteredClient.withId(UUID.randomUUID().toString())
        .clientId("pkce-client")
        .clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
        .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
        .redirectUri("http://localhost:8080/login/oauth2/code/pkce-client")
        .scope("demo.read")
        .clientSettings(ClientSettings.builder()
                .requireAuthorizationConsent(true)
                .requireProofKey(true) // 启用PKCE
                .build())
        .build();

六、OAuth2与OpenID Connect的关系

OpenID Connect(OIDC)是基于OAuth2的身份认证协议,增加了:

  1. ID令牌:包含用户身份信息的JWT令牌
  2. 用户信息端点:用于获取用户详细信息的端点
  3. 发现端点:提供OIDC配置信息的端点

OIDC的核心流程是在OAuth2授权码流程基础上,增加了openid作用域,授权服务器会返回ID令牌。

七、总结

OAuth2协议作为微服务架构中身份认证和授权的标准解决方案,通过灵活的授权模式和令牌机制,实现了安全的第三方授权。在实际应用中,需要根据具体场景选择合适的授权模式,同时遵循安全最佳实践,确保系统的安全性。

本文从OAuth2的核心概念、四种授权模式、令牌机制、实战实现到安全防护进行了全面解析,希望能帮助大家深入理解并正确应用OAuth2协议,构建安全可靠的微服务系统。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 果酱带你啃java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、OAuth2协议核心概念与应用场景
    • 1.1 为什么需要OAuth2?
    • 1.2 OAuth2核心角色
    • 1.3 OAuth2协议流程
    • 二、OAuth2的四种授权模式
      • 2.1 授权码模式(Authorization Code)
      • 2.2 简化模式(Implicit)
      • 2.3 密码模式(Resource Owner Password Credentials)
      • 2.4 客户端凭证模式(Client Credentials)
    • 三、OAuth2令牌详解
      • 3.1 令牌类型
      • 3.2 JWT令牌结构
      • 3.3 令牌存储与管理
    • 四、资源服务器实现
      • 4.1 资源服务器配置
      • 4.2 受保护资源实现
      • 4.3 资源访问测试
    • 五、OAuth2最佳实践与安全防护
      • 5.1 安全最佳实践
      • 5.2 常见安全风险与防护
      • 5.3 PKCE实现(授权码模式增强)
    • 六、OAuth2与OpenID Connect的关系
    • 七、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档