带有 OAuth2 安全方案的 Swagger DOCS,Spring Boot 应用程序

问题描述 投票:0回答:1

我有一个使用 Spring Boot 制作的 Rest API,并由外部服务授权服务器使用 OAUTH2 进行保护,并且我正在使用 springdoc-openapi 创建文档。

问题是,当向 /token 端点发送 post 请求时,授权服务器不会返回带有 accessToken 的响应,而是抛出错误。

授权服务器:安全配置

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final String[] WHITELIST = {"/h2-console/**", "/css/**", "/images/**", "/js/**", "/actuator/health"};

    @Bean
    @Order(1)
    public SecurityFilterChain authFilterChain(HttpSecurity httpSecurity) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(httpSecurity);
        httpSecurity.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults());

        httpSecurity
                .cors(cors -> cors.configurationSource(corsConfigurationSource()))
                .exceptionHandling(exceptions -> exceptions
                .defaultAuthenticationEntryPointFor(
                        new LoginUrlAuthenticationEntryPoint("/login"),
                        new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                )
        ).oauth2ResourceServer( rs -> rs.jwt(Customizer.withDefaults()));
        return httpSecurity.build();

    }
    @Bean
    @Order(2)
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .csrf(csrf -> csrf.disable()) //disable csrf
                .headers( headers -> headers.frameOptions(fo -> fo.sameOrigin())) //allowing h2 to be displayed as a frame
                .authorizeHttpRequests(authz -> authz
                        .requestMatchers( WHITELIST).permitAll()
                        .requestMatchers(HttpMethod.POST, "/register").permitAll()
                        .requestMatchers(HttpMethod.GET, "/login").permitAll()
                        .requestMatchers(HttpMethod.OPTIONS,"/oauth2/token").permitAll()
                        .requestMatchers(HttpMethod.POST,"/oauth2/token").permitAll()
                        .anyRequest().authenticated())
                .formLogin(formLogin ->
                        formLogin.loginPage("/login"))
                .logout( logout -> logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET")))
                .build();


    }

...

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*", "http://172.28.48.1:8083"));
        configuration.addAllowedMethod(HttpMethod.POST);
        configuration.addAllowedMethod(HttpMethod.OPTIONS);
        configuration.addAllowedHeader("Authorization");
        configuration.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

账户服务:带有安全方案的 Swagger 配置

@Configuration
public class SwaggerConfig {
    @Autowired
    private OAuth2Config oAuth2Config;

    private String getAuthorizationUri() {
        return oAuth2Config.findAuthServiceUris().get("authorizationUri");
    }
    private String getTokenUri() {
        return oAuth2Config.findAuthServiceUris().get("tokenUri");
    }
    private Components apiComponents() {
        Components components = new Components();
        components.addSecuritySchemes("microservices_oauth2", microservicesOauth2());
        return components;
    }
    private SecurityScheme microservicesOauth2() {
        SecurityScheme securityScheme = new SecurityScheme();
        securityScheme.type(SecurityScheme.Type.OAUTH2);
        securityScheme.flows(new OAuthFlows().clientCredentials(new OAuthFlow().authorizationUrl(this.getAuthorizationUri()).tokenUrl(this.getTokenUri())));
        securityScheme.description("OAuth2 Client Credentials Flow for Microservices");
        return securityScheme;
    }
    private List<SecurityRequirement> securityRequirements() {

        SecurityRequirement securityRequirement = new SecurityRequirement();
        securityRequirement.addList("microservices_oauth2");
        return List.of(securityRequirement);
    }


    @Bean
    public OpenAPI openAPI() {

        return new OpenAPI().info(
                    new Info().title("Account Docs")
                            .license(new License().name("The MIT License (MIT)").url("https://opensource.org/license/mit"))
                            .version("0.1.0")
                            .contact(new Contact().name("Durian Sosa").email("[email protected]").url("https://dmsosa.github.io/dmblog/"))
                            .description("Account API for Microservices Architecture")
                    )
                .components(apiComponents())
                .security(securityRequirements());
    }
}

招摇页面: enter image description here

错误 enter image description here

enter image description here

这似乎是一个 CORS 问题,但我已在授权服务器的第一个过滤器中添加了 cors 配置。也许我不完全理解这两个 SecurityFilterChains 如何协同工作?希望有任何提示

预期结果:OAuth2 服务器在收到正确的客户端凭据后返回带有访问令牌的响应

在发送请求时 OAuth2Server 的日志中,显示了这一点

\ Invoking DisableEncodeUrlFilter (1/11)
2024-07-08T16:41:44.092+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Invoking WebAsyncManagerIntegrationFilter (2/11)
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Invoking SecurityContextHolderFilter (3/11)
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Invoking HeaderWriterFilter (4/11)
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Invoking LogoutFilter (5/11)
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.s.w.a.logout.LogoutFilter            : Did not match request to Ant [pattern='/logout', GET]
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Invoking UsernamePasswordAuthenticationFilter (6/11)
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] w.a.UsernamePasswordAuthenticationFilter : Did not match request to Ant [pattern='/login', POST]
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Invoking RequestCacheAwareFilter (7/11)
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.s.w.s.HttpSessionRequestCache        : matchingRequestParameterName is required for getMatchingRequest to lookup a value, but not provided
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Invoking SecurityContextHolderAwareRequestFilter (8/11)
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Invoking AnonymousAuthenticationFilter (9/11)
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Invoking ExceptionTranslationFilter (10/11)
2024-07-08T16:41:44.093+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Invoking AuthorizationFilter (11/11)
2024-07-08T16:41:44.094+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] estMatcherDelegatingAuthorizationManager : Authorizing SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@51f918f2]
2024-07-08T16:41:44.095+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] estMatcherDelegatingAuthorizationManager : Checking authorization on SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@51f918f2] using org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer$$Lambda/0x00000294d6ba32d8@1e5ae884
2024-07-08T16:41:44.095+01:00 DEBUG 19404 --- [auth-service] [nio-9000-exec-8] o.s.security.web.FilterChainProxy        : Secured OPTIONS /oauth2/token
2024-07-08T16:41:44.097+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match request to [Is Secure]
2024-07-08T16:41:44.098+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2024-07-08T16:41:44.098+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] .s.s.w.c.SupplierDeferredSecurityContext : Created SecurityContextImpl [Null authentication]
2024-07-08T16:41:44.098+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] .s.s.w.c.SupplierDeferredSecurityContext : Created SecurityContextImpl [Null authentication]
2024-07-08T16:41:44.098+01:00 TRACE 19404 --- [auth-service] [nio-9000-exec-8] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=172.28.48.1, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]]
spring-boot oauth-2.0 swagger-ui
1个回答
0
投票

将“*”添加到允许的标头,将“localhost”添加到允许的来源并向两个SecurityFilterChains添加cors配置后,授权按预期工作。

@Bean
    @Order(2)
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .csrf(csrf -> csrf.disable()) //disable csrf
                .cors(cors -> cors.configurationSource(corsConfigurationSource()))


    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*", "http://172.28.48.1:8083", "http://localhost:8083"));
        configuration.addAllowedMethod(HttpMethod.POST);
        configuration.addAllowedMethod(HttpMethod.OPTIONS);
        configuration.addAllowedHeader("Authorization");
        configuration.addAllowedHeader("*");
        configuration.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

这解决了问题,但我不完全明白为什么,想知道......

© www.soinside.com 2019 - 2024. All rights reserved.