SpringScurity学习笔记
过滤器链结构
工作流程
1.用户登录
表单通过post请求提交,首先到达UsernamePasswordAuthenticationFilter
过滤器的attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
方法,源码如下:
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
String username = this.obtainUsername(request);
String password = this.obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
该方法将用户的用户名和密码封装成AuthenticationManager
可以处理的Authentication
对象。具体步骤如下:
- 首先我们从
构造方法
中可以得知,该过滤器 只对post请求方式的"/login"接口有效;然后在该过滤器中,再利用obtainUsername
和obtainPassword
方法,提取出请求里边的用户名/密码,提取方式就是request.getParameter
. - 接下来会 构造一个
UsernamePasswordAuthenticationToken
对象,传入username
和password
。其中username
对应了UsernamePasswordAuthenticationToken
中的principal
属性,而password
则对应了它的credentials
属性。再利用setDetails 方法
给details
属性赋值.
(tips:UsernamePasswordAuthenticationToken 本身是没有 details 属性的**,**这个属性是在它的父类 AbstractAuthenticationToken 中定义的。details 是一个对象,这个对象里边存放的是 WebAuthenticationDetails 实例,该实例主要描述了 请求的 remoteAddress 以及请求的 sessionId 这两个信息。) - 最后一步,就是利用
AuthenticationManager
对象来调用authenticate()
方法去做认证校验
2.验证用户名和密码
上面说了。在最后一步会进行认证,
return this.getAuthenticationManager().authenticate(authRequest);
this.getAuthenticationManager()
拿到authenticationManager
代码如下:
protected AuthenticationManager getAuthenticationManager() {
return this.authenticationManager;
}
而authenticationManager
是一个接口,所以实际拿到的是它的实现类ProviderManager
public class ProviderManager implements AuthenticationManager
此处省略n行源代码
经过该类一系列骚操作(因为这一段源代码看不懂。。。),最后来到
{
算了,算了,我看不懂这个源码了,继续学习去了。。。。。
}
反正如果成功会调用其父类AbstractAuthenticationProcessingFilter
的successfulAuthentication(...)
方法,失败会调用unsuccessfulAuthentication
方法,这两个方法的源码如下:
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
this.rememberMeServices.loginSuccess(request, response, authResult);
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
this.successHandler.onAuthenticationSuccess(request, response, authResult);
}
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Authentication request failed: " + failed.toString(), failed);
this.logger.debug("Updated SecurityContextHolder to contain null Authentication");
this.logger.debug("Delegating to authentication failure handler " + this.failureHandler);
}
this.rememberMeServices.loginFail(request, response);
this.failureHandler.onAuthenticationFailure(request, response, failed);
}
其实我们如果只是使用的话,仅仅需要实现userdetailservice的loaduserbyusername方法,去自定义我们的验证逻辑,然后返回一个实现了userdetail接口的对象即可,框架会自己调用。