Before starting the answer, I know that there is ReCaptcha, which is simpler and simpler, but I can not use it. The production server is not connected to the network. So let's go.
I am using Spring mvc 3 with Spring Security on the maven Project and weblogic as a web server (dock at development time). I will be very specific in this matter.
Before I see my configurations and files, I would like to show you a list of my problems:
- I tried ReCaptcha before JCaptcha with the same coding structure and it works fine.
- logger.debug does not appear at all in the CaptchaCaptureFilter and / or CaptchaVerifierFilter class (as long as it appears in the ArtajasaAuthenticationProvider class).
- I see a captcha image, but regardless of the answer, it is always invalid.
In its current state, it does not work on the pier or in the weblog, but if I changed the position of the custom filter to the one below, it only works on the pier.
<custom-filter ref="captchaCaptureFilter" position="FIRST"/> <custom-filter ref="captchaVerifierFilter" after="FIRST"/>
Thanks for watching and many thanks for answering my question. See below for details.
The repository for JCaptcha is:
<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 settings I made in the .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/" /> <custom-filter ref="captchaCaptureFilter" before="FORM_LOGIN_FILTER"/> <custom-filter ref="captchaVerifierFilter" after="FORM_LOGIN_FILTER"/> <anonymous /> </http> <beans:bean id="captchaCaptureFilter" class="com.util.CaptchaCaptureFilter" /> <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>
And these are the java classes:
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");
CaptchaVerifierFilter.java
public class CaptchaVerifierFilter extends OncePerRequestFilter { protected Logger logger = Logger.getLogger(CaptchaVerifierFilter.class); private String failureUrl; private CaptchaCaptureFilter captchaCaptureFilter;
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>