當前位置: 妍妍網 > 碼農

快用 API Key 來保護你的 SpringBoot 介面安全!

2024-02-22碼農

來源:https://www.baeldung.com/spring-boot-api-key-secret

# 1、概述

安全性在REST API開發中扮演著重要的角色。一個不安全的REST API可以直接存取到後台系統中的敏感數據。因此,企業組織需要關註API安全性。

Spring Security 提供了各種機制來保護我們的 REST API。其中之一是 API 金鑰。API 金鑰是客戶端在呼叫 API 呼叫時提供的令牌。

在本教程中,我們將討論如何在Spring Security中實作基於API金鑰的身份驗證。

# 2、REST API Security

Spring Security可以用來保護REST API的安全性。REST API是無狀態的,因此不應該使用會話或cookie。相反,應該使用Basic authentication,API Keys,JWT或OAuth2-based tokens來確保其安全性。

2.1. Basic Authentication

Basic authentication是一種簡單的認證方案。客戶端發送HTTP請求,其中包含Authorization檔頭的值為Basic base64_url編碼的使用者名稱:密碼。Basic authentication僅在HTTPS / SSL等其他安全機制下才被認為是安全的。

2.2. OAuth2

OAuth2是REST API安全的行業標準。它是一種開放的認證和授權標準,允許資源所有者透過存取令牌將授權委托給客戶端,以獲得對私有數據的存取許可權。

2.3. API Keys

一些REST API使用API金鑰進行身份驗證。API金鑰是一個標記,用於向API客戶端標識API,而無需參照實際使用者。標記可以作為查詢字串或在請求頭中發送。

# 3、用API Keys保護REST API

3.1 添加Maven 依賴

讓我們首先在我們的pom.xml中聲明spring-boot-starter-security依賴關系:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>

3.2 建立自訂過濾器(Filter)

實作思路是從請求頭中獲取API Key,然後使用我們的配置檢查秘鑰。在這種情況下,我們需要在Spring Security 配置類中添加一個自訂的Filter。

我們將從實作GenericFilterBean開始。GenericFilterBean是一個基於javax.servlet.Filter介面的簡單Spring實作。

讓我們建立AuthenticationFilter類:

public classAuthenticationFilterextendsGenericFilterBean{@OverridepublicvoiddoFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)throws IOException, ServletException {try { Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception exp) { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE); PrintWriter writer = httpResponse.getWriter(); writer.print(exp.getMessage()); writer.flush(); writer.close(); } filterChain.doFilter(request, response); }}

我們只需要實作doFilter()方法,在這個方法中我們從請求頭中獲取API Key,並將生成的Authentication物件設定到當前的SecurityContext例項中。

然後請求被傳遞給其余的過濾器處理,接著轉發給DispatcherServlet最後到達我們的控制器。

在AuthenticationService類中,實作從Header中獲取API Key並構造Authentication物件,程式碼如下:

public classAuthenticationService{privatestaticfinal String AUTH_TOKEN_HEADER_NAME = "X-API-KEY";privatestaticfinal String AUTH_TOKEN = "Baeldung";publicstatic Authentication getAuthentication(HttpServletRequest request){ String apiKey = request.getHeader(AUTH_TOKEN_HEADER_NAME);if ((apiKey == null) || !apiKey.equals(AUTH_TOKEN)) {thrownew BadCredentialsException("Invalid API Key"); }returnnew ApiKeyAuthentication(apiKey, AuthorityUtils.NO_AUTHORITIES); }}

在這裏,我們檢查請求頭是否包含 API Key,如果為空 或者Key值不等於金鑰,那麽就丟擲一個 BadCredentialsException。如果請求頭包含 API Key,並且驗證透過,則將金鑰添加到安全上下文中,然後呼叫下一個安全過濾器。getAuthentication 方法非常簡單,我們只是比較 API Key 頭部和金鑰是否相等。

為了構建 Authentication 物件,我們必須使用 Spring Security 為了標準身份驗證而構建物件時使用的相同方法。所以,需要擴充套件 AbstractAuthenticationToken 類並手動觸發身份驗證。

3.3. 擴充套件AbstractAuthenticationToken

為了成功地實作我們套用的身份驗證功能,我們需要將傳入的API Key轉換為AbstractAuthenticationToken型別的身份驗證物件。

AbstractAuthenticationToken類實作了Authentication介面,表示一個認證請求的主體和認證資訊。

讓我們建立ApiKeyAuthentication類:

public classApiKeyAuthenticationextendsAbstractAuthenticationToken{privatefinal String apiKey;publicApiKeyAuthentication(String apiKey, Collection<?extends GrantedAuthority> authorities) {super(authorities);this.apiKey = apiKey; setAuthenticated(true); }@Overridepublic Object getCredentials(){returnnull; }@Overridepublic Object getPrincipal(){return apiKey; }}

ApiKeyAuthentication 類是型別為 AbstractAuthenticationToken 的物件,其中包含從 HTTP 請求中獲取的 apiKey 資訊。在構造方法中使用 setAuthenticated(true) 方法。因此,Authentication物件包含 apiKey 和authenticated欄位:

3.4. Security Config

透過建立建一個SecurityFilterChain bean,可以透過編程方式把我們上面編寫的自訂過濾器(Filter)進行註冊。

我們需要在 HttpSecurity 例項上使用 addFilterBefore() 方法在 UsernamePasswordAuthenticationFilter 類之前添加 AuthenticationFilter。

建立SecurityConfig 類:

@Configuration@EnableWebSecuritypublic classSecurityConfig{@Beanpublic SecurityFilterChain filterChain(HttpSecurity http)throws Exception { http.csrf() .disable() .authorizeRequests() .antMatchers("/**") .authenticated() .and() .httpBasic() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter. class);return http.build(); }}

此外註意程式碼中我們吧繪畫策略(session policy)設定為無狀態(STATELESS),因為我們使用的是REST。

3.5. ResourceController

最後,我們建立ResourceController,實作一個Get請求 /home

@RestControllerpublic classResourceController{@GetMapping("/home")public String homeEndpoint() {return"Baeldung !"; }}

3.6. 禁用 Auto-Configuration

@SpringBootApplication(exclude = {SecurityAutoConfiguration. class, UserDetailsServiceAutoConfiguration. class})public classApiKeySecretAuthApplication{publicstaticvoidmain(String[] args){ SpringApplication.run(ApiKeySecretAuthApplication. class, args); }}

# 4. 測試

我們先不提供API Key進行測試

curl --location --request GET 'http://localhost:8080/home'

返回 401 未經授權錯誤。

請求頭中加上API Key後,再次請求

curl --location --request GET 'http://localhost:8080/home' \--header 'X-API-KEY: Baeldung'

請求返回狀態200

程式碼: https://github.com/eugenp/tutorials/tree/master/spring-security-modules/spring-security-web-boot-4

熱門推薦