带有 client_credentials 的 Spring Oauth2 不验证用户

Posted

技术标签:

【中文标题】带有 client_credentials 的 Spring Oauth2 不验证用户【英文标题】:Spring Oauth2 with client_credentials does not validate user 【发布时间】:2021-11-11 01:03:08 【问题描述】:

我正在尝试使用 Spring Boot 设置一个简单的 Oauth2 服务器,目前只有 client_credentials 流,在内存中使用用户和客户端等。基本上是最基本的项目,所以我可以在它上面进行构建。有一段时间没有在 Spring 中使用 Oauth2,所以我有点卡住了。我可以获得访问令牌,但似乎 Spring 实际上并没有验证客户端发送的用户名/密码。下面是两个配置类(AuthorizationServerConfigurerAdapter、WebSecurityConfigurerAdapter):

@Configuration
@EnableAuthorizationServer
public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter 

   private final AuthenticationManager authenticationManager;

   private final PasswordEncoder passwordEncoder;

   private final UserDetailsService userService;

   @Value("$jwt.signing-key")
   private String jwtSigningKey;

   @Value("$jwt.accessTokenValidititySeconds")
   private int accessTokenValiditySeconds;

   @Value("$jwt.refreshTokenValiditySeconds")
   private int refreshTokenValiditySeconds;

   public OAuthConfiguration(AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder, UserDetailsService userService) 
       this.authenticationManager = authenticationManager;
       this.passwordEncoder = passwordEncoder;
       this.userService = userService;
   

   @Override
   public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
       clients.inMemory()
               .withClient("test-client-id")
               .secret(passwordEncoder.encode("test-client-secret"))
               .accessTokenValiditySeconds(accessTokenValiditySeconds)
               .refreshTokenValiditySeconds(refreshTokenValiditySeconds)
               .authorizedGrantTypes("client_credentials")
               .scopes("read", "write")
               .resourceIds("api");
   

   @Override
   public void configure(final AuthorizationServerEndpointsConfigurer endpoints) 
       endpoints
               .accessTokenConverter(accessTokenConverter())
               .userDetailsService(userService)
               .authenticationManager(authenticationManager);
   

   @Bean
   JwtAccessTokenConverter accessTokenConverter() 
       JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
       converter.setSigningKey(jwtSigningKey);
       return converter;
   


和:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter 

    private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

   public WebSecurityConfiguration(CustomAuthenticationEntryPoint customAuthenticationEntryPoint) 
       this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;
   

   @Bean
   public DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) 
       DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
       provider.setPasswordEncoder(passwordEncoder());
       provider.setUserDetailsService(userDetailsService);
       return provider;
   

   @Bean
   public PasswordEncoder passwordEncoder() 
       return new BCryptPasswordEncoder();
   

    @Bean
    @Override
    public UserDetailsService userDetailsServiceBean() throws Exception 
        return super.userDetailsServiceBean();
    

    @Bean
   @Override
   public AuthenticationManager authenticationManagerBean() throws Exception 
       return super.authenticationManagerBean();
   

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.inMemoryAuthentication()
            .withUser("user1")
            .password("$2a$10$sWszOXuTlN0amQi8vXp4cerb.tJUQo.4FzLAnTCsSqChsYhlLdQWW")
            .roles("USER");
    

    @Override
   protected void configure(HttpSecurity http) throws Exception 
       http
               .cors().disable().csrf().disable()
               .authorizeRequests()
               .anyRequest().authenticated()
               .and().exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(new CustomAccessDeniedHandler());
   


所以我可以使用任何用户名/密码调用“/oauth/token”端点,从而获得访问令牌。但是,正在验证客户端 ID 和客户端密码。

感谢任何帮助

【问题讨论】:

如果您从 spring 开始使用 oauth 服务器,那么它们是 not going to implement 密码流(资源所有者),因为它似乎在 oauth 2.1 specs 中已被弃用。 【参考方案1】:

这是 grant_type client_credentials 的默认行为。因此,您无需使用此 grant_type 将 usernamepassword 传递给令牌端点。在此流程中,AuthorizationServer 将仅验证客户端 ID 和客户端密码。

POST /token 
Host: authorization-server.com
 
grant_type=client_credentials
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx

【讨论】:

谢谢!那么 Spring 是否有一些用于令牌授予和验证用户的默认实现,或者如果我也想验证用户是否需要实现自己的(扩展 ClientCredentialsTokenGranter?)

以上是关于带有 client_credentials 的 Spring Oauth2 不验证用户的主要内容,如果未能解决你的问题,请参考以下文章

OneLogin是否支持client_credentials?

spring security client_credentials grant_type - 支持刷新令牌

如何使用 client_credentials 从资源服务器访问另一个 oauth2 资源?

是否支持 Spring Boot WebClient OAuth2 client_credentials?

如何在角度 http 请求中实现 client_credentials 授权类型?

Oaut2RestTemplate 与 client_credentials 错误(不允许匿名)