具有CustomAuthenticationProvider和CustomPasswordEncoder的Spring-Boot

时间:2019-03-20 13:37:19

标签: java spring spring-boot spring-security

我不清楚如何将CustomPasswordEncoder粘贴到spring boot的身份验证过程中。我在配置中定义,春季启动应将CustomAuthenticationProvider与UserDetailsS​​ervice和CustomPasswordEncoder一起使用

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;


    @Autowired
    protected void configureGlobal(AuthenticationManagerBuilder builder) throws Exception {

        builder.authenticationProvider(customAuthenticationProvider)
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        PasswordEncoder encoder = new CustomPasswordEncoder();
        return encoder;
    }       
}

我的CustomPasswordEncoder将编码为md5值(我知道它是不安全的,但是它是旧数据库)

@Component
public class CustomPasswordEncoder implements PasswordEncoder{

    @Override
    public String encode(CharSequence rawPassword) {
return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes());

    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {

        return rawPassword.toString().equals(encodedPassword);
    }
}

在CustomAuthtenticationProvider中,将完成身份验证检查。传递的密码将使用passwordEncoder.encode()进行编码。将从数据库中提取用户,然后再次使用passwordEncoder进行匹配。如果匹配成功,则将生成身份验证对象。

    @Component
    public class CustomAuthenticationProvider implements AuthenticationProvider {

        @Autowired
        private UserServiceImpl userService;
        @Autowired
        private CustomPasswordEncoder passwordEncoder;



        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            System.out.println("authentication = [" + authentication + "]");
            String name = authentication.getName();
            Object credentials = authentication.getCredentials();

            String password = credentials.toString();
            //why is this necessary isnt it called automatically?
            String passwordEncoded = passwordEncoder.encode((CharSequence) credentials);
            Optional<UserEntity> userOptional = userService.findByUsername(name);

            if (userOptional.isPresent() && passwordEncoder.matches(passwordEncoded, userOptional.get().getPassword())) {
                List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
                grantedAuthorities.add(new SimpleGrantedAuthority(userOptional.get().getRoles().toString()));
                Authentication auth = new
                        UsernamePasswordAuthenticationToken(name, password, grantedAuthorities);
                return auth;
            }
            else{
                throw new BadCredentialsException("Authentication failed for " + name);
            }
        }

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

这是正确的方法吗?我以为CustomPasswordEncoder会“自动”使用,或者只有在使用提供的authenticationProvider之一(如jdbcAuthenticationProvider)时才这样。也许有人可以解释身份验证过程的事件顺序。我在网上做了一些研究,但仍然无法详细了解。

1 个答案:

答案 0 :(得分:1)

首先,您可以从matches方法中看到,它使用编码后的密码验证原始密码(因此由用户输入)。因此,编码代码属于matches方法,而不是您现在拥有的方法。

public class CustomPasswordEncoder implements PasswordEncoder{

    @Override
    public String encode(CharSequence rawPassword) {
      return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes());
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        String rawEncoded = encode(rawPassword);
        return Objects.equals(rawEncoded, encodedPassword);
    }
}

现在,您可以从代码中删除任何编码行/步骤。

但是,您实际上并不需要自定义AuthenticationProvider,因为通常只有在添加其他认证机制(例如LDAP或OAuth)时才需要。

您需要的是将UserService转换为UserDetailsService的适配器,并使用它。我假设UserDetailsServiceImpl正是这样做的。如果没有,您可以使用类似下面的代码。

public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserService delegate;

    public UserDetailsServiceAdapter(UserService delegate) {
        this.delegate=delegate;
    }

    public UserDetails loadUserByUsername(String username) {
       reutrn userService.findByUsername(name)
                 .map(this::toUserDetails).orElseThrow(() -> new UsernameNotFoundException("Unknown user " + username);
    }

    private UserDetails toUserDetails(User user) {
        Set<GrantedAuthority> authorities = new HashSet<>();
        user.getRoles().forEach(r -> authorities.add(new SimpleGrantedAuthority(r));
         return new UserDetails(user.getUsername(), user.getPassword(), authorities);
    }
}

现在,您可以在配置中使用PasswordEncoder和此适配器,而无需自定义AuthenticationProvider

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    protected void configureGlobal(AuthenticationManagerBuilder builder) throws Exception {

        builder.userDetailsService(userDetailsService)
               .passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        PasswordEncoder encoder = new CustomPasswordEncoder();
        return encoder;
    }       
}