> 文章列表 > Spring Security 05 密码加密

Spring Security 05 密码加密

Spring Security 05 密码加密

目录

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();}
}