一、概念:
Spring Security所解决的问题就是安全访问控制,而安全访问控制功能其实就是所有进入系统的请求进行拦截,校验每一个请求是否能够访问它所期望的资源,可以通过Filter或AOP等技术来实现,Spring Security对web资源的保护是靠Filter来实现的。
当初始化Spring Security时,会创建一个名为SpringSecurityFilterChain的servlet过滤器,类型是org.springframework.security.web.FilterChainProxy(有doFilter方法),它实现了javax.servlet.Filter,因此外部请求的类都会经过此类。如图:
实际上,看源代码我们知道FilterChainProxy是一个代理,真正起作用的是FilterChainProxy中SecurityFilterChain所包含的各个Filter,同时 这些Filter作为Bean被Spring管理,它们是Spring Security核心,各有各的职责,但他们并不直接处理用户的认证,也不直接处理用户的授权,而是把它们交给了认证管理器(AuthenticationManager)和决策管理器(AccessDecisionManager)进行处理,下图是FilterChainProxy相关类的UML图示(直接网上找图)。
不知道UML图的小伙伴们,可以去学一波这个姿势。
Spring Security功能的实现主要是由一系列过滤器链相互配合完成。
(1)SecurityContextPersistenceFilter:这个Filter是整个拦截过程的入口和出口(也就是第一个和最后- 一个拦截器) , 会在请求开始时从配置好的SecurityContextRepository中获取SecurityContext ,然后把它设置给SecurityContextHolder.在请求完成后将SecurityContextHolder持有的SecurityContext再保存到配置好的SecurityContextRepository ,同时清除securityContextHolder所持有的SecurityContext ; .
(2)UsernamePasswordAuthenticationFilter:用于处理来自表单提交的认证。该表单必须提供对应的用户名和密码,其内部还有登录成功或失败后进行处理的AuthenticationSuccessHandler和AuthenticationFailureHandler ,这些都可以根据需求做相关改变;
(3)FilteSecurityInterceptor:是用于保护web资源的,使用AccessDecisionManager对当前用户进行授权访问,前面已经详细介绍过了;
(4)ExceptionTranslationFilter:ExceptionTranslationFilter能够捕获来自FilterChain 所有的异常,并进行处理。但是它只会处理两类异常:ExceptionTranslationFilter能够捕获来自FilterChain 所有的异常,并进行处理。但是它只会处理两类异常:
AuthenticationException和AccessDeniedException ,其它的异常它会继续抛出。
二、认证流程:
(1)用户提交用户名和密码被UsernamePasswordAuthenticationFilter获取到,然后请求的信息被封装为Authentication的实现类UsernamePasswordAuthenticationToken对象。我们来看一下这个源码:UsernamePasswordAuthenticationFilter类的attemptAuthentication方法。
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"));
}
//设置Authentication
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();
//创建Authentication
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
//设置主机地址和sessionId
setDetails(request, authRequest);
//通过这个方法去找到A