我不清楚如何将CustomPasswordEncoder粘合到spring boot的身份验证过程中。我在配置中定义,春季启动应将CustomAuthenticationProvider与UserDetailsService和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会“自动”使用,或者只有在使用提供的诸如jdbcAuthenticationProvider之类的authenticationProviders时才如此。也许有人可以解释身份验证过程的事件顺序。我在网上做了一些研究,但仍然无法详细了解。
最佳答案
首先,您可以从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;
}
}