上文已经说了Spring Security启动bean
是怎样被加载到spring容器。也说到了默认情况下的过滤器链里面的11个过滤器。
UsernamePasswordAuthenticationFilter
负责的就是对用户名密码进行认证。
1.UsernamePasswordAuthenticationFilter
的实现原理
- 拦截请求
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
对/login
的请求进行拦截,我们看到请求类型只能是POST
- 允许对用户名密码的参数进行定义,默认情况下是
username
和password
- 认证的过程
我们看一下doFilter()
方法,这个方法定义在父类AbstractAuthenticationProcessingFilter
中
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
catch (InternalAuthenticationServiceException failed) {
logger.error(
"An internal error occurred while trying to authenticate the user.",
failed);
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authResult);
}
这个方法调用了attemptAuthentication()
方法,这个方法定义在子类UsernamePasswordAuthenticationFilter
中
成功调用sessionStrategy.onAuthentication()
方法处理session,然后在调用successfulAuthentication()
这个方法处理操作成功
失败调用unsuccessfulAuthentication()
方法,处理失败信息
我们看一下attemptAuthentication()
这个方法
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);
}
其实我们从这段代码可以看到这个方法机会只做了几件事情
1.验证是不是POST
类型的请求
2.获取参数
3.调用AuthenticationManager的authenticate()方法进行认证
4.认证成功返回一个Authentication类型变量,认证失败抛出AuthenticationException异常
5.综上所述,认证的处理其实是委托给了AuthenticationManager来做处理的
2.AuthenticationManager
怎样处理认证
-
先看一下处理的流程图
认证流程
由于源代码比较简单所以不引入源代码,然而有几个类我们都没有见过,需要好好的看一下
2.1 AuthenticationManager
接口
这个接口值定义了一个方法
Authentication authenticate(Authentication authentication)throws AuthenticationException;
从注释中我们可以知道,一旦验证成功会填充好Authentication
对象,包括权限的信息,同时这个方法的实现必须遵循以下约定
- 1.如果账号不可用,抛出异常
DisabledException
- 2.如果账号锁定,抛出异常
LockedException
- 3.如果密码错误,抛出异常
BadCredentialsException
- 4.不可用和已锁定的账号应该立即抛出异常,不能再进行密码验证
2.2 ProviderManager
类
-
ProviderManager
为AuthenticationManager
的实现类 - 看一下其中的一个变量
private List<AuthenticationProvider> providers = Collections.emptyList();
里面存储了需要验证的provider,然后循环利用provider进行验证,稍后我们看一下具体的一个provider的实现
- 所以
ProviderManager
负责的是组合各种provider进行验证
2.3 AuthenticationProvider
接口
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)throws AuthenticationException;
boolean supports(Class<?> authentication);
}
- 这个接口里面主要定义了两个方法
- 常用的实现类
DaoAuthenticationProvider
由于逻辑比较简单可以自行查看源代码,
主要是用UserDetailsService根据username获取到用户,然后再比较密码是否相同来完成验证的逻辑 -
AuthenticationProvider
表示某一个具体的验证规则
2.4 Authentication
类
这个是DaoAuthenticationProvider
验证成功后创建Authentication
我们可以看到的是 Authentication
里面存放的是主体(简单点就是用户吧)认证或者认证成功后的信息
网友评论