在Spring Boot應(yīng)用程序中集成JWT(JSON Web Token)進(jìn)行認(rèn)證時(shí),一個(gè)常見的需求是只對(duì)特定URL模式的請(qǐng)求應(yīng)用JWT過濾器,而不是所有請(qǐng)求。默認(rèn)情況下,如果直接使用http.addFilterBefore(customJwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class),該自定義過濾器可能會(huì)在所有請(qǐng)求進(jìn)入U(xiǎn)sernamePasswordAuthenticationFilter之前被執(zhí)行,這在某些場(chǎng)景下可能不是最優(yōu)解,例如,對(duì)于公開的API或靜態(tài)資源,我們不希望它們經(jīng)過JWT認(rèn)證邏輯。
為了實(shí)現(xiàn)對(duì)特定URL模式的精確過濾,Spring Security提供了AbstractAuthenticationProcessingFilter抽象類和RequestMatcher接口,它們是解決此類問題的關(guān)鍵。
AbstractAuthenticationProcessingFilter: 這是Spring Security中用于處理特定認(rèn)證請(qǐng)求的抽象基類。它在內(nèi)部持有一個(gè)RequestMatcher實(shí)例,只有當(dāng)請(qǐng)求與該RequestMatcher匹配時(shí),過濾器才會(huì)嘗試進(jìn)行認(rèn)證處理(即調(diào)用attemptAuthentication方法)。這使得我們可以將認(rèn)證邏輯與請(qǐng)求路徑解耦,實(shí)現(xiàn)按需認(rèn)證。
RequestMatcher: 這是一個(gè)核心接口,定義了如何判斷一個(gè)HttpServletRequest是否匹配某種規(guī)則。Spring Security提供了多種內(nèi)置實(shí)現(xiàn),例如:
首先,我們需要修改原有的CustomJwtAuthenticationFilter,使其繼承自AbstractAuthenticationProcessingFilter,并在構(gòu)造函數(shù)中接收一個(gè)RequestMatcher。
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; 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.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class CustomJwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter { // 構(gòu)造函數(shù),接收一個(gè)RequestMatcher,該匹配器定義了哪些請(qǐng)求需要此過濾器處理 public CustomJwtAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher) { super(requiresAuthenticationRequestMatcher); } // 實(shí)際的認(rèn)證邏輯 @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { // 從請(qǐng)求中提取JWT令牌的邏輯 String token = extractJwtFromRequest(request); if (token == null) { // 如果沒有令牌,則拋出認(rèn)證異常,由AuthenticationEntryPoint處理 throw new BadCredentialsException("No JWT token found in request."); } // 假設(shè)JwtAuthenticationToken是一個(gè)自定義的Authentication實(shí)現(xiàn), // 包含了JWT令牌信息,等待AuthenticationManager處理 JwtAuthenticationToken authenticationToken = new JwtAuthenticationToken(token); // 將令牌提交給AuthenticationManager進(jìn)行認(rèn)證 // AuthenticationManager會(huì)找到對(duì)應(yīng)的AuthenticationProvider來驗(yàn)證令牌 return this.getAuthenticationManager().authenticate(authenticationToken); } // 輔助方法:從請(qǐng)求中提取JWT令牌 private String extractJwtFromRequest(HttpServletRequest request) { // 示例:從Authorization頭中提取Bearer Token String bearerToken = request.getHeader("Authorization"); if (bearerToken != null && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); // 移除"Bearer "前綴 } return null; } // 可以選擇性地覆蓋successfulAuthentication和unsuccessfulAuthentication方法 // 來處理認(rèn)證成功或失敗后的邏輯,例如設(shè)置安全上下文或記錄日志。 }
注意:JwtAuthenticationToken 和處理JWT令牌的AuthenticationProvider需要您自行實(shí)現(xiàn)。這里主要關(guān)注過濾器的結(jié)構(gòu)。
為了讓JWT過濾器僅作用于/api/**路徑,我們可以使用AntPathRequestMatcher。
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; // ... // 在您的Security配置類中或單獨(dú)定義 RequestMatcher apiPathsMatcher = new AntPathRequestMatcher("/api/**");
最后,在您的Spring Security配置類(通常是繼承WebSecurityConfigurerAdapter的類)中,將這個(gè)定制的JWT過濾器添加到過濾器鏈中。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.AuthenticationEntryPoint; // 假設(shè)您有自定義的認(rèn)證入口點(diǎn) @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private final UserDetailsService userDetailsService; // 假設(shè)您有UserDetailsService private final AuthenticationEntryPoint jwtAuthenticationEntryPoint; // 假設(shè)您有JWT認(rèn)證入口點(diǎn) public SecurityConfig(UserDetailsService userDetailsService, AuthenticationEntryPoint jwtAuthenticationEntryPoint) { this.userDetailsService = userDetailsService; this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 配置您的AuthenticationManager,例如使用UserDetailsService和密碼編碼器 auth.userDetailsService(userDetailsService); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { // 暴露AuthenticationManager為Bean,供CustomJwtAuthenticationFilter使用 return super.authenticationManagerBean(); } // 定義CustomJwtAuthenticationFilter為Bean @Bean public CustomJwtAuthenticationFilter customJwtAuthenticationFilter() throws Exception { // 創(chuàng)建一個(gè)匹配器,指定只有/api/**路徑的請(qǐng)求才會(huì)被此JWT過濾器處理 AntPathRequestMatcher apiMatcher = new AntPathRequestMatcher("/api/**"); CustomJwtAuthenticationFilter filter = new CustomJwtAuthenticationFilter(apiMatcher); // 必須設(shè)置AuthenticationManager,因?yàn)锳bstractAuthenticationProcessingFilter需要它來執(zhí)行認(rèn)證 filter.setAuthenticationManager(authenticationManagerBean()); // 可以選擇性設(shè)置認(rèn)證成功/失敗處理器 // filter.setAuthenticationSuccessHandler(...) // filter.setAuthenticationFailureHandler(...) return filter; } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() // 禁用CSRF,因?yàn)镴WT通常是無狀態(tài)的 .authorizeRequests() // 確保/api/**路徑需要認(rèn)證。當(dāng)請(qǐng)求到達(dá)這些路徑時(shí),如果尚未認(rèn)證, // customJwtAuthenticationFilter會(huì)嘗試處理 .antMatchers("/api/**").authenticated() // 其他路徑可以設(shè)置為permitAll()或根據(jù)需求配置 .antMatchers("/users", "/login", "/").permitAll() .anyRequest().authenticated() // 任何其他未匹配的請(qǐng)求也需要認(rèn)證 .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // JWT是無狀態(tài)的 .and() .exceptionHandling() .authenticationEntryPoint(jwtAuthenticationEntryPoint) // 未認(rèn)證或認(rèn)證失敗的入口點(diǎn) .accessDeniedPage("/403") // 訪問被拒絕的頁面 .and() // 將自定義的JWT過濾器添加到UsernamePasswordAuthenticationFilter之前 // CustomJwtAuthenticationFilter現(xiàn)在只處理/api/**路徑 .addFilterBefore(customJwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } }
在上述配置中,antMatchers("/api/**").authenticated() 確保了所有/api/**路徑的請(qǐng)求都需要認(rèn)證。當(dāng)請(qǐng)求匹配/api/**時(shí),customJwtAuthenticationFilter會(huì)嘗試提取并驗(yàn)證JWT令牌。如果令牌有效,請(qǐng)求將繼續(xù)處理;否則,jwtAuthenticationEntryPoint將介入處理認(rèn)證失敗。對(duì)于其他未匹配/api/**的路徑,customJwtAuthenticationFilter根本不會(huì)被觸發(fā),從而實(shí)現(xiàn)了精確的過濾。
通過繼承AbstractAuthenticationProcessingFilter并利用RequestMatcher,我們可以為Spring Boot Security中的JWT認(rèn)證過濾器實(shí)現(xiàn)精準(zhǔn)的URL模式匹配。這種方法不僅提高了應(yīng)用程序的安全性,因?yàn)樗辉诒匾獣r(shí)才執(zhí)行認(rèn)證邏輯,同時(shí)也優(yōu)化了性能,避免了不必要的處理開銷。掌握這種技術(shù),能夠幫助開發(fā)者構(gòu)建更加健壯和高效的Spring Security認(rèn)證體系。
以上就是Spring Boot Security:為特定URL模式定制JWT認(rèn)證過濾器的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!
每個(gè)人都需要一臺(tái)速度更快、更穩(wěn)定的 PC。隨著時(shí)間的推移,垃圾文件、舊注冊(cè)表數(shù)據(jù)和不必要的后臺(tái)進(jìn)程會(huì)占用資源并降低性能。幸運(yùn)的是,許多工具可以讓 Windows 保持平穩(wěn)運(yùn)行。
微信掃碼
關(guān)注PHP中文網(wǎng)服務(wù)號(hào)
QQ掃碼
加入技術(shù)交流群
Copyright 2014-2025 http://m.miracleart.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號(hào)