理解SpringSecurity中的PasswordEncoder接口

举报
别团等shy哥发育 发表于 2023/01/09 18:53:31 2023/01/09
【摘要】 @toc 1、理解PasswordEncoder接口下图回顾了PasswordEncoder在身份验证中处于什么位置。  一般而言,系统并不以明文形式管理密码,因此密码通常要经过某种转换,这使得读取和窃取密码变得较为困难。对于这一指责,SpringSecurity定义了一个单独的接口。 1.1 PasswordEncoder接口的定义  实现这个接口是为了告知SpringSecurity如何...

@toc

1、理解PasswordEncoder接口

下图回顾了PasswordEncoder在身份验证中处于什么位置。

image-20220214160229731

  一般而言,系统并不以明文形式管理密码,因此密码通常要经过某种转换,这使得读取和窃取密码变得较为困难。对于这一指责,SpringSecurity定义了一个单独的接口。

1.1 PasswordEncoder接口的定义

  实现这个接口是为了告知SpringSecurity如何验证用户的密码。在身份验证过程中,PasswordEncoder会判定密码是否有效。每个系统都会存储以某种方式编码过的密码。最好把密码哈希化存储起来,这样别人就不会读到明文密码了。PasswordEncoder还可以对密码进行编码。接口声明的encode()和matches()方式实际上是对其职责的定义。这两个方法都是同一接口的一部分,因此它们紧密相连。应用程序对密码进行编码的方式与验证密码的方式相关。

public interface PasswordEncoder {
   String encode(CharSequence rawPassword);
   boolean matches(CharSequence rawPassword, String encodedPassword);
   default boolean upgradeEncoding(String encodedPassword) {
      return false;
   }
}

  该接口定义了两个抽象方法,其中几个具有默认实现。在处理PasswordEncoder实现时,最常见的是抽象的encode()和matches()方法。

  encode(CharSequence rawPassword)方法的目的是返回所提供字符串的转换。就SpringSecurity功能而言,它用于为指定密码提供加密或哈希化。

  之后可以使用matches(CharSequence rawPassword, String encodedPassword)方法检查已编码的字符串是否与演示密码匹配。可以在身份验证过程中使用matches()方法根据一组已知凭据来检验所提供的的密码。

  第三个方法被称为upgradeEncoding(String encodedPassword),在接口中默认设置为false。如果重写它以返回true,那么为了更好的安全性,将重新对已编码的密码进行编码(不推荐这种方式)。

1.2 实现PasswordEncoder接口

  可以看到,matches()和encode()这两个方法具有很强的关联性。如果重写它们,应该确保它们时钟在功能方面有所对应:由encode()方法返回的字符串应该时钟可以使用同一个PasswordEncoder的matches()方法进行验证。

1.2.1 明文管理密码NoOpPasswordEncoder

public class PlainTextPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence rawPassword) {
        //不更改密码,只是原样返回
        return rawPassword.toString();
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        //检查两个字符串是否相等
        return rawPassword.equals(encodedPassword);
    }
}

编码的结构总是与原密码相同。因此检查它们是否匹配,只需要使用equals方法比较两个字符串是否相等即可。

1.2.2实现使用SHA-512的PasswordEncoder

public class Sha512PasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence rawPassword) {
        return hashWithSHA512(rawPassword.toString());
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        String hashedPassword=encode(rawPassword);
        return encodedPassword.equals(hashedPassword);
    }

    /**
     * 用SHA-512对输入进行哈希化的方法实现
     * @param input
     * @return
     */
    private String hashWithSHA512(String input){
        StringBuilder result=new StringBuilder();
        try{
            MessageDigest md=MessageDigest.getInstance("SHA-512");
            byte[] digested=md.digest(input.getBytes());
            for (int i = 0; i < digested.length; i++) {
                result.append(Integer.toHexString(0xFF&digested[i]));
            }
        }catch (NoSuchAlgorithmException e){
            throw new RuntimeException("Bad algorithm");
        }
        return result.toString();
    }
}

  我们从encode()方法调用hashWithSHA512()这个方法,该方法现在返回其输入的哈希值。为了针对输入来验证哈希值,matches()方法会将其输入中的原始密码进行哈希化,并将其与执行验证的哈希值进行比较以判断是否相等。

1.3 从PasswordEncoder提供的实现中选择

SpringSecurity还提供了其它的PasswordEncoder实现选项。选项如下:

  • NoOpPasswordEncoder:不编码密码,而保持明文。我们仅将此实现用于示例。因为他不会对密码进行哈希化,所以永远不要在生产环境中使用它。

  • StandardPasswordEncoder:使用SHA-256对密码进行哈希化。

  • Pbkdf2PasswordEncoder:使用基于密码的密钥派生函数2(PBKDF2)。

    // Create an encoder with all the defaults
    Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder();
    String result = encoder.encode("myPassword");
    assertTrue(encoder.matches("myPassword", result));
    
  • BCryptPasswordEncoder:使用bcrypt强哈希函数对密码进行编码。

    // Create an encoder with strength 16
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
    String result = encoder.encode("myPassword");
    assertTrue(encoder.matches("myPassword", result));
    
  • SCryptPasswordEncoder:使用scrypt哈希函数对密码进行编码。

    // Create an encoder with all the defaults
    SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
    String result = encoder.encode("myPassword");
    assertTrue(encoder.matches("myPassword", result));
    

这里只简单介绍了下其中两个实现,更详细的介绍请查看SpringSecurity官网。

https://docs.spring.io/spring-security/site/docs/5.3.13.RELEASE/reference/html5/#authentication-password-storage

2、身份验证流程中的主要接口总结

接口 描述
UserDetails 代表SpringSecurity所看到的用户
GrantedAuthority 定义应用程序目的范围内允许用户执行的操作(读、写、删除等)
UserDetailsService 表示用于按用户名检索用户详细信息的对象
UserDetailsManager 一个较为特殊的UserDetailsService接口。除了按用户名检索用户外,它还可以用于更改用户集合或特定用户
PasswordEncoder 指定如何对密码进行加密或哈希化,以及检查给定的已编码字符串是由与明文密码匹配
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。