Java-Security(三):加密的用法、分析

在第一篇文章,我们展示了一个demo,其中讲到了对用户的密码进行了明文展示的用法,其实那么做是不安全的,在实际项目中往往会采用各种加密方法(比如:bcrypt,md5,sha1,sha2等)来实现对密码的保护。

本片文章将会主要讲解如何在Spring Security实现对密码加密的各种用法,以及对BCrypt的用法进一步分析。

概念

Spring Security 为我们提供了一套加密规则和密码比对规则,org.springframework.security.crypto.password.PasswordEncoder 接口,该接口里面定义了三个方法。

public interface PasswordEncoder {
    //加密(外面调用一般在注册的时候加密前端传过来的密码保存进数据库)
    String encode(CharSequence rawPassword);

    //加密前后对比(一般用来比对前端提交过来的密码和数据库存储密码, 也就是明文和密文的对比)
    boolean matches(CharSequence rawPassword, String encodedPassword);

    //是否需要再次进行编码, 默认不需要
    default boolean upgradeEncoding(String encodedPassword) {
        return false;
    }
}

在Spring Security下 PasswordEncoder 的实现类包含:

 其中常用到的分别有下面这么几个:   

    BCryptPasswordEncoder:Spring Security 推荐使用的,使用BCrypt强哈希方法来加密。
    MessageDigestPasswordEncoder:用作传统的加密方式加密(支持 MD5、SHA-1、SHA-256...)
    DelegatingPasswordEncoder:最常用的,根据加密类型id进行不同方式的加密,兼容性强
    NoOpPasswordEncoder:明文, 不做加密
    其他

Spring Security中加密的用法:

使用bcrypt bean

applicationContext-shiro.xml中配置:

    <bean id="secureRandom" class="java.security.SecureRandom"/>
    <bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
        <constructor-arg name="version" value="$2A" /> <!-- salt随机生成版本 默认$2A-->
        <constructor-arg name="strength" value="10"/> <!-- 使用salt进行加密迭代次数,默认10-->
        <constructor-arg name="random" ref="secureRandom"/> <!-- 随机算法 -->
    </bean>

    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="user" password="$2a$10$LCe6jsoHUrEvWI1KURrqbu/xfuPU5aZj2RkPTVS0d7MUJiT55Lt/y"
                                   authorities="ROLE_USER"/>
                <security:user name="admin" password="$2a$10$BR3Np37NbmtWHqpSZE6AMeCMG4Rm.UOUEZ3dYrW3oUXHNuSBXjDwi"
                               authorities="ROLE_USER, ROLE_ADMIN"/>
            </security:user-service>
            <security:password-encoder ref="bCryptPasswordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

说明:

1)需要配置 bCryptPasswordEncoder的bean,在该bean配置时,可以指定其构造函数相关参数:

version:salt随机生成版本,默认:采用 BCryptVersion.$2A.getVersion();

strength:使用salt进行加密迭代次数,默认:10;

random:随机算法,默认:new SecureRandom()

2)需要在<authentication-provider>标签下的<password-encoder ref=''/>指定该bean。

密码加密用法:

        // BCrypt加密与验证,内部默认:
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        System.out.println("passwordEncoder 123456:" + passwordEncoder.encode("123456"));
        System.out.println("passwordEncoder 123456:" + passwordEncoder.encode("123456"));
        // BCrypt密文解析

        //参数解释
        //1)2a:加密算法版本号。
        //2)10:加密轮次,默认为10,数值越大,加密时间和越难破解呈指数增长。可在BCryptPasswordEncoder构造参数传入。
        //3)密码加密:前面的内容是盐,后面的内容才是真正的密文。
        //以下方式可以更清晰的看出盐和全文。
        String salt = BCrypt.gensalt(BCryptPasswordEncoder.BCryptVersion.$2A.getVersion(), 10, new SecureRandom());
        String result = BCrypt.hashpw("123456", salt);//全文
        System.out.println("salt:" + salt + ",salt's length:" + salt.length()); // salt长度是29
        System.out.println("result:" + result);

在对密码加密时,可以采用上边这3种方法:

1)BCryptPasswordEncoder的实例,直接调用 encode方法,此时version,strlength,random都采用默认值。

2)也可以使用BCrypt来实现,实际上上边BCypt的操作就是BCryptPasswordEncoder#encode内部的方法实现。

3)另外,也可以直接在代码中引入applicaitonContext-security.xml中的md5 bean到代码中 @Resources("bCryptPasswordEncoder") private PasswordEncoder bCryptPasswordEncoder;

使用md5 bean

applicationContext-shiro.xml中配置

    <bean id="md5" class="org.springframework.security.crypto.password.MessageDigestPasswordEncoder">
        <constructor-arg name="algorithm" value="MD5"/>
        <property name="iterations" value="10"/>
    </bean>

    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="user" password="{sBNW6rB991DqeGbH6ikVJcTe6XwPoHtPW/iyWkwbrF4=}38dee1075a2eaa458bc3fb7e7a945ef8"
                               authorities="ROLE_USER"/>
                <security:user name="admin" password="{sBNW6rB991DqeGbH6ikVJcTe6XwPoHtPW/iyWkwbrF4=}38dee1075a2eaa458bc3fb7e7a945ef8"
                               authorities="ROLE_USER, ROLE_ADMIN"/>
            </security:user-service>
            <security:password-encoder ref="md5"/>
        </security:authentication-provider>
    </security:authentication-manager>

说明:

1)需要配置md5 bean,在配置bean时,必须指定MessageDigestPasswordEncoder的构造函数参数:algorithm:指定算法类型,这里是MD5;

2)另外,md5#iterations参数:迭代次数如果不指定,默认为1,这里指定为10;

2)需要在<authentication-provider>标签下的<password-encoder ref=''/>指定该bean。

密码加密用法:

        MessageDigestPasswordEncoder md5 = new MessageDigestPasswordEncoder("MD5");
        md5.setIterations(10);
        md5Password = "{MD5}" + md5.encode("password");
        System.out.println("MD5密码:" + md5Password);
        System.out.println("MD5密码对比:" + passwordEncoder.matches("password", md5Password));

在对密码加密时,可以采用上边方法:

1)MessageDigestPasswordEncoder的实例,可以设置其迭代次数。

2)另外,也可以直接在代码中引入applicaitonContext-security.xml中的md5 bean到代码中 @Resources("md5") private PasswordEncoder md5;

缺省 password-encoder,使用 DelegatingPasswordEncoder ,自动适配 PasswordEncoder

applicationContext-shiro.xml中配置:

    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <!-- noop NoOpPasswordEncoder.getInstance()-->
                <security:user name="user" password="{noop}userpwd" authorities="ROLE_USER"/>
                <security:user name="admin" password="{noop}adminpwd" authorities="ROLE_USER, ROLE_ADMIN"/>
                  <!-- bcrypt new BCryptPasswordEncoder() -->
                <security:user name="user1" password="{bcrypt}$2a$10$LCe6jsoHUrEvWI1KURrqbu/xfuPU5aZj2RkPTVS0d7MUJiT55Lt/y"
                               authorities="ROLE_USER"/>
                <security:user name="admin1" password="{bcrypt}$2a$10$BR3Np37NbmtWHqpSZE6AMeCMG4Rm.UOUEZ3dYrW3oUXHNuSBXjDwi"
                               authorities="ROLE_USER, ROLE_ADMIN"/>
                <!-- MD5 new MessageDigestPasswordEncoder("MD5") -->
                <security:user name="user2" password="{MD5}{sBNW6rB991DqeGbH6ikVJcTe6XwPoHtPW/iyWkwbrF4=}38dee1075a2eaa458bc3fb7e7a945ef8"
                               authorities="ROLE_USER"/>
                <security:user name="admin2" password="{MD5}{sBNW6rB991DqeGbH6ikVJcTe6XwPoHtPW/iyWkwbrF4=}38dee1075a2eaa458bc3fb7e7a945ef8"
                               authorities="ROLE_USER, ROLE_ADMIN"/>
            </security:user-service>
            <security:password-encoder ref="md5"/>
        </security:authentication-provider>
    </security:authentication-manager>

1)如果在<security:authentication-provider>下指定了<security:password-encoder ref="xxx"/>就不需要在<security:user name="xxx" password="yyy"authorities="zzz"/>中的 password 前边加上加密类型({noop}{bcrypt}{MD5}等),否则会导致密码验证失败;
2)如果在<security:authentication-provider>下未指定<security:password-encoder ref="xxx"/>就必须要在<security:user name="xxx" password="yyy" authorities="zzz"/>中的 password 前边加上加密类型({noop}、{bcrypt}、{MD5}等),否则会导致密码验证失败。因为此时验证密码是否成功,会调用org.springframework.security.crypto.password.DelegatingPasswordEncoder.java中的encoder方法、matches方法,而DelegatingPasswordEncoder中查找密码加密对应的PasswordEncoder时,会根据密码前缀的加密类型查找:如果查找失败,会导致查找不到delegate,也就是delegate为null。

猜你喜欢

转载自www.cnblogs.com/yy3b2007com/p/12203823.html