为什么我能够使用 Java Spring 中的密码哈希进行身份验证安全

Why am i able to authenticate with the password hash in Java Spring Security

我正在使用 spring-boot-starter-security。我将 WebSecurityConfigation 配置为使用 DaoAuthenticationProvider 提供商和 BCryptPasswordEncoder 进行身份验证。 UserDetailsService 实现 returns 一个 User 对象,password 字段设置为实际哈希值。

它似乎工作正常。但是我注意到我可以使用密码 哈希成功进行身份验证。

例如,密码本身是一个生成的 UUID 51a80a6a-8618-4583-98d2-d77d103a62c6,它被编码为 a$u4OSZf7B9yJvQ5UYNNpy7O4f3g0gfUMl2Xmm3h282W.3emSN3WqxO

完整的网络安全配置:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DemoUserDetailsService userDetailsService;

    @Autowired
    private DaoAuthenticationProvider authenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
        auth.userDetailsService(userDetailsService);
        auth.inMemoryAuthentication().withUser("user").password("password").roles("SUPER", "BASIC");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/**").hasRole("BASIC").and().httpBasic();
        http.csrf().disable();
    }
}

@Service
public class DemoUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        UserDAO userDAO = userRepository.findByEmailAndActivated(email);
        if (userDAO == null) {
            throw new UsernameNotFoundException(String.format("Email %s not found", email));
        }
        return new User(email, userDAO.getPasswordHash(), getGrantedAuthorities(email));
    }

    private Collection<? extends GrantedAuthority> getGrantedAuthorities(String email) {
        return asList(() -> "ROLE_BASIC");
    }
}

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

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

为什么我可以使用两个字符串进行身份验证?我做错了什么,或者这是预期的还是某些配置?我无法在文档中找到任何内容。

最好的方法是连接调试器。使用PasswordEncoder匹配密码的实际逻辑在DaoAuthenticationProvider

additionalAuthenticationChecks方法中
 protected void additionalAuthenticationChecks(UserDetails userDetails,
            UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        Object salt = null;

        if (this.saltSource != null) {
            salt = this.saltSource.getSalt(userDetails);
        }

        if (authentication.getCredentials() == null) {
            logger.debug("Authentication failed: no credentials provided");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
        }

        String presentedPassword = authentication.getCredentials().toString();

        if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
            logger.debug("Authentication failed: password does not match stored value");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
        }
    }

这委托给 BCryptPasswordEncoder

public boolean matches(CharSequence rawPassword, String encodedPassword) {
        if (encodedPassword == null || encodedPassword.length() == 0) {
            logger.warn("Empty encoded password");
            return false;
        }
    if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
        logger.warn("Encoded password does not look like BCrypt");
        return false;
    }

    return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}

我猜这是因为根据您的配置,您实际上得到了两个 DaoAuthenticationProvider。一种是显式的,由您配置,一种是隐式的,在您调用 auth.userDetailsService(userDetailsService); 时在后台配置,并且对于这个隐式提供程序,您没有设置密码编码器。

试试这个:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder()); 
    auth.inMemoryAuthentication().withUser("user").password("password").roles("SUPER", "BASIC");
}

并删除您手动配置的提供程序 - 似乎您实际上并不需要它。

希望对您有所帮助。