盡管有許多博客文章詳細介紹了如何使用Spring Security,但是當問題域位于標準LDAP或數據庫身份驗證之外時,我仍然經常發現配置挑戰。 在本文中,我將介紹一些針對Spring Security的簡單自定義,使其能夠與基于REST的API調用一起使用。 具體來說,用例是您擁有一個API服務,該服務將返回包含SHA-256密碼哈希的用戶對象。
設定
運行此樣本的先決條件是Git和Maven,以及您選擇的IDE(已通過Eclipse和IntelliJ進行了測試)。
可以在以下位置找到源代碼: https : //github.com/dajevu/Spring3SecurityUsingAPI 。 拉下代碼后,執行以下步驟:
- 在終端窗口中,cd到位于源代碼所在根目錄下的Shared目錄。
- 發出命令mvn clean install。 這將構建Shared子項目,并將jar安裝到本地mvn存儲庫中。
- 在Eclipse或IntelliJ中,將項目導入為Maven項目。 在Eclipse中,這將導致創建3個項目:Shared,SpringWebApp和RestfulAPI。 在IntelliJ中,這將表示為子項目。 編譯過程完成后,不應有任何錯誤。
- 將目錄更改為RestfulAPI。 然后,發出命令mvn jetty:run以運行API webapp。 然后,您可以發出以下URL,該URL將帶回以JSON表示的User對象:http:// localhost:9090 / RestfulAPI / api / v1 / user / john
- 打開一個新的終端窗口,cd到位于項目根目錄下的SpringWebApp目錄。 發出命令mvn jetty:run。 這將啟動一個包含Spring Security的標準Spring Webapp。 您可以在以下位置訪問單個HTML頁面:http:// localhost:8080 / SpringWebApp /。 單擊“登錄”鏈接后,使用用戶名john和密碼doe登錄。 您應該被重定向到Hello Admin頁面。
為了演示該解決方案,使用了三個Maven模塊,如下所示:

- SpringWebApp 。 這是一個典型的Spring Webapp,僅提供一個JSP頁面。 頁面的內容將取決于用戶當前是否登錄。 首次訪問該頁面時,將顯示一個Login鏈接,該鏈接將其定向到內置的Spring Security登錄表單。 當他們嘗試登錄時, R ESTEasy客戶端用于調用API服務(如下所述),該服務返回一個JSON字符串,該字符串通過RESTEasy客戶端轉換為Java對象。 以下各節討論了如何配置Spring Security的詳細信息。
- RestfulAPI 。 提供JSON請求的API服務。 它是使用RESTEasy(JAX-RS實現)配置的,下一節將對其進行詳細描述。
- 共享的 。 它包含其他兩個項目之間共享的一些Java類。 具體來說,用戶對象DTO和RESTEasy代理定義(由于RESTEasy客戶端也可以使用它,因此已共享)。
RestfulAPI解剖
API Webapp是使用RESTEasy的Spring實現配置的。 RESTEasy文檔非常詳盡,因此我將不對其設置進行詳細說明。 定義了一個API調用(在Shared項目的UserProxy中),該調用返回靜態JSON字符串。 API的代理(或接口)定義如下:
Resteasy API代理
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path(UserProxy.Urls.BASE_URL)
public interface UserProxy {public interface Urls {public static final String BASE_URL = "/api/v1";public static final String USER = "/user/{username}";}@GET@Produces( { MediaType.APPLICATION_JSON })@Path(UserProxy.Urls.USER)public User getUserByUsername(@PathParam("username") String username);
}
對于那些熟悉JAX-RS的人來說,您將可以輕松地遵循此配置。 它定義了一個API URI,它將響應發送到/ api / v1 / user / {username}的URL路徑的請求,其中{username}被替換為實際的用戶名值。 該服務的實現僅返回一個靜態響應,如下所示:
關于唯一復雜的事情是使用用戶密碼的SHA-256哈希。 很快我們將看到Spring Security如何解釋這一點。 訪問URL時,將返回以下JSON字符串:
該Webapp的web.xml包含用于服務于RESTEasy請求的設置配置,因此,如果您好奇的話,請看一下。
SpringWebApp解剖
現在我們可以看一下Spring Security的配置。 項目的web.xml文件將其配置為Spring應用程序,并將文件applicationContext-security.xml指定為初始Spring配置文件。 讓我們仔細看一下這個文件,因為這是大多數魔術發生的地方:
讓我們遍歷每個行號以描述其功能。 第3至5行指示Spring在com.acme目錄中查找Spring支持的類,并且將支持Spring批注。 第7行用于加載application.properties文件中指定的屬性(用于指定API主機)。 第9至11行為該應用程序啟用Spring Security。 通常,作為http的子元素,您將指定使用角色來保護哪些頁面,但是為了使本示例簡單起見,未對其進行配置。
第13-17行是基于Spring Security的自定義開始的地方。 我們通過其bean ref定義了一個名為userDetailsS??rv的自定義身份驗證提供程序。 該bean通過自定義類com.acme.security.UserDetailsS??ervice實現(第19行)。 讓我們仔細看一下這個類:
如您所見,此類實現了Spring接口org.springframework.security.core.userdetails.UserDetailsS??ervice。 這需要覆蓋方法loadUserByUsername。 此方法負責從身份驗證提供者/源中檢索用戶。 返回的用戶(或者,如果找不到匹配的用戶,則拋出UsernameNotFoundException-第28行)必須包含密碼屬性,以便Spring Security與表單中提供的內容進行比較。 如前所述,在這種情況下,密碼以SHA-256哈希值返回。
在我們的API實現中,使用API??Helper類提取用戶查找,我們將在后面介紹。 然后,將返回的API數據填充到名為UserDetails的自定義類中。 這將使用相同的名稱實現Spring接口。 該接口需要getUsername()和getPassword()方法的具體實現。 Spring將在Security的下一個處理步驟中調用這些值,以將這些值與Web表單中記錄的值進行比較。
Spring如何將SHA-256中返回的密碼與表單密碼值進行比較。 如果回頭看一下XML配置,它包含以下設置:
注意passwordEncoder -該參考指向Spring類ShaPasswordEncoder。 此類將計算通過Web表單提供的密碼的SHA-256密碼,然后Spring會將計算出的值與我們通過API返回的值進行比較。
讓我們通過看一下APIHelper類來解決這個問題:
第8和9行的第一件事是注入API.host屬性。 您還記得,這是在application.properties文件中設置的。 這標識了要在其中發布API調用的主機(因為它在本地運行,所以指定了localhost)。 第17至20行使用RESTEasy客戶端機制之一發布JSON RESTful調用(RESTEasy也具有所謂的客戶端代理實現,它更易于使用/代碼更少,但沒有提供太多的低級控制)。 然后,通過第26行的Jackson方式,將來自API的結果響應從JSON轉換為User Java對象。然后將該Java對象返回給UserDetails服務。
總結/總結
如您所見,定制Spring Security以針對API調用(或實際上是任何外部服務)進行身份驗證所涉及的實際工作非常簡單。 只需要實現幾個類,但是嘗試第一次弄清楚這一點可能很棘手。 因此,之所以我包括完整的端到端示例。
參考:來自Jeff's SOA Ruminations博客的JCG合作伙伴 Jeff Davis 使用API??身份驗證的Spring Security 。
翻譯自: https://www.javacodegeeks.com/2012/10/spring-security-using-api-authentication.html