Spring boot WebSecurityConfigurerAdapter basic authentication 用户密码验证问题

Spring boot WebSecurityConfigurerAdapter basic authentication user password validation issue

我正在使用 Spring Boot 编写一个简单的 REST API,我想启用基本身份验证。因此,我使用了 WebSecurityConfigurerAdapter,如下所示。为了简单起见,我只想检查密码(pwd123)并允许任何用户登录。请参考下面的代码。

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new AuthenticationProvider() {
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                if (authentication == null || authentication.getCredentials() == null) {
                    throw new BadCredentialsException("Bad credentials");
                }
                if (authentication.getCredentials().equals("pwd123")) {
                    return new UsernamePasswordAuthenticationToken(authentication.getName(),
                            authentication.getCredentials().toString(),
                            Collections.emptyList());
                }

                return null;
            }

            @Override
            public boolean supports(Class<?> authentication) {
                return authentication.equals(UsernamePasswordAuthenticationToken.class);
            }
        });
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
                .and().httpBasic();
    }
}

假设 user_A 已使用有效密码访问 REST API,即 pwd123,然后使用错误密码执行发送 API 调用。但是,允许用户访问 API,这就是问题所在。

当我进行调试时,我意识到 authenticationIsRequired 函数在 BasicAuthenticationFilter class 中 Spring安全性,在这种情况下 returns false。请参考该代码。

    private boolean authenticationIsRequired(String username) {
    // Only reauthenticate if username doesn't match SecurityContextHolder and user
    // isn't authenticated (see SEC-53)
    Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
    if (existingAuth == null || !existingAuth.isAuthenticated()) {
        return true;
    }
    // Limit username comparison to providers which use usernames (ie
    // UsernamePasswordAuthenticationToken) (see SEC-348)
    if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) {
        return true;
    }
    // Handle unusual condition where an AnonymousAuthenticationToken is already
    // present. This shouldn't happen very often, as BasicProcessingFitler is meant to
    // be earlier in the filter chain than AnonymousAuthenticationFilter.
    // Nevertheless, presence of both an AnonymousAuthenticationToken together with a
    // BASIC authentication request header should indicate reauthentication using the
    // BASIC protocol is desirable. This behaviour is also consistent with that
    // provided by form and digest, both of which force re-authentication if the
    // respective header is detected (and in doing so replace/ any existing
    // AnonymousAuthenticationToken). See SEC-610.
    return (existingAuth instanceof AnonymousAuthenticationToken);
}

请让我知道我的实现中缺少什么

如评论中所述,您可以尝试提供自定义 UserDetailsService 而不是提供自定义 AuthenticationProvider。这是完整的配置:

@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests((authorizeRequests) -> authorizeRequests
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return (username) -> new User(username, "{noop}pwd123", AuthorityUtils.createAuthorityList("ROLE_USER"));
    }

}

当您发展到通过 third-party 服务查找用户时,您可以在自定义 UserDetailsService 中添加代码来执行此操作(lambda 函数或实际的 class实现接口)并继续返回 org.springframework.security.core.userdetails.User.

注意: 我实际上不建议在生产中使用 plain-text 密码。您可以将 {noop}pwd123 替换为 {bcrypt}<bcrypt encoded password here>.

正如评论和答案中所建议的那样,即使您使用 InMemoryUserDetailsManager 问题也没有得到解决,这意味着,一旦用户使用正确的用户名和密码进行身份验证,他的密码就不会在随后的 REST API 调用中验证,即可以使用任何密码。这是因为 BasicAuthenticationFilter class 中的功能会跳过拥有有效 JSESSION cookie 的用户。

要解决此问题,我们应该配置 http 以通过以下方式创建 state-less 会话 http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

configure函数中WebSecurityConfigurerAdapter

请参考