IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 5.Spring security中的密码加密 -> 正文阅读

[网络协议]5.Spring security中的密码加密

密码加密

5.3PasswordEncoder详解

Spring security中通过PasswordEncoder接口定义了密码加密和比对的相关操作:

public interface PasswordEncoder {
    // 对明文密码进行加密
    String encode(CharSequence rawPassword);

    // 进行密码比对
    boolean matches(CharSequence rawPassword, String encodedPassword);

    // 判断当前密码是否需要升级
    default boolean upgradeEncoding(String encodedPassword) {
		return false;
	}
}
5.3.1PasswordEncoder常见实现类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-knDpDp98-1646291833010)(en-resource://database/653:1)]@w=900

5.3.2DelegatingPasswordEncoder

在spring security5.0之后,默认的密码加密方案是DelegatingPasswordEncoder。它是一个代理类,主要用来代理其他不同的密码加密方案。
先从PasswordEncoderFactories类开始看起,因为正是由它里边的静态方法createDelegatingPasswordEncoder提供了默认的DelegatingPasswordEncoder实例:

public final class PasswordEncoderFactories {
    public static PasswordEncoder createDelegatingPasswordEncoder() {
		String encodingId = "bcrypt";
		// 存储每一种密码加密方案的id和所对应的加密类
		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());
        // 默认值为bcrypt,因此代理类中默认使用的加密方案是BCryptPasswordEncoder
		return new DelegatingPasswordEncoder(encodingId, encoders);
	}
}

接下来分析DelegatingPasswordEncoder

public class DelegatingPasswordEncoder implements PasswordEncoder {
    // 用来包裹将来生成的加密方案的id,例如bcrypt
    private static final String PREFIX = "{";
	private static final String SUFFIX = "}";

    // 默认的加密方案id
	private final String idForEncode;

    // 默认的加密方案(BCryptPasswordEncoder),它的值是根据idForEncode从
    // idToPasswordEncoder映射中提取出来的
	private final PasswordEncoder passwordEncoderForEncode;

    // 保存id和加密方案之间的映射
	private final Map<String, PasswordEncoder> idToPasswordEncoder;

    // 默认的密码比对器,当根据密码加密方案的id无法找到对应的加密方案时,会使用默认的(直接抛出异常)
	private PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder();

    @Override
	public String encode(CharSequence rawPassword) {
		// 加上前缀{id},用来描述所采用的具体加密方案
		return PREFIX + this.idForEncode + SUFFIX + this.passwordEncoderForEncode.encode(rawPassword);
	}

    @Override
	public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
		if (rawPassword == null && prefixEncodedPassword == null) {
			return true;
		}
        // 提取加密方案id,例如bcrypt
		String id = extractId(prefixEncodedPassword);
		PasswordEncoder delegate = this.idToPasswordEncoder.get(id);
        
		if (delegate == null) {
			return this.defaultPasswordEncoderForMatches.matches(rawPassword, prefixEncodedPassword);
		}

        // 提取加密后的密码
		String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
		return delegate.matches(rawPassword, encodedPassword);
	}

    @Override
	public boolean upgradeEncoding(String prefixEncodedPassword) {
		String id = extractId(prefixEncodedPassword);
        // 如果当前加密字符串所采用的加密方案不是默认的加密方案,就会自动进行密码升级
		if (!this.idForEncode.equalsIgnoreCase(id)) {
			return true;
		}
		else {
            // 否则调用默认加密方案的upgradeEncoding方法判断密码是否需要升级
			String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
			return this.idToPasswordEncoder.get(id).upgradeEncoding(encodedPassword);
		}
	}
}

5.4实战

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 由于默认使用的是DelegatingPasswordEncoder,也可以省略掉PasswordEncoder的实例,只在密码前加上前缀,
     * 例如:"{bcrypt}$2a$10$Zg.29wwumBjpQk0xTEBnZeTBWIGxVyth4ByXjdGoRo5.qJZlda9Au"(即,123)。
     */
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("javaboy")
                .password("$2a$10$Zg.29wwumBjpQk0xTEBnZeTBWIGxVyth4ByXjdGoRo5.qJZlda9Au")
                // .password("{bcrypt}$2a$10$Zg.29wwumBjpQk0xTEBnZeTBWIGxVyth4ByXjdGoRo5.qJZlda9Au")
                .roles("admin");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .csrf().disable();
    }
}

5.5加密方案自动升级

使用DelegatingPasswordEncoder的另外一个好处就是会自动进行密码加密方案升级。

@Configuration
public class UserService implements UserDetailsService, UserDetailsPasswordService {
    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails updatePassword(UserDetails user, String newPassword) {
        Integer result = userMapper.updatePassword(user.getUsername, newPassword);

        if (result == 1) {
            ((User) user).setPassword(newPassword);
        }

        return user;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userMapper.loadUserByUsername(username);
    }
}

例如DaoAuthenticationProvider中的createSuccessAuthentication方法中就触发了加密方案的自动升级。

5.6是谁的PasswordEncoder

如果开发者向spring容器中注册了一个PasswordEncoder实例,那么无论是全局的AuthenticationManager还是局部的,都将使用该实例;如果开发者没有提供任何PasswordEncoder实例,那么无论是全局的还是局部的,都将使用DelegatingPasswordEncoder实例。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-03-04 15:57:23  更:2022-03-04 15:57:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:32:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码