javascript
使用Spring Security对RESTful服务进行身份验证
1.概述
本文重點介紹如何針對提供安全服務的安全REST API進行身份驗證 -主要是RESTful用戶帳戶和身份驗證服務。
2.目標
首先,讓我們看一下參與者-典型的啟用了Spring Security的應用程序需要針對某些事物進行身份驗證-該事物可以是數據庫,LDAP或可以是REST服務。 數據庫是最常見的情況。 但是,RESTful UAA(用戶帳戶和身份驗證)服務也可以正常工作。
就本文而言,REST UAA服務將在/ authentication上公開一個GET操作,該操作將返回 Spring Security執行完整身份驗證過程所需的Principal信息 。
3.客戶
通常,啟用了Spring Security的簡單應用程序將使用簡單的用戶服務作為身份驗證源:
<authentication-manager alias="authenticationManager"><authentication-provider user-service-ref="customUserDetailsService" /> </authentication-manager>這將實現org.springframework.security.core.userdetails.UserDetailsS??ervice并將基于提供的用戶名 返回Principal :
@Component public class CustomUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) { ...} }當客戶端通過RESTful UAA服務進行身份驗證時,僅使用用戶名 已不再足夠 -客戶端現在將身份驗證請求發送到服務時,需要完整的憑據(包括用戶名和密碼) 。 由于服務本身是安全的,因此這很有意義,因此請求本身必須包含身份驗證憑據才能正確處理。
從Spring Security的角度來看,不能在loadUserByUsername內完成此操作,因為此時密碼不再可用-我們需要盡快控制身份驗證過程。
我們可以通過向Spring Security提供完整的身份驗證提供程序來做到這一點:
<authentication-manager alias="authenticationManager"><authentication-provider ref="restAuthenticationProvider" /> </authentication-manager>覆蓋整個身份驗證提供程序使我們有更多的自由來執行從服務中自定義的委托人檢索,但是確實帶來了相當大的復雜性。 標準身份驗證提供程序– DaoAuthenticationProvider –滿足了我們的大部分需求,因此一種好的方法是簡單地擴展它并僅修改必要的內容。
不幸的是,這是不可能的,因為retrieveUser (我們希望擴展的方法)是final 。 這有點不直觀(有一個JIRA討論了這個問題 )–看起來這里的設計意圖僅僅是提供一個不理想的替代實現,但也不是主要問題–我們的RestAuthenticationProvider復制并粘貼了大多數實現DaoAuthenticationProvider并重寫所需的內容–從服務中檢索主體:
@Override protected UserDetails retrieveUser(String name, UsernamePasswordAuthenticationToken auth){String password = auth.getCredentials().toString();UserDetails loadedUser = null;try {ResponseEntity<Principal> authenticationResponse = authenticationApi.authenticate(name, password);if (authenticationResponse.getStatusCode().value() == 401) {return new User("wrongUsername", "wrongPass", Lists.<GrantedAuthority> newArrayList());}Principal principalFromRest = authenticationResponse.getBody();Set<String> privilegesFromRest = Sets.newHashSet(); // fill in the privilegesFromRest from the PrincipalString[] authoritiesAsArray = privilegesFromRest.toArray(new String[privilegesFromRest.size()]);List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(authoritiesAsArray);loadedUser = new User(name, password, true, authorities);} catch (Exception ex) {throw new AuthenticationServiceException(repositoryProblem.getMessage(), ex);}return loadedUser; }讓我們從頭開始 -與REST服務的HTTP通信-這由authenticationApi處理, authenticationApi是提供實際服務的身份驗證操作的簡單API。 可以使用任何支持HTTP的庫來實現操作本身–在這種情況下,實現是使用RestTemplate進行的 :
public ResponseEntity<Principal> authenticate(String username, String pass) {HttpEntity<Principal> entity = new HttpEntity<Principal>(createHeaders(username, pass))return restTemplate.exchange(authenticationUri, HttpMethod.GET, entity, Principal.class); }HttpHeaders createHeaders(String email, String password) {HttpHeaders acceptHeaders = new HttpHeaders() {{set(com.google.common.net.HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON.toString());}};String authorization = username + ":" + password;String basic = new String(Base64.encodeBase64(authorization.getBytes(Charset.forName("US-ASCII"))));acceptHeaders.set("Authorization", "Basic " + basic);return acceptHeaders; }FactoryBean可用于在上下文中設置RestTemplate 。
接下來 ,如果身份驗證請求導致HTTP 401 Unauthorized ,很可能是由于來自客戶端的不正確憑據,則返回具有錯誤憑據的主體,以便Spring Security身份驗證過程可以拒絕它們:
return new User("wrongUsername", "wrongPass", Lists.<GrantedAuthority> newArrayList());最后,Spring Security Principal需要一些權限-特定主體在身份驗證過程后將擁有并在本地使用的特權。 / authenticate操作已經檢索了包括特權的完整主體,因此需要按照Spring Security的要求從請求的結果中提取這些特權并將其轉換為GrantedAuthority對象。
這些特權的存儲方式在這里無關緊要-它們可以存儲為簡單的String或復雜的Role-Privilege結構-但無論細節如何,我們只需要使用它們的名稱來構造GrantedAuthoritiy對象。 創建最終的Spring Security主體后,將其返回到標準身份驗證過程:
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(authoritiesAsArray); loadedUser = new User(name, password, true, authorities);4.測試身份驗證服務
編寫一個在幸福路徑上使用身份驗證REST服務的集成測試非常簡單:
@Test public void whenAuthenticating_then200IsReceived() {// WhenResponseEntity<Principal> response = authenticationRestTemplate.authenticate("admin", "adminPass");// ThenassertThat(response.getStatusCode().value(), is(200)); }完成此簡單測試之后,也可以實施更復雜的集成測試-但這不在本文討論范圍之內。
5.結論
本文介紹了如何針對REST服務進行身份驗證,而不是針對本地系統(如數據庫)進行身份驗證。 有關可以用作身份驗證提供程序的安全RESTful服務的完整實現,請查看github項目 。
翻譯自: https://www.javacodegeeks.com/2012/12/authentication-against-a-restful-service-with-spring-security.html
總結
以上是生活随笔為你收集整理的使用Spring Security对RESTful服务进行身份验证的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux的共享内存如何实现(linux
- 下一篇: 环境影响登记表备案回执(环境影响登记表备