[英]How to config multiple level authentication for spring boot RESTful web service?
我們正在使用 Spring Boot 構建一個 RESTful web 服務。 我們希望使用 2 級身份驗證來保護端點。
首先,對於每個請求,我們要檢查請求頭中是否有指定的 apiKey,如果沒有,我們將拒絕該請求。 如果請求有 apiKey,我們將使用用戶名/密碼登錄對某些請求進行下一次身份驗證。 有只需要 apiKey 身份驗證的公共端點,以及首先需要 apiKey 身份驗證,然后需要用戶名/密碼身份驗證才能訪問它們的私有端點。
對於apiKey auth,我在這里復制了代碼,我還可以找到很多關於用戶名/密碼認證的例子。
我的問題是:如何在 WebSecurityConfigurerAdapter 中進行 Java 配置以將它們組合在一起。
現在我為這 2 個身份驗證過濾器定義了 2 個擴展 WebSecurityConfigurerAdapter 的配置類,但請求只會通過其中一個,具體取決於我設置為 @Order(1) 的那個。
謝謝。
整個答案得到了一個有效的 Spring Boot 應用程序的支持,並帶有單元測試來確認它。
如果你覺得這個答案有幫助,請給它投票。
簡短的回答是您的安全配置可能如下所示
http
.sessionManagement()
.disable()
//application security
.authorizeRequests()
.anyRequest().hasAuthority("API_KEY")
.and()
.addFilterBefore(new ApiKeyFilter(), HeaderWriterFilter.class)
.addFilterAfter(new UserCredentialsFilter(), ApiKeyFilter.class)
.csrf().ignoringAntMatchers(
"/api-key-only",
"/dual-auth"
)
;
// @formatter:on
}
}
讓我告訴你發生了什么。 我鼓勵您查看我的示例,特別是涵蓋您的許多場景的單元測試。
我們有兩個安全級別 1. 每個 API 都必須由 ApiKey 保護 2. 只有一些 API 必須由 UserCredentials 保護
在我的示例項目中,我選擇了以下解決方案
我使用 WebSecurityConfigurerAdapter 來滿足 ApiKey 要求
.authorizeRequests() .anyRequest().hasAuthority("API_KEY")
我通過啟用它來使用方法級別的安全性
@EnableGlobalMethodSecurity(prePostEnabled = true)
然后在我的控制器中要求它
@PreAuthorize("hasAuthority('USER_CREDENTIALS')")
public String twoLayersOfAuth() {
//only logic here
}
ApiKey 過濾器超級簡單
public class ApiKeyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String authorization = request.getHeader("Authorization");
final String prefix = "ApiKey ";
if (hasText(authorization) && authorization.startsWith(prefix)) {
String key = authorization.substring(prefix.length());
if ("this-is-a-valid-key".equals(key)) {
RestAuthentication<SimpleGrantedAuthority> authentication = new RestAuthentication<>(
key,
Collections.singletonList(new SimpleGrantedAuthority("API_KEY"))
);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
和第二層身份驗證甚至簡單(它依賴於第一層執行)
public class UserCredentialsFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String userCredentials = request.getHeader("X-User-Credentials");
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if ("valid-user".equals(userCredentials) && authentication instanceof RestAuthentication) {
RestAuthentication<SimpleGrantedAuthority> restAuthentication =
(RestAuthentication<SimpleGrantedAuthority>)authentication;
restAuthentication.addAuthority(new SimpleGrantedAuthority("USER_CREDENTIALS"));
}
filterChain.doFilter(request, response);
}
}
請注意:當沒有認證或認證不足時,每個過濾器如何不關心會發生什么。 這一切都為您服務。 您的過濾器只需要驗證正確的數據;
Spring、Spring Boot 和 Spring Security 有一些一流的測試設施。
我可以調用具有兩個安全級別的 api-only 端點
mvc.perform(
post("/api-key-only")
.header("Authorization", "ApiKey this-is-a-valid-key")
.header("X-User-Credentials", "valid-user")
)
.andExpect(status().isOk())
.andExpect(authenticated()
.withAuthorities(
asList(
new SimpleGrantedAuthority("API_KEY"),
new SimpleGrantedAuthority("USER_CREDENTIALS")
)
)
)
.andExpect(content().string("API KEY ONLY"))
;
否則我可以通過第一級安全並被第二級拒絕
mvc.perform(
post("/dual-auth")
.header("Authorization", "ApiKey this-is-a-valid-key")
)
.andExpect(status().is4xxClientError())
.andExpect(authenticated()
.withAuthorities(
asList(
new SimpleGrantedAuthority("API_KEY")
)
)
)
;
當然,我們總是有幸福的道路
mvc.perform(
post("/dual-auth")
.header("Authorization", "ApiKey this-is-a-valid-key")
.header("X-User-Credentials", "valid-user")
)
.andExpect(status().isOk())
.andExpect(content().string("DUAL AUTH"))
.andExpect(authenticated()
.withAuthorities(
asList(
new SimpleGrantedAuthority("API_KEY"),
new SimpleGrantedAuthority("USER_CREDENTIALS")
)
)
)
;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.