[英]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
過濾器中的基本流程是這樣的
獲取Authorization
標頭。 如果它不存在,則拋出AuthenticationException
。 在這種情況下, AuthenticationExceptionMapper
將發送標頭"WWW-Authenticate", "Basic realm=\\"" + e.getRealm() + "\\"
,它是 Basic Auth 協議的一部分
獲得標頭后,我們對其進行解析以獲取 Base64 編碼的用戶名:密碼。 然后我們對其進行解碼,然后拆分,然后將用戶名和密碼分開。 如果此過程中的任何一個失敗,則再次拋出映射到 400 Bad Request 的WebApplicationException
。
檢查用戶名和密碼。 示例源代碼只檢查用戶名是否為user
密碼是否為password
,但您需要在過濾器中使用某些服務來驗證此信息。 如果其中任何一個失敗,則拋出AuthenticationException
如果一切順利, 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.