簡體   English   中英

保護澤西島的 REST 服務

[英]Securing REST services in Jersey

我對網絡服務非常陌生。 我已經使用 Jersey 2 與 Spring 集成公開了一些 REST 服務。 現在我需要使用用戶名/密碼進行身份驗證來保護這些休息服務。 我被告知不要使用 Spring Security。

我不知道如何做到這一點。 我確實在網上搜索過,但各種鏈接顯示了各種實現,我無法決定如何進行。

使用用戶名和密碼進行身份驗證的常用方法是使用基本身份驗證 基本上客戶端需要發送一個請求頭Authorization ,頭值為Basic Base64Encoded(username:password) 所以我的用戶名是peeskillet和我的密碼是pass ,作為客戶,我應該將標題設置為

Authorization: Basic cGVlc2tpbGxldDpwYXNz

在 servlet 環境中,容器應該支持基本身份驗證。 您將在 web.xml 上配置此支持。 您可以在 Java EE 教程的48.2 保護 Web 應用程序中看到一個示例。 您還會在示例中注意到

<transport-guarantee>CONFIDENTIAL</transport-guarantee>

那是為了 SSL 支持。 推薦用於基本身份驗證。

如果您不想處理使用安全域和登錄模塊、領域等的麻煩,則需要自定義 servlet 支持,或者如果您只是不在 servlet 環境中,則實現基本身份驗證在ContainerRequestFilter中真的不是太難。

你可以在jersey/examples/https-clientserver-grizzly看到一個完整的例子,說明如何做到這一點。 您應該關注SecurityFilter

過濾器中的基本流程是這樣的

  1. 獲取Authorization標頭。 如果它不存在,則拋出AuthenticationException 在這種情況下, AuthenticationExceptionMapper將發送標頭"WWW-Authenticate", "Basic realm=\\"" + e.getRealm() + "\\" ,它是 Basic Auth 協議的一部分

  2. 獲得標頭后,我們對其進行解析以獲取 Base64 編碼的用戶名:密碼。 然后我們對其進行解碼,然后拆分,然后將用戶名和密碼分開。 如果此過程中的任何一個失敗,則再次拋出映射到 400 Bad Request 的WebApplicationException

  3. 檢查用戶名和密碼。 示例源代碼只檢查用戶名是否為user密碼是否為password ,但您需要在過濾器中使用某些服務來驗證此信息。 如果其中任何一個失敗,則拋出AuthenticationException

  4. 如果一切順利, User將從authenticate方法創建,並注入到Authorizer (這是一個SecurityContext )。 在 JAX-RS 中, SecurityContext通常用於授權。

對於授權,如果您想保護某些資源的某些區域,您可以為您的類或方法使用@RolesAllowed批注。 Jersey 通過注冊RolesAllowedDynamicFeature支持此注釋。

幕后發生的事情是將從請求中獲取SecurityContext 通過我鏈接的示例,您可以看到Authorizer ,它有一個重寫的方法isUserInRole 將調用此方法以檢查@RolesAllowed({"ADMIN"}) 因此,當您創建SecurityContext ,您應該確保在重寫的方法中包含用戶的角色。

對於測試,您可以簡單地使用瀏覽器。 如果一切設置正確,當你嘗試訪問該資源,你應該看到(在Firefox)對話框中看到這個帖子 如果你使用cURL ,你可以做

C:/>curl -v -u username:password http://localhost:8080/blah/resource

這將發出基本身份驗證請求。 由於-v開關,您應該看到所有涉及的標頭。 如果您只想使用客戶端 API 進行測試,您可以在此處查看如何設置它。 上述三種情況中的任何一種,都會為您完成Base64編碼,因此您不必擔心。

至於 SSL,您應該查看容器的文檔以獲取有關如何設置它的信息。

所以這真的是你想要實現的問題。 我的情況是讓這個東西用移動設備和單頁應用程序 JavaScript 運行。

基本上,您需要做的就是生成某種標頭,該值將在您的客戶端發出的每個連續請求中都需要。

所以你做了一個端點,你在其中等待帶有用戶/密碼的帖子:

@Path("/login")
public class AuthenticationResource {

@POST
@Consumes("application/json")
public Response authenticate(Credentials credential) {
    boolean canBeLoggedIn = (...check in your DB or anywher you need to)

    if (canBeLoggedIn) {
        UUID uuid = UUID.randomUUID();
        Token token = new Token();
        token.setToken(uuid.toString());
        //save your token with associated with user
        (...)

        return Response.ok(token).type(MediaType.APPLICATION_JSON_TYPE).build();
    } else {
        return Response.status(Response.Status.UNAUTHORIZED).build();
    }
}
}

現在您需要保護需要該令牌的資源:

   @Path("/payment")
   @AuthorizedWithToken
   public class Payments {

    @GET
    @Produces("application/json")
    public Response sync() {
     (...)
    }

    }

注意@AuthorizedWithToken注釋。 您可以使用特殊元注釋@NameBinding自行創建此注釋

@NameBinding
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthorizedWithToken {}

現在對於實現標頭檢查的過濾器:

@AuthorizedWithToken
@Provider
public class XAuthTokenFilter implements ContainerRequestFilter {

    private static String X_Auth_Token = "X-Auth-Token";

    @Override
    public void filter(ContainerRequestContext crc) throws IOException {
        String headerValue = crc.getHeaderString(X_Auth_Token);
        if (headerValue == null) {
            crc.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Missing " + X_Auth_Token + " value").build());
            return;
        }

        if(! TOKEN_FOUND_IN_DB) {
            crc.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Wrong " + X_Auth_Token + " value").build());
            return;
        }
    }
}

您可以創建任意數量的自己的注釋來檢查 http 請求中的各種內容並將它們混合。 但是,您需要注意優先級,但這實際上很容易找到。 此方法需要使用https但這很明顯。

安全性有兩種主要形式:

  • 基於容器
  • 基於應用

保護 Spring 應用程序的標准方法是使用 Spring Security(以前稱為 Acegi)。 知道為什么不允許您使用它會很有趣。

您可以使用基於容器的安全性,但我猜您對 spring 的使用也排除了該選項。 由於選擇 Spring 通常是為了避免使用完整的 J2EE 容器(編輯:盡管正如下面其他人指出的,大多數普通 servlet 容器確實允許您實現各種基於容器的安全方法)

這實際上只為您提供了一種選擇,那就是推出您自己的安全性。

您對 Jersey 的使用表明這可能是一個 REST 應用程序。 在這種情況下,您真的應該堅持使用標准的 HTTP 身份驗證方法,這些方法以強度相反的順序具有以下風格:

  • 基本的
  • 消化
  • 形式
  • 證書

REST 應用程序通常應該是“無狀態的”,這基本上排除了基於表單的身份驗證(因為您需要使用 Session),而留給您的是 BASIC、Digest 和 Certificate。

你的下一個問題是,我在驗證誰。 如果您可以期望根據他們請求的 URL 知道用戶的用戶名和密碼(假設它是所有用戶的一組憑據),那么 Digest 是最好的選擇,因為從不發送密碼,只有一個散列。 如果您不知道密碼(因為您要求第三方系統對其進行驗證等),那么您就無法使用 BASIC。 但是您可以通過使用 SSL 來增強 BASIC 的安全性,或者更好的是,將 BASIC 與客戶端證書身份驗證相結合。 事實上,基於 HTTPS 的 BASIC 身份驗證是保護大多數 REST 應用程序的標准技術。

您可以輕松實現一個 Servlet 過濾器,它會查找身份驗證標頭並自行驗證憑據。 有很多此類過濾器的示例,它是一個獨立的類文件。 如果未找到憑據,過濾器將返回 401,並在響應標頭中傳遞基本身份驗證提示。 如果憑據無效,您將返回 403。應用程序安全本身幾乎是整個職業,但我希望這會有所幫助。

正如前幾篇文章所說,您可以采用不同的選項,實現不同的開銷。 從實用的角度來看,如果您打算從這里開始並正在尋找一種簡單實現的舒適方式,我建議使用 BASIC 身份驗證的基於容器的選項。

如果使用tomcat,可以設置一個realm ,實現起來比較簡單。 您可以使用 JDBCRealm,它從數據庫中的指定列中獲取用戶和密碼,並通過 server.xml 和 web.xml 對其進行配置。 每次您嘗試訪問您的應用程序時,這都會自動提示您輸入憑據。 您沒有任何應用程序端實現要做。

我現在可以告訴您的是,您已經完成了將 Jersey 與 Spring 集成的大部分“臟”工作。 我建議您使用基於應用程序的解決方案,它不會將您綁定到特定容器。 Spring Security 起初可能令人生畏,但是當您馴服野獸時,您會發現它實際上是一只友好的小狗。

事實是 Spring Security 是非常可定制的,只需實現它們的接口即可。 並且有很多文檔和支持。 另外,您已經有一個基於 Spring 的應用程序。

由於您尋求的只是指導,我可以為您提供一些教程。 你可以利用這個博客。

http://www.baeldung.com/rest-with-spring-series/ http://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security- 3-1-part-3/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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