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!
How to Integrate JCaptcha in Spring Security