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

Spring Auth Server授权码模式调用/oauth2/authorize返回401问题排查

Spring Authorization Server 0.3.0 访问/oauth2/authorize返回401问题解决

环境与现有配置

项目使用0.3.0版本的spring-security-oauth2-authorization-server依赖,Maven配置如下:

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-oauth2-authorization-server</artifactId>
  <version>0.3.0</version>
</dependency>

项目包含两个核心配置类:

授权服务配置类 AuthorizationServerConfig

原有代码如下:

@Configuration
@ComponentScan(basePackageClasses = AuthorizationServerConfig.class)
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig {

  private final RegisteredClientProvider registeredClientProvider;

  @Autowired
  public AuthorizationServerConfig(RegisteredClientProvider registeredClientProvider) {
    this.registeredClientProvider = registeredClientProvider;
  }

  @Bean
  @Order(Ordered.HIGHEST_PRECEDENCE)
  public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    return http.formLogin(Customizer.withDefaults()).build();
  }

  @Bean
  @ConditionalOnMissingBean
  public RegisteredClientRepository registeredClientRepository() {

    RegisteredClient codeClient = RegisteredClient.withId(UUID.randomUUID().toString())
    .clientId("code-auth-client")
    .clientSecret("{noop}secret")
    .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
    .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
    .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
    .redirectUri("http://127.0.0.1:8080/redirect/")
    .scope("read-access")
    .build();

    return new InMemoryRegisteredClientRepository(codeClient);
  }

}

该配置类设计目标为导入OAuth2授权服务默认配置,定义最高优先级的授权服务安全过滤链,开启默认表单登录;同时注册内存客户端仓库,配置授权码模式客户端code-auth-client,支持CLIENT_SECRET_BASIC、CLIENT_SECRET_POST两种客户端认证方式,重定向地址为http://127.0.0.1:8080/redirect/,拥有read-access权限域。

Spring Security通用配置类 SpringSecurityConfiguration

原有代码如下:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SpringSecurityConfiguration {

  @Bean
  public InMemoryUserDetailsManager userDetailsService() {
    UserDetails user = User.withDefaultPasswordEncoder()
        .username("user")
        .password("password")
        .roles("USER")
        .build();
    return new InMemoryUserDetailsManager(user);
  }

  @Bean
  SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests(authorizeRequests ->
            authorizeRequests.anyRequest().authenticated()
        )
        .formLogin(withDefaults());
    return http.build();
  }

}

该配置类设计目标为开启Web安全与全局方法权限控制,配置内存用户存储,内置用户名为user、密码为password的USER角色账号;同时配置默认安全过滤链,要求所有请求认证后才可访问,开启默认表单登录。

问题现象

通过Postman配置请求参数(参数配置参考截图:Postman请求参数配置),发送GET请求访问http://localhost:9000/oauth2/authorize端点,携带参数如下:

  • response_type=code
  • client_id=code-auth-client
  • scope=read-access
  • URL编码后的redirect_uri=http://127.0.0.1:8080/redirect/
    预期获取授权码用于后续兑换访问令牌,但接口直接返回401未授权错误。
    此前尝试调整SecurityFilterChain类型Bean配置未解决问题,实现逻辑参考公开的Spring Security OAuth2授权服务教程,未额外搭建客户端应用;后续补充grant_type=authorization_code参数后问题仍然存在。

根因分析

  1. 配置类存在冗余无效代码:AuthorizationServerConfig中注入的RegisteredClientProvider没有任何实际使用,多余的@ComponentScan注解无实际作用,且@ConditionalOnMissingBean注解加在RegisteredClientRepositoryBean上没有必要。
  2. 安全过滤链顺序不明确:仅给授权服务的SecurityFilterChain配置了最高优先级,默认安全过滤链没有显式指定@Order值,Spring Security匹配过滤链时可能出现顺序错乱,导致授权服务端点被默认过滤链提前拦截,授权服务的安全配置不生效。
  3. 请求逻辑不符合授权码流程要求:/oauth2/authorize端点要求用户必须完成身份认证才可访问,直接在Postman中发送裸GET请求时,没有携带已认证用户的会话Cookie,请求会被认证拦截器直接拦截返回401。额外添加grant_type=authorization_code参数是无效操作,该参数仅在调用/oauth2/token端点兑换令牌时需要,授权端点不识别该参数。

修复方案

1. 修正授权服务配置类

删除冗余代码,修正后代码如下:

@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig {

  @Bean
  @Order(Ordered.HIGHEST_PRECEDENCE)
  public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    return http.formLogin(Customizer.withDefaults()).build();
  }

  @Bean
  public RegisteredClientRepository registeredClientRepository() {
    RegisteredClient codeClient = RegisteredClient.withId(UUID.randomUUID().toString())
    .clientId("code-auth-client")
    .clientSecret("{noop}secret")
    .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
    .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
    .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
    .redirectUri("http://127.0.0.1:8080/redirect/")
    .scope("read-access")
    .build();

    return new InMemoryRegisteredClientRepository(codeClient);
  }

}

2. 修正默认安全配置类

给默认安全过滤链显式指定低于授权服务过滤链的优先级,修正后代码如下:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SpringSecurityConfiguration {

  @Bean
  public InMemoryUserDetailsManager userDetailsService() {
    UserDetails user = User.withDefaultPasswordEncoder()
        .username("user")
        .password("password")
        .roles("USER")
        .build();
    return new InMemoryUserDetailsManager(user);
  }

  @Bean
  @Order(2)
  SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests(authorizeRequests ->
            authorizeRequests.anyRequest().authenticated()
        )
        .formLogin(withDefaults());
    return http.build();
  }

}

3. 调整Postman测试逻辑

授权码模式是基于浏览器重定向、会话保持的流程,测试时需要按照以下步骤操作:

  • 开启Postman的自动重定向、Cookie自动存储功能
  • 首次发送GET请求到/oauth2/authorize端点时,会被302跳转到默认登录页,此时提交用户名user、密码password完成表单登录,Postman会自动保存服务端返回的会话Cookie
  • 携带有效会话Cookie再次请求/oauth2/authorize端点,完成授权确认后会自动302跳转到配置的重定向地址,地址后携带的code参数就是需要的授权码

提示:如果只是接口测试不需要走浏览器流程,可以将授权端点配置为支持HTTP Basic认证,但不符合授权码模式的生产使用规范。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:27:25