簡體   English   中英

如何為 Spring Boot RESTful Web 服務配置多級身份驗證?

[英]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 保護

在我的示例項目中,我選擇了以下解決方案

  1. 我使用 WebSecurityConfigurerAdapter 來滿足 ApiKey 要求

    .authorizeRequests() .anyRequest().hasAuthority("API_KEY")
  2. 我通過啟用它來使用方法級別的安全性

    @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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM