How to Integrate JCaptcha in Spring Security

The repository for JCaptcha is this one:

<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>

Here are some configuration I made in .xml files:

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>

spring-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"/>

    <!-- REMOVED 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 -->

<!-- REMOVED beans:bean id="captchaVerifierFilter" class="com.util.CaptchaVerifierFilter"

      p:failureUrl="/session/loginfailed/"

      p:captchaCaptureFilter-ref="captchaCaptureFilter"/-->



    <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.MyAuthenticationProvider" p:captchaCaptureFilter-ref="captchaCaptureFilter" />

<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>

And these are the java classes:

MyAuthenticationProvider.java

public class MyAuthenticationProvider implements AuthenticationProvider {



@Autowired

private UserService userService;

private Logger logger = LoggerFactory.getLogger(ArtajasaAuthenticationProvider.class);

private CaptchaCaptureFilter captchaCaptureFilter;



@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);

    logger.debug("userResponse: {}", captchaCaptureFilter.getUserCaptchaResponse());

    if (StringUtils.isBlank(username)

            || StringUtils.isBlank(password)) {

        throw new BadCredentialsException("No Username and/or Password Provided.");

    }

    else if(StringUtils.isBlank(captchaCaptureFilter.getUserCaptchaResponse())) {

        throw new BadCredentialsException("Captcha Response is Empty");

    }

    else {

        // 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 valid!");

            resetCaptchaFields();



            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.");

            }

        }

        else {

            logger.debug("Captcha is invalid!");

            resetCaptchaFields();



            throw new BadCredentialsException("Invalid Captcha.");

        }

    }

}



@Override

public boolean supports(Class<?> authentication) {

    return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));

}



/**

 * Reset Captcha fields

 */

public void resetCaptchaFields() {

    captchaCaptureFilter.setUserCaptchaResponse(null);

}



public CaptchaCaptureFilter getCaptchaCaptureFilter() {

    return captchaCaptureFilter;

}



public void setCaptchaCaptureFilter(CaptchaCaptureFilter captchaCaptureFilter) {

    this.captchaCaptureFilter = captchaCaptureFilter;

}

}

CaptchaCaptureFilter.java

public class CaptchaCaptureFilter extends OncePerRequestFilter {



private 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;

}

}

Last but not least, 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>

done!

你可能感兴趣的:(Spring Security)