• 在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);  
    if (!isAllowEmptyValidateCode()) checkValidateCode(request); 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. }  
    protected void checkValidateCode(HttpServletRequest request) { String sessionValidateCode = obtainSessionValidateCode(request); String validateCodeParameter = obtainValidateCodeParameter(request); if (StringUtils.isEmpty(validateCodeParameter) || !sessionValidateCode.equalsIgnoreCase(validateCodeParameter)) { throw new AuthenticationServiceException(messages.getMessage("validateCode.notEquals")); } }

    这里有几点想说明一下:

    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" />  
    <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>  
    <beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.AuthenticationProcessingFilterEntryPoint"> <beans:property name="loginFormUrl" value="/login"></beans:property> </beans:bean>

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

    Xml代码
    1. <http use-expressions="true" entry-point-ref="authenticationProcessingFilterEntryPoint">   
    <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>  
    <beans:bean id="validateCodeAuthenticationFilter" class="com.cloudframework.extend.spring.security.web.authentication.ValidateCodeUsernamePasswordAuthenticationFilter"> <beans:property name="filterProcessesUrl" value="/logon"></beans:property> <beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></beans:property> <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></beans:property> <beans:property name="authenticationManager" ref="authenticationManager"></beans:property> </beans:bean> <beans:bean id="loginLogAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> <beans:property name="defaultTargetUrl" value="/main"></beans:property> </beans:bean> <beans:bean id="simpleUrlAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <beans:property name="defaultFailureUrl" value="/login"></beans:property> </beans:bean>

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

    Xml代码
    1. <authentication-manager alias="authenticationManager">  
    <authentication-manager alias="authenticationManager">做点优化

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

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

    后记

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

  • 相关阅读:
    《ERP从内部集成起步》读书笔记——第5章 MRP系统的时间概念 5.1 时间三要素 5.1.1 计划期
    MVC 图片上传小试笔记
    MVC3 something about form
    dotnetcharting.dll 菜鸟笔记
    MVC 下分离业务逻辑,优化修改
    看不见的女朋友
    相信自己
    肉体的痛苦给心灵的折磨一个宣泄的出口
    八零后为什么比我们那时还艰难
    一个人住七年
  • 原文地址:https://www.cnblogs.com/danghuijian/p/4400214.html
Copyright © 2020-2023  润新知