0

我正在使用spring-boot-starter-security. 我将我的配置WebSecurityConfigation为使用DaoAuthenticationProvider提供程序和BCryptPasswordEncoder身份验证。该UserDetailsService实现还返回一个字段设置为实际哈希的User对象。password

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

例如,密码本身是一个生成的 UUID 51a80a6a-8618-4583-98d2-d77d103a62c6,它被编码为$2a$10$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;
}

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

4

2 回答 2

2

我猜这件事发生是因为根据您的配置,您实际上得到了两个DaoAuthenticationProviders。一个是显式的,由您配置,一个是隐式的,在您调用时在后台配置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");
}

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

希望能帮助到你。

于 2017-10-09T08:20:19.420 回答
-2

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

 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);
}
于 2017-10-09T07:41:16.290 回答