一、Spring Security简介
Spring Security是一个基于Spring框架的安全解决方案,提供了认证和授权等安全方面的大服务,包括身份认证和权限处理两大服务。Spring Security的实现依赖于大量的过滤器,采用责任链模式对请求请求不同的过滤处理。在日常使用中,Spring Security已经实现了基于表单的登录认证和授权模式,只需简单的配置,即可做到拿来即用。当然Spring Security也提供了灵活的其他认证入口,通过实现其暴漏出来的接口即可自定义自己的登录认证和授权方法。
- 认证(Authentication):是Spring Security识别当前登录用户身份的主要方式。
- 授权(Authorization):是Spring Security对当前认证用户权限管理的主要方式。
二、Spring Security框架中认证流程中几个非常重要的类
1、 FilterChainProxy
这是一个Spring Security
框架中非常重要的类,它用于维护一组过滤器连,属于Servlet级别的过滤器。所有的请求在进入WEB容器之前都会经过该过滤器,该过滤器会对请求进行不同程度的拦截和处理。FilterChainProxy
根据不同的请求获取对应的过滤器链组,之后按照顺序执行该请求对应的过滤器链组中的每一个过滤器的逻辑对该请求进行处理,直到所有的过滤器都执行完成。FilterChainProxy
实际上是一个Filter
,它的doFilter方法最终会调用自己内部的doFilterInternal
方法。
public class FilterChainProxy extends GenericFilterBean {
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
……省略部分代码……
// 根据请求类型,获取请求下的所有的过滤器
List filters = getFilters(firewallRequest);
// 循环执行匹配到该请求下的所有的过滤器
FilterChain reset = (req, res) -> {
// Deactivate path stripping as we exit the security filter chain
firewallRequest.reset();
chain.doFilter(req, res);
};
this.filterChainDecorator.decorate(reset, filters).doFilter(firewallRequest, firewallResponse);
}
}
2、 AbstractAuthenticationProcessingFilter
这个是认证过程中抽出来的抽象类,自定义的过滤器可直接实现这个抽象类自定义过滤方式,UsernamePasswordAuthenticationFilter
就是直接实现的这个抽象类。AbstractAuthenticationProcessingFilter
实际上是一个Filter
,它的doFilter方法最终会调用自己内部的attemptAuthentication
方法。这是一个抽象方法,需要子类实现它,UsernamePasswordAuthenticationFilter
就是直接实现该抽象类做到,实现了attemptAuthentication
方法。
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
implements ApplicationEventPublisherAware, MessageSourceAware {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
}
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
try {
// 调用内部的attemptAuthentication方法,该方法是抽象方法。需要子类实现。
Authentication authenticationResult = attemptAuthentication(request, response);
if (authenticationResult == null) {
// return immediately as subclass has indicated that it hasn't completed
return;
}
this.sessionStrategy.onAuthentication(authenticationResult, request, response);
// Authentication success
if (this.continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
// 认证成功之后会调用该方法处理认证成功之后的逻辑。
successfulAuthentication(request, response, chain, authenticationResult);
}
catch (InternalAuthenticationServiceException failed) {
this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
unsuccessfulAuthentication(request, response, failed);
}
catch (AuthenticationException ex) {
// Authentication failed
// 认证失败会调用该方法处理认证失败之后的逻辑。
unsuccessfulAuthentication(request, response, ex);
}
}
}
3、 UsernamePasswordAuthenticationFilter
UsernamePasswordAuthenticationFilte
是AbstractAuthenticationProcessingFilter
的直接实现,也是Spring Security表单登录的默认实现过滤器,主要是将表单登录的所需要的username
和password
从request中获取出来封装成还未认证过的UsernamePasswordAuthenticationToken
,它实现了AbstractAuthenticationProcessingFilter
的抽象方法attemptAuthentication
方法,最终将登录逻辑的具体实现委托给AuthenticationManager
去实现,而AuthenticationManager
是一个接口,里面只有一个抽象方法authenticate
,必须由子类实现,而最直接的实现该接口的类就是ProviderManager
。
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
@Override
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());
}
// 从请求中获取username参数
String username = obtainUsername(request);
username = (username != null) ? username.trim() : "";
// 从请求中获取password参数
String password = obtainPassword(request);
password = (password != null) ? password : "";
// 构造一个没有完全认证的AuthenticationToken,最主要的是就是用来存放未认证之前的用户信息的。未认证之前,AuthenticationToken里面的authenticated属性为false,认证通过直接会被设置为true。
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
password);
// Allow subclasses to set the "details" property
// 实际上就是保存了一下用户的详细信息
setDetails(request, authRequest);
// 调用AuthenticationManager的authenticate方法处理认证逻辑,实际上会调到具体的子类实现上。
return this.getAuthenticationManager().authenticate(authRequest);
}
}
4、AbstractAuthenticationToken
AbstractAuthenticationToken
是一个用来存放用户信息的抽象类,比如表单登录的username
和password
就会被存放在该类的实现中,而它的直接实现就是UsernamePasswordAuthenticationToken
。AbstractAuthenticationToken
里面有几个重要的属性:
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
// 当前登录用户的权限集合
private final Collection authorities;
// 用户信息的详细信息
private Object details;
// 当前登录用户是否完全认证。 未认证之前,authenticated属性为false,认证通过直接会被设置为true。
private boolean authenticated = false;
}
5、 UsernamePasswordAuthenticationToken
UsernamePasswordAuthenticationToken
是 AbstractAuthenticationToken
的直接实现,也是Spring Security表单登录的默认实现用来存放用户信息的类。该类的作用就是存放未认证之前的用户信息,也就是表单提交的username和password。
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
// 在父类的基础之上,UsernamePasswordAuthenticationToken增加了该属性信息,源码实现的时候,未认证之前该属性代表的是username,认证之后存放的是UserDetails
private final Object principal;
// 在父类的基础之上,UsernamePasswordAuthenticationToken增加了该属性信息,源码实现的时候,未认证之前该属性代表的是password,认证之后存放的是认证主体Authentication的一些信息
private Object credentials;
}
6、 AuthenticationManager
AuthenticationManager
是实际上用来做认证的接口,内部就一个authenticate
抽象方法,子类必须实现它,ProviderManager
是AuthenticationManager
的最直接的实现。
public interface AuthenticationManager {
// 实际上的认证入口
Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
7、 ProviderManager
ProviderManager
是AuthenticationManager
的最直接的实现,实现了authenticate
抽象方法,也是Spring Security表单登录的默认实现认证相关的类,认证的处理逻辑就是在这个里面处理的。而在 ProviderManager
又把认证逻辑委托给了对应的AbstractUserDetailsAuthenticationProvider
,他是实际上干活的类,认证逻辑最终在这里执行。
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Class