Spring Security核心Filter执行流程

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

一、 Filter顺序

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

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

以下顺序,由上往下
在这里插入图片描述

二、讲解一下几个重要的过滤器

  1. 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");
         }
      }
   }
}
  1. 过滤器一般负责核心的处理流程,而具体的业务实现,通常交给其中聚合的其他实体类。例如存储安全上下文和读取安全上下文的工作完全委托给了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();
   }
}
  1. 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在下面的章节详细分析。

  1. AnonymousAuthenticationFilter

匿名认证过滤器,可能有人会想:匿名了还有身份?
Spring 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,来把一系列的过滤器串联起来。

  1. 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);
        }
     }
  }
}
  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这两个入口起了作用

  1. 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());
            }
        }
    }
}
  1. 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进行比对。

  1. 添加自定义过滤器
    上面说的过滤器都是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。,也就是替换UsernamePasswordAuthenticationFilter

本文转自

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security 使用 Filter 来处理身份验证和授权。它提供了一组 Filter,用于拦截和处理请求。 在 Spring Security 中,最重要的 Filter 是 `FilterChainProxy`。它是一个特殊的 Filter,负责管理一组其他 Filter,这些 Filter 一起构成了一个过滤器链。 以下是 Spring Security 中常用的 Filter: 1. `UsernamePasswordAuthenticationFilter`:用于处理基于表单的身份验证请求,并根据提供的用户名和密码进行身份验证。 2. `BasicAuthenticationFilter`:用于处理基于 HTTP 基本身份验证的请求。 3. `RememberMeAuthenticationFilter`:用于处理 "记住我" 功能的请求。 4. `AnonymousAuthenticationFilter`:用于为匿名用户提供默认的身份验证。 5. `ExceptionTranslationFilter`:用于捕获和处理身份验证和授权异常,并将其转换为适当的 HTTP 响应。 6. `FilterSecurityInterceptor`:用于基于访问决策器(Access Decision Manager)进行访问控制的主要过滤器。 你可以使用 Spring Security 的配置类来配置这些 Filter,并定义它们的顺序和应用规则。例如,你可以通过继承 `WebSecurityConfigurerAdapter` 类并重写 `configure(HttpSecurity http)` 方法来配置 Spring Security 过滤器链。 示例代码如下: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/public/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll(); } } ``` 在上述示例中,`configure(HttpSecurity http)` 方法配置了一个过滤器链,其中包含了 `UsernamePasswordAuthenticationFilter`、`ExceptionTranslationFilter` 等等。 这只是一个简单的示例,你可以根据自己的需求添加和配置其他的过滤器。希望这能帮到你!如果还有其他问题,请继续提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值