Spring Boot OAuth请求/oauth/token返回401 Unauthorized问题排查
我帮你排查了下,调用/oauth/token返回401 Unauthorized的问题主要出在两个核心点上,下面是具体的修复步骤:
1. 未允许客户端通过表单提交凭证
你的curl请求是通过表单参数传递client_id和client_secret的,但AuthorizationServer的安全配置里没有开启允许这种认证方式的开关。默认情况下OAuth2要求客户端通过Basic Auth传递凭证,所以需要手动开启表单认证支持。
修改AuthorizationServerConfiguration中的configure(oauthServer)方法:
@Throws(Exception::class) override fun configure(oauthServer: AuthorizationServerSecurityConfigurer?) { oauthServer!!.realm(REALM + "/client") .allowFormAuthenticationForClients() // 新增此行,允许表单提交客户端凭证 }
2. 缺失密码编码器(Spring Security 5强制要求)
Spring Security 5之后,所有密码(包括用户密码和客户端密钥)都必须经过密码编码器处理,不能直接使用明文。你的配置里不管是用户的登录密码还是客户端的secret都是明文,这会直接导致认证失败。
步骤1:添加密码编码器Bean
在OAuth2SecurityConfiguration中新增密码编码器的Bean(测试环境可以用NoOpPasswordEncoder,生产环境请务必替换为BCryptPasswordEncoder这类安全加密方式):
@Bean fun passwordEncoder(): PasswordEncoder { // 测试用,生产环境推荐使用BCryptPasswordEncoder.getInstance() return NoOpPasswordEncoder.getInstance() }
步骤2:给用户认证绑定密码编码器
修改globalUserDetails方法,指定密码编码器:
@Autowired @Throws(Exception::class) fun globalUserDetails(auth: AuthenticationManagerBuilder, passwordEncoder: PasswordEncoder) { auth.inMemoryAuthentication() .passwordEncoder(passwordEncoder) // 绑定密码编码器 .withUser("bill").password("abc123").roles("ADMIN").and() .withUser("demo").password("1234").roles("USER") }
步骤3:给客户端配置绑定密码编码器
在AuthorizationServerConfiguration中注入PasswordEncoder,然后修改客户端的secret配置:
@Autowired private val passwordEncoder: PasswordEncoder? = null @Throws(Exception::class) override fun configure(clients: ClientDetailsServiceConfigurer?) { clients!!.inMemory() .withClient("client-id") .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit") .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT") .scopes("read", "write", "trust") .secret(passwordEncoder!!.encode("secret")) // 用编码器处理客户端密钥 .accessTokenValiditySeconds(120) .refreshTokenValiditySeconds(600) }
(如果用NoOpPasswordEncoder,encode方法会直接返回明文,也可以暂时保持.secret("secret"),但为了统一和扩展性,建议还是用编码器处理)
验证修改
完成以上修改后重启服务,再执行你的curl请求,应该就能成功获取access_token了。
另外提醒:生产环境绝对不能使用NoOpPasswordEncoder,一定要用BCryptPasswordEncoder这类安全加密方式,并且客户端密钥和用户密码都要提前加密好再配置。
内容的提问来源于stack exchange,提问作者user672009

