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

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sinat_29899265/article/details/80747924

这章主要用来分析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。
  • 认证处理机制,如UsernamePasswordAuthenticationFilter,CasAuthenticationFilter,BasicAuthenticationFilter等,以至于SecurityContextHolder可以被更新为包含一个有效的Authentication请求。
  • SecurityContextHolderAwareRequestFilter,它将会把HttpServletRequest封装成一个继承自HttpServletRequestWrapper的SecurityContextHolderAwareRequestWrapper,同时使用SecurityContext实现了HttpServletRequest中与安全相关的方法。
  • JaasApiIntegrationFilter,如果SecurityContextHolder中拥有的Authentication是一个JaasAuthenticationToken,那么该Filter将使用包含在JaasAuthenticationToken中的Subject继续执行FilterChain。
  • RememberMeAuthenticationFilter,如果之前的认证处理机制没有更新SecurityContextHolder,并且用户请求包含了一个Remember-Me对应的cookie,那么一个对应的Authentication将会设给SecurityContextHolder。
  • AnonymousAuthenticationFilter,如果之前的认证机制都没有更新SecurityContextHolder拥有的Authentication,那么一个AnonymousAuthenticationToken将会设给SecurityContextHolder。
  • ExceptionTransactionFilter,用于处理在FilterChain范围内抛出的AccessDeniedException和AuthenticationException,并把它们转换为对应的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
]

下面我们来依次讲解一下几个重要的过滤器。

3.2 SecurityContextPersistenceFilter

试想一下,如果我们不使用Spring Security,如果保存用户信息呢,大多数情况下会考虑使用Session对吧?在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();
   }

}

SecurityContextPersistenceFilter和HttpSessionSecurityContextRepository配合使用,构成了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设置了相关的配置,其二配置了AuthenticationEntryPoint。AuthenticationEntryPoint在下面的章节详细分析。

3.4 AnonymousAuthenticationFilter

匿名认证过滤器,可能有人会想:匿名了还有身份?Spirng Security为了整体逻辑的统一性,即使是未通过认证的用户,也给予了一个匿名身份。而AnonymousAuthenticationFilter位于常用的身份认证过滤器(如UsernamePasswordAuthenticationFilter、RememberMeAuthenticationFilter)之后,意味着只有在上述身份过滤器执行完毕后,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;
   }
   ...
}

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

3.5 ExceptionTranslationFilter

通过前面的介绍我们知道在Spring Security的Filter链表中ExceptionTranslationFilter就放在FilterSecurityInterceptor的前面。而ExceptionTranslationFilter是捕获来自FilterChain的异常,并对这些异常做处理。ExceptionTranslationFilter能够捕获来自FilterChain所有的异常,但是它只会处理两类异常,AuthenticationException和AccessDeniedException,其它的异常它会继续抛出。如果捕获到的是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,就是Http401AuthenticationEntryPoint,Http403ForbiddenEntryPoint这两个入口起了作用。

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的工作流程的理解可以理解如下:FilterSecurityInterceptor从SecurityContextHolder中获取Authentication对象,然后比对用户拥有的权限和资源所需的权限。前者可以通过Authentication对象直接获得,而后者则需要引入我们之前一直未提到过的两个类:SecurityMetadataSource,AccessDecisionManager。SecurityMetadataSource用来获取资源所需要的权限,而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/

展开阅读全文

没有更多推荐了,返回首页