实现原理很简单,编写一个类实现BeanPostProcessor接口,并实现该接口的postProcessBeforeInitialization方法,该方法将在初始化时创建一个实例对象。实现该方法可以对UsernamePasswordAuthenticationFilter进行构造注入重构。
首先看看Spring Security3中的UsernamePasswordAuthenticationFilter的实现代码:
package org.springframework.security.web.authentication; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.util.TextEscapeUtils; import org.springframework.util.Assert; /** * Processes an authentication form submission. Called {@code AuthenticationProcessingFilter} prior to Spring Security * 3.0. * <p> * Login forms must present two parameters to this filter: a username and * password. The default parameter names to use are contained in the * static fields {@link #SPRING_SECURITY_FORM_USERNAME_KEY} and {@link #SPRING_SECURITY_FORM_PASSWORD_KEY}. * The parameter names can also be changed by setting the {@code usernameParameter} and {@code passwordParameter} * properties. * <p> * This filter by default responds to the URL {@code /j_spring_security_check}. * * @author Ben Alex * @author Colin Sampaleanu * @author Luke Taylor * @since 3.0 */ public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { //~ Static fields/initializers ===================================================================================== public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username"; public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password"; /** * @deprecated If you want to retain the username, cache it in a customized {@code AuthenticationFailureHandler} */ @Deprecated public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME"; private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY; private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY; private boolean postOnly = true; //~ Constructors =================================================================================================== public UsernamePasswordAuthenticationFilter() { super("/j_spring_security_check"); } //~ Methods ======================================================================================================== public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } String username = obtainUsername(request); String password = obtainPassword(request); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } /** * Enables subclasses to override the composition of the password, such as by including additional values * and a separator.<p>This might be used for example if a postcode/zipcode was required in addition to the * password. A delimiter such as a pipe (|) should be used to separate the password and extended value(s). The * <code>AuthenticationDao</code> will need to generate the expected password in a corresponding manner.</p> * * @param request so that request attributes can be retrieved * * @return the password that will be presented in the <code>Authentication</code> request token to the * <code>AuthenticationManager</code> */ protected String obtainPassword(HttpServletRequest request) { return request.getParameter(passwordParameter); } /** * Enables subclasses to override the composition of the username, such as by including additional values * and a separator. * * @param request so that request attributes can be retrieved * * @return the username that will be presented in the <code>Authentication</code> request token to the * <code>AuthenticationManager</code> */ protected String obtainUsername(HttpServletRequest request) { return request.getParameter(usernameParameter); } /** * Provided so that subclasses may configure what is put into the authentication request's details * property. * * @param request that an authentication request is being created for * @param authRequest the authentication request object that should have its details set */ protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } /** * Sets the parameter name which will be used to obtain the username from the login request. * * @param usernameParameter the parameter name. Defaults to "j_username". */ public void setUsernameParameter(String usernameParameter) { Assert.hasText(usernameParameter, "Username parameter must not be empty or null"); this.usernameParameter = usernameParameter; } /** * Sets the parameter name which will be used to obtain the password from the login request.. * * @param passwordParameter the parameter name. Defaults to "j_password". */ public void setPasswordParameter(String passwordParameter) { Assert.hasText(passwordParameter, "Password parameter must not be empty or null"); this.passwordParameter = passwordParameter; } /** * Defines whether only HTTP POST requests will be allowed by this filter. * If set to true, and an authentication request is received which is not a POST request, an exception will * be raised immediately and authentication will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method * will be called as if handling a failed authentication. * <p> * Defaults to <tt>true</tt> but may be overridden by subclasses. */ public void setPostOnly(boolean postOnly) { this.postOnly = postOnly; } public final String getUsernameParameter() { return usernameParameter; } public final String getPasswordParameter() { return passwordParameter; } }
import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.Ordered; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * 实现用户名、密码参数重构 * @author Barudisshu */ public class UsernamePasswordAuthenticationFilterPostProcessor implements BeanPostProcessor, Ordered { private final String usernameParameter; private final String passwordParameter; /** * 完全构造函数 * * @param usernameParameter 用户名. * @param passwordParameter 密码. */ public UsernamePasswordAuthenticationFilterPostProcessor(String usernameParameter, String passwordParameter) { this.usernameParameter = usernameParameter; this.passwordParameter = passwordParameter; } /** * 如果该类为UsernamePasswordAuthenticationFilter的一个实例,它的用户名和密码将在post动作中设值。 * * @param bean 要实现的Bean. * @param beanName Spring Bean的名称. * @return 处理后的Bean. * @throws BeansException 异常. */ @Override public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException { if (bean instanceof UsernamePasswordAuthenticationFilter) { UsernamePasswordAuthenticationFilter filter = (UsernamePasswordAuthenticationFilter) bean; filter.setUsernameParameter(this.usernameParameter); filter.setPasswordParameter(this.passwordParameter); } return bean; } /** * 不进行处理的情况 * * @param bean 要实现的Bean. * @param beanName Spring Bean的名称. * @return 处理后的Bean. * @throws BeansException 异常. */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * Returns the order of this post processor. * * @return LOWEST_PRECEDENCE always. */ @Override public int getOrder() { return LOWEST_PRECEDENCE; } }
<!--不采用默认j_username、j_password,实现自定义构造参数--> <bean id="usernamePasswordAuthenticationFilterPostProcessor" class="net.individuals.security.UsernamePasswordAuthenticationFilterPostProcessor"> <constructor-arg index="0" value="username"/> <constructor-arg index="1" value="password"/> </bean>