Spring Security详解(三)认证之核心过滤器


这章主要用来分析Spring Security中的过滤器链包含了哪些关键的过滤器,并且各自的作用是什么。

3 核心过滤器

3.1 概述

Filter顺序

Spring Security的官方文档向我们提供了filter的顺序,无论实际应用中你用到了哪些,整体的顺序是保持不变的:

  • ChannelProcessingFilter,重定向到其他协议的过滤器。也就是说如果你访问的channel错了,那首先就会在channel之间进行跳转,如http变为https。
  • SecurityContextPersistenceFilter,请求来临时在SecurityContextHolder中建立一个SecurityContext,然后在请求结束的时候,清空SecurityContextHolder。并且任何对SecurityContext的改变都可以被copy到HttpSession。
  • ConcurrentSessionFilter,因为它需要使用SecurityContextHolder的功能,而且更新对应session的最后更新时间,以及通过SessionRegistry获取当前的SessionInformation以检查当前的session是否已经过期,过期则会调用LogoutHandler
  • 认证处理机制,如UsernamePasswordAuthenticationFilterCasAuthenticationFilterBasicAuthenticationFilter等,以至于SecurityContextHolder可以被更新为包含一个有效的Authentication请求。
  • SecurityContextHolderAwareRequestFilter,它将会把HttpServletRequest封装成一个继承自HttpServletRequestWrapperSecurityContextHolderAwareRequestWrapper,同时使用SecurityContext实现了HttpServletRequest中与安全相关的方法
  • JaasApiIntegrationFilter,如果SecurityContextHolder中拥有的Authentication是一个JaasAuthenticationToken,那么该Filter将使用包含在JaasAuthenticationToken中的Subject继续执行FilterChain。
  • RememberMeAuthenticationFilter,如果之前的认证处理机制没有更新SecurityContextHolder,并且用户请求包含了一个Remember-Me对应的cookie,那么一个对应的Authentication将会设给SecurityContextHolder
  • AnonymousAuthenticationFilter,如果之前的认证机制都没有更新SecurityContextHolder拥有的Authentication,那么一个AnonymousAuthenticationToken将会设给SecurityContextHolder。
  • ExceptionTransactionFilter,用于处理在FilterChain范围内抛出的AccessDeniedExceptionAuthenticationException,并把它们转换为对应的Http错误码返回或者对应的页面。
  • FilterSecurityInterceptor,保护Web URI,进行权限认证,并且在访问被拒绝时抛出异常。

如果你想知道你的项目都配置了哪些过滤器,可以从Spring Boot的启动日志中找到,上一节我们配置了一个基础的表单登陆的demo,它在启动时 ,Spring Security自动配置的过滤器有:

Creating filter chain: o.s.s.web.util.matcher.AnyRequestMatcher@1, 
[o.s.s.web.context.SecurityContextPersistenceFilter@8851ce1, 
o.s.s.web.header.HeaderWriterFilter@6a472566, o.s.s.web.csrf.CsrfFilter@61cd1c71, 
o.s.s.web.authentication.logout.LogoutFilter@5e1d03d7, 
o.s.s.web.authentication.UsernamePasswordAuthenticationFilter@122d6c22, 
o.s.s.web.savedrequest.RequestCacheAwareFilter@5ef6fd7f, 
o.s.s.web.servletapi.SecurityContextHolderAwareRequestFilter@4beaf6bd, 
o.s.s.web.authentication.AnonymousAuthenticationFilter@6edcad64, 
o.s.s.web.session.SessionManagementFilter@5e65afb6, 
o.s.s.web.access.ExceptionTranslationFilter@5b9396d3, 
o.s.s.web.access.intercept.FilterSecurityInterceptor@3c5dbdf8
]

配置详解 里已经说明Filter是怎样被添加到http的,下面我们来依次讲解一下几个重要的过滤器。

3.2 SecurityContextPersistenceFilter

在Spring Security中,用户在登录过一次之后,后续的访问便是通过sessionId来识别,从而认为用户已经被认证。具体在何处存放用户信息,便是第一篇文章中提到的SecurityContextHolder;认证相关的信息是如何被存放到其中的,便是通过SecurityContextPersistenceFilter。上面我们已经提到过,SecurityContextPersistenceFilter的两个主要作用便是请求来临时创建SecurityContext安全上下文信息和请求结束时清空SecurityContextHolder。在使用NameSpace时,Spring Security默认会将SecurityContext保存在HttpSession中。
但如果是基于微服务的话,对应在http的无状态也就意味着不允许存在session。这可以通过setAllowSessionCreation(false) 实现。

源码分析

public class SecurityContextPersistenceFilter extends GenericFilterBean {

    static final String FILTER_APPLIED = "__spring_security_scpf_applied";
    //安全上下文存储的仓库
    private SecurityContextRepository repo;

    public SecurityContextPersistenceFilter() {
        //HttpSessionSecurityContextRepository是SecurityContextRepository接口的一个实现类
        //使用HttpSession来存储SecurityContext
        this(new HttpSessionSecurityContextRepository());
    }

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        if (request.getAttribute(FILTER_APPLIED) != null) {
            // ensure that filter is only applied once per request
            chain.doFilter(request, response);
            return;
        }
        final boolean debug = logger.isDebugEnabled();
        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
        if (forceEagerSessionCreation) {
            HttpSession session = request.getSession();

            if (debug && session.isNew()) {
                logger.debug("Eagerly created session: " + session.getId());
            }
        }
        HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
                                                                         response);
        //从Session中获取安全上下文信息
        SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
        try {
            //请求开始时,设置安全上下文信息,这样就避免了用户直接从Session中获取安全上下文信息
            SecurityContextHolder.setContext(contextBeforeChainExecution);
            chain.doFilter(holder.getRequest(), holder.getResponse());
        }
        finally {
            //请求结束后,清空安全上下文信息
            SecurityContext contextAfterChainExecution = SecurityContextHolder
                .getContext();
            SecurityContextHolder.clearContext();
            repo.saveContext(contextAfterChainExecution, holder.getRequest(),
                             holder.getResponse());
            request.removeAttribute(FILTER_APPLIED);
            if (debug) {
                logger.debug("SecurityContextHolder now cleared, as request processing completed");
            }
        }
    }

}

过滤器一般负责核心的处理流程,而具体的业务实现,通常交给其中聚合的其他实体类。例如存储安全上下文和读取安全上下文的工作完全委托给了HttpSessionSecurityContextRepository去处理:

public class HttpSessionSecurityContextRepository implements SecurityContextRepository {
   // 'SPRING_SECURITY_CONTEXT'是安全上下文默认存储在Session中的键值
   public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
   ...
   private final Object contextObject = SecurityContextHolder.createEmptyContext();
   private boolean allowSessionCreation = true;
   private boolean disableUrlRewriting = false;
   private String springSecurityContextKey = SPRING_SECURITY_CONTEXT_KEY;

   private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

   //从当前request中取出安全上下文,如果session为空,则会返回一个新的安全上下文
   public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
      HttpServletRequest request = requestResponseHolder.getRequest();
      HttpServletResponse response = requestResponseHolder.getResponse();
      HttpSession httpSession = request.getSession(false);
      SecurityContext context = readSecurityContextFromSession(httpSession);
      if (context == null) {
         context = generateNewContext();
      }
      ...
      return context;
   }

   ...

   public boolean containsContext(HttpServletRequest request) {
      HttpSession session = request.getSession(false);
      if (session == null) {
         return false;
      }
      return session.getAttribute(springSecurityContextKey) != null;
   }

   private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
      if (httpSession == null) {
         return null;
      }
      ...
      // Session存在的情况下,尝试获取其中的SecurityContext
      Object contextFromSession = httpSession.getAttribute(springSecurityContextKey);
      if (contextFromSession == null) {
         return null;
      }
      ...
      return (SecurityContext) contextFromSession;
   }

   //初次请求时创建一个新的SecurityContext实例
   protected SecurityContext generateNewContext() {
      return SecurityContextHolder.createEmptyContext();
   }

}

SecurityContextPersistenceFilterHttpSessionSecurityContextRepository配合使用,构成了Spring Security整个调用链路的入口。

3.3 UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter用于处理来自表单提交的认证。

public class UsernamePasswordAuthenticationFilter extends
   	AbstractAuthenticationProcessingFilter {
   //用户名默认的参数名 可通过setUsernameParameter修改
   private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
   //密码默认的参数名 可通过setPasswordParameter修改
   private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
   //是否只允许post请求
   private boolean postOnly = true;

   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();
       //组装成username+password形式的token
   	UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
   			username, password);
   	// Allow subclasses to set the "details" property
   	setDetails(request, authRequest);
       //交给内部的AuthenticationManager去认证,并返回认证后的信息
   	return this.getAuthenticationManager().authenticate(authRequest);
   }
}

UsernamePasswordAuthenticationFilter本身的代码只包含了上述这么一个方法,而在其父类AbstractAuthenticationProcessingFilter中包含了大量的细节:

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
     implements ApplicationEventPublisherAware, MessageSourceAware {
   //包含了一个身份认证器
   private AuthenticationManager authenticationManager;
   //用于实现remeberMe
   private RememberMeServices rememberMeServices = new NullRememberMeServices();
   private RequestMatcher requiresAuthenticationRequestMatcher;
   //这两个Handler分别代表了认证成功和失败相应的处理器
   private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
   private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
   
   public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
   		throws IOException, ServletException {

   	HttpServletRequest request = (HttpServletRequest) req;
   	HttpServletResponse response = (HttpServletResponse) res;
   	...
   	Authentication authResult;
   	try {
   		//此处实际上就是调用UsernamePasswordAuthenticationFilter的attemptAuthentication方法
   		authResult = attemptAuthentication(request, response);
   		if (authResult == null) {
   			//子类未完成认证,立刻返回
   			return;
   		}
   		sessionStrategy.onAuthentication(authResult, request, response);
   	}
   	//在认证过程中可以直接抛出异常,在过滤器中,就像此处一样,进行捕获
   	catch (InternalAuthenticationServiceException failed) {
   		//内部服务异常
   		unsuccessfulAuthentication(request, response, failed);
   		return;
   	}
   	catch (AuthenticationException failed) {
   		//认证失败
   		unsuccessfulAuthentication(request, response, failed);
   		return;
   	}
   	//认证成功
   	if (continueChainBeforeSuccessfulAuthentication) {
   		chain.doFilter(request, response);
   	}
   	//注意,认证成功后过滤器把authResult结果也传递给了成功处理器
   	successfulAuthentication(request, response, chain, authResult);
   }
   
}

整个流程主要就是调用了authenticationManager完成认证,根据认证结果执行successfulAuthentication或者unsuccessfulAuthentication,无论成功失败,一般的实现都是转发或者重定向等处理。

protected void successfulAuthentication(HttpServletRequest request,
   		HttpServletResponse response, FilterChain chain, Authentication authResult)
   		throws IOException, ServletException {
           ...
           successHandler.onAuthenticationSuccess(request, response, authResult);
   }

successHandler重定向到DefaultSavedRequest url

public class SavedRequestAwareAuthenticationSuccessHandler extends
		SimpleUrlAuthenticationSuccessHandler {
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication authentication)
			throws ServletException, IOException {
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (savedRequest == null) {
			super.onAuthenticationSuccess(request, response, authentication);
			return;
		}
		String targetUrlParameter = getTargetUrlParameter();
		if (isAlwaysUseDefaultTargetUrl()
				|| (targetUrlParameter != null && StringUtils.hasText(request
						.getParameter(targetUrlParameter)))) {
			requestCache.removeRequest(request, response);
			super.onAuthenticationSuccess(request, response, authentication);

			return;
		}
        //清理认证信息
		clearAuthenticationAttributes(request);

		// Use the DefaultSavedRequest URL
		String targetUrl = savedRequest.getRedirectUrl();
		logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
		getRedirectStrategy().sendRedirect(request, response, targetUrl);
	}
}

而它的父类SimpleUrlAuthenticationSuccessHandler里:

public class SimpleUrlAuthenticationSuccessHandler extends
		AbstractAuthenticationTargetUrlRequestHandler{
    public void onAuthenticationSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication authentication)
			throws IOException, ServletException {
		handle(request, response, authentication);
		clearAuthenticationAttributes(request);
	}
}

这个handle来自于AbstractAuthenticationTargetUrlRequestHandler

public abstract class AbstractAuthenticationTargetUrlRequestHandler {
    protected void handle(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws IOException, ServletException {
		...
		redirectStrategy.sendRedirect(request, response, targetUrl);
	}
}

failureHandler结构和successHander类似,有兴趣的可以研究一下。

在文章开头我们指出,配置了http.formLogin()后会自动加载UsernamePasswordAuthenticationFilter,那么是在什么时候进行加载filter呢?在FormLoginConfigurer中找到了利用父类AbstractAuthenticationFilterConfigurer进行了对filter的配置:

public FormLoginConfigurer() {
   	super(new UsernamePasswordAuthenticationFilter(), null);
   	usernameParameter("username");
   	passwordParameter("password");
}

而AbstractAuthenticationFilterConfigurer中:

public abstract class AbstractAuthenticationFilterConfigurer extends ...{
  ...
  //formLogin不出所料配置了AuthenticationEntryPoint
  private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
  //认证失败的处理器
  private AuthenticationFailureHandler failureHandler;
  ...
  protected AbstractAuthenticationFilterConfigurer(F authenticationFilter,
   		String defaultLoginProcessingUrl) {
   	this();
   	this.authFilter = authenticationFilter;
   	if (defaultLoginProcessingUrl != null) {
   		loginProcessingUrl(defaultLoginProcessingUrl);
   	}
   }
}

也就是说,formLogin()配置了之后最起码做了两件事,其一,为UsernamePasswordAuthenticationFilter设置了相关的配置,其二配置了AuthenticationEntryPointAuthenticationEntryPoint在下面的章节详细分析。

3.4 AnonymousAuthenticationFilter

匿名认证过滤器,可能有人会想:匿名了还有身份?Spirng Security为了整体逻辑的统一性,即使是未通过认证的用户,也给予了一个匿名身份。而AnonymousAuthenticationFilter位于常用的身份认证过滤器(如UsernamePasswordAuthenticationFilterRememberMeAuthenticationFilter)之后,意味着只有在上述身份过滤器执行完毕后,SecurityContext依旧没有用户信息,AnonymousAuthenticationFilter会给予用户一个匿名身份。

源码分析

public class AnonymousAuthenticationFilter extends GenericFilterBean implements
      InitializingBean {
   private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
   private String key;
   private Object principal;
   private List<GrantedAuthority> authorities;
   //自动创建一个"anonymousUser"的匿名用户,其具有ANONYMOUS角色
   public AnonymousAuthenticationFilter(String key) {
      this(key, "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
   }
   ...
   public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
         throws IOException, ServletException {
      //过滤器链都执行到匿名认证过滤器还没有身份信息,塞一个匿名身份进去
      if (SecurityContextHolder.getContext().getAuthentication() == null) {
         SecurityContextHolder.getContext().setAuthentication(
               createAuthentication((HttpServletRequest) req));
      }
      chain.doFilter(req, res);
   }

   protected Authentication createAuthentication(HttpServletRequest request) {
     //创建一个AnonymousAuthenticationToken
      AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,
            principal, authorities);
      auth.setDetails(authenticationDetailsSource.buildDetails(request));

      return auth;
   }
   ...
}

到这里可以看出,AnonymousAuthenticationFilterUsernamePasswordAuthenticationFilter都是对Authentication进行一系列操作,这就印证了前面说要创建一个全局的SecurityContext,来把一系列的过滤器串联起来。

3.5 ExceptionTranslationFilter

通过前面的介绍我们知道在Spring Security的Filter链表中ExceptionTranslationFilter就放在FilterSecurityInterceptor的前面。而ExceptionTranslationFilter是捕获来自FilterChain的异常,并对这些异常做处理。ExceptionTranslationFilter能够捕获来自FilterChain所有的异常,但是它只会处理两类异常,AuthenticationExceptionAccessDeniedException,其它的异常它会继续抛出。如果捕获到的是AuthenticationException,那么将会使用其对应的AuthenticationEntryPoint的commence()处理。如果捕获的异常是一个AccessDeniedException,那么将视当前访问的用户是否已经登录认证做不同的处理,如果未登录,则会使用关联的AuthenticationEntryPoint的commence()方法进行处理,否则将使用关联的AccessDeniedHandler的handle()方法进行处理。

源码分析

public class ExceptionTranslationFilter extends GenericFilterBean {
  //处理异常转换的核心方法
  private void handleSpringSecurityException(HttpServletRequest request,
        HttpServletResponse response, FilterChain chain, RuntimeException exception)
        throws IOException, ServletException {
     if (exception instanceof AuthenticationException) {
       	//重定向到登录端点
        sendStartAuthentication(request, response, chain,
              (AuthenticationException) exception);
     }
     else if (exception instanceof AccessDeniedException) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) {
		  //重定向到登录端点
           sendStartAuthentication(
                 request,
                 response,
                 chain,
                 new InsufficientAuthenticationException(
                       "Full authentication is required to access this resource"));
        }
        else {
           //交给accessDeniedHandler处理
           accessDeniedHandler.handle(request, response,
                 (AccessDeniedException) exception);
        }
     }
  }
}

3.5.1 AuthenticationEntryPoint

AuthenticationEntryPoint是在用户没有登录时用于引导用户进行登录认证的,在实际应用中应根据具体的认证机制选择对应的AuthenticationEntryPoint
AuthenticationEntryPoint有很多实现类,这里拿出LoginUrlAuthenticationEntryPoint来进行分析。

public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoint{
    ...
    //执行重定向(或转发)到登录表单URL。
    public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException, ServletException {

		String redirectUrl = null;

		if (useForward) {

			if (forceHttps && "http".equals(request.getScheme())) {
				// First redirect the current request to HTTPS.
				// When that request is received, the forward to the login page will be
				// used.
				redirectUrl = buildHttpsRedirectUrlForRequest(request);
			}

			if (redirectUrl == null) {
				String loginForm = determineUrlToUseForThisRequest(request, response,
						authException);

				if (logger.isDebugEnabled()) {
					logger.debug("Server side forward to: " + loginForm);
				}

				RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);

				dispatcher.forward(request, response);

				return;
			}
		}
		else {
			redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);
		}
		redirectStrategy.sendRedirect(request, response, redirectUrl);
	}

}

其余的实现类和此类似,有时候我们访问受限页面,又没有配置登录,就看到了一个空荡荡的默认错误页面,上面显示着401,403,就是Http401AuthenticationEntryPointHttp403ForbiddenEntryPoint这两个入口起了作用。

3.5.2 AccessDeniedHandler

AccessDeniedHandler用于在用户已经登录了,但是访问了其自身没有权限的资源时做出对应的处理。ExceptionTranslationFilter拥有的AccessDeniedHandler默认是AccessDeniedHandlerImpl,其会返回一个403错误码到客户端。我们可以通过显示的配置AccessDeniedHandlerImpl,同时给其指定一个errorPage使其可以返回对应的错误页面。当然我们也可以实现自己的AccessDeniedHandler
AccessDeniedHandler有一个默认实现类AccessDeniedHandlerImpl,这个默认实现类会根据errorPage和状态码来判断,最终决定跳转的页面。

public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
	public void handle(HttpServletRequest request, HttpServletResponse response,
			AccessDeniedException accessDeniedException) throws IOException,
			ServletException {
		if (!response.isCommitted()) {
			if (errorPage != null) {
				// Put exception into request scope (perhaps of use to a view)
				request.setAttribute(WebAttributes.ACCESS_DENIED_403,
						accessDeniedException);

				// Set the 403 status code.
				response.setStatus(HttpStatus.FORBIDDEN.value());

				// forward to error page.
				RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
				dispatcher.forward(request, response);
			}
			else {
				response.sendError(HttpStatus.FORBIDDEN.value(),
					HttpStatus.FORBIDDEN.getReasonPhrase());
			}
		}
	}
}

3.6 FilterSecurityInterceptor

FilterSecurityInterceptor的工作流程的理解可以理解如下:FilterSecurityInterceptorSecurityContextHolder中获取Authentication对象,然后比对用户拥有的权限和资源所需的权限。用户拥有的权限可以通过Authentication对象直接获得,而后者则需要引入两个类:SecurityMetadataSourceAccessDecisionManagerSecurityMetadataSource用来获取资源所需要的权限,而AccessDecisionManager用来进行权限认证,也就是比对用户是否有访问资源的权限。关于权限的这一部分会在后面的章节详细分析,这里先了解一下。

@Override
protected void configure(HttpSecurity http) throws Exception {
   http
   	.authorizeRequests()
   		.antMatchers("/resources/**").permitAll()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
            .anyRequest().authenticated();
}

如上所示,当访问url为"/admin/**",那么SecurityMetadataSource获取到的权限就是"ADMIN",然后利用AccessDecisionManager进行比对。

3.7 添加自定义过滤器

上面说的过滤器都是Spring Security为我们提供的,如果你想自定义一个过滤器,比如说JwtAuthenticationTokenFilter,需要在SecurityConfig做如下配置:

...
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    @Bean
    public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
        return new JwtAuthenticationTokenFilter();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ...
        http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
    }
}

查看HttpSecurity,发现一共有四种添加方式:

public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
		comparator.registerAfter(filter.getClass(), afterFilter);
	return addFilter(filter);
}
public HttpSecurity addFilterBefore(Filter filter,
			Class<? extends Filter> beforeFilter) {
		comparator.registerBefore(filter.getClass(), beforeFilter);
	return addFilter(filter);
}
public HttpSecurity addFilter(Filter filter) {
		Class<? extends Filter> filterClass = filter.getClass();
	...
	this.filters.add(filter);
	return this;
}
public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {
	this.comparator.registerAt(filter.getClass(), atFilter);
	return addFilter(filter);
}
  • addFilterAfter(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class),在UsernamePasswordAuthenticationFilter过滤器位置之后注册JwtAuthenticationTokenFilter
  • addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class),在UsernamePasswordAuthenticationFilter过滤器位置之前注册JwtAuthenticationTokenFilter
  • addFilter(usernamePasswordAuthenticationFilter()),注册过滤器JwtAuthenticationTokenFilter。
  • addFilterAt(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class),在UsernamePasswordAuthenticationFilter过滤器位置注册JwtAuthenticationTokenFilter

参考: https://www.cnkirito.moe/spring-security-4/

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security是一个用于保护Java应用程序的框架,它提供了一系列过滤器来处理安全相关的任务。在Spring Security中,过滤器被组织成一个过滤器链(Filter Chain),它根据配置的顺序依次执行。以下是一些常用的Spring Security过滤器及其功能: 1. SecurityContextPersistenceFilter:用于在请求处理期间存储和检索SecurityContext,即当前用户的安全上下文。 2. LogoutFilter:处理用户注销请求,并清除当前用户的认证信息。 3. UsernamePasswordAuthenticationFilter:用于处理基于用户名和密码的认证请求。 4. ConcurrentSessionFilter:用于处理并发会话控制,限制同一用户的同时登录数。 5. BasicAuthenticationFilter:用于处理基于HTTP基本身份验证的认证请求。 6. RequestCacheAwareFilter:用于缓存请求,以便在重定向后重新发送请求。 7. SecurityContextHolderAwareRequestFilter:用于包装请求,使其能够识别特定的安全上下文。 8. AnonymousAuthenticationFilter:用于在请求上下文中添加匿名用户的认证信息。 9. SessionManagementFilter:用于处理会话管理,例如过期检查、并发控制等。 10. ExceptionTranslationFilter:用于捕获并处理异常,将其转换为合适的响应。 11. FilterSecurityInterceptor:用于实施访问控制,检查用户是否具有访问资源的权限。 这些过滤器可以根据具体的安全需求进行配置和组合,以提供所需的安全功能。通过指定不同的过滤器顺序、添加自定义过滤器或替换默认过滤器,您可以灵活地定制Spring Security过滤器链。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值