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="username@artajasa.co.id"> </ 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!