实际企业应用中,用户密码一般都会进行加密处理,这样才能使企业应用更加安全。既然密码的加密如此之重要,那么Acegi(Spring Security)作为成熟的安全框架,当然也我们提供了相应的处理方式。

  针对用户密码的加密工作,DaoAuthenticationProvider同时暴露了passwordEncoder和saltSource属性。PasswordEncoder和SaltSource是可选的属性,PasswordEncoder负责对认证库中的密码进行加解密。而SaltSource则是在产生密码时给它加点“盐”,以增强密码在认证库中的安全性。Acegi安全系统提供的PasswordEncoder实现中包括MD5、SHA和明文编码。

Acegi提供了三种加密器:
   PlaintextPasswordEncoder---默认,不加密,返回明文.
   ShaPasswordEncoder---哈希算法(SHA)加密
   Md5PasswordEncoder---消息摘要(MD5)加密

Acegi安全系统提供了两个SaltSource的实现:
   SystemWideSaltSource,它用同样的"盐"对系统中所有的密码进行编码
   ReflectionSaltSource只对从UserDetails对象返回的获得这种"盐"的指定属性进行检查。请参考JavaDoc以获取对这些可选特性的更详细信息。

图1为Acegi内置的PasswordEncoder继承链。默认时,DaoAuthenticationProvider会实例化PlaintextPasswordEncoder对象,即应对用户密码未进行加密处理的情况。

图2为Acegi内置的SaltSource继承链。默认时,SaltSource的取值为null,即未启用密码私钥。

1、 PlaintextPasswordEncoder明文密码
  在使用明文密码期间,如果系统允许登录用户不区分密码大小写,则应设置ignorePasswordCase属性值为true。
  在Acegi安全配置文件中添加:

<bean id="daoAuthenticationProvider"  
    class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">  
    <property name="userDetailsService" ref="inMemDaoImpl" />  
    <!-- 增加 -->  
    <property name="passwordEncoder" ref="plaintextPasswordEncoder" />  
</bean>  
  
<!-- 增加 -->  
<bean id="plaintextPasswordEncoder"  
    class="org.acegisecurity.providers.encoding.PlaintextPasswordEncoder">  
    <property name="ignorePasswordCase" value="true"></property>  
</bean> 

2、 ShaPasswordEncoder哈希算法(SHA)加密
  在Acegi安全配置文件中添加:

<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">  
    <property name="userDetailsService" ref="inMemDaoImpl" />  
    <!-- 增加 -->  
    <property name="passwordEncoder" ref="shaPasswordEncoder" />  
    <!-- <property name="passwordEncoder" ref="shaMessageDigestPasswordEncoder" /> -->  
</bean>  
  
<!-- 增加, 以下两种配置方式等效 -->  
<bean id="shaPasswordEncoder" class="org.acegisecurity.providers.encoding.ShaPasswordEncoder">  
    <constructor-arg>  
        <value>256</value>  
    </constructor-arg>  
    <property name="encodeHashAsBase64" value="false"></property>  
</bean>  
<!--   
<bean id="shaMessageDigestPasswordEncoder" class="org.acegisecurity.providers.encoding.MessageDigestPasswordEncoder">  
    <constructor-arg>  
        <value>SHA-256</value>  
    </constructor-arg>  
    <property name="encodeHashAsBase64" value="false"></property>  
</bean>  
 --> 

3、 Md5PasswordEncoder消息摘要(MD5)加密
  在Acegi安全配置文件中添加:

<bean id="daoAuthenticationProvider"  
    class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">  
    <property name="userDetailsService" ref="inMemDaoImpl" />  
    <!-- 增加 -->  
    <property name="passwordEncoder" ref="md5PasswordEncoder" />  
    <!-- <property name="passwordEncoder" ref="md5MessageDigestPasswordEncoder" /> -->  
</bean>  
  
<!-- 增加. 以下两种配法等效 -->  
<bean id="md5PasswordEncoder" class="org.acegisecurity.providers.encoding.Md5PasswordEncoder">  
    <property name="encodeHashAsBase64" value="false"></property>  
</bean>  
<!--   
<bean id="md5MessageDigestPasswordEncoder" class="org.acegisecurity.providers.encoding.MessageDigestPasswordEncoder">  
    <constructor-arg>  
        <value>MD5</value>  
    </constructor-arg>  
    <property name="encodeHashAsBase64" value="false"></property>  
</bean>  
 -->  

4、 SystemWideSaltSource
  SystemWideSaltSource会采用一个静态字符串表示密码私钥(salt),所有用户的密码处理都会采用这一私钥。与未启用密码私钥相比,SystemWideSaltSource更为安全,因为它使得密码的破解变得更困难。默认时,它会采用“密码{密码私钥}”形式加密密码。
  配置如下:

<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">  
    <property name="userDetailsService" ref="userDetailsService" />  
    <property name="passwordEncoder" ref="md5PasswordEncoder" /><!-- 增加 -->  
      
    <!-- 增加 -->  
    <property name="saltSource">  
        <bean
            class="org.acegisecurity.providers.dao.salt.SystemWideSaltSource">  
            <property name="systemWideSalt" value="javaee"></property>  
        </bean>  
    </property>  
</bean> 

5、 ReflectionSaltSource
  尽管采用SystemWideSaltSource能够加强密码的保护,但由于SystemWideSaltSource采用了全局性质的密码私钥,因此仍存在一定的缺陷。
  而ReflectionSaltSource采用了个案性质的密码私钥,即其密码加密所采用的私钥是动态变化的,因此它更为安全。ReflectionSaltSource仍采用“密码{密码私钥}”的形式加密密码。
  配置如下:

<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">  
    <property name="userDetailsService" ref="userDetailsService" />  
    <property name="passwordEncoder" ref="md5PasswordEncoder" />  
      
    <!-- 增加 -->  
    <property name="saltSource">  
        <bean  
            class="org.acegisecurity.providers.dao.salt.ReflectionSaltSource">  
            <property name="userPropertyToUse" value="getUsername"></property>  
        </bean>  
    </property>  
</bean> 

  此时,ReflectionSaltSource将调用UserDetails对象的getUsername()方法获得各用户的密码私钥。比如:javaee(用户名)/password(密码) 用户的密码将被以“password{javaee}”的形式进行加密处理。
  通过指定userPropertyToUse属性的值,开发者能够控制密码私钥的来源。比如上述的getUsername的含义就是动态调用相应的getUsername()方法,并将返回结果直接作为密码的私钥。在实际企业应用中,Acegi应用开发者可能会提供自身的UserDetails实现类,这时userPropertyToUse属性的取值范围更加广泛。

 6、 在web.xml中,提供了三种方式方便大家调试

<!--   
<context-param>  
    <param-name>contextConfigLocation</param-name>  
    <param-value>  
        /WEB-INF/applicationContext-acegi-security-plaintext.xml  
    </param-value>  
</context-param>  
-->  
  
<!--   
<context-param>  
    <param-name>contextConfigLocation</param-name>  
    <param-value>  
        /WEB-INF/applicationContext-acegi-security-sha.xml  
    </param-value>  
</context-param>  
-->  
  
<context-param>  
    <param-name>contextConfigLocation</param-name>  
    <param-value>  
        /WEB-INF/applicationContext-acegi-security-md5.xml  
    </param-value>  
</context-param>  
  
<!--   
<context-param>  
    <param-name>contextConfigLocation</param-name>  
    <param-value>  
        /WEB-INF/applicationContext-acegi-security-md5-salt.xml  
    </param-value>  
</context-param>
-->

 7、 这里提供了一个用于生成加密密码的类

package org.acegi.sample;  
  
  
import org.acegisecurity.providers.encoding.Md5PasswordEncoder;  
import org.acegisecurity.providers.encoding.MessageDigestPasswordEncoder;  
import org.acegisecurity.providers.encoding.ShaPasswordEncoder;  
import org.apache.commons.logging.Log;  
import org.apache.commons.logging.LogFactory;  
  
/** 
 * 加密管理类 
 *  
 * @author zhanjia 
 * 
 */  
public class PasswordProcessing {  
  
    private static final Log log = LogFactory.getLog(PasswordProcessing.class);  
      
    public static void processMd5() {  
        log.info("以MD5方式加密......................");  
          
        // 直接指定待采用的加密算法(MD5)  
        MessageDigestPasswordEncoder mdpeMd5 = new MessageDigestPasswordEncoder("MD5");  
        // 生成32位的Hex版, 这也是encodeHashAsBase64的默认值  
        mdpeMd5.setEncodeHashAsBase64(false);  
        log.info(mdpeMd5.encodePassword("password", null));  
        // 生成24位的Base64版  
        mdpeMd5.setEncodeHashAsBase64(true);  
        log.info(mdpeMd5.encodePassword("password", null));  
          
        // 等效于上述代码  
        Md5PasswordEncoder mpe = new Md5PasswordEncoder();  
        mpe.setEncodeHashAsBase64(false);  
        log.info(mpe.encodePassword("password", null));  
        mpe.setEncodeHashAsBase64(true);  
        log.info(mpe.encodePassword("password", null));  
    }  
      
    public static void processSha() {  
        log.info("以SHA方式加密......................");  
          
        // 直接指定待采用的加密算法(SHA)及加密强度(256)  
        MessageDigestPasswordEncoder mdpeSha = new MessageDigestPasswordEncoder("SHA-256");  
        mdpeSha.setEncodeHashAsBase64(false);  
        log.info(mdpeSha.encodePassword("password", null));  
        mdpeSha.setEncodeHashAsBase64(true);  
        log.info(mdpeSha.encodePassword("password", null));  
          
        // 等效于上述代码  
        ShaPasswordEncoder spe = new ShaPasswordEncoder(256);         
        spe.setEncodeHashAsBase64(false);  
        log.info(spe.encodePassword("password", null));  
        spe.setEncodeHashAsBase64(true);  
        log.info(spe.encodePassword("password", null));  
    }  
      
    public static void processSalt() {  
        log.info("以MD5方式加密、加私钥(盐)......................");  
          
        Md5PasswordEncoder mpe = new Md5PasswordEncoder();  
        mpe.setEncodeHashAsBase64(false);  
          
        // 等效的两行地代码  
        log.info(mpe.encodePassword("password{javaee}", null)); // javaee为密码私钥  
        log.info(mpe.encodePassword("password", "javaee")); // javaee为密码私钥  
        // 结果:87ce7b25b469025af0d5c6752038fb56  
    }  
      
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        processMd5();  
        processSha();  
        processSalt();  
    }  
  
}  

 

posted on 2018-04-23 10:55  菜鸟麻花  阅读(171)  评论(0编辑  收藏  举报