`
kobe学java
  • 浏览: 249123 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

在spring security3上实现验证码

 
阅读更多

转载:
在spring security3上实现验证码

http://www.javaeye.com/topic/720867

关于验证码的实现

验证码的例子现在多如牛毛,大家google一下就有很多,通常的做法是在session中生成一个随机串,再由用户提交到server端去验证。因为最近在看SS3,所以这里主要想讲验证码与SS3的结合。

验证码与SS3结合的实现方案

方案一:自己写个filter

严格来讲,这不是与SS3结合,而只是在项目中实现一个filter,然后将其拦截次序放在SS3的filter前面即可。这样做的好处是简单且通 用,以后不玩SS3了,也可以使用。缺点也很明显,那就是在SS3中配置的一应事物都要重新配置,例如拦截的URL、失败的URL、报错的资源文件、异常 后的重定向设置等等。

方案二:定制一个新的SS3的filter

通过在<http>中声明一个<custom-filter>,设置其"before"属性,让它 在"FORM_LOGIN_FILTER"前作拦截即可。这样做看上去更集中、直观,对SS3的<http>元素的默认设置改变也很小,但是 和方法一相似,还是要配置很多事物,写很多的基础代码。

方案三:扩展UsernamePasswordAuthenticationFilter

改方案通过继承UsernamePasswordAuthenticationFilter,并重载attemptAuthentication方 法,在其中增加校验验证码的逻辑。其优点是省去了编写上文中说的基础代码,相关的URL、资源文件的配置只要再此filter中配置一次即可;其缺点就是 将其插入到已有的NamingSpace声明的拦截器链中非常麻烦。我们要理解NamingSpace的配置信息、理解拦截器的顺序、别名、作用,以及要 深入SS3的源码,看UsernamePasswordAuthenticationFilter的源码,了解它及其父类中有哪些属性是必须配置或我们需 要重新配置的。

虽然这种方法烦了一点,但是我最终还是选择了此方法。因为其难度是建立于理解SS3之上的,而其好处也显而易见。

实现步骤

1.自定义UsernamePasswordAuthenticationFilter

这一步还是很简单的,我们声明一个类:ValidateCodeUsernamePasswordAuthenticationFilter,它继 承UsernamePasswordAuthenticationFilter,并重载attemptAuthentication方法,增加校验验证码 码的逻辑。这里要稍稍抗议一下SS3的作者们,那个"postOnly"属性为什么不写个读方法呢……以前在扩展Acegi的时候也遇到过类似的情况某个 私有成员变量没有读方法而被迫重写了大段的代码,虽然很多事copy的……

来看一下attemAuthentication的代码片段:

Java代码
  1. if (!isAllowEmptyValidateCode())  
  2.     checkValidateCode(request);  
  3. return this.getAuthenticationManager().authenticate(authRequest);  

 

checkValidateCode也很简单:

Java代码
  1. protected void checkValidateCode(HttpServletRequest request) {  
  2.     String sessionValidateCode = obtainSessionValidateCode(request);  
  3.     String validateCodeParameter = obtainValidateCodeParameter(request);  
  4.     if (StringUtils.isEmpty(validateCodeParameter) || !sessionValidateCode.equalsIgnoreCase(validateCodeParameter)) {  
  5.         throw new AuthenticationServiceException(messages.getMessage("validateCode.notEquals"));  
  6.     }  
  7. }  

 

这里有几点想说明一下:

  1. 这里使用messages,符合上文中我们说直接利用SS3中的基础构建。
  2. 当校验出错时,抛出AuthenticationServiceException异常,这里其实大家可以自定义一个异常类,继承AuthenticationException即可。抛出这个异常后,父类中的代码会为我们处理这个异常,让我们享受一下继承的优势。
  3. 之所以在Filter中校验验证码是因为之类有我们需要的web接口,实现更加方便。

 2.配置自定义的UsernamePasswordAuthenticationFilter

替换<form-login>

原有在<http>中的<form-login>肯定是不能在用了,我们使用一个<custom-filter>来替换:

Java代码
  1. <custom-filter ref="validateCodeAuthenticationFilter" position="FORM_LOGIN_FILTER" />  

 position表示我们替换了原来别名"FORM_LOGIN_FILTER"所标示的 类:UsernamePasswordAuthenticationFilter。但事情并非这么简单,通过阅读SS3的手册2.3、5.4节,我们得知 还需要一个AuthenticationEntryPoint:

Xml代码
  1. <beans:bean id="authenticationProcessingFilterEntryPoint"  
  2.     class="org.springframework.security.web.authentication.AuthenticationProcessingFilterEntryPoint">  
  3.     <beans:property name="loginFormUrl" value="/login"></beans:property>  
  4. </beans:bean>  

相应的,<http>也需要做点修改:

Xml代码
  1. <http use-expressions="true" entry-point-ref="authenticationProcessingFilterEntryPoint">   

 配置ValidateCodeUsernamePasswordAuthenticationFilter

配置ValidateCodeUsernamePasswordAuthenticationFilter时,我们需要将所需的属性配置完全,包括 认证成功、失败的处理器。这里多出来的配置,主要是在bean上,而bean中需要的属性,如认证过滤URL、认证成功URL、认证失败URL 在<form-login>中也是需要配置的,所以我们的工作并不多:

Xml代码
  1. <beans:bean id="validateCodeAuthenticationFilter"  
  2.     class="com.cloudframework.extend.spring.security.web.authentication.ValidateCodeUsernamePasswordAuthenticationFilter">  
  3.     <beans:property name="filterProcessesUrl" value="/logon"></beans:property>  
  4.     <beans:property name="authenticationSuccessHandler"  
  5.         ref="loginLogAuthenticationSuccessHandler"></beans:property>  
  6.     <beans:property name="authenticationFailureHandler"  
  7.         ref="simpleUrlAuthenticationFailureHandler"></beans:property>  
  8.     <beans:property name="authenticationManager" ref="authenticationManager"></beans:property>  
  9. </beans:bean>  
  10. <beans:bean id="loginLogAuthenticationSuccessHandler"  
  11.     class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">  
  12.     <beans:property name="defaultTargetUrl" value="/main"></beans:property>  
  13. </beans:bean>  
  14. <beans:bean id="simpleUrlAuthenticationFailureHandler"  
  15.     class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">  
  16.     <beans:property name="defaultFailureUrl" value="/login"></beans:property>  
  17. </beans:bean>  

 注意一下这里的"authenticationManager"属性,在NamingSpace的默认配置里,我们不需要特别指定这个属 性,SS3会为我们找到<authentication-manager>。此时,我们需要给<authentication- manager>配置一个别名:

Xml代码
  1. <authentication-manager alias="authenticationManager">  

做点优化

从配置文件中,我们看到登录链接被引用了多次,我们可以将其写在一个.properties文件中,并在xml中引用。

 

 到此,一切事物准备就绪,验证码可以正常工作了。

后记

我从08年起,利用Acegi1.0.6构建公司内多系统见的认证、授权功能,当时没有NamingSpace,有的只是一个针对请求的拦截器链和 Spring beans,虽然繁琐,但是清晰、明了。顺着这个链走下去,让你了解什么认证、授权工作的步骤及其内因,例如著名的投票策略。多年以后再回首,曾经的 Acegi,摇身一变成了Spring Security,丰富了很多的功能,文档也做了很多的改进,但是也像他的亲爹Spring一样,穿上了一件又一件的花衣服,NamingSpace是很 酷,但也增加了一个初学者了解其内里的难度。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics