來源: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
Spring Boot 基礎就不介紹了,推薦看這個實戰專案:
https://github.com/javastacks/spring-boot-best-practice
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 class AuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(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 class AuthenticationService {
private static final String AUTH_TOKEN_HEADER_NAME = "X-API-KEY";
private static final String AUTH_TOKEN = "Baeldung";
public static Authentication getAuthentication(HttpServletRequest request) {
String apiKey = request.getHeader(AUTH_TOKEN_HEADER_NAME);
if ((apiKey == null) || !apiKey.equals(AUTH_TOKEN)) {
throw new BadCredentialsException("Invalid API Key");
}
return new 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 class ApiKeyAuthentication extends AbstractAuthenticationToken {
private final String apiKey;
public ApiKeyAuthentication(String apiKey,
Collection<?extends GrantedAuthority> authorities) {
super(authorities);
this.apiKey = apiKey;
setAuthenticated(true);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public 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
@EnableWebSecurity
public class SecurityConfig {
@Bean
public 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
@RestController
public class ResourceController {
@GetMapping("/home")
public String homeEndpoint() {
return "Baeldung !";
}
}
3.6. 禁用 Auto-Configuration
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class})
public class ApiKeySecretAuthApplication {
public static void main(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
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
2.勁爆!Java 協程要來了,,,
3.Spring Boot 2.x 教程,太全了!
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/556914.html
標籤:其他
下一篇:返回列表
