源码分析Spring Security基本原理
一、概述
-
Spring Security 本质是
一个过滤器链
,FilterSecurityInterceptor
是Spring Security 最底层的一个拦截器,是一个方法级的权限过滤器,该类实现了以下方法:
-
接着是一个
ExceptionTranslationFilter
,是一个异常过滤器,用来处理认证授权过程中抛出的异常。
-
然后是
UsernamePasswordAuthenticationFilter
,用于对/login
的POST
请求做拦截,校验表单中的用户名和密码。
二、源码分析
1、FilterSecurityInterceptor
- 该类的 doFilter 方法源码如下:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { this.invoke(new FilterInvocation(request, response, chain)); } public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException { if (this.isApplied(filterInvocation) && this.observeOncePerRequest) { filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse()); } else { if (filterInvocation.getRequest() != null && this.observeOncePerRequest) { filterInvocation.getRequest() .setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE); } InterceptorStatusToken token = super.beforeInvocation(filterInvocation); try { filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse()); } finally { super.finallyInvocation(token); } super.afterInvocation(token, (Object)null); } } public interface FilterChain { void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException; }
2、ExceptionTranslationFilter
- 该类的 doFilter 方法源码如下:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { this.doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain); } private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { try { chain.doFilter(request, response); } catch (IOException var7) { throw var7; } catch (Exception var8) { Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var8); RuntimeException securityException = (AuthenticationException)this.throwableAnalyzer. getFirstThrowableOfType(AuthenticationException.class, causeChain); if (securityException == null) { securityException = (AccessDeniedException)this.throwableAnalyzer. getFirstThrowableOfType(AccessDeniedException.class, causeChain); } if (securityException == null) { this.rethrow(var8); } if (response.isCommitted()) { throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", var8); } this.handleSpringSecurityException(request, response, chain, (RuntimeException)securityException); } } ```
3、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 static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login", "POST"); private String usernameParameter = "username"; private String passwordParameter = "password"; private boolean postOnly = true; public UsernamePasswordAuthenticationFilter() { super(DEFAULT_ANT_PATH_REQUEST_MATCHER); } public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) { super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager); } 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); username = username != null ? username : ""; username = username.trim(); String password = this.obtainPassword(request); password = password != null ? password : ""; UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); this.setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } } @Nullable protected String obtainPassword(HttpServletRequest request) { return request.getParameter(this.passwordParameter); } @Nullable protected String obtainUsername(HttpServletRequest request) { return request.getParameter(this.usernameParameter); } protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) { authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); } public void setUsernameParameter(String usernameParameter) { Assert.hasText(usernameParameter, "Username parameter must not be empty or null"); this.usernameParameter = usernameParameter; } public void setPasswordParameter(String passwordParameter) { Assert.hasText(passwordParameter, "Password parameter must not be empty or null"); this.passwordParameter = passwordParameter; } public void setPostOnly(boolean postOnly) { this.postOnly = postOnly; } public final String getUsernameParameter() { return this.usernameParameter; } public final String getPasswordParameter() { return this.passwordParameter; } }
- 这个类允许我们去继承和重写
attemptAuthentication
,如果要重写这个方法,则还必须继承其父类AbstractAuthenticationProcessingFilter
并重写successfulAuthentication
方法和unsuccessfulAuthentication
方法。
三、过滤器链加载过程
- Spring Security 包含了十多个过滤器,这些过滤器是如何加载到容器中的呢?
1、源码分析
- 要使用SpringSecurity,首先要对其所用的过滤器进行配置,由于使用了Spring Boot进行自动化配置,因此这一步通常可以省略。
1.1、DelegatingFilterProxy
- 使用该类进行过滤器链的配置
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { Filter delegateToUse = this.delegate; if (delegateToUse == null) { synchronized(this.delegateMonitor) { delegateToUse = this.delegate; if (delegateToUse == null) { WebApplicationContext wac = this.findWebApplicationContext(); if (wac == null) { throw new IllegalStateException( "No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?" ); } delegateToUse = this.initDelegate(wac); } this.delegate = delegateToUse; } } this.invokeDelegate(delegateToUse, request, response, filterChain); } protected Filter initDelegate(WebApplicationContext wac) throws ServletException { String targetBeanName = this.getTargetBeanName();// FilterChainProxy Assert.state(targetBeanName != null, "No target bean name set"); Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class); if (this.isTargetFilterLifecycle()) { delegate.init(this.getFilterConfig()); } return delegate; }
1.2、FilterChainProxy
- FilterChainProxy类的 doFilter 使用 doFilterInternal 进行过滤器链的进一步设置:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { boolean clearContext = request.getAttribute(FILTER_APPLIED) == null; if (!clearContext) { this.doFilterInternal(request, response, chain); } else { try { request.setAttribute(FILTER_APPLIED, Boolean.TRUE); this.doFilterInternal(request, response, chain); } catch (RequestRejectedException var9) { this.requestRejectedHandler.handle((HttpServletRequest)request, (HttpServletResponse)response, var9); } finally { SecurityContextHolder.clearContext(); request.removeAttribute(FILTER_APPLIED); } } } private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request); HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response); List<Filter> filters = this.getFilters((HttpServletRequest)firewallRequest); if (filters != null && filters.size() != 0) { if (logger.isDebugEnabled()) { logger.debug(LogMessage.of(() -> { return "Securing " + requestLine(firewallRequest); })); } FilterChainProxy.VirtualFilterChain virtualFilterChain = new FilterChainProxy.VirtualFilterChain(firewallRequest, chain, filters); virtualFilterChain.doFilter(firewallRequest, firewallResponse); } else { if (logger.isTraceEnabled()) { logger.trace(LogMessage.of(() -> { return "No security for " + requestLine(firewallRequest); })); } firewallRequest.reset(); chain.doFilter(firewallRequest, firewallResponse); } }
1.3、doFilterInternal的getFilters
List<Filter> filters = this.getFilters((HttpServletRequest)firewallRequest); 中的 getFilters :
private List<Filter> getFilters(HttpServletRequest request) {
int count = 0;
Iterator var3 = this.filterChains.iterator();
SecurityFilterChain chain;
do {
if (!var3.hasNext()) {
return null;
}
chain = (SecurityFilterChain)var3.next();
if (logger.isTraceEnabled()) {
++count;
logger.trace(LogMessage.format("Trying to match request against %s (%d/%d)", chain, count,
this.filterChains.size()));
}
} while(!chain.matches(request));
return chain.getFilters();
}
public interface SecurityFilterChain {
boolean matches(HttpServletRequest var1);
List<Filter> getFilters();
}
- 该方法使用迭代器,将所有需要加载的过滤器加载到容器中。