2012-10-12 28 views
4

在開始回答之前,我知道ReCaptcha更簡單易用,但我無法使用它。生產服務器不在線。所以,我們走了。如何在Spring Security中集成JCaptcha

我使用Spring mvc 3與maven Project和weblogic作爲web服務器(開發期間的jetty)上的spring security。我會在這一個非常具體。

之前看到我的配置和文件,我想向你展示我的問題清單:

  • 我試圖驗證碼JCaptcha的相同編碼結構之前,它工作正常。
  • logger.debug完全不出現在CaptchaCaptureFilter類和/或CaptchaVerifierFilter類中(雖然它出現在ArtajasaAuthenticationProvider類中)。
  • 我可以看到驗證碼圖片,但無論答案是什麼,它總是無效的。
  • 在當前狀態下,它在碼頭或weblogic中都不起作用,但如果將自定義過濾器位置更改爲下面的位置,則它只能在jetty中使用。

    <custom-filter ref="captchaCaptureFilter" position="FIRST"/> 
    <custom-filter ref="captchaVerifierFilter" after="FIRST"/> 
    

感謝觀看和回答我的問題,非常感謝。 以下是詳細信息。

爲JCaptcha的庫是這一個:

<repository> 
     <id>sourceforge-releases</id> 
     <name>Sourceforge Releases</name> 
     <url>https://oss.sonatype.org/content/repositories/sourceforge-releases</url> 
    </repository> 

<dependency> 
     <groupId>com.octo.captcha</groupId> 
     <artifactId>jcaptcha-integration-simple-servlet</artifactId> 
     <version>2.0-alpha-1</version> 
    </dependency> 

這裏有一些配置我.xml文件提出:

的web.xml

<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value> 
     /WEB-INF/applicationContext.xml 
     /WEB-INF/spring/spring-security.xml 
    </param-value> 
</context-param> 
<listener> 
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> 
</listener> 

<filter> 
    <filter-name>springSecurityFilterChain</filter-name> 
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
</filter> 

<filter-mapping> 
    <filter-name>springSecurityFilterChain</filter-name> 
    <url-pattern>/*</url-pattern> 
    <dispatcher>FORWARD</dispatcher> 
    <dispatcher>REQUEST</dispatcher> 
</filter-mapping> 

<servlet> 
    <servlet-name>jcaptcha</servlet-name> 
    <servlet-class>com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet</servlet-class> 
</servlet> 
<servlet-mapping> 
    <servlet-name>jcaptcha</servlet-name> 
    <url-pattern>/jcaptcha.jpg</url-pattern> 
</servlet-mapping> 

彈簧security.xml文件

<http auto-config="true" use-expressions="true"> 
    <intercept-url pattern="/resources/**" access="permitAll()" /> 
    <intercept-url pattern="/jcaptcha.jpg" access="permitAll()" /> 
    <intercept-url pattern="/**" access="isAuthenticated()" /> 

    <form-login login-page="/session/login/" default-target-url="/" 
       authentication-failure-url="/session/loginfailed/" /> 
    <logout logout-success-url="/session/logout/" /> 
    <access-denied-handler error-page="/session/403/" /> 

    <!--JCaptcha Filtering--> 
    <custom-filter ref="captchaCaptureFilter" before="FORM_LOGIN_FILTER"/> 
    <custom-filter ref="captchaVerifierFilter" after="FORM_LOGIN_FILTER"/> 
    <anonymous /> 
</http> 

<!-- For capturing CAPTCHA fields --> 
<beans:bean id="captchaCaptureFilter" class="com.util.CaptchaCaptureFilter" /> 

<!-- For verifying CAPTCHA fields --> 
<!-- Private key is assigned by the JCaptcha service --> 
<beans:bean id="captchaVerifierFilter" class="com.util.CaptchaVerifierFilter" 
     p:failureUrl="/session/loginfailed/" 
     p:captchaCaptureFilter-ref="captchaCaptureFilter"/> 

<beans:bean id="customAuthFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> 
    <beans:property name="sessionAuthenticationStrategy" ref="sas"/> 
    <beans:property name="authenticationManager" ref="authenticationManager" /> 
    <beans:property name="allowSessionCreation" value="true" /> 
</beans:bean> 

<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"> 
    <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry"/> 
    <beans:property name="maximumSessions" value="1" /> 
</beans:bean> 

<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" /> 

<beans:bean id="userService" class="com.service.mybatis.UserManager" /> 

<beans:bean id="customAuthenticationProvider" class="com.util.ArtajasaAuthenticationProvider" /> 
<authentication-manager alias="authenticationManager"> 
    <authentication-provider ref="customAuthenticationProvider" /> 
</authentication-manager> 

<beans:bean id="accessDeniedHandler" class="com.util.ThouShaltNoPass"> 
    <beans:property name="accessDeniedURL" value="/session/403/" /> 
</beans:bean> 

這些都是Java類:

ArtajasaAuthenticationProvider.java

public class ArtajasaAuthenticationProvider implements AuthenticationProvider { 

@Autowired 
private UserService userService; 
private Logger logger = LoggerFactory.getLogger(ArtajasaAuthenticationProvider.class); 

@Override 
public Authentication authenticate(Authentication authentication) throws AuthenticationException { 
    String username = String.valueOf(authentication.getPrincipal()); 
    String password = String.valueOf(authentication.getCredentials()); 
    logger.debug("Checking authentication for user {}", username); 
    if (StringUtils.isBlank(username) 
      || StringUtils.isBlank(password)) { 
     throw new BadCredentialsException("No Username and/or Password Provided."); 
    } else { 
     Pengguna user = userService.select(username); 
     if (user == null) { 
      throw new BadCredentialsException("Invalid Username and/or Password."); 
     } 
     if (user.getPassword().equals(new PasswordUtil().generateHash(password, user.getSalt()))) { 
      List<GrantedAuthority> authorityList = (List<GrantedAuthority>) userService.getAuthorities(user); 
      return new UsernamePasswordAuthenticationToken(username, password, authorityList); 
     } else { 
      throw new BadCredentialsException("Invalid Username and/or Password."); 
     } 
    } 
} 

@Override 
public boolean supports(Class<?> authentication) { 
    return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); 
} 
} 

CaptchaCaptureFilter.java

public class CaptchaCaptureFilter extends OncePerRequestFilter { 

protected Logger logger = Logger.getLogger(CaptchaCaptureFilter.class); 
private String userCaptchaResponse; 
private HttpServletRequest request; 

@Override 
public void doFilterInternal(HttpServletRequest req, HttpServletResponse res, 
          FilterChain chain) throws IOException, ServletException { 

    logger.debug("Captcha capture filter"); 

    // Assign values only when user has submitted a Captcha value. 
    // Without this condition the values will be reset due to redirection 
    // and CaptchaVerifierFilter will enter an infinite loop 
    if (req.getParameter("jcaptcha") != null) { 
     request = req; 
     userCaptchaResponse = req.getParameter("jcaptcha"); 
    } 

    logger.debug("userResponse: " + userCaptchaResponse); 

    // Proceed with the remaining filters 
    chain.doFilter(req, res); 
} 

public String getUserCaptchaResponse() { 
    return userCaptchaResponse; 
} 

public void setUserCaptchaResponse(String userCaptchaResponse) { 
    this.userCaptchaResponse = userCaptchaResponse; 
} 

public HttpServletRequest getRequest() { 
    return request; 
} 

public void setRequest(HttpServletRequest request) { 
    this.request = request; 
} 
} 

CaptchaVerifierFilter.java

public class CaptchaVerifierFilter extends OncePerRequestFilter { 

protected Logger logger = Logger.getLogger(CaptchaVerifierFilter.class); 
private String failureUrl; 
private CaptchaCaptureFilter captchaCaptureFilter; 

// Inspired by log output: AbstractAuthenticationProcessingFilter.java:unsuccessfulAuthentication:320) 
// Delegating to authentication failure handlerorg.springframework.se[email protected]15d4273 
private SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler(); 

@Override 
public void doFilterInternal(HttpServletRequest req, HttpServletResponse res, 
          FilterChain chain) throws IOException, ServletException { 

    logger.debug("Captcha verifier filter"); 
    logger.debug("userResponse: " + captchaCaptureFilter.getUserCaptchaResponse()); 

    // Assign values only when user has submitted a Captcha value 
    if (captchaCaptureFilter.getUserCaptchaResponse() != null) { 

     // Send HTTP request to validate user's Captcha 
     boolean captchaPassed = SimpleImageCaptchaServlet.validateResponse(captchaCaptureFilter.getRequest(), captchaCaptureFilter.getUserCaptchaResponse()); 

     // Check if valid 
     if (!captchaPassed) { 
      logger.debug("Captcha is invalid!"); 

      // Redirect user to login page 
      failureHandler.setDefaultFailureUrl(failureUrl); 
      failureHandler.onAuthenticationFailure(req, res, new BadCredentialsException("Captcha invalid! " + captchaCaptureFilter.getRequest() + " " + captchaCaptureFilter.getUserCaptchaResponse())); 

     } else { 
      logger.debug("Captcha is valid!"); 
     } 

     // Reset Captcha fields after processing 
     // If this method is skipped, everytime we access a page 
     // CaptchaVerifierFilter will infinitely send a request to the Google Captcha service! 
     resetCaptchaFields(); 
    } 

    // Proceed with the remaining filters 
    chain.doFilter(req, res); 
} 

/** 
* Reset Captcha fields 
*/ 
public void resetCaptchaFields() { 
    captchaCaptureFilter.setUserCaptchaResponse(null); 
} 

public String getFailureUrl() { 
    return failureUrl; 
} 

public void setFailureUrl(String failureUrl) { 
    this.failureUrl = failureUrl; 
} 

public CaptchaCaptureFilter getCaptchaCaptureFilter() { 
    return captchaCaptureFilter; 
} 

public void setCaptchaCaptureFilter(CaptchaCaptureFilter captchaCaptureFilter) { 
    this.captchaCaptureFilter = captchaCaptureFilter; 
} 
} 

最後但並非最不重要的login.jsp

<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core_rt' %> 

<form id="login" name="f" action="<c:url value='/j_spring_security_check'/>" method="POST"> 
    <div class="container"> 

    <div class="content"> 
     <div class="row"> 
      <div class="login-form"> 
       <h3>Login</h3> 
       <br /> 
        <fieldset> 
         <div class="clearfix"> 
          username: ecr 
          <input type="text" name='j_username' value='<c:if test="${not empty param.login_error}"><c:out value="${SPRING_SECURITY_LAST_USERNAME}"/></c:if>' placeholder="[email protected]"> 
         </div> 
         <div class="clearfix"> 
          password: ecr123 
          <input type="password" name='j_password' placeholder="password"> 
         </div> 
         <div class="clearfix"> 
          <img src="../../jcaptcha.jpg" /> 
          <br /> 
          <input type="text" name="jcaptcha" placeholder="masukkan captcha" /> 
         </div> 
         <br /> 
         <button class="btn btn-primary" type="submit"><i class="icon-lock"></i> Sign in</button> 
        </fieldset> 
      </div> 
     </div> 
    </div> 
    <br /> 
    <c:if test="${not empty error}"> 
      <div class="alert alert-error"> 
      <button type="button" class="close" data-dismiss="alert"><i class="icon-remove"></i></button> 
      Login Failed, try again.<br /> 
      <c:out value="${sessionScope['SPRING_SECURITY_LAST_EXCEPTION'].message}"/> 
      </div> 
      </c:if> 
    </div> 

回答

3

問題解決了!我找到了答案。所以我們畢竟不需要CaptchaVerifierFilter。我驗證AuthenticationProvider內的驗證碼。

這些是更改列表:

在spring-security中。XML,這個

<!--JCaptcha Filtering--> 
<custom-filter ref="captchaCaptureFilter" before="FORM_LOGIN_FILTER"/> 
<custom-filter ref="captchaVerifierFilter" after="FORM_LOGIN_FILTER"/> 

成爲這一個

<!--JCaptcha Filtering--> 
<custom-filter ref="captchaCaptureFilter" before="FORM_LOGIN_FILTER"/> 

刪除

<!-- For verifying CAPTCHA fields --> 
<!-- Private key is assigned by the JCaptcha service --> 
<beans:bean id="captchaVerifierFilter" class="com.util.CaptchaVerifierFilter" 
    p:failureUrl="/session/loginfailed/" 
    p:captchaCaptureFilter-ref="captchaCaptureFilter"/> 

和驗證驗證碼在這裏

<beans:bean id="customAuthenticationProvider" class="com.pusilkom.artajasa.ecr.backend.util.MyAuthenticationProvider" 
      p:captchaCaptureFilter-ref="captchaCaptureFilter"/> 
+0

你能告訴請問,AuthenticationProvider確實發生了哪些驗證? –

0

我試圖驗證的一個多方法JCaptcha即在春季賽前無損認證。首先,JCaptcha將得到驗證,如果這是正確的,控制權將轉到Spring Security認證。我也沒有在security-context.xml中添加CaptchaCaptureFilter。

下面是我試過的。它工作正常。

public String login() { 
    this.captchaPassed = false; 

    // Check if captcha entered is correct. If yes then only proceed with 
    // Spring Security Authentication 
    this.captchaPassed = checkLoginCaptcha(); 

    if (captchaPassed) { 
     boolean success = authenticationService.login(userName, password); 

     if (success) { 
      StringBuilder userNameBuilder = new StringBuilder(); 
      userNameBuilder.append(userName); 
      FacesContext.getCurrentInstance().getExternalContext() 
        .getSessionMap() 
        .put("USER_NAME_PARAM", userNameBuilder.toString()); 
      return ApplicationConstants.HOME_PAGE; 
     } else { 
      this.message = "Wrong Username or Password Entered. Please LOGIN again."; 
      this.userName = null; 
      this.password = null; 
      this.captchaString = null; 

      return ApplicationConstants.LOGIN_PAGE; 
     } 
    } else { 
     this.message = "Invalid captcha entered. Please LOGIN again."; 
     return ApplicationConstants.LOGIN_PAGE; 
    } 
} 

public boolean checkLoginCaptcha() { 

    HttpServletRequest req = (HttpServletRequest) FacesContext 
      .getCurrentInstance().getExternalContext().getRequest(); 
    String str = null; 
    boolean flag = false; 

    try { 
     str = req.getParameter("loginForm:jcaptchaString"); 

     if (str != null) { 
      flag = SimpleImageCaptchaServlet.validateResponse(req, str); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
     flag = false; 
    } 

    return flag; 
} 
1

我不確定它是否是正確的做法,但它的作品完美。 我已經創建了與您的類相同的類,但篩選器類代碼中的更改很少,並且在security-context.xml中發生了一些小的更改。

  1. 我驗證在CaptchaCaptureFilter驗證碼和驗證結果存儲在變量iscaptchaPassed其中在CaptchaCaptureFilter一個屬性。隨着 iscaptchaPassed我還將響應存儲爲captchaResponse,以便在稍後在CaptchaVerifierFilter中檢查它是否爲空。

公共類CaptchaCaptureFilter延伸OncePerRequestFilter {

private String captchaResponse; 

private boolean iscaptchaPassed; 

// setter和getter

@Override 
public void doFilterInternal(HttpServletRequest req, HttpServletResponse res,FilterChain chain) throws IOException, ServletException { 

    logger.info("Captcha capture filter"); 

    String captcha_Response=req.getParameter("jcaptcha"); 
    logger.info("response captcha captured : " +captcha_Response); 

    if(captcha_Response!=null) 
    { 
     iscaptchaPassed = SimpleImageCaptchaServlet.validateResponse(req, req.getParameter("jcaptcha")); 
     captchaResponse=captcha_Response; 
     logger.info("isCaptchaPassed value is "+iscaptchaPassed); 
    } 

    // Proceed with the remaining filters 
    chain.doFilter(req, res); 
} 
  • 閱讀值captchaCaptureFilter在CaptchaVerifyFilter注入和處理如下所示。
  • 公共類CaptchaVerifierFilter延伸OncePerRequestFilter {

    protected Logger logger = LoggerFactory.getLogger(Filter.class); 
    
    private CaptchaCaptureFilter captchaCaptureFilter; 
    
    private String failureUrl; 
    
    //getters and setters**strong text** 
    
    private SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler(); 
    
    @Override 
    public void doFilterInternal(HttpServletRequest req, HttpServletResponse res,FilterChain chain) throws IOException, ServletException { 
    
        //logger.info("Captcha verifier filter"); 
        boolean captchaPassed=captchaCaptureFilter.getIscaptchaPassed(); 
        String captchaResponse=captchaCaptureFilter.getCaptchaResponse(); 
        //logger.info("captcha captured :"+captchaResponse+" validation result of captcha : " +captchaPassed); 
    
        if(captchaResponse!=null) 
        { 
         if(captchaPassed) 
         { 
          logger.info("Captcha is valid!"); 
         } 
         else 
         { 
          logger.info("Captcha is invalid!"); 
          failureHandler.setDefaultFailureUrl(failureUrl); 
          failureHandler.onAuthenticationFailure(req, res, new BadCredentialsException("Captcha invalid!")); 
         } 
         resetCaptchaFields(); 
        } 
    
        chain.doFilter(req, res);   
    } 
    
    /** 
    * Reset Captcha fields 
    */ 
    public void resetCaptchaFields() { 
        captchaCaptureFilter.setCaptchaResponse(null); 
        captchaCaptureFilter.setIscaptchaPassed(false);; 
    } 
    
  • 變化安全context.xml的下面。
  • 安全:自定義過濾器REF = 「captchaCaptureFilter」= 「FIRST」

    安全之前:自定義過濾器REF = 「captchaVerifierFilter」 後= 「FORM_LOGIN_FILTER」

    我加入captchaCaptureFilter之前的所有驗證碼驗證的過濾器。在UserNameAndPasswordAuthFilter是FORM_LOGIN_FILTER後使用驗證的結果。