首页 > 代码库 > Spring Security-- 验证码功能的实现

Spring Security-- 验证码功能的实现

spring security4 添加验证码

http://www.itwendao.com/article/detail/165400.html

http://www.itdadao.com/articles/c15a754492p0.html

 

 

 

https://stackoverflow.com/questions/29783059/redirect-in-a-filter-with-spring-boot/29789630

在spring security的内置login处理是无法满足要求的,需要自己进行各种定制。这里介绍login中实现验证码的实现。

实现方法

可以有三种方法可以实现验证码的功能

第一种

自定义一个filter,放在SpringSecurity过滤器之前,在用户登录的时候会先经过这个filter,然后在这个filter中实现对验证码进行验证的功能,这种方法不推荐,因为它已经脱离了SpringSecurity

第二种

自定义一个filter让它继承自UsernamePasswordAuthenticationFilter,然后重写attemptAuthentication方法在这个方法中实现验证码的功能,如果验证码错误就抛出一个继承自AuthenticationException的验证吗错误的异常比如(CaptchaException),然后这个异常就会被SpringSecurity捕获到并将异常信息返回到前台,这种实现起来比较简单。

@Override  public Authentication attemptAuthentication(HttpServletRequest request,          HttpServletResponse response) throws AuthenticationException {      String requestCaptcha = request.getParameter(this.getCaptchaFieldName());      String genCaptcha = (String)request.getSession().getAttribute("code");      logger.info("开始校验验证码,生成的验证码为:"+genCaptcha+" ,输入的验证码为:"+requestCaptcha);      if( !genCaptcha.equals(requestCaptcha)){          throw new CaptchaException(                  this.messageSource.getMessage("AbstractUserDetailsAuthenticationProvider.badCaptcha",null,"Default",null));      }      return super.attemptAuthentication(request, response);  }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

接着在配置文件中配置:

<bean id="loginFilter" class="com.zrhis.system.security.DefaultUsernamePasswordAuthenticationFilter">      <property name="authenticationManager"  ref="authenticationManager"></property>      <property name="authenticationSuccessHandler">          <bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">              <property name="defaultTargetUrl" value=http://www.mamicode.com/"/index.jsp"></property>          </bean>      </property>      <property name="authenticationFailureHandler">          <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">              <property name="defaultFailureUrl" value=http://www.mamicode.com/"/login.jsp"></property>          </bean>      </property>  </bean>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

最后在http中加入custom-filter配置,将这个filter放在SpringSecurity的FORM_LOGIN_FILTER之前.

<custom-filter ref="loginFilter" before="FORM_LOGIN_FILTER"/>  
  • 1

第三种

直接替换掉SpringSecurity的UsernamePasswordAuthenticationFilter,这种比较复杂,但是更为合理,也是我现在正在用的。 
如果用这种方法那么http 中的auto-config就必须去掉,而form-login配置也必须去掉,因为这个不需要了,里面的属性都需要我们自行注入。

首先需要创建并配置一个login.jsp作为登录页面EntryPoint

<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">      <property name="loginFormUrl" value=http://www.mamicode.com/"/login.jsp" />  </bean>  
  • 1
  • 2
  • 3

然后在http中配置下

<sec:http access-decision-manager-ref="accessDecisionManager"          entry-point-ref="authenticationEntryPoint">  
  • 1
  • 2

然后我们来写CaptchaAuthenticationFilter,同样需要继承自UsernamePasswordAuthenticationFilter

public class CaptchaAuthenticationFilter extends UsernamePasswordAuthenticationFilter{      public static final String SPRING_SECURITY_FORM_CAPTCHA_KEY = "j_captcha";      public static final String SESSION_GENERATED_CAPTCHA_KEY = Constant.SESSION_GENERATED_CAPTCHA_KEY;      private String captchaParameter = SPRING_SECURITY_FORM_CAPTCHA_KEY;      public Authentication attemptAuthentication(HttpServletRequest request,              HttpServletResponse response) throws AuthenticationException {          String genCode = this.obtainGeneratedCaptcha(request);          String inputCode = this.obtainCaptcha(request);          if(genCode == null)              throw new CaptchaException(this.messages.getMessage("LoginAuthentication.captchaInvalid"));          if(!genCode.equalsIgnoreCase(inputCode)){              throw new CaptchaException(this.messages.getMessage("LoginAuthentication.captchaNotEquals"));          }          return super.attemptAuthentication(request, response);      }      protected String obtainCaptcha(HttpServletRequest request){          return request.getParameter(this.captchaParameter);      }      protected String obtainGeneratedCaptcha (HttpServletRequest request){          return (String)request.getSession().getAttribute(SESSION_GENERATED_CAPTCHA_KEY);      }  }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

在配置文件中配置CaptchaAuthenticationFilter

<bean id="captchaAuthenticaionFilter" class="com.zrhis.system.security.CaptchaAuthenticationFilter">      <property name="authenticationManager" ref="authenticationManager" />      <property name="authenticationFailureHandler" ref="authenticationFailureHandler" />      <property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />      <property name="filterProcessesUrl" value=http://www.mamicode.com/"/login.do" />  </bean>  <bean id="authenticationSuccessHandler" class="com.zrhis.system.security.SimpleLoginSuccessHandler">      <property name="defaultTargetUrl" value=http://www.mamicode.com/"/WEB-INF/app.jsp"></property>      <property name="forwardToDestination" value=http://www.mamicode.com/"true"></property>  </bean>  <bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">      <property name="defaultFailureUrl" value=http://www.mamicode.com/"/login.jsp" />  </bean>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

从配置文件中就可以看出来authenticationManager、authenticationFailureHandler、authenticationSuccessHandler、filterProcessesUrl等都需要我们自行注入了。 
filterProcessesUrl定义的是登录验证的地址,默认的是j_spring_security_check这里我们改成login.do

authenticationSuccessHandler中的defaultTargetUrl定义的是登录成功后跳转到的页面

authenticationFailureHandler中的defaultTargetUrl定义的是登录失败后跳转到的页面

我们的首页app.jsp在/WEB-INF下所以需要使用服务器跳转,所以需要将forwardToDestination设为true,因为客户端跳转是不能直接访问WEB-INF下的内容的。

最后在http中将FORM_LOGIN_FILTER替换掉,最终http中完整的配置就变成了下面的内容

<sec:http access-decision-manager-ref="accessDecisionManager"      entry-point-ref="authenticationEntryPoint">      <sec:access-denied-handler ref="accessDeniedHandler"/>      <sec:session-management invalid-session-url="/login.jsp" />      <sec:custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR"/>      <sec:custom-filter ref="captchaAuthenticaionFilter" position="FORM_LOGIN_FILTER"/>  </sec:http>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

custom-filter中before是在这个filter之前,after是之后,position是替换。

http://www.infocool.net/kb/WWW/201701/268891.html

 在使用Spring Security框架过程中,经常会有这样的需求,即在登录验证时,附带增加额外的数据,如验证码、用户类型等。下面将介绍如何实现。

  注:我的工程是在Spring Boot框架基础上的,使用xml方式配置的话请读者自行研究吧。

  • 实现自定义的WebAuthenticationDetails

  该类提供了获取用户登录时携带的额外信息的功能,默认实现WebAuthenticationDetails提供了remoteAddress与sessionId信息。开发者可以通过Authentication的getDetails()获取WebAuthenticationDetails。我们编写自定义类CustomWebAuthenticationDetails继承自WebAuthenticationDetails,添加我们关心的数据(以下是一个token字段)。

package com.cgs.courses.service;import javax.servlet.http.HttpServletRequest;import org.springframework.security.web.authentication.WebAuthenticationDetails;public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {    /**     *      */    private static final long serialVersionUID = 6975601077710753878L;    private final String token;    public CustomWebAuthenticationDetails(HttpServletRequest request) {        super(request);        token = request.getParameter("token");    }    public String getToken() {        return token;    }    @Override    public String toString() {        StringBuilder sb = new StringBuilder();        sb.append(super.toString()).append("; Token: ").append(this.getToken());        return sb.toString();    }}

 

   注:在登录页面,可将token字段放在form表单中,也可以直接加在url的参数中,进而把额外数据发送给后台。

  • 实现自定义的AuthenticationDetailsSource

  该接口用于在Spring Security登录过程中对用户的登录信息的详细信息进行填充,默认实现是WebAuthenticationDetailsSource,生成上面的默认实现WebAuthenticationDetails。我们编写类实现AuthenticationDetailsSource,用于生成上面自定义的CustomWebAuthenticationDetails。

package com.cgs.courses.service;

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;

@Component
public class CustomAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {

    @Override
    public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
        return new CustomWebAuthenticationDetails(context);
    }
}

 

  • 配置使用自定义的AuthenticationDetailsSource

  只要看这一句.formLogin().authenticationDetailsSource(authenticationDetailsSource)

    @Autowired    private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource;    protected void configure(HttpSecurity http) throws Exception {        http            .headers()                .cacheControl()                .contentTypeOptions()                .httpStrictTransportSecurity()                .xssProtection()                .and()            .authorizeRequests()                .antMatchers(                    "/css/**",                    "/js/**")                .permitAll()                .antMatchers("/**")                .authenticated()                .and()            .formLogin()                .loginPage("/login")                .permitAll()                .defaultSuccessUrl("/todo.html", true)                .authenticationDetailsSource(authenticationDetailsSource)            .and()                .logout()                .logoutUrl("/logout")                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))                .logoutSuccessUrl("/login")                .and()            .csrf().disable();    }

 

  • 实现自定义的AuthenticationProvider

  AuthenticationProvider提供登录验证处理逻辑,我们实现该接口编写自己的验证逻辑。

package com.cgs.courses.service;import org.springframework.security.authentication.AuthenticationProvider;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.stereotype.Component;@Componentpublic class CustomAuthenticationProvider implements AuthenticationProvider {        @Override    public Authentication authenticate(Authentication authentication)             throws AuthenticationException {        CustomWebAuthenticationDetails details = (CustomWebAuthenticationDetails) authentication.getDetails();  // 如上面的介绍,这里通过authentication.getDetails()获取详细信息        // System.out.println(details); details.getRemoteAddress(); details.getSessionId(); details.getToken();
// 下面是验证逻辑,验证通过则返回UsernamePasswordAuthenticationToken,
// 否则,可直接抛出错误(AuthenticationException的子类,在登录验证不通过重定向至登录页时可通过session.SPRING_SECURITY_LAST_EXCEPTION.message获取具体错误提示信息)
if (验证通过) {
return UsernamePasswordAuthenticationToken(省略参数);
} else {
throw new AuthenticationException的子类("你要显示的错误信息")
} } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); }}

 

  • 配置使用自定义的AuthenticationProvider
  @Autowired
    private AuthenticationProvider authenticationProvider;

  @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider); }

 

http://www.cnblogs.com/phoenix-smile/p/5666686.html

 

    @Bean    public AuthenticationFailureHandler authenticationFailureHandler() {        ExceptionMappingAuthenticationFailureHandler failureHandler = new ExceptionMappingAuthenticationFailureHandler();        Map<String, String> failureUrlMap = new HashMap<>();        failureUrlMap.put(AccountExpiredException.class.getName(), LoginAuthenticationFailureHandler.EXPIRE_URL);        failureUrlMap.put(BadCredentialsException.class.getName(), LoginAuthenticationFailureHandler.PASS_ERROR_URL);        failureUrlMap.put(CaptchaException.class.getName(), LoginAuthenticationFailureHandler.KAPTCHA_ERROR_URL);        failureHandler.setExceptionMappings(failureUrlMap);        return failureHandler;    }

 

Spring Security-- 验证码功能的实现