菜鸟-手把手教你把Acegi应用到实际项目中(5)

在实际企业应用中,用户密码一般都会进行加密处理,这样才能使企业应用更加安全。既然密码的加密如此之重要,那么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对象,即应对用户密码未进行加密处理的情况。

菜鸟-手把手教你把Acegi应用到实际项目中(5)

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

菜鸟-手把手教你把Acegi应用到实际项目中(5)

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

  1. <bean id="daoAuthenticationProvider"
  2. class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
  3. <property name="userDetailsService" ref="inMemDaoImpl" />
  4. <!-- 增加 -->
  5. <property name="passwordEncoder" ref="plaintextPasswordEncoder" />
  6. </bean>
  7. <!-- 增加 -->
  8. <bean id="plaintextPasswordEncoder"
  9. class="org.acegisecurity.providers.encoding.PlaintextPasswordEncoder">
  10. <property name="ignorePasswordCase" value="true"></property>
  11. </bean>
<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安全配置文件中添加:

  1. <bean id="daoAuthenticationProvider"
  2. class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
  3. <property name="userDetailsService" ref="inMemDaoImpl" />
  4. <!-- 增加 -->
  5. <property name="passwordEncoder" ref="shaPasswordEncoder" />
  6. <!-- <property name="passwordEncoder" ref="shaMessageDigestPasswordEncoder" /> -->
  7. </bean>
  8. <!-- 增加, 以下两种配置方式等效 -->
  9. <bean id="shaPasswordEncoder"
  10. class="org.acegisecurity.providers.encoding.ShaPasswordEncoder">
  11. <constructor-arg>
  12. <value>256</value>
  13. </constructor-arg>
  14. <property name="encodeHashAsBase64" value="false"></property>
  15. </bean>
  16. <!--
  17. <bean id="shaMessageDigestPasswordEncoder"
  18. class="org.acegisecurity.providers.encoding.MessageDigestPasswordEncoder">
  19. <constructor-arg>
  20. <value>SHA-256</value>
  21. </constructor-arg>
  22. <property name="encodeHashAsBase64" value="false"></property>
  23. </bean>
  24. -->
<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安全配置文件中添加:

  1. <bean id="daoAuthenticationProvider"
  2. class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
  3. <property name="userDetailsService" ref="inMemDaoImpl" />
  4. <!-- 增加 -->
  5. <property name="passwordEncoder" ref="md5PasswordEncoder" />
  6. <!-- <property name="passwordEncoder" ref="md5MessageDigestPasswordEncoder" /> -->
  7. </bean>
  8. <!-- 增加. 以下两种配法等效 -->
  9. <bean id="md5PasswordEncoder"
  10. class="org.acegisecurity.providers.encoding.Md5PasswordEncoder">
  11. <property name="encodeHashAsBase64" value="false"></property>
  12. </bean>
  13. <!--
  14. <bean id="md5MessageDigestPasswordEncoder"
  15. class="org.acegisecurity.providers.encoding.MessageDigestPasswordEncoder">
  16. <constructor-arg>
  17. <value>MD5</value>
  18. </constructor-arg>
  19. <property name="encodeHashAsBase64" value="false"></property>
  20. </bean>
  21. -->
<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更为安全,因为它使得密码的破解变得更困难。默认时,它会采用“密码{密码私钥}”形式加密密码。
      配置如下:

  1. <bean id="daoAuthenticationProvider"
  2. class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
  3. <property name="userDetailsService" ref="userDetailsService" />
  4. <property name="passwordEncoder" ref="md5PasswordEncoder" /><!-- 增加 -->
  5. <!-- 增加 -->
  6. <property name="saltSource">
  7. <bean
  8. class="org.acegisecurity.providers.dao.salt.SystemWideSaltSource">
  9. <property name="systemWideSalt" value="javaee"></property>
  10. </bean>
  11. </property>
  12. </bean>
<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仍采用“密码{密码私钥}”的形式加密密码。
      配置如下:

  1. <bean id="daoAuthenticationProvider"
  2. class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
  3. <property name="userDetailsService" ref="userDetailsService" />
  4. <property name="passwordEncoder" ref="md5PasswordEncoder" />
  5. <!-- 增加 -->
  6. <property name="saltSource">
  7. <bean
  8. class="org.acegisecurity.providers.dao.salt.ReflectionSaltSource">
  9. <property name="userPropertyToUse" value="getUsername"></property>
  10. </bean>
  11. </property>
  12. </bean>
<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中,我提供了三种方式方便大家调试

  1. <!--
  2. <context-param>
  3. <param-name>contextConfigLocation</param-name>
  4. <param-value>
  5. /WEB-INF/applicationContext-acegi-security-plaintext.xml
  6. </param-value>
  7. </context-param>
  8. -->
  9. <!--
  10. <context-param>
  11. <param-name>contextConfigLocation</param-name>
  12. <param-value>
  13. /WEB-INF/applicationContext-acegi-security-sha.xml
  14. </param-value>
  15. </context-param>
  16. -->
  17. <context-param>
  18. <param-name>contextConfigLocation</param-name>
  19. <param-value>
  20. /WEB-INF/applicationContext-acegi-security-md5.xml
  21. </param-value>
  22. </context-param>
  23. <!--
  24. <context-param>
  25. <param-name>contextConfigLocation</param-name>
  26. <param-value>
  27. /WEB-INF/applicationContext-acegi-security-md5-salt.xml
  28. </param-value>
  29. </context-param>-->
<!--
<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、 这里我提供了一个用于生成加密密码的类

  1. package org.acegi.sample;
  2. import org.acegisecurity.providers.encoding.Md5PasswordEncoder;
  3. import org.acegisecurity.providers.encoding.MessageDigestPasswordEncoder;
  4. import org.acegisecurity.providers.encoding.ShaPasswordEncoder;
  5. import org.apache.commons.logging.Log;
  6. import org.apache.commons.logging.LogFactory;
  7. /**
  8. * 加密管理类
  9. *
  10. * @author zhanjia
  11. *
  12. */
  13. public class PasswordProcessing {
  14. private static final Log log = LogFactory.getLog(PasswordProcessing.class);
  15. public static void processMd5() {
  16. log.info("以MD5方式加密......................");
  17. // 直接指定待采用的加密算法(MD5)
  18. MessageDigestPasswordEncoder mdpeMd5 = new MessageDigestPasswordEncoder("MD5");
  19. // 生成32位的Hex版, 这也是encodeHashAsBase64的默认值
  20. mdpeMd5.setEncodeHashAsBase64(false);
  21. log.info(mdpeMd5.encodePassword("password", null));
  22. // 生成24位的Base64版
  23. mdpeMd5.setEncodeHashAsBase64(true);
  24. log.info(mdpeMd5.encodePassword("password", null));
  25. // 等效于上述代码
  26. Md5PasswordEncoder mpe = new Md5PasswordEncoder();
  27. mpe.setEncodeHashAsBase64(false);
  28. log.info(mpe.encodePassword("password", null));
  29. mpe.setEncodeHashAsBase64(true);
  30. log.info(mpe.encodePassword("password", null));
  31. }
  32. public static void processSha() {
  33. log.info("以SHA方式加密......................");
  34. // 直接指定待采用的加密算法(SHA)及加密强度(256)
  35. MessageDigestPasswordEncoder mdpeSha = new MessageDigestPasswordEncoder("SHA-256");
  36. mdpeSha.setEncodeHashAsBase64(false);
  37. log.info(mdpeSha.encodePassword("password", null));
  38. mdpeSha.setEncodeHashAsBase64(true);
  39. log.info(mdpeSha.encodePassword("password", null));
  40. // 等效于上述代码
  41. ShaPasswordEncoder spe = new ShaPasswordEncoder(256);
  42. spe.setEncodeHashAsBase64(false);
  43. log.info(spe.encodePassword("password", null));
  44. spe.setEncodeHashAsBase64(true);
  45. log.info(spe.encodePassword("password", null));
  46. }
  47. public static void processSalt() {
  48. log.info("以MD5方式加密、加私钥(盐)......................");
  49. Md5PasswordEncoder mpe = new Md5PasswordEncoder();
  50. mpe.setEncodeHashAsBase64(false);
  51. // 等效的两行地代码
  52. log.info(mpe.encodePassword("password{javaee}", null)); // javaee为密码私钥
  53. log.info(mpe.encodePassword("password", "javaee")); // javaee为密码私钥
  54. // 结果:87ce7b25b469025af0d5c6752038fb56
  55. }
  56. /**
  57. * @param args
  58. */
  59. public static void main(String[] args) {
  60. processMd5();
  61. processSha();
  62. processSalt();
  63. }
  64. }
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();
} }

开发环境:
MyEclipse 5.0GA
Eclipse3.2.1
JDK1.5.0_10
tomcat5.5.23
acegi-security-1.0.7
Spring2.0

Jar包:
acegi-security-1.0.7.jar
Spring.jar(2.0.8)
commons-codec.jar
jstl.jar (1.1)
standard.jar
commons-logging.jar(1.0)

上一篇:菜鸟-手把手教你把Acegi应用到实际项目中(10)-保护业务方法


下一篇:最大似然估计与期望最大化(EM)算法