springsecurity更换加密方式

目录

      • [1. 理解Spring Security的加密接口](#1. 理解Spring Security的加密接口)
      • [2. 迁移方案(新旧密码共存)](#2. 迁移方案(新旧密码共存))
      • [3. 密码存储格式要求](#3. 密码存储格式要求)
      • [4. 数据库密码迁移脚本](#4. 数据库密码迁移脚本)
      • [5. 安全配置示例](#5. 安全配置示例)
      • [6. 密码自动升级策略(可选)](#6. 密码自动升级策略(可选))
      • [7. 注意事项](#7. 注意事项)
      • [8. 最佳实践](#8. 最佳实践)
      • 9.总结

随着现在对软件系统安全性要求的越来越严苛,对于很多项目,升级密码都是势在必行的,若项目使用Spring Security框架,只需稍作修改,便可达到更换加密方式,且自动更新存量数据的密码加密放肆。在Spring Security中更换密码加密方式,通常涉及迁移旧有用户密码并配置新的加密策略。以下是完整步骤和示例代码:

1. 理解Spring Security的加密接口

核心接口是PasswordEncoder,常用实现类:

  • BCryptPasswordEncoder (推荐)
  • SCryptPasswordEncoder
  • Pbkdf2PasswordEncoder
  • Argon2PasswordEncoder
  • NoOpPasswordEncoder (明文,仅测试用)

2. 迁移方案(新旧密码共存)

使用DelegatingPasswordEncoderDelegatingPasswordEncoder 是 Spring Security 中用于密码加密的核心组件,主要用于代理多种加密策略,兼容旧密码格式并支持升级加密方式,支持多种加密格式自动适配:

先来看DelegatingPasswordEncoder的构造方法:

java 复制代码
public DelegatingPasswordEncoder(String idForEncode, Map<String, PasswordEncoder> idToPasswordEncoder) {
        if (idForEncode == null) {
            throw new IllegalArgumentException("idForEncode cannot be null");
        } else if (!idToPasswordEncoder.containsKey(idForEncode)) {
            throw new IllegalArgumentException("idForEncode " + idForEncode + "is not found in idToPasswordEncoder " + idToPasswordEncoder);
        } else {
            for(String id : idToPasswordEncoder.keySet()) {
                if (id != null) {
                    if (id.contains("{")) {
                        throw new IllegalArgumentException("id " + id + " cannot contain " + "{");
                    }

                    if (id.contains("}")) {
                        throw new IllegalArgumentException("id " + id + " cannot contain " + "}");
                    }
                }
            }

            this.idForEncode = idForEncode;
            this.passwordEncoderForEncode = (PasswordEncoder)idToPasswordEncoder.get(idForEncode);
            this.idToPasswordEncoder = new HashMap(idToPasswordEncoder);
        }
    }

其中参数idForEncode决定了密码编码器的类型,而idToPasswordEncoder决定了匹配密码时的兼容的密码编码器的类型,且idToPasswordEncoder中必须包含idForEncode,否则新密码加密后将无法匹配。

查看DelegatingPasswordEncoder类的类图可知,DelegatingPasswordEncoder类是PasswordEncoder的实现类。

根据DelegatingPasswordEncoder的构造器,定义PasswordEncoder密码编码器:

java 复制代码
@Bean
public PasswordEncoder passwordEncoder() {
    String idForEncode = "bcrypt"; // 默认使用BCrypt
    Map<String, PasswordEncoder> idToPasswordEncoder = new HashMap<>();
    idToPasswordEncoder.put(idForEncode, new BCryptPasswordEncoder());
    idToPasswordEncoder.put("sha256", new MessageDigestPasswordEncoder("SHA-256")); // 旧系统加密方式
    
    return new DelegatingPasswordEncoder(idForEncode, idToPasswordEncoder);
}

3. 密码存储格式要求

认证时根据前缀匹配加密器:

复制代码
存储格式示例:
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG 
{sha256}5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8

4. 数据库密码迁移脚本

sql 复制代码
-- 将旧密码加前缀
UPDATE t_user SET password = CONCAT('{sha256}', password) 
WHERE password NOT LIKE '{_}%';

5. 安全配置示例

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication()
            .dataSource(dataSource)
            .passwordEncoder(passwordEncoder())
            .usersByUsernameQuery("SELECT username, password, enabled FROM users WHERE username=?")
            .authoritiesByUsernameQuery("SELECT username, authority FROM authorities WHERE username=?");
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 委托式编码器配置
        String idForEncode = "bcrypt"; // 默认使用BCrypt
        Map<String, PasswordEncoder> idToPasswordEncoder = new HashMap<>();
        idToPasswordEncoder.put(idForEncode, new BCryptPasswordEncoder());
        idToPasswordEncoder.put("sha256", new MessageDigestPasswordEncoder("SHA-256")); // 旧系统加密方式

        return new DelegatingPasswordEncoder(idForEncode, idToPasswordEncoder);
    }
}

6. 密码自动升级策略(可选)

密码自动升级策略,适用于用户登录时自动更新密码格式,需要自定义PasswordEncoder的实现类,并在第5点安全配置中使用,可参照BCryptPasswordEncoder进行实现。

java 复制代码
public class AutoUpgradePasswordEncoder implements PasswordEncoder {
    
    private final PasswordEncoder currentEncoder;
    private final PasswordEncoder newEncoder;
    
    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        if (currentEncoder.matches(rawPassword, encodedPassword)) {
            // 匹配成功且是旧编码时,触发密码升级
            if (encodedPassword.startsWith("{sha256}")) {
                String newPass = newEncoder.encode(rawPassword);
                // 调用DAO更新数据库密码...
            }
            return true;
        }
        return newEncoder.matches(rawPassword, encodedPassword);
    }
    
    // encode()方法需实现...
}

7. 注意事项

  1. 前缀不可省略{id}encodedPassword格式是识别关键

  2. 加密强度

    java 复制代码
    // 调整BCrypt强度 (默认10)
    new BCryptPasswordEncoder(12)
  3. 禁用弱加密:避免使用MD5/SHA-1等已被破解的算法

  4. 密码迁移:在系统低负载时段执行数据库更新

  5. 测试策略:保留旧密码的用户测试账号

8. 最佳实践

  1. 主推算法:BCrypt(自适应强度抗GPU破解)

  2. 过渡方案

    SHA-256/MD5 匹配成功 旧系统 DelegatingPasswordEncoder 密码验证 升级为BCrypt存储

  3. 安全规范

    • 密码强度要求:至少10位(字母+数字+特殊字符)
    • 定期轮换:每90天提示修改密码(配合密码历史策略)

9.总结

Spring Security项目的密码升级主要是通过配置PasswordEncoder进行实现,但需要注意需要进行密码的迁移(旧密码添加前缀)。

复制代码
- 密码强度要求:至少10位(字母+数字+特殊字符)
- 定期轮换:每90天提示修改密码(配合密码历史策略)

通过上述设计,系统可以同时支持新旧用户认证,并逐步将旧密码升级到更安全的加密方式,整个过程对用户几乎无感知。

相关推荐
却话巴山夜雨时i1 小时前
295. 数据流的中位数【困难】
java·服务器·前端
java干货1 小时前
优雅停机!Spring Boot 应用如何使用 Hook 线程完成“身后事”?
java·spring boot·后端
tealcwu1 小时前
【Unity技巧】实现在Play时自动保存当前场景
java·unity·游戏引擎
uup1 小时前
Java 多线程下的可见性问题
java
用户8307196840821 小时前
通过泛型限制集合只读或只写
java
Pluchon1 小时前
硅基计划4.0 算法 记忆化搜索
java·数据结构·算法·leetcode·决策树·深度优先
大飞哥~BigFei1 小时前
deploy发布项目到国外中央仓库报如下错误Project name is missing
java
白羊无名小猪1 小时前
正则表达式(捕获组)
java·mysql·正则表达式