目录
DelegatingPasswordEncoder
使用 PasswordEncoder
密码加密实战
密码自动升级
实际密码比较是由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 不为空则使用指定的 passwordEncoderreturn this.passwordEncoder; } else {// 使用默认的 DelegatingPasswordEncoderPasswordEncoder 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();}}
密码加密实战
@Beanpublic PasswordEncoder BcryptPasswordEncoder(){return new BCryptPasswordEncoder();}
@Beanpublic UserDetailsService userDetailsService(){UserDetails user = User.withUsername("admin").password("$2a$10$WGFkRsZC0kzafTKOPcWONeLvNvg2jqd3U09qd5gjJGSHE5b0yoy6a").roles("ADMIN").build();return new InMemoryUserDetailsManager(user);}
使用灵活密码加密方案 推荐
@Beanpublic 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,credentialsNonExpiredfrom userwhere 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;
@Autowiredpublic UserServiceImpl(UserMapper userMapper) {this.userMapper = userMapper;}
@Overridepublic UserDetails loadUserByUsername(String username) {User user = userMapper.loadUserByUsername(username);if(ObjectUtils.isEmpty(user)){throw new RuntimeException("用户不存在");}user.setRoles(userMapper.getRolesByUid(user.getId()));return user;}
@Overridepublic 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;}
@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return userService.loadUserByUsername(username);}
@Overridepublic 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;}
@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {return authenticationConfiguration.getAuthenticationManager();}
@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests().mvcMatchers("/index").permitAll().anyRequest().authenticated().and().formLogin().and().userDetailsService(userDetailService); // 自定义数据源return http.csrf().disable().build();}
}