聊聊FilterSecurityInterceptor

 2023-09-05 阅读 172 评论 0

摘要:为什么80%的码农都做不了架构师?>>> 序 本文就来研究一下spring security的FilterSecurityInterceptor 问题 前面的文章讲了SecurityContextPersistenceFilter是如何将context从session读取并写入的。其中还讲到了AbstractAuthenticationProcessingFilter是

为什么80%的码农都做不了架构师?>>>   hot3.png

本文就来研究一下spring security的FilterSecurityInterceptor

问题

前面的文章讲了SecurityContextPersistenceFilter是如何将context从session读取并写入的。其中还讲到了AbstractAuthenticationProcessingFilter是如何将鉴权成功的authentication写入context的。那么spring security是如何处理没有authentication的请求呢。答案就在FilterSecurityInterceptor。

filter顺序

spring security内置的各种filter:

AliasFilter ClassNamespace Element or Attribute
CHANNEL_FILTERChannelProcessingFilterhttp/intercept-url@requires-channel
SECURITY_CONTEXT_FILTERSecurityContextPersistenceFilterhttp
CONCURRENT_SESSION_FILTERConcurrentSessionFiltersession-management/concurrency-control
HEADERS_FILTERHeaderWriterFilterhttp/headers
CSRF_FILTERCsrfFilterhttp/csrf
LOGOUT_FILTERLogoutFilterhttp/logout
X509_FILTERX509AuthenticationFilterhttp/x509
PRE_AUTH_FILTERAbstractPreAuthenticatedProcessingFilter SubclassesN/A
CAS_FILTERCasAuthenticationFilterN/A
FORM_LOGIN_FILTERUsernamePasswordAuthenticationFilterhttp/form-login
BASIC_AUTH_FILTERBasicAuthenticationFilterhttp/http-basic
SERVLET_API_SUPPORT_FILTERSecurityContextHolderAwareRequestFilterhttp/@servlet-api-provision
JAAS_API_SUPPORT_FILTERJaasApiIntegrationFilterhttp/@jaas-api-provision
REMEMBER_ME_FILTERRememberMeAuthenticationFilterhttp/remember-me
ANONYMOUS_FILTERAnonymousAuthenticationFilterhttp/anonymous
SESSION_MANAGEMENT_FILTERSessionManagementFiltersession-management
EXCEPTION_TRANSLATION_FILTERExceptionTranslationFilterhttp
FILTER_SECURITY_INTERCEPTORFilterSecurityInterceptorhttp
SWITCH_USER_FILTERSwitchUserFilterN/A

FilterSecurityInterceptor

spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/access/intercept/FilterSecurityInterceptor.java

/*** Performs security handling of HTTP resources via a filter implementation.* <p>* The <code>SecurityMetadataSource</code> required by this security interceptor is of* type {@link FilterInvocationSecurityMetadataSource}.* <p>* Refer to {@link AbstractSecurityInterceptor} for details on the workflow.* </p>** @author Ben Alex* @author Rob Winch*/
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implementsFilter {
/*** Method that is actually called by the filter chain. Simply delegates to the* {@link #invoke(FilterInvocation)} method.** @param request the servlet request* @param response the servlet response* @param chain the filter chain** @throws IOException if the filter chain fails* @throws ServletException if the filter chain fails*/public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {FilterInvocation fi = new FilterInvocation(request, response, chain);invoke(fi);}
public void invoke(FilterInvocation fi) throws IOException, ServletException {if ((fi.getRequest() != null)&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)&& observeOncePerRequest) {// filter already applied to this request and user wants us to observe// once-per-request handling, so don't re-do security checkingfi.getChain().doFilter(fi.getRequest(), fi.getResponse());}else {// first time this request being called, so perform security checkingif (fi.getRequest() != null) {fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);}InterceptorStatusToken token = super.beforeInvocation(fi);try {fi.getChain().doFilter(fi.getRequest(), fi.getResponse());}finally {super.finallyInvocation(token);}super.afterInvocation(token, null);}}//......
}    

所有的请求到了这一个filter,如果这个filter之前没有执行过的话,那么首先执行的InterceptorStatusToken token = super.beforeInvocation(fi);这个是由AbstractSecurityInterceptor提供。它就是spring security处理鉴权的入口。

AbstractSecurityInterceptor

spring-security-core-4.2.3.RELEASE-sources.jar!/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java

protected InterceptorStatusToken beforeInvocation(Object object) {Assert.notNull(object, "Object was null");final boolean debug = logger.isDebugEnabled();if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {throw new IllegalArgumentException("Security invocation attempted for object "+ object.getClass().getName()+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "+ getSecureObjectClass());}Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);if (attributes == null || attributes.isEmpty()) {if (rejectPublicInvocations) {throw new IllegalArgumentException("Secure object invocation "+ object+ " was denied as public invocations are not allowed via this interceptor. "+ "This indicates a configuration error because the "+ "rejectPublicInvocations property is set to 'true'");}if (debug) {logger.debug("Public object - authentication not attempted");}publishEvent(new PublicInvocationEvent(object));return null; // no further work post-invocation}if (debug) {logger.debug("Secure object: " + object + "; Attributes: " + attributes);}if (SecurityContextHolder.getContext().getAuthentication() == null) {credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound","An Authentication object was not found in the SecurityContext"),object, attributes);}Authentication authenticated = authenticateIfRequired();// Attempt authorizationtry {this.accessDecisionManager.decide(authenticated, object, attributes);}catch (AccessDeniedException accessDeniedException) {publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,accessDeniedException));throw accessDeniedException;}if (debug) {logger.debug("Authorization successful");}if (publishAuthorizationSuccess) {publishEvent(new AuthorizedEvent(object, attributes, authenticated));}// Attempt to run as a different userAuthentication runAs = this.runAsManager.buildRunAs(authenticated, object,attributes);if (runAs == null) {if (debug) {logger.debug("RunAsManager did not change Authentication object");}// no further work post-invocationreturn new InterceptorStatusToken(SecurityContextHolder.getContext(), false,attributes, object);}else {if (debug) {logger.debug("Switching to RunAs Authentication: " + runAs);}SecurityContext origCtx = SecurityContextHolder.getContext();SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());SecurityContextHolder.getContext().setAuthentication(runAs);// need to revert to token.Authenticated post-invocationreturn new InterceptorStatusToken(origCtx, true, attributes, object);}}

这里调用了accessDecisionManager来进行判断,如果没有登录态,在到达这个filter之前会先经过AnonymousAuthenticationFilter.java,其Authentication的值为AnonymousAuthenticationToken。如果匿名请求需要登录态的url,或者权限不够,则抛出AccessDeniedException。

小结

spring security两个入口filter分别如下:

  • AbstractAuthenticationProcessingFilter(主要处理登录)
  • FilterSecurityInterceptor(主要处理鉴权)

而SecurityContextPersistenceFilter主要是为这两个filter准备context。

转载于:https://my.oschina.net/go4it/blog/1591160

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/101.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息