做权限控制的时候用了spring security2.0,但是后来又增加了登录页面需要验证码功能,想了一下,实现如下:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.AuthenticationException;
import org.springframework.security.SpringSecurityMessageSource;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.util.RedirectUtils;
import org.springframework.security.util.UrlUtils;
import org.springframework.util.Assert;
/**
* 加入了验证码的过滤器
*
* @author Alfoo Liu <liuxy2@xxx.com>
* @since 2010-1-7 10:28:35
*/
public class AuthenticationCodeFilter extends SpringSecurityFilter implements InitializingBean, MessageSourceAware {
public final static String SPRING_SECURITY_FORM_AUTHCODE_KEY = "j_authCode";
private final static String AUTHCODE_KEY_IN_SESSION = "AUTHCODE";
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";
public static final String SPRING_SECURITY_LAST_EXCEPTION_KEY = "SPRING_SECURITY_LAST_EXCEPTION";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String authCodeParameter = SPRING_SECURITY_FORM_AUTHCODE_KEY;
private String authenticationFailureUrl = "/login.dm?error=true";
private boolean enableAuthCode = true;
private final static Log log = LogFactory
.getLog(AuthenticationCodeFilter.class);
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private boolean useRelativeContext = false;
private String filterProcessesUrl = "/j_spring_security_check";
@Override
public void doFilterHttp(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// Place the last username attempted into HttpSession for views
HttpSession session = request.getSession();
// boolean s = session != null || getAllowSessionCreation();
// 获取页面传过来的验证码值
String authCode = this.obtainAuthCode(request);
String username = obtainUsername(request);
log.debug("auth code = " + authCode);
if (enableAuthCode && requiresAuthentication(request, response)) {
if (authCode == null || authCode.trim().length() == 0) {
log.error("validate authcode failed, auth code is null.");
session.setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY,
username);
AuthenticationException exception = new AuthenticationCodeErrorException(
messages
.getMessage(
"AbstractUserDetailsAuthenticationProvider.empty_authcode",
"Please input authcode."));// "验证码不能为空。"
unsuccessfulAuthentication(request, response, exception);
return;
}
String expected = (String) session
.getAttribute(AUTHCODE_KEY_IN_SESSION);
if (!authCode.equals(expected)) {// AuthCodeValidationException
session.setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY,
username);
AuthenticationException exception = new AuthenticationCodeErrorException(
messages
.getMessage(
"AbstractUserDetailsAuthenticationProvider.error_authcode",
"authcode is error."));
unsuccessfulAuthentication(request, response, exception);
return;
}
}
chain.doFilter(request, response);
}
protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(null);
if (logger.isDebugEnabled()) {
logger
.debug("Updated SecurityContextHolder to contain null Authentication");
}
request.getSession().setAttribute(
SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
if (authenticationFailureUrl == null) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Authentication Failed:" + failed.getMessage());
} else {
sendRedirect(request, response, authenticationFailureUrl);
}
}
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
if ("".equals(request.getContextPath())) {
return uri.endsWith(filterProcessesUrl);
}
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
}
protected void sendRedirect(HttpServletRequest request,
HttpServletResponse response, String url) throws IOException {
RedirectUtils.sendRedirect(request, response, url, useRelativeContext);
}
public int getOrder() {
return FilterChainOrder.AUTHENTICATION_PROCESSING_FILTER - 100;
}
protected String obtainAuthCode(HttpServletRequest request) {
return request.getParameter(getAuthCodeParameter());
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
public boolean isEnableAuthCode() {
return enableAuthCode;
}
public void setEnableAuthCode(boolean enableAuthCode) {
this.enableAuthCode = enableAuthCode;
}
public String getAuthCodeParameter() {
return authCodeParameter;
}
public void setAuthCodeParameter(String authCodeParameter) {
this.authCodeParameter = authCodeParameter;
}
public String getAuthenticationFailureUrl() {
return authenticationFailureUrl;
}
public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
this.authenticationFailureUrl = authenticationFailureUrl;
}
public void setUseRelativeContext(boolean useRelativeContext) {
this.useRelativeContext = useRelativeContext;
}
public void afterPropertiesSet() throws Exception {
Assert.isTrue(UrlUtils.isValidRedirectUrl(authenticationFailureUrl), authenticationFailureUrl + " isn't a valid redirect URL");
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
}
applicationContext-security.xml配置如下:
<beans:bean id="authenticationProcessingFilter" class="com.asiainfo.webframe.core.security.AuthenticationCodeFilter"> <custom-filter before="AUTHENTICATION_PROCESSING_FILTER" /> <!--以下两项的配置应该要和form-login的配置相同--> <beans:property name="authenticationFailureUrl" value="/login.dm?error=true" /> </beans:bean>
http的配置仍然是auto-config="true"。