You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security OAuth2用户存储及API Token使用配置求助

Spring WebFlux Security OAuth2 实现方案

以下是满足你需求的完整配置与代码示例,基于Google/GitHub OAuth2登录,包含用户信息持久化、Token管理及资源服务器调用逻辑:

1. 核心依赖配置

在pom.xml中添加必要依赖(Gradle可自行转换格式):

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>
    <!-- 以Reactive MongoDB为例,若用JPA替换为spring-boot-starter-data-jpa -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
    </dependency>
</dependencies>

2. 自定义用户实体与数据仓库

创建与数据库表关联的用户实体,存储OAuth2授权信息:

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "users")
public class AppUser {
    @Id
    private String id;
    private String provider; // 授权提供者:google/github/vk
    private String providerId; // 第三方平台用户ID
    private String username;
    private String email;
    private String avatarUrl;

    // 构造器、getter、setter省略
}

对应的Reactive数据仓库:

import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import reactor.core.publisher.Mono;

public interface AppUserRepository extends ReactiveCrudRepository<AppUser, String> {
    Mono<AppUser> findByProviderAndProviderId(String provider, String providerId);
}

3. 自定义OAuth2用户服务(处理用户存储)

实现ReactiveOAuth2UserService,在用户登录成功后将第三方用户信息同步到本地数据库:

import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.user.OAuth2User;
import reactor.core.publisher.Mono;

@Component
public class CustomReactiveOAuth2UserService implements ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> {

    private final AppUserRepository userRepository;

    public CustomReactiveOAuth2UserService(AppUserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public Mono<OAuth2User> loadUser(OAuth2UserRequest userRequest) {
        ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultReactiveOAuth2UserService();
        
        return delegate.loadUser(userRequest)
                .flatMap(oAuth2User -> {
                    String provider = userRequest.getClientRegistration().getRegistrationId();
                    String providerId = oAuth2User.getName();
                    
                    return userRepository.findByProviderAndProviderId(provider, providerId)
                            .switchIfEmpty(Mono.defer(() -> {
                                AppUser newUser = new AppUser();
                                newUser.setProvider(provider);
                                newUser.setProviderId(providerId);
                                newUser.setUsername(oAuth2User.getAttribute("name"));
                                newUser.setEmail(oAuth2User.getAttribute("email"));
                                newUser.setAvatarUrl(oAuth2User.getAttribute("picture"));
                                return userRepository.save(newUser);
                            }))
                            .thenReturn(oAuth2User);
                });
    }
}

4. 调整Security配置

更新安全配置,指定自定义用户服务并完善OAuth2登录逻辑:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@Configuration
@EnableWebFluxSecurity
public class OAuth2LoginSecurityConfig {

    private final CustomReactiveOAuth2UserService customOAuth2UserService;

    public OAuth2LoginSecurityConfig(CustomReactiveOAuth2UserService customOAuth2UserService) {
        this.customOAuth2UserService = customOAuth2UserService;
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        http
            .authorizeExchange(exchanges -> exchanges
                .anyExchange().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2
                .userInfoEndpoint(userInfo -> userInfo
                    .userService(customOAuth2UserService)
                )
            )
            .logout(logout -> logout
                .logoutSuccessUrl("/")
            )
            .csrf(csrf -> csrf.disable()); // 根据实际场景调整CSRF策略

        return http.build();
    }
}

5. 配置OAuth2客户端信息

在application.yml中添加Google/GitHub的客户端配置(需提前在对应平台创建OAuth应用):

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: YOUR_GOOGLE_CLIENT_ID
            client-secret: YOUR_GOOGLE_CLIENT_SECRET
            scope: openid,email,profile
          github:
            client-id: YOUR_GITHUB_CLIENT_ID
            client-secret: YOUR_GITHUB_CLIENT_SECRET
            scope: user:email,read:user
        provider:
          # 后续切换VK时添加以下配置
          vk:
            authorization-uri: https://oauth.vk.com/authorize
            token-uri: https://oauth.vk.com/access_token
            user-info-uri: https://api.vk.com/method/users.get?fields=photo_max,email
            user-name-attribute: response

6. Token管理与资源服务器调用

6.1 获取已保存的Token

Spring Security默认通过ReactiveOAuth2AuthorizedClientService保存用户Token,可直接注入获取:

import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
public class TokenController {

    private final ReactiveOAuth2AuthorizedClientService authorizedClientService;

    public TokenController(ReactiveOAuth2AuthorizedClientService authorizedClientService) {
        this.authorizedClientService = authorizedClientService;
    }

    @GetMapping("/token")
    public Mono<String> getToken(OAuth2AuthenticationToken authentication) {
        return authorizedClientService.loadAuthorizedClient(
                        authentication.getAuthorizedClientRegistrationId(),
                        authentication.getName()
                )
                .map(authorizedClient -> authorizedClient.getAccessToken().getTokenValue());
    }
}

6.2 自动携带Token调用资源服务器

配置WebClient实现自动在请求头中携带OAuth2 Token:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {

    @Bean
    public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrationRepository) {
        ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Filter =
                new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository);
        oauth2Filter.setDefaultOAuth2AuthorizedClient(true);

        return WebClient.builder()
                .filter(oauth2Filter)
                .build();
    }
}

使用该WebClient调用资源服务器API:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@RestController
public class ResourceController {

    private final WebClient webClient;

    public ResourceController(WebClient webClient) {
        this.webClient = webClient;
    }

    @GetMapping("/call-github-api")
    public Mono<String> callGithubApi() {
        return webClient.get()
                .uri("https://api.github.com/user")
                .retrieve()
                .bodyToMono(String.class);
    }
}

内容的提问来源于stack exchange,提问作者Roman Anokhin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 05:13:14