Spring Boot Security中JWT過濾器針對特定URL模式的配置

Spring Boot Security中JWT過濾器針對特定URL模式的配置

本文詳細(xì)介紹了如何在spring Boot Security中,精確控制JWT認(rèn)證過濾器只應(yīng)用于特定的URL模式,而非全局生效。通過繼承AbstractAuthenticationProcessingFilter并結(jié)合RequestMatcher接口,開發(fā)者可以自定義過濾器的觸發(fā)條件,實(shí)現(xiàn)對如/api/**等指定路徑的JWT認(rèn)證,同時保持其他路徑的開放性或采用不同的認(rèn)證機(jī)制,從而優(yōu)化安全配置的靈活性和效率。

spring boot應(yīng)用中集成jwt(json web Token)進(jìn)行認(rèn)證是常見的做法。然而,默認(rèn)的jwt過濾器通常會攔截所有請求,這在某些場景下可能不是最優(yōu)解。例如,我們可能只希望對/api/**開頭的接口進(jìn)行jwt認(rèn)證,而其他公共接口(如/login、/register)則無需經(jīng)過jwt過濾器處理。本文將指導(dǎo)您如何通過spring security提供的abstractauthenticationprocessingfilter和requestmatcher接口,實(shí)現(xiàn)jwt過濾器針對特定url模式的精確控制。

核心概念解析

要實(shí)現(xiàn)JWT過濾器的精確控制,我們需要理解spring security中的幾個關(guān)鍵組件:

  • AbstractAuthenticationProcessingFilter: 這是Spring Security提供的一個抽象基類,用于實(shí)現(xiàn)基于請求的認(rèn)證過濾器。它的核心特性是,只會處理那些與其內(nèi)部RequestMatcher匹配的請求。通過繼承它,我們可以將自定義的認(rèn)證邏輯(如JWT驗(yàn)證)綁定到特定的URL模式上。
  • RequestMatcher: 這是一個接口,定義了如何匹配傳入的httpServletRequest。當(dāng)RequestMatcher的matches()方法返回true時,AbstractAuthenticationProcessingFilter才會執(zhí)行其認(rèn)證邏輯。
  • AntPathRequestMatcher: RequestMatcher接口的一個常用實(shí)現(xiàn),允許我們使用Ant風(fēng)格的路徑模式(如/api/**, /users/*)來匹配URL。
  • OrRequestMatcher: 另一個RequestMatcher實(shí)現(xiàn),用于組合多個RequestMatcher。如果其中任何一個子匹配器匹配成功,則OrRequestMatcher返回true。這在需要匹配多個不連續(xù)的URL模式時非常有用。

實(shí)現(xiàn)步驟

我們將通過以下三個主要步驟來配置JWT過濾器:

步驟一:創(chuàng)建自定義JWT認(rèn)證過濾器

首先,您的JWT認(rèn)證過濾器需要繼承AbstractAuthenticationProcessingFilter。這意味著它將不再是一個簡單的Filter實(shí)現(xiàn),而是具備了根據(jù)RequestMatcher選擇性執(zhí)行的能力。

import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.util.matcher.RequestMatcher; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;  // 假設(shè)您已有一個用于處理JWT的AuthenticationManager // 或者您可以在attemptAuthentication方法中直接處理JWT驗(yàn)證邏輯 public class CustomJwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {      // 構(gòu)造函數(shù)接收一個RequestMatcher,用于定義哪些URL需要此過濾器處理     public CustomJwtAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher) {         super(requiresAuthenticationRequestMatcher);     }      /**      * 這是AbstractAuthenticationProcessingFilter的核心方法,      * 當(dāng)請求URL與構(gòu)造函數(shù)中傳入的RequestMatcher匹配時,此方法會被調(diào)用。      * 在這里,您將實(shí)現(xiàn)從請求中提取JWT并進(jìn)行認(rèn)證的邏輯。      */     @Override     public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)             throws AuthenticationException, IOException {         // 1. 從請求頭(如Authorization: Bearer <token>)中提取JWT         String authHeader = request.getHeader("Authorization");         if (authHeader == null || !authHeader.startsWith("Bearer ")) {             // 如果沒有有效的JWT,可以拋出異常或返回null             // AbstractAuthenticationProcessingFilter 會將AuthenticationException傳遞給AuthenticationEntryPoint             throw new AuthenticationServiceException("JWT Token is missing or invalid");         }         String jwtToken = authHeader.substring(7); // 移除 "Bearer " 前綴          // 2. 根據(jù)JWT創(chuàng)建Authentication對象(例如,一個包含JWT字符串的UsernamePasswordAuthenticationToken)         // 這里只是一個示例,實(shí)際的JWT解析和用戶查找邏輯會更復(fù)雜         // 您可能需要一個JwtTokenProvider或類似的Service來驗(yàn)證和解析JWT         // 假設(shè)您有一個JwtAuthenticationToken,它包含了JWT信息         JwtAuthenticationToken authenticationToken = new JwtAuthenticationToken(jwtToken);          // 3. 將AuthenticationToken提交給AuthenticationManager進(jìn)行認(rèn)證         // getAuthenticationManager() 方法由 AbstractAuthenticationProcessingFilter 提供         // 并且需要在SecurityConfig中暴露AuthenticationManager Bean         return getAuthenticationManager().authenticate(authenticationToken);     }      // 您可能還需要重寫 successfulAuthentication 和 unsuccessfulAuthentication 方法     // 以處理認(rèn)證成功或失敗后的邏輯(如設(shè)置SecurityContext、返回錯誤信息等) }

步驟二:定義URL匹配器

接下來,我們需要創(chuàng)建一個RequestMatcher實(shí)例,它能準(zhǔn)確識別出我們希望JWT過濾器處理的URL模式。根據(jù)需求,我們可以使用AntPathRequestMatcher或OrRequestMatcher。

示例:匹配`/api/`路徑**

import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.stream.Collectors;  // 這是一個輔助類,用于生成匹配特定API路徑的RequestMatcher public class ApiUrlRequestMatcher {      /**      * 創(chuàng)建一個RequestMatcher,用于匹配所有以指定模式開頭的API路徑。      *      * @param patterns 期望匹配的URL模式列表,例如 "/api/**", "/admin/**"      * @return 組合后的RequestMatcher      */     public static RequestMatcher createApiMatcher(List<String> patterns) {         // 將每個模式轉(zhuǎn)換為AntPathRequestMatcher         List<RequestMatcher> matchers = patterns.stream()                 .map(AntPathRequestMatcher::new)                 .collect(Collectors.toList());         // 使用OrRequestMatcher將所有模式組合起來         return new OrRequestMatcher(matchers);     }      /**      * 單個AntPathRequestMatcher的簡單示例      */     public static RequestMatcher createSingleApiMatcher(String pattern) {         return new AntPathRequestMatcher(pattern);     } }

步驟三:配置Spring Security鏈

最后,在您的Spring Security配置類(通常是繼承WebSecurityConfigurerAdapter的類)中,實(shí)例化CustomJwtAuthenticationFilter并將其添加到安全過濾器鏈中。

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.RequestMatcher; import java.util.Arrays; // For List.of in older Java versions  @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter {      // 假設(shè)您有一個JwtAuthenticationEntryPoint來處理認(rèn)證失敗的響應(yīng)     // @Autowired     // private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;      @Override     protected void configure(HttpSecurity http) throws Exception {         // 1. 定義需要JWT過濾器處理的URL模式         RequestMatcher jwtFilterMatcher = ApiUrlRequestMatcher.createApiMatcher(                 Arrays.asList("/api/**", "/admin/**") // 示例:匹配 /api/** 和 /admin/**         );         // 或者只匹配一個模式:         // RequestMatcher jwtFilterMatcher = ApiUrlRequestMatcher.createSingleApiMatcher("/api/**");           // 2. 創(chuàng)建CustomJwtAuthenticationFilter實(shí)例         // 注意:這里需要確保CustomJwtAuthenticationFilter能獲取到AuthenticationManager         // 可以通過setAuthenticationManager()方法設(shè)置,或者在構(gòu)造函數(shù)中傳入         CustomJwtAuthenticationFilter customJwtAuthenticationFilter = new CustomJwtAuthenticationFilter(jwtFilterMatcher);         customJwtAuthenticationFilter.setAuthenticationManager(authenticationManagerBean()); // 注入AuthenticationManager           http.csrf().disable() // 禁用CSRF,因?yàn)镴WT是無狀態(tài)的             .authorizeRequests()                 // 確保這些路徑需要認(rèn)證,以便JWT過濾器能發(fā)揮作用                 .antMatchers("/api/**", "/admin/**").authenticated() // 這些路徑需要認(rèn)證                 .antMatchers("/users").authenticated() // 示例:/users也需要認(rèn)證,但可能不是通過JWT過濾器                 .anyRequest().permitAll() // 其他所有請求都允許訪問,無需認(rèn)證             .and()                 // 配置會話管理為無狀態(tài),適用于JWT                 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)             .and()                 // 配置異常處理,如認(rèn)證入口點(diǎn)和拒絕訪問頁面                 // .exceptionHandling()                 //     .authenticationEntryPoint(jwtAuthenticationEntryPoint)                 //     .AccessDeniedPage("/403")             // .and()                 // 配置表單登錄(如果您的應(yīng)用同時支持表單登錄)                 // 這里的配置與JWT過濾器是并行的,互不影響                 .formLogin()                     .loginPage("/login")                     .defaultSuccessUrl("/users")                     .failureUrl("/login?error=true")                     .permitAll()             .and()                 // 配置登出                 .logout()                     .logoutSuccessUrl("/")                     .permitAll()             .and()                 // 將自定義JWT過濾器添加到Spring Security過濾器鏈中                 // 確保它在UsernamePasswordAuthenticationFilter之前執(zhí)行,                 // 這樣JWT認(rèn)證可以在默認(rèn)的表單登錄認(rèn)證之前進(jìn)行                 .addFilterBefore(customJwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);     }      // 暴露AuthenticationManager為一個Bean,以便CustomJwtAuthenticationFilter可以使用它     @Bean     @Override     public AuthenticationManager authenticationManagerBean() throws Exception {         return super.authenticationManagerBean();     }      // 您可能還需要配置PasswordEncoder和UserDetailsService等     // @Override     // protected void configure(AuthenticationManagerBuilder auth) throws Exception {     //     auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());     // } }

注意事項與最佳實(shí)踐

  1. 過濾器順序: addFilterBefore(customJwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) 是關(guān)鍵。它確保了您的JWT過濾器在Spring Security默認(rèn)的基于用戶名密碼的表單登錄過濾器之前執(zhí)行。對于匹配的請求,JWT認(rèn)證會先嘗試完成。
  2. 授權(quán)配置: 在authorizeRequests()中,antMatchers(“/api/**”).authenticated()(或其他您希望JWT過濾器處理的路徑)是必不可少的。這告訴Spring Security這些路徑需要認(rèn)證。如果請求通過了JWT過濾器并成功認(rèn)證,它將滿足authenticated()的要求。
  3. AuthenticationManager的注入: AbstractAuthenticationProcessingFilter需要一個AuthenticationManager來處理認(rèn)證邏輯。通常,您可以通過重寫authenticationManagerBean()方法將其暴露為一個Bean,然后在您的自定義過濾器中注入或設(shè)置。
  4. 無狀態(tài)會話: 對于JWT認(rèn)證,將sessionCreationPolicy設(shè)置為SessionCreationPolicy.STATELESS至關(guān)重要。這表示您的應(yīng)用不會創(chuàng)建或使用HTTP會話來存儲用戶狀態(tài),所有

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊7 分享