Spring Auth Server授权码模式调用/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配置请求参数(参数配置参考截图:
),发送GET请求访问http://localhost:9000/oauth2/authorize端点,携带参数如下:
response_type=codeclient_id=code-auth-clientscope=read-access- URL编码后的
redirect_uri=http://127.0.0.1:8080/redirect/
预期获取授权码用于后续兑换访问令牌,但接口直接返回401未授权错误。
此前尝试调整SecurityFilterChain类型Bean配置未解决问题,实现逻辑参考公开的Spring Security OAuth2授权服务教程,未额外搭建客户端应用;后续补充grant_type=authorization_code参数后问题仍然存在。
根因分析
- 配置类存在冗余无效代码:
AuthorizationServerConfig中注入的RegisteredClientProvider没有任何实际使用,多余的@ComponentScan注解无实际作用,且@ConditionalOnMissingBean注解加在RegisteredClientRepositoryBean上没有必要。 - 安全过滤链顺序不明确:仅给授权服务的
SecurityFilterChain配置了最高优先级,默认安全过滤链没有显式指定@Order值,Spring Security匹配过滤链时可能出现顺序错乱,导致授权服务端点被默认过滤链提前拦截,授权服务的安全配置不生效。 - 请求逻辑不符合授权码流程要求:
/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
相关产品推荐
相关产品推荐

