简介
UsernamePasswordAuthenticationFilter是AbstractAuthenticationProcessingFilter针对使用用户名和密码进行身份认证
而定制化的一个过滤器。其添加是在调用http.formLogin()时作用,默认的登录请求pattern为"/login",并且为POST请求。当我们登录的时候,也就是匹配到loginProcessingUrl,这个过滤器就会委托认证管理器authenticationManager来验证登录
1、AbstractAuthenticationProcessingFilter
登陆流程入口是AbstractAuthenticationProcessingFilter的doFilter方法
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
implements ApplicationEventPublisherAware, MessageSourceAware {
protected ApplicationEventPublisher eventPublisher;
protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private AuthenticationManager authenticationManager;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private RememberMeServices rememberMeServices = new NullRememberMeServices();
private RequestMatcher requiresAuthenticationRequestMatcher;
private boolean continueChainBeforeSuccessfulAuthentication = false;
private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
private boolean allowSessionCreation = true;
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//判断当前路径是否是指定的登陆路径,如果不是,那么跳过此过滤器
//UsernamePasswordAuthenticationFilter默认登陆路径是/login,post接口
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
//对请求进行权限验证,这是一个抽象方法
//其实现 在UsernamePasswordAuthenticationFilter中
authResult = attemptAuthentication(request, response);
if (authResult == null) {
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
catch (InternalAuthenticationServiceException failed) {
//身份验证失败的默认行为。
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
//身份验证失败的默认行为。
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
//身份验证成功的默认行为。
successfulAuthentication(request, response, chain, authResult);
}
}
AbstractAuthenticationProcessingFilter其实没做什么,只是对请求路径进行了过滤,判断是否是登陆路径。对请求认证的代码在其子类UsernamePasswordAuthenticationFilter中
2、UsernamePasswordAuthenticationFilter
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
//设置默认登陆路径
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
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);//获取用户名,默认通过request.getParamete方式获取
String password = obtainPassword(request);//获取密码,默认通过request.getParamete方式获取
if (username == null) {username = "";}
if (password == null) {password = "";}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// 允许子类设置“details”属性
setDetails(request, authRequest);
//调用AuthenticationManager的authenticate进行认证和授权
return this.getAuthenticationManager().authenticate(authRequest);
}
}
3、自定义用户名和密码登陆逻辑
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
//登陆失败返回结果自定义
public void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
SecurityContextHolder.clearContext();
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
String msg = "";
if (failed instanceof LockedException) {
msg = "账户被锁定,请联系管理员!";
} else if (failed instanceof CredentialsExpiredException) {
msg = "密码过期,请联系管理员!";
} else if (failed instanceof AccountExpiredException) {
msg = "账户过期,请联系管理员!";
} else if (failed instanceof DisabledException) {
msg = "账户被禁用,请联系管理员!";
} else if (failed instanceof BadCredentialsException) {
msg = "用户名或者密码输入错误,请重新输入!";
}else if (failed instanceof AuthenticationServiceException) {
msg = failed.getMessage();
}
out.write(new ObjectMapper().writeValueAsString(JsonResultHelper.JsonResultFail(msg)));
out.flush();
out.close();
}
//登陆成功返回结果自定义
public void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(JsonResultHelper.JsonResultSuccess(authResult)));
out.flush();
out.close();
}
}
MyUsernamePasswordAuthenticationFilter的注册
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
ExceptionTranslationFilter w;
return NoOpPasswordEncoder.getInstance();
}
@Bean
public MyUserDetailsService myUserDetailsService(){
return new MyUserDetailsService();
}
@Bean
public MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter = new MyUsernamePasswordAuthenticationFilter();
myUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager());
return myUsernamePasswordAuthenticationFilter;
}
@Override
protected void configure(HttpSecurity auth) throws Exception
auth.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
auth.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}