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

Spring Boot SecurityFilterChain注解致Postman与Swagger权限互斥问题

Spring Boot Azure AD权限控制异常:Postman与Swagger访问权限互斥问题

问题现象

  • Postman和Swagger均通过Azure AD正确获取令牌后,仅Postman能访问控制器接口,Swagger返回401未授权
  • 当MySecurityService类添加@Component/@Service/@Configuration注解时:
    • Postman请求控制器正常通过
    • Swagger请求返回HTTP 401,响应头包含www-authenticate: Bearer
  • 移除上述注解后,访问权限反转:Swagger可正常访问控制器,Postman无法访问

相关代码

MySecurityService类

package br.com.xxxx.authentication.service;

import br.com.xxxx.authentication.service.abstraction.interfaces.IMySecurityService;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.core.Ordered;
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.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class MySecurityService implements IMySecurityService {

    @Order(Ordered.HIGHEST_PRECEDENCE)
    @Bean
    public SecurityFilterChain clientSecurityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception {
        http.oauth2Login();
        final var logoutSuccessHandler = new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository);
        logoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}");
        http.logout(logout -> logout.logoutSuccessHandler(logoutSuccessHandler));
        http.cors().disable();
        http.csrf().disable();
        http.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
        http.authorizeHttpRequests().requestMatchers("/swagger*/**", "/v3/api-docs*/**", "/login/**", "/oauth2/**", "/user/login/**").permitAll().anyRequest().authenticated();
        return http.build();
    }
}

IMySecurityService接口

package br.com.xxxx.authentication.service.abstraction.interfaces;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.web.SecurityFilterChain;

public interface IMySecurityService {
    SecurityFilterChain clientSecurityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception;
}

调试日志及请求信息

当使用@Configuration注解时,Swagger返回401;移除注解后权限反转。以下是应用启动及请求日志:

Connected to the target VM, address: '127.0.0.1:57781', transport: 'socket'

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.1)

2024-01-26T12:56:48.083-03:00  INFO 74604 --- [           main] b.com.xxxx.PlatformMstemplateApplication  : Starting PlatformMstemplateApplication using Java 18.0.2.1 with PID 74604 (C:\Reconstruindo\Platform.MsTemplate.Java\target\classes started by 4960000083 in C:\Reconstruindo\Platform.MsTemplate.Java)
2024-01-26T12:56:48.089-03:00  INFO 74604 --- [           main] b.com.xxxx.PlatformMstemplateApplication  : No active profile set, falling back to 1 default profile: "default"
2024-01-26T12:56:50.445-03:00  INFO 74604 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=e197ce10-f929-386d-a01e-9b5a9061e13f
2024-01-26T12:56:51.076-03:00  INFO 74604 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2024-01-26T12:56:51.087-03:00  INFO 74604 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-01-26T12:56:51.088-03:00  INFO 74604 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.10]
2024-01-26T12:56:51.287-03:00  INFO 74604 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-01-26T12:56:51.290-03:00  INFO 74604 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3054 ms
2024-01-26T12:57:05.845-03:00 ERROR 74604 --- [nio-8080-exec-8] Retira.MsTemplate                        : executando controller
{"@timestamp":"2024-01-26T12:57:05.8453726-03:00","@version":"1","message":"executando controller","logger_name":"Retira.MsTemplate","thread_name":"http-nio-8080-exec-8","level":"ERROR","level_value":40000,"CONSOLE_LOG_THRESHOLD":"TRACE","FILE_LOG_PATTERN":"%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %5p 74604 --- [%t] %-40.40logger{39} : %m%n%wEx","FILE_LOG_CHARSET":"UTF-8","PID":"74604","CONSOLE_LOG_PATTERN":"%clr(%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}){faint} %clr(%5p) %clr(74604){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx","FILE_LOG_THRESHOLD":"TRACE","CONSOLE_LOG_CHARSET":"UTF-8"}
2024-01-26T12:57:05.853-03:00 ERROR 74604 --- [nio-8080-exec-8] Retira.MsTemplate                        : executando repositotory
{"@timestamp":"2024-01-26T12:57:05.8533763-03:00","@version":"1","message":"executando repositotory","logger_name":"Retira.MsTemplate","thread_name":"http-nio-8080-exec-8","level":"ERROR","level_value":40000,"CONSOLE_LOG_THRESHOLD":"TRACE","FILE_LOG_PATTERN":"%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %5p 74604 --- [%t] %-40.40logger{39} : %m%n%wEx","FILE_LOG_CHARSET":"UTF-8","PID":"74604","CONSOLE_LOG_PATTERN":"%clr(%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}){faint} %clr(%5p) %clr(74604){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx","FILE_LOG_THRESHOLD":"TRACE","CONSOLE_LOG_CHARSET":"UTF-8"}
2024-01-26T12:57:06.533-03:00 ERROR 74604 --- [nio-8080-exec-8] Retira.MsTemplate                        : finalizando repositotory
{"@timestamp":"2024-01-26T12:57:06.5333754-03:00","@version":"1","message":"finalizando repositotory","logger_name":"Retira.MsTemplate","thread_name":"http-nio-8080-exec-8","level":"ERROR","level_value":40000,"CONSOLE_LOG_THRESHOLD":"TRACE","FILE_LOG_PATTERN":"%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %5p 74604 --- [%t] %-40.40logger{39} : %m%n%wEx","FILE_LOG_CHARSET":"UTF-8","PID":"74604","CONSOLE_LOG_PATTERN":"%clr(%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}){faint} %clr(%5p) %clr(74604){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx","FILE_LOG_THRESHOLD":"TRACE","CONSOLE_LOG_CHARSET":"UTF-8"}

问题分析与解决方案

核心原因

  1. SecurityFilterChain注册冲突:带@Configuration时,自定义的SecurityFilterChain生效,Swagger请求虽被配置为permitAll,但Swagger UI默认通过会话传递令牌,而Resource Server仅识别请求头中的Bearer令牌,导致验证失败。
  2. 无注解时的默认配置:移除注解后,自定义配置不加载,Spring Boot启用默认安全规则,允许会话认证,Swagger的会话令牌可通过,但Postman的请求头令牌无法被默认规则识别,导致访问失败。

解决步骤

1. 修复SecurityFilterChain配置,兼容两种认证方式

保留@Configuration注解,调整配置同时支持请求头令牌(Postman)和会话认证(Swagger),并确保Swagger全路径放行:

@Configuration
public class MySecurityService implements IMySecurityService {

    @Order(Ordered.HIGHEST_PRECEDENCE)
    @Bean
    public SecurityFilterChain clientSecurityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception {
        // 配置OAuth2登录,支持Swagger UI的会话认证
        http.oauth2Login(oauth2 -> oauth2.loginPage("/oauth2/authorization/azure-ad"));
        
        // 配置OIDC登出逻辑
        final var logoutSuccessHandler = new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository);
        logoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}");
        http.logout(logout -> logout.logoutSuccessHandler(logoutSuccessHandler));
        
        // 放行所有Swagger相关路径,包括UI、文档和OAuth2回调
        http.authorizeHttpRequests(auth -> auth
                .requestMatchers(
                        "/swagger-ui/**",
                        "/swagger-ui.html",
                        "/v3/api-docs/**",
                        "/v3/api-docs.yaml",
                        "/login/**",
                        "/oauth2/**",
                        "/user/login/**"
                ).permitAll()
                .anyRequest().authenticated());
        
        // 配置Resource Server支持JWT,兼容Postman的请求头令牌
        http.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
        
        // 按需配置CORS/CSRF:保留CORS支持,忽略Swagger路径的CSRF校验
        http.cors(Customizer.withDefaults());
        http.csrf(csrf -> csrf.ignoringRequestMatchers("/v3/api-docs/**", "/swagger-ui/**"));
        
        return http.build();
    }
}

2. 配置Swagger自动携带Bearer令牌

确保OpenAPI配置中启用Azure AD OAuth2认证,让Swagger UI登录后自动在请求头中添加令牌:

@Configuration
public class OpenApiConfig {
    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                .components(new Components()
                        .addSecuritySchemes("azure-ad", new SecurityScheme()
                                .type(SecurityScheme.Type.OAUTH2)
                                .flows(new OAuthFlows()
                                        .authorizationCode(new OAuthFlow()
                                                .authorizationUrl("https://login.microsoftonline.com/{你的租户ID}/oauth2/v2.0/authorize")
                                                .tokenUrl("https://login.microsoftonline.com/{你的租户ID}/oauth2/v2.0/token")
                                                .scopes(new Scopes()
                                                        .addString("api://{你的客户端ID}/access_as_user", "以用户身份访问API"))))))
                .addSecurityItem(new SecurityRequirement().addList("azure-ad"));
    }
}

3. 验证令牌传递

  • Postman:确认请求头包含Authorization: Bearer {你的令牌}
  • Swagger UI:登录后通过浏览器开发者工具查看网络请求,确认请求头自动携带Bearer令牌

验证结果

调整配置后,Postman通过请求头令牌、Swagger通过OAuth2会话令牌均可正常访问控制器接口,权限互斥问题解决。


内容的提问来源于stack exchange,提问作者Carlos Henrique

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:20:55