Introduction
In this tutorial we'll integrate Spring Security 3 with the popular reCAPTCHA service. For the Spring Security module we'll based the application from the one we built for Spring Security 3 - MVC: Using a Simple User-Service Tutorial. For the reCAPTCHA module we'll based the implementation from the Using reCAPTCHA with Java/JSP article. Our design goal here is to integrate the reCAPTCHA service unobtrusively. To realize that we'll be relying on Spring Security filters.Here's what we will do:
1. Declare two CAPTCHA filters
2. Modify a JSP file to include reCAPTCHA login
3. Add two filters in the spring-security.xml configuration
Requirements:
1. A basic understanding of how to setup a simple Spring Security 3 application (See here)
2. A basic understanding of how to setup reCAPTCHA with JSP (See here)
3. An account with the reCAPTCHA service (See here). For the account, make sure you assigned the host that matches your web application. For example http://localhost.
What is reCAPTCHA?
reCAPTCHA is a free CAPTCHA service that helps to digitize books, newspapers and old time radio shows....Here's a screenshot of the reCAPTCHA service:
A CAPTCHA is a program that can tell whether its user is a human or a computer. You've probably seen them รณ colorful images with distorted text at the bottom of Web registration forms. CAPTCHAs are used by many websites to prevent abuse from "bots," or automated programs usually written to generate spam. No computer program can read distorted text as well as humans can, so bots cannot navigate sites protected by CAPTCHAs.
About 200 million CAPTCHAs are solved by humans around the world every day. In each case, roughly ten seconds of human time are being spent. Individually, that's not a lot of time, but in aggregate these little puzzles consume more than 150,000 hours of work each day. What if we could make positive use of this human effort? reCAPTCHA does exactly that by channeling the effort spent solving CAPTCHAs online into "reading" books.
Source: http://www.google.com/recaptcha/learnmore
Architecture
Scenario 1: Spring SecurityA form-based login has two input fields: username and password. To authenticate a user, the user must enter valid information. After submitting the values, the web application will query the database if the submitted values are present. The web application performs the validation.
Scenario 2: reCAPTCHA
A reCAPTCHA form has one input field. To authenticate a user, the user must match the text images shown on the form. After submitting the values, the web application will send a POST request to the reCAPTCHA service. The service performs the validation and returns the result as a booblean value. The web application can use this result to determine if the user should be allowed access or not.
Spring Security and reCAPTCHA
With Spring Security and reCAPTCHA the same logic still applies. To authenticate a user, the user must enter his username, password, and the two words that matches the CAPTCHA text images.
Here's a screenshot of our new login page:
After submitting the values, here's what should happen:
1. A custom filter stores the entered values from the CAPTCHA form for later use. (Scenario 2)
2. The web application queries the database to check if the entered username and password exist. If invalid, deny access. If valid, continue with the remaining filters. (Scenario 1)
3. A second custom filter retrieves the stored values from the CAPTCHA form and sends a POST request to the CAPTCHA service. (Scenario 2)
Development
The Filters
Our application has two custom filters:1. CAPTCHA Capture filter 2. CAPTCHA Verifier filterThe purpose of the Capture filter is to store the information entered by the user in the CAPTCHA form. Whereas the Verifier filter's purpose is to send a POST request to the CAPTCHA service and wait for the result. If the result is valid, allow the user to proceed; otherwise, it will show the login page again.
We'll place these two filters in-between the FORM_LOGIN_FILTER alias which is the one responsible for the form-based login for Spring Security. Why do we need to place these two filters in-between this alias?
<security:http auto-config="true" > ... <security:custom-filter ref="captchaCaptureFilter" before="FORM_LOGIN_FILTER"/> <security:custom-filter ref="captchaVerifierFilter" after="FORM_LOGIN_FILTER"/> </security:http>
Issue 1:
Placing the capture and verifier filters after the FORM_LOGIN_FILTER resets the requests parameters which means they will always be null. The verifier has nothing to verify resulting to denied access!
Issue 2:
Placing the capture and verifier filter before the alias works but you won't be able to redirect the user to the login page if in case the user has entered an invalid information. You can throw an Exception like BadCredentialsException but the exception will be shown on the web page! Again, you can't redirect to a JSP page because the response stream has been already written.
Here's the capture filter:
CaptchaCaptureFilter.java
package org.krams.tutorial.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.springframework.web.filter.OncePerRequestFilter; /** * Filter for capturing Captcha fields. * It's purpose is to store these values internally */ public class CaptchaCaptureFilter extends OncePerRequestFilter { protected Logger logger = Logger.getLogger("filter"); private String recaptcha_response; private String recaptcha_challenge; private String remoteAddr; @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("recaptcha_response_field") != null) { recaptcha_response = req.getParameter("recaptcha_response_field"); recaptcha_challenge = req.getParameter("recaptcha_challenge_field"); remoteAddr = req.getRemoteAddr(); } logger.debug("challenge: " + recaptcha_challenge); logger.debug("response: " + recaptcha_response); logger.debug("remoteAddr: " + remoteAddr); // Proceed with the remaining filters chain.doFilter(req, res); } public String getRecaptcha_response() { return recaptcha_response; } public void setRecaptcha_response(String recaptchaResponse) { recaptcha_response = recaptchaResponse; } public String getRecaptcha_challenge() { return recaptcha_challenge; } public void setRecaptcha_challenge(String recaptchaChallenge) { recaptcha_challenge = recaptchaChallenge; } public String getRemoteAddr() { return remoteAddr; } public void setRemoteAddr(String remoteAddr) { this.remoteAddr = remoteAddr; } }
Here's the verifier filter:
CaptchaVerifierFilter.java
package org.krams.tutorial.filter; import java.io.IOException; import java.util.Properties; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.tanesha.recaptcha.ReCaptchaImpl; import net.tanesha.recaptcha.ReCaptchaResponse; import org.apache.log4j.Logger; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.web.filter.OncePerRequestFilter; /** * Filter for verifying if the submitted Captcha fields * are valid. * <p> * This filter also allows you to set a proxy if needed */ public class CaptchaVerifierFilter extends OncePerRequestFilter { protected Logger logger = Logger.getLogger("filter"); private Boolean useProxy = false; private String proxyPort; private String proxyHost; private String failureUrl; private CaptchaCaptureFilter captchaCaptureFilter; private String privateKey; // Inspired by log output: AbstractAuthenticationProcessingFilter.java:unsuccessfulAuthentication:320) // Delegating to authentication failure handlerorg.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@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("challenge: " + captchaCaptureFilter.getRecaptcha_challenge()); logger.debug("response: " + captchaCaptureFilter.getRecaptcha_response()); logger.debug("remoteAddr: " + captchaCaptureFilter.getRemoteAddr()); // Assign values only when user has submitted a Captcha value if (captchaCaptureFilter.getRecaptcha_response() != null) { // Create a new recaptcha (by Soren Davidsen) ReCaptchaImpl reCaptcha = new ReCaptchaImpl(); // Set the private key (assigned by Google) reCaptcha.setPrivateKey(privateKey); // Assign proxy if needed if (useProxy) { Properties systemSettings = System.getProperties(); systemSettings.put("http.proxyPort",proxyPort); systemSettings.put("http.proxyHost",proxyHost); } // Send HTTP request to validate user's Captcha ReCaptchaResponse reCaptchaResponse = reCaptcha.checkAnswer(captchaCaptureFilter.getRemoteAddr(), captchaCaptureFilter.getRecaptcha_challenge(), captchaCaptureFilter.getRecaptcha_response()); // Check if valid if (!reCaptchaResponse.isValid()) { logger.debug("Captcha is invalid!"); // Redirect user to login page failureHandler.setDefaultFailureUrl(failureUrl); failureHandler.onAuthenticationFailure(req, res, new BadCredentialsException("Captcha invalid!")); } 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.setRemoteAddr(null); captchaCaptureFilter.setRecaptcha_challenge(null); captchaCaptureFilter.setRecaptcha_response(null); } public Boolean getUseProxy() { return useProxy; } public void setUseProxy(Boolean useProxy) { this.useProxy = useProxy; } public String getProxyPort() { return proxyPort; } public void setProxyPort(String proxyPort) { this.proxyPort = proxyPort; } public String getProxyHost() { return proxyHost; } public void setProxyHost(String proxyHost) { this.proxyHost = proxyHost; } 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; } public String getPrivateKey() { return privateKey; } public void setPrivateKey(String privateKey) { this.privateKey = privateKey; } }
Configuration
We've already seen earlier the required configuration to activate the CAPTCHA filters. Here's the full spring-security.xml configuration file:spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"> <security:http auto-config="true" use-expressions="true" access-denied-page="/krams/auth/denied" > <security:intercept-url pattern="/krams/auth/login" access="permitAll"/> <security:intercept-url pattern="/krams/main/admin" access="hasRole('ROLE_ADMIN')"/> <security:intercept-url pattern="/krams/main/common" access="hasRole('ROLE_USER')"/> <security:form-login login-page="/krams/auth/login" authentication-failure-url="/krams/auth/login?error=true" default-target-url="/krams/main/common"/> <security:logout invalidate-session="true" logout-success-url="/krams/auth/login" logout-url="/krams/auth/logout"/> <security:custom-filter ref="captchaCaptureFilter" before="FORM_LOGIN_FILTER"/> <security:custom-filter ref="captchaVerifierFilter" after="FORM_LOGIN_FILTER"/> </security:http> <!-- For capturing CAPTCHA fields --> <bean id="captchaCaptureFilter" class="org.krams.tutorial.filter.CaptchaCaptureFilter" /> <!-- For verifying CAPTCHA fields --> <!-- Private key is assigned by the reCATPCHA service --> <bean id="captchaVerifierFilter" class="org.krams.tutorial.filter.CaptchaVerifierFilter" p:useProxy="false" p:proxyPort="" p:proxyHost="" p:failureUrl="/krams/auth/login?error=true" p:captchaCaptureFilter-ref="captchaCaptureFilter" p:privateKey="ADD-YOUR-PRIVATE-KEY-HERE"/> <!-- Declare an authentication-manager to use a custom userDetailsService --> <security:authentication-manager> <security:authentication-provider user-service-ref="userDetailsService"> <security:password-encoder ref="passwordEncoder"/> </security:authentication-provider> </security:authentication-manager> <!-- Use a Md5 encoder since the user's passwords are stored as Md5 in the database --> <bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/> <!-- An in-memory list of users. No need to access an external database layer. See Spring Security 3.1 Reference 5.2.1 In-Memory Authentication --> <!-- john's password is admin, while jane;s password is user --> <security:user-service id="userDetailsService"> <security:user name="john" password="21232f297a57a5a743894a0e4a801fc3" authorities="ROLE_USER, ROLE_ADMIN" /> <security:user name="jane" password="ee11cbb19052e40b07aac0ca060c23ee" authorities="ROLE_USER" /> </security:user-service> </beans>Notice the only changes we made here is add the two filters:
<security:http auto-config="true" > ... <security:custom-filter ref="captchaCaptureFilter" before="FORM_LOGIN_FILTER"/> <security:custom-filter ref="captchaVerifierFilter" after="FORM_LOGIN_FILTER"/> </security:http>
And declare the beans:
<!-- For capturing CAPTCHA fields --> <bean id="captchaCaptureFilter" class="org.krams.tutorial.filter.CaptchaCaptureFilter" /> <!-- For verifying CAPTCHA fields --> <!-- Private key is assigned by the reCATPCHA service --> <bean id="captchaVerifierFilter" class="org.krams.tutorial.filter.CaptchaVerifierFilter" p:useProxy="false" p:proxyPort="" p:proxyHost="" p:failureUrl="/krams/auth/login?error=true" p:captchaCaptureFilter-ref="captchaCaptureFilter" p:privateKey="ADD-YOUR-PRIVATE-KEY-HERE"/>The verifier filter is configurable. You can enable proxy if needed and assign a failure URL which is usually the same URL declared in your form-login tag. Don't forget to add your private key which is provided freely by reCAPTCHA!
The Login Page
Our last task is modify the login page so that the CAPTCHA form is shown along with the username and password fields. To implement the login page we just mix directly the contents from the original loginpage.jsp and the one from http://code.google.com/apis/recaptcha/docs/java.htmlHere's the login page:
loginpage.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> <%@ page import="net.tanesha.recaptcha.ReCaptcha" %> <%@ page import="net.tanesha.recaptcha.ReCaptchaFactory" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h1>Login</h1> <div id="login-error">${error}</div> <c:url value="/j_spring_security_check" var="secureUrl"/> <form action="${secureUrl}" method="post" > <% ReCaptcha c = ReCaptchaFactory.newReCaptcha("ADD-YOUR-PUBLIC-KEY-HERE", "ADD-YOUR-PRIVATE-KEY-HERE", false); out.print(c.createRecaptchaHtml(null, null)); %> <p> <label for="j_username">Username</label> <input id="j_username" name="j_username" type="text" /> </p> <p> <label for="j_password">Password</label> <input id="j_password" name="j_password" type="password" /> </p> <input type="submit" value="Login"/> </form> </body> </html>The main changes here are the addition of the ReCaptcha:
<% ReCaptcha c = ReCaptchaFactory.newReCaptcha("ADD-YOUR-PUBLIC-KEY-HERE", "ADD-YOUR-PRIVATE-KEY-HERE", false); out.print(c.createRecaptchaHtml(null, null)); %>Upon running the application, this will automatically build and show the CAPTCHA form.
Run the Application
To run the application, use the following URL:http://localhost:8080/spring-security-recaptcha/or to go directly to the login page:
http://localhost:8080/spring-security-recaptcha/krams/auth/login
Reminders:
1. Make sure you've signed-up for a reCAPTCHA account!
2. Don't forget to add your Public and Private keys in the JSP page
3. Don't forget to add the Private key in the spring-security.xml
4. Enable the proxy setting if you have one
The application has two built-in users:
username: john / password: admin username: jane / password: user
Conclusion
That's it. We've successfully integrated reCAPTCHA with an existing Spring Security application without changing any of the classes. We've modified our configuration by just adding two simple custom filters. We've also successfully followed the guidelines presented in Using reCAPTCHA with Java/JSP article.Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring-security-recaptcha/
You can download the project as a Maven build. Look for the spring-security-recaptcha.zip in the Download sections.
You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run
If you want to learn more about Spring MVC and integration with other technologies, feel free to read my other tutorials in the Tutorials section.
Share the joy:
|
Subscribe by reader Subscribe by email Share
Please can you explain how can we make the captcha appears only at the third login try?
ReplyDeletehttp://stackoverflow.com/questions/28576874/google-recaptcha-v2-verification-failed-on-server-side
DeleteU can check this
I suggest you provide a custom authentication-failure-handler-ref. Inside this custom failure handler, you can create a session variable that counts how many times the user has tried. Then probably you can have a logic in your Controller that if this variable has been incremented 3x, redirect the client to a login page with ReCaptcha enabled. That's how I would tackle this problem
ReplyDeleteUnfortunatelly keeping the counter in the session object is not the best idea. You can imagine a bot which tries to login using the new session after each failed login attempt.
DeleteThe above example is very nice to picture how to use the reCaptcha, but it should not be used for production. I will try to explain why. The condition about using captcha validation is based on the request (req.getParameter("recaptcha_response_field") != null). If you write a bot which will send only username and password without recaptcha_response_field then it will not be validated against captcha challenge.
To avoid the above problems you will need to store some additional info in the persistence layer then you will be able to check if the captcha validation is required for a particular user.
Hi,
ReplyDeleteAs far as I understand the CaptchaVerifierFilter and CaptchaCaptureFilter are singletons. Which means that its not safe to store the reCaptcha response and challenge as you are. If two requests are sent to the web app at the same time the one they are handled concurrently and you'll never be sure which requests response and challenge are stored in the CaptchaCaptureFilter. You are going to have to store these values in the session or maybe as a request attribute.
I am seeing some error when I try to run it in Spring Eclipse IDE (STS)
ReplyDeleteSEVERE: Exception starting filter springSecurityFilterChain
java.lang.ClassCastException: org.springframework.web.filter.DelegatingFilterProxy cannot be cast to javax.servlet.Filter
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:275)
at org.apache.catalina.core.ApplicationFilterConfig.setFilterDef(ApplicationFilterConfig.java:422)
at org.apache.catalina.core.ApplicationFilterConfig.(ApplicationFilterConfig.java:115)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4001)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4651)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:785)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:445)
at org.apache.catalina.startup.Embedded.start(Embedded.java:825)
at org.codehaus.mojo.tomcat.AbstractRunMojo.startContainer(AbstractRunMojo.java:558)
at org.codehaus.mojo.tomcat.AbstractRunMojo.execute(AbstractRunMojo.java:255)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:107)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:319)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:534)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
Jul 26, 2011 12:16:33 AM org.apache.catalina.core.StandardContext start
SEVERE: Error filterStart
Can you please suggest something?
Thanks in advance
Is CaptchaCaptureFilter required ?
ReplyDeleteamazing , best Spring 3 MVC examples site!
ReplyDeleteI'll try it asap
reCaptcha is very good option provided on sites using Captcha form .
ReplyDeleteAmazing - again! a question please , I want to use it on the registration page , is that a problem ? I have a with many fields , can it work out ? Must it be login only?
ReplyDeleteWill there will be a problem to use reCaptcha without Spring Integrating? less secure? use the default way on pure-JSP ? is it wrong?
Waiting for you reply and thanks
Thanks
Ok I have found a way : here
ReplyDeletehttp://wheelersoftware.com/articles/recaptcha-java.html
Spring MVC 2.5 example for a form with reCaptcha
@Anonymous, I use reCaptcha in a registration page. I've seen numerous websites doing the same. So I don't see any problem with it. At the end, login and registration are essentially the same. reCaptcha doesn't force to use Java. So there's no problem if you don't use Java/Spring
ReplyDeleteThanks for the help (and for the page), very good example. I have a problem, I used this tutorial to add a select in the login form which has to have any value to permit the login.
ReplyDeleteAll works well but when the user/pass validates and the post filter calls the method onAuthenticationFailure (because is nothing selected in the select) redirects well but doesn't cancel the login (the spring security context is in session). Let me know if you wanna/need to see my code.
I answer myself. The code above doesn't log out, you have to do it programatically in the pos filter. One way of doing this is the next:
ReplyDelete((SecurityContextImpl) req.getSession().getAttribute("SPRING_SECURITY_CONTEXT")).setAuthentication(null);
sir i am a beginner. i want to add recaptcha into my application. i have read the instructions given in google code. But the problem is that , when i am submitting the form it is showing message in different page. i want to add the message on failure just down to the captcha box, and upon success it should navigate to the requested page. i am using servlet and jsp. I havse been trying to do it for last 4 days. Please help me out on this.
ReplyDeleteThis tutorial is referenced by many many blogs. However, this implementation has some serious flaws.
ReplyDelete1. The Captcha validation is completely optional. If the captcha fields are not present in the form then the validation will not be done. The validation is easily circumvented.
2. As the response parameters are stored in a singleton bean (CaptchaCaptureFilter) and not in the current request, validation responses are not tied to the current request but always stay global.
A better implementation would be to extend the UsernamePasswordAuthenticationFilter (or create a complete custom filter) and embed the ReCaptcha verification.
@Anonymous, thanks for the message. However, as you've noted, this is a tutorial and it is not meant for production use. I'm surprised when people take tutorials as production code. But in regards to your suggestion, can you write this better implementation so we can see it for comparison purposes? I'm glad that this tutorial is referenced by many blogs. Before this was written, I have few references (if any) that deals with this topic. So that inspired me to write one. If there are bugs here, consider such code as a snapshot.
ReplyDeletehttp://pastebin.com/xNssT40X
DeleteIn the security xml, replace the original FOLES_LOGIN_FILTER with the implementation found at the pastebin url.
@Anonymous, #1 why would you remove the captcha fields in the form, when in the first place it has to be there. It's like why remove the form tag of an html page, when you need a form. It doesn't sound logical. #2 You can add a session request type to that bean. Of course, you'll have to do some tests. Don't get me wrong, but there's a difference between stating that there's a better implementation versus writing and sharing it.
ReplyDeleteWhat if the user sends a login request without the captcha answer. Will he user be able to login without getting the captcha verified? Does not this break the hole idea with this extra protection. If I am correct, this solution is a little bit to simple to cheat.
ReplyDeleteBest Regards smasseman
Ooops.
ReplyDeleteThis security risk was already commented by another user.
Sorry about that.
Best Regards smasseman
@krams (in reply to Jan 12, 2012 02:58 PM): Please take a look at the UserPasswordAuthenticationFilter implementation @ http://pastebin.com/xNssT40X
ReplyDeleteIn the security xml, replace the original FOLES_LOGIN_FILTER with the implementation found at the pastebin url.
...
hi krams
ReplyDeletei expecting atleast one reply to my questions,i have done this captcha example everything works fine for me ,but i facing one problem that is the captcha generated code i was entering leaving last 2 or 4 letters it is still accepting and redirecting to next page.if i enter only word before space then it was showing invalid username and password .please help me krams
@venu, the implementation included in this tutorial might have missed some use cases. It's difficult to cover all cases when the program itself hasn't tested heavily. I will have to investigate why is this happening. This might take me some time.
ReplyDeleteI'm profiling an application with a very similar configuration of reCAPTCHA in Spring Security. The results of the profiling shows that the CAPTCHA Verifier filter is filtering every server request (except static content) after the login page succedded and it's using a lot of time to process every request. For instance, in a test, captchaVerifierFilter consumes 11% of time of the whole time of the test, but only 1% is spent in bussines process and some of this bussines process are complex nto only some CRUD operations. Do you know how can i stop this behavior of the captchaVerifierFilter?
ReplyDeleteDid you use the OncePerRequestFilter: "Filter base class that guarantees to be just executed once per request, on any servlet container. It provides a doFilterInternal method with HttpServletRequest and HttpServletResponse arguments."
Deletehttp://static.springsource.org/spring/docs/1.2.9/api/org/springframework/web/filter/OncePerRequestFilter.html
Hi Krams,
ReplyDeletewhy this project not work correctly! please help me. i can't run it.
where is FORM_LOGIN_FILTER filter defined in above example
ReplyDeleteall works great. only look and make it yourself. thanks
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteHi, I am getting error the “Input error: k: Format of site key was invalid”. Could you please help me ?
ReplyDeleteI have added private key p:privateKey="6LdorQcTAAAAABMEHRhcl6dJlMJ6nWDSsFkI8_Ob"
ReplyDeleteNice Explanation on Spring..
ReplyDeleteHow can I do the same in thymeleaf? A blog post on the same would be nice. since Spring and Thymeleaf are mostly integrated tools now.
ReplyDeleteBisnis
ReplyDeleteindonesia
lampung
Lampung
Lampung
lampung
Elektronika
Bisnis
Thanks for sharing this wonderful information. I hope you will share more helpful information regarding the content.
ReplyDeleteweb portal development company in chennai
sem services in chennai
professional web design company in chennai
Really Great Post & Thanks for sharing.
ReplyDeleteOflox Is The Best Web Designing Company In Dehradun
Nice article very helpful and informative thanks for sharing this information. Fashion bloggers in India
ReplyDeleteThis is Nice Article, anda I think I get Solution After Read This Article
ReplyDeletecetak kemasan murah
ReplyDeleteCETAK KEMASAN MURAH
cetak kemasan murah
Thanks before your article is the best
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThanks before your article is amazing
ReplyDeletenice article so helpful
ReplyDeleteamazing, thank you for this
ReplyDelete