当填写完成登录表单提交后,首先会被对应的提交表单提起的过滤器进行拦截,这里过滤器的作用就是拦截登录表单提交验证请求,并根据相应的表单信息构造对应的登录凭证,这里来看看过滤器是如何构造相应的用户凭证。
package com.template.security.filter; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Created by IntelliJ IDEA. * User: Zhong Gang * Date: 12-11-9 * Time: 下午10:00 */ public class MultipleAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter { private List<AuthenticationTokenResolver> tokenResolvers = new ArrayList<AuthenticationTokenResolver>(); /** * @param defaultFilterProcessesUrl the default value for <tt>filterProcessesUrl</tt>. */ protected MultipleAuthenticationProcessingFilter(String defaultFilterProcessesUrl) { super(defaultFilterProcessesUrl); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { for (AuthenticationTokenResolver tokenResolver : tokenResolvers) { if (tokenResolver.support(request)) { Authentication authentication = tokenResolver.resolve(request); return this.getAuthenticationManager().authenticate(authentication); } } throw new UnsupportedOperationException("No authentication token resolver found!"); } public void setTokenResolvers(List<AuthenticationTokenResolver> tokenResolvers) { this.tokenResolvers = tokenResolvers; } }
package com.template.security.filter; import org.springframework.security.core.Authentication; import javax.servlet.http.HttpServletRequest; /** * Created by IntelliJ IDEA. * User: Zhong Gang * Date: 12-11-9 * Time: 下午10:08 */ public interface AuthenticationTokenResolver { boolean support(HttpServletRequest request); Authentication resolve(HttpServletRequest request); }
package com.template.security.filter; import com.template.utils.StringUtils; import org.springframework.security.core.Authentication; import javax.servlet.http.HttpServletRequest; /** * Created by IntelliJ IDEA. * User: Zhong Gang * Date: 12-11-9 * Time: 下午10:27 */ public abstract class AbstractAuthenticationTokenResolver implements AuthenticationTokenResolver { protected String parameterName; protected String parameterValue; protected AbstractAuthenticationTokenResolver() { } protected AbstractAuthenticationTokenResolver(String parameterName) { this.parameterName = parameterName; } @Override public boolean support(HttpServletRequest request) { String parameterValue = request.getParameter(parameterName); if (StringUtils.isEmpty(parameterValue)) { return false; } return parameterValue.equals(this.parameterValue); } @Override public abstract Authentication resolve(HttpServletRequest request); public void setParameterName(String parameterName) { this.parameterName = parameterName; } public void setParameterValue(String parameterValue) { this.parameterValue = parameterValue; } }
package com.template.security.filter; import com.template.security.authentication.token.BackendAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; /** * Created by IntelliJ IDEA. * User: Zhong Gang * Date: 12-11-9 * Time: 下午10:29 */ public class BackendAuthenticationTokenResolver extends AbstractAuthenticationTokenResolver { protected BackendAuthenticationTokenResolver() { super(); } @Override public Authentication resolve(HttpServletRequest request) { String username = request.getParameter("username"); String password = request.getParameter("password"); String captcha = request.getParameter("captcha"); List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); return new BackendAuthenticationToken(username, password, authorities, captcha); } }
package com.template.security.filter; import com.template.security.authentication.token.ForendAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; /** * Created by IntelliJ IDEA. * User: Zhong Gang * Date: 12-11-9 * Time: 下午10:29 */ public class ForendAuthenticationTokenResolver extends AbstractAuthenticationTokenResolver { protected ForendAuthenticationTokenResolver() { super(); } @Override public Authentication resolve(HttpServletRequest request) { String email = request.getParameter("email"); String phone = request.getParameter("phone"); List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); return new ForendAuthenticationToken(email, phone, authorities); } }
这里实现AbstractAuthenticationProcessingFilter接口的类MultipleAuthenticationProcessingFilter,用来根据相应的登录表单提交信息构造相应的登录用户凭证。为了实现根据前台登录表单信息构造前台用户凭证,根据后台登录表单信息构造后台用户凭证,使用了策略模式来实现,实现AbstractAuthenticationTokenResolver接口的BackendAuthenticationTokenResolver和ForendAuthenticationTokenResolver分别用来构造后台用户凭证和前台用户凭证,再来看看配置文件是如何进行配置的吧。
<beans:bean id="multipleAuthenticationProcessingFilter" class="com.template.security.filter.MultipleAuthenticationProcessingFilter"> <beans:constructor-arg value="/login/check"/> <beans:property name="tokenResolvers"> <beans:list> <beans:ref bean="backendAuthenticationTokenResolver"/> <beans:ref bean="forendAuthenticationTokenResolver"/> </beans:list> </beans:property> <beans:property name="authenticationManager" ref="authenticationManager"/> <beans:property name="authenticationSuccessHandler" ref="multipleAuthenticationSuccessHandler"/> <beans:property name="authenticationFailureHandler" ref="multipleAuthenticationFailureHandler"/> </beans:bean> <beans:bean id="backendAuthenticationTokenResolver" class="com.template.security.filter.BackendAuthenticationTokenResolver"> <beans:property name="parameterName" value="token"/> <beans:property name="parameterValue" value="backend"/> </beans:bean> <beans:bean id="forendAuthenticationTokenResolver" class="com.template.security.filter.ForendAuthenticationTokenResolver"> <beans:property name="parameterName" value="token"/> <beans:property name="parameterValue" value="forend"/> </beans:bean>
这里不论是前台登录还是后台登录,都提交到相同的地址/login/check,不过为了区分请求到底是前台登录认证还是后台登录认证,这里使用了一个名为token的请求参数来区分,当token的值为backend的时候表明是后台登录认证,当token的值为forend的时候表明是前台登录认证。
<custom-filter ref="multipleAuthenticationProcessingFilter" before="FORM_LOGIN_FILTER"/>
这段配置表明自定义的认证过滤器将在Spring Security默认的UsernamePasswordAuthenticationFilter前执行,研究UsernamePasswordAuthenticationFilter源码你就会发现平时使用到的登录认证请求地址j_spring_security_check就是被这个过滤器进行拦截并进行处理的,所以如果只是简单的登录认证,你只需要在登录页面进行一些修改就完成可以了,因为后台的处理已经完全交由Spring Security来帮我们处理了。