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

  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应用到实际项目中(5)的更多相关文章

  1. 菜鸟-手把手教你把Acegi应用到实际项目中(8)-扩展UserDetailsService接口

    一个能为DaoAuthenticationProvider提供存取认证库的的类,它必须要实现UserDetailsService接口: public UserDetails loadUserByUse ...

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

    前面已经讲过关于保护Web资源的方式,其中包括直接在XML文件中配置和自定义实现FilterInvocationDefinitionSource接口两种方式.在实际企业应用中,保护Web资源显得非常重 ...

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

    相信不少朋友们对于学习Acegi的过程是比较痛苦的,而且可能最初一个例子都没能真正运行起来.即使能运行起来,对于里面那么多的配置,更搞不清楚为什么要那么配,多配一个和少配一个究竟有什么区别? 最终头都 ...

  4. 菜鸟-手把手教你把Acegi应用到实际项目中(11)-切换用户

    在某些应用场合中,我们可能需要用到切换用户的功能,从而以另一用户的身份进行相关操作.这一点类似于在Linux系统中,用su命令切换到另一用户进行相关操作.      既然实际应用中有这种场合,那么我们 ...

  5. 菜鸟-手把手教你把Acegi应用到实际项目中(12)-Run-As认证服务

    有这样一些场合,系统用户必须以其他角色身份去操作某些资源.例如,用户A要访问资源B,而用户A拥有的角色为AUTH_USER,资源B访问的角色必须为AUTH_RUN_AS_DATE,那么此时就必须使用户 ...

  6. 菜鸟-手把手教你把Acegi应用到实际项目中(7)-缓存用户信息

    首先讲讲EhCache.在默认情况下,即在用户未提供自身配置文件ehcache.xml或ehcache-failsafe.xml时,EhCache会依据其自身Jar存档包含的ehcache-fails ...

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

    今天就讲个ConcurrentSessionFilter. 在Acegi 1.x版本中,控制并发HttpSession和Remember-Me认证服务不能够同时启用,它们之间存在冲突问题,这是该版本的 ...

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

    在企业应用中,用户的用户名.密码和角色等信息一般存放在RDBMS(关系数据库)中.前面几节我们采用的是InMemoryDaoImpl,即基于内存的存放方式.这节我们将采用RDBMS存储用户信息. Us ...

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

    7) daoAuthenticationProvider 进行简单的基于数据库的身份验证.DaoAuthenticationProvider获取数据库中的账号密码并进行匹配,若成功则在通过用户身份的同 ...

随机推荐

  1. C#动态数组ArrayList和List<T>的比较

    C#中一维动态数组(即列表)分ArrayList和List<T>两种,其容量可随着我们的需要自动进行扩充 一.ArrayList类(少用) ArrayList位于System.Collec ...

  2. Java compiler level does not match the version of the installed Java project facet.问题

    从同事那里拷贝过来的web项目,导入到eclipse中,出现Java compiler level does not match the version of the installed Java p ...

  3. Struts2 - Interceptor中取得ActionName、Namespace、Method

    在Struts2的Interceptor中取得当前执行对应的ActionName.Namespace.Method方法: 可以使用: System.out.println(invocation.get ...

  4. Struts2 - 常用的constant总结

    见注释 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC &quo ...

  5. 剑指offer系列48---左旋转字符串

    [题目]对于一个给定的字符序列S,旋转指定位置左边的字符到右边.. * 例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”. * [思路]先分成两个部分: ...

  6. 2015年8月TIOBE编程语言排行榜

    名副其实的月经贴.

  7. android学习笔记25——事件处理Handler

    Handler消息传递机制 ==> android消息机制是另一种形式的“事件处理”,这种机制主要是为了解决android应用的多线程问题. ——android平台不允许Activity新启动的 ...

  8. FastReport使用总结三——条码简介

    FastReport Desinger中支持的Barcode类型如下图所示: 设置其Barcode属性可以实现支持不同的条码类型. 注意: 支持的条码类型说明如下: 总结: 1.通过设置Barcode ...

  9. HDU3516 树的构造

    题目大意:平面上有n个点,构成一个单调递减的序列.即对于任意的i<j,有xi<xj,yi>yj.现在要用一棵树连接这n个点.树边为有向边,只能向右或向上.求最小的权值. 分析:本题其 ...

  10. 多用less命令,不会输入h查看对应的详细文档

    在开发项目时候,难免要查看日志排查错误.之前只会用cat , more, less, tac, tail的简单功能, 但在实际工程中还是不够用的,至少效率很低.今天抽空看了下以下的博客,并实际进行了简 ...