Spring Security 教程(五):加密算法升级

2022-08-16 21:50 阅读

上一章简单的处理了密码加密问题。但密码加密中有一个更为棘手的问题,这便是加密算法的升级。

如果系统设计之初,还使用md5加密,之后改为了更安全的sha1sha256,再之后又要改为pbkdf2bcrypt之类的现代算法。如果又出现更好的加密技术,或者原有加密算法被发现有漏洞,还得继续更改。

这种更改在已上线的系统中是不可承受的,因为一旦改动加密算法,所有用户的密码都会失效。没有谁可以承受这样的后果。有一些经验丰富的技术人员会使用一些复杂的手段,让加密算法升级变得可行。但Spring Security提供了一种代价极低、无感的方式实现这一目标。

基本原理是,提供一系列的加密算法,如md5 sha1 sha256 pbkdf2 bcrypt等,并将其中一个作为默认算法。在加密的时候,将加密算法名称放到密码字段里,这样就知道当前的密码使用的是什么算法。如:

{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0

如系统需要升级算法,只需要将默认算法改为新算法。用户登录时,可以使用老算法进行匹配,如果匹配成功,则自动使用新算法对用户密码进行升级。

比如系统之前使用的是sha256,密码用户的密码为:

{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0

之后使用bcrypt作为新的默认算法。该用户进行登录时,使用sha256算法进行加密匹配。一旦匹配成功,在用户登录成功的同时,使用新的默认算法bcrypt对用户密码进行加密,并写入数据库。该用户数据库的密码就变成了:

{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG

这样就再也不怕升级加密算法了,对用户完全无感;对开发人员而言,也只需改一两行代码;如果完全使用Spring Security的方案,则一行代码都不用改,只需升级Spring Security版本即可。

这么方便,那么配置会不会很复杂呢?简单到不能更简单:

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

或者自定义加密算法:

    @Bean
    public PasswordEncoder passwordEncoder() {
        String idForEncode = "bcrypt";
        Map encoders = new HashMap<>();
        encoders.put(idForEncode, new BCryptPasswordEncoder());
        encoders.put("noop", NoOpPasswordEncoder.getInstance());
        encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
        encoders.put("scrypt", new SCryptPasswordEncoder());
        encoders.put("sha256", new StandardPasswordEncoder());
        PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(idForEncode, encoders);
    }

另外还需实现修改密码的接口UserDetailsPasswordService

    @Bean
    public UserDetailsPasswordService userDetailsPasswordService() {
        return new UserDetailsPasswordService() {
            @Override
            public UserDetails updatePassword(UserDetails user, String newPassword) {
                ...
            }
        };
    }

总结

如果说之前还对使用Spring Security有些犹豫的话,这个设计则完全坚定了我使用Spring Security的决心。单纯为了这个功能使用Spring Security都不亏。

咨询
交流群
电话