目录
实际密码比较是由PasswordEncoder完成的,因此只需要使用PasswordEncoder 不同实现就可以实现不同方式加密。
public interface PasswordEncoder {
// 进行明文加密
String encode(CharSequence rawPassword);
// 比较密码
boolean matches(CharSequence rawPassword, String encodedPassword);
// 密码升级
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
DelegatingPasswordEncoder
根据上面 PasswordEncoder的介绍,可能会以为 Spring security 中默认的密码加密方案应该是四种自适应单向加密函数中的一种,其实不然,在 spring Security 5.0之后,默认的密码加密方案其实是 DelegatingPasswordEncoder。从名字上来看,DelegatingPaswordEncoder 是一个代理类,而并非一种全新的密码加密方案,DeleggtinePasswordEncoder 主要用来代理上面介绍的不同的密码加密方案。为什么采DelegatingPasswordEncoder 而不是某一个具体加密方式作为默认的密码加密方案呢?主要考虑了如下两方面的因素:
- 兼容性:使用 DelegatingPasswrordEncoder 可以帮助许多使用旧密码加密方式的系统顺利迁移到 Spring security 中,它允许在同一个系统中同时存在多种不同的密码加密方案。
- 便捷性:密码存储的最佳方案不可能一直不变,如果使用 DelegatingPasswordEncoder作为默认的密码加密方案,当需要修改加密方案时,只需要修改很小一部分代码就可以实现。
DelegatingPasswordEncoder源码
public class DelegatingPasswordEncoder implements PasswordEncoder {
....
}
PasswordEncoderFactories源码
public final class PasswordEncoderFactories {
private PasswordEncoderFactories() {
}
@SuppressWarnings("deprecation")
public static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(encodingId, new BCryptPasswordEncoder());
encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
encoders.put("SHA-256",
new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
encoders.put("argon2", new Argon2PasswordEncoder());
return new DelegatingPasswordEncoder(encodingId, encoders);
}
}
使用 PasswordEncoder
查看WebSecurityConfigurerAdapter类中源码
static class LazyPasswordEncoder implements PasswordEncoder {
private ApplicationContext applicationContext;
private PasswordEncoder passwordEncoder;
LazyPasswordEncoder(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public String encode(CharSequence rawPassword) {
return this.getPasswordEncoder().encode(rawPassword);
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return this.getPasswordEncoder().matches(rawPassword, encodedPassword);
}
public boolean upgradeEncoding(String encodedPassword) {
return this.getPasswordEncoder().upgradeEncoding(encodedPassword);
}
private PasswordEncoder getPasswordEncoder() {
if (this.passwordEncoder != null) {
// 若指定的 passwordEncoder 不为空则使用指定的 passwordEncoder
return this.passwordEncoder;
} else {
// 使用默认的 DelegatingPasswordEncoder
PasswordEncoder passwordEncoder = (PasswordEncoder)AuthenticationConfiguration.getBeanOrNull(this.applicationContext, PasswordEncoder.class);
if (passwordEncoder == null) {
passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
this.passwordEncoder = passwordEncoder;
return passwordEncoder;
}
}
public String toString() {
return this.getPasswordEncoder().toString();
}
}
密码加密实战
@Bean
public PasswordEncoder BcryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService(){
UserDetails user = User.withUsername("admin").password("$2a$10$WGFkRsZC0kzafTKOPcWONeLvNvg2jqd3U09qd5gjJGSHE5b0yoy6a").roles("ADMIN").build();
return new InMemoryUserDetailsManager(user);
}
使用灵活密码加密方案 推荐
@Bean
public UserDetailsService userDetailsService(){
UserDetails user = User.withUsername("admin").password("$2a$10$WGFkRsZC0kzafTKOPcWONeLvNvg2jqd3U09qd5gjJGSHE5b0yoy6a").roles("ADMIN").build();
return new InMemoryUserDetailsManager(user);
}
密码自动升级
@Mapper
public interface UserMapper {
//根据用户名查询用户
User loadUserByUsername(String username);
Integer updatePassword(@Param("username") String username, @Param("password") String password);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yang.mapper.UserMapper">
<update id="updatePassword">
update `user` set password = #{password}
where username= #{username}
</update>
<!--查询单个-->
<select id="loadUserByUsername" resultType="com.yang.entity.User">
select id,
username,
password,
enabled,
accountNonExpired,
accountNonLocked,
credentialsNonExpired
from user
where username = #{username}
</select>
</mapper>
public interface UserService {
UserDetails loadUserByUsername(String username);
Integer updateUser(String username, String password);
}
@Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
@Autowired
public UserServiceImpl(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public UserDetails loadUserByUsername(String username) {
User user = userMapper.loadUserByUsername(username);
if(ObjectUtils.isEmpty(user)){
throw new RuntimeException("用户不存在");
}
user.setRoles(userMapper.getRolesByUid(user.getId()));
return user;
}
@Override
public Integer updateUser(String username, String password) {
return userMapper.updatePassword(username, password);
}
}
@Component
public class UserDetailService implements UserDetailsService, UserDetailsPasswordService {
private final UserService userService;
public UserDetailService(UserService userService) {
this.userService = userService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userService.loadUserByUsername(username);
}
@Override
public UserDetails updatePassword(UserDetails user, String newPassword) {
Integer updateRow = userService.updateUser(user.getUsername(), newPassword);
if (updateRow == 1){
((User) user).setPassword(newPassword);
}
return user;
}
}
@Configuration
public class WebSecurityConfig {
private final UserDetailService userDetailService;
public WebSecurityConfig(UserDetailService userDetailService) {
this.userDetailService = userDetailService;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/index")
.permitAll()
.anyRequest().authenticated()
.and().formLogin()
.and().userDetailsService(userDetailService); // 自定义数据源
return http.csrf().disable().build();
}
}