java - 带有 CustomAuthenticationProvider 和 CustomPasswordEncoder 的 Spring-Boot
问题描述
我不清楚如何将我的 CustomPasswordEncoder 粘贴到 Spring Boot 的身份验证过程中。我在配置中定义 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 将“自动”使用,或者仅当您使用提供的 authenticationProviders 之一(如 jdbcAuthenticationProvider)时才会使用这种情况。也许有人可以解释身份验证过程的事件顺序。我在网上做了一些研究,但我仍然无法详细了解这一点。
解决方案
首先,您可以从该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;
}
}
推荐阅读
- vb.net - 使用 vb.net 应用程序从数据库中获取特定值
- c# - 网络浏览器控件停止显示 instagram
- javascript - 无法读取属性“AddEventListener”的问题
- php - 如何根据时间戳计算某天的小时数?
- javascript - sourceMappingURL 在浏览器中被缩短
- java - 使用 Jackson 反序列化 Java 中的 JSON
- haskell - maxTotalHeaderLength 是否按预期工作?
- java - Android Studio 无法为 android 执行方法:onClick
- julia - 将 SuiteSparse.SPQR.QRSparseQ 转换为 SparseMatrixCSC?
- javascript - “this”指的是在方法内部定义的 IIFE 内部定义的内部箭头函数?