
有很多關于客戶端的 OAuth的帖子,例如如何連接到Twitter或Facebook之類的服務提供商,但是關于OAuth的帖子卻很少,但是來自服務器端的帖子,更具體地講,如何使用OAuth實施身份驗證機制來保護用戶資源,而不用于訪問它們( 客戶端部分 )。
在本文中,我將討論如何使用Spring Security ( Spring Security OAuth )保護您的資源。 該示例非常簡單,足以了解實現OAuth服務提供商的基礎知識。
我發現這篇文章通過一個簡單的示例說明了OAuth是什么以及它如何工作。 我認為這是使用OAuth的良好起點http://hueniverse.com/2007/10/beginners-guide-to-oauth-part-ii-protocol-workflow/
現在是時候開始編寫我們的服務提供商了。 首先,我將解釋我們的服務提供商將提供什么。
想象您正在開發一個網站(稱為CV ),用戶可以在該網站上注冊,然后可以上傳自己的簡歷 。 現在,我們將把這個網站轉換為服務提供商,在其中OAuth將用于保護資源(注冊用戶的簡歷)。 再次想象一下,有些公司已經與簡歷人員達成協議,當他們發布職位空缺時,用戶將可以直接從簡歷站點上載課程到人力資源部門,而無需通過電子郵件發送或從文檔中復制粘貼。 如您所見,這里是OAuth開始管理CV網站和Company RH網站之間的安全性的地方。
總而言之,我們有一個具有受保護的資源(文檔本身)的履歷服務提供者 ( CV )。 消費者是向用戶提供直接從簡歷中獲取其簡歷的可能性的公司。 因此,當用戶訪問公司職位空缺(在我們的示例中為fooCompany )并想要申請職位時,他只需要授權FooCompany “職位空缺”網站就可以從CV網站下載其簡歷 。
因為我們將使用Spring Security OAuth認證,首先我們要配置Spring Security 用SpringMVC進入CV應用。 這里沒什么特別的:
在web.xml文件中,我們定義了安全過濾器 :
<filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter><filter-mapping><filter-name>springSecurityFilterChain</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
在root-context.xml中,我們定義了受保護的資源和身份驗證管理器。 在這種情況下,使用內存中的方法:
<http auto-config='true'><intercept-url pattern="/**" access="ROLE_USER" />
</http><authentication-manager><authentication-provider><user-service><user name="leonard" password="nimoy" authorities="ROLE_USER" /></user-service></authentication-provider>
</authentication-manager>
下一步,創建一個Spring控制器 ,該控制器返回已登錄用戶的履歷 :
@RequestMapping(value="/cvs", method=RequestMethod.GET)
@ResponseBody
public String loadCV() {StringBuilder cv = new StringBuilder();cv.append("Curriculum Vitae -- Name: ").append(getUserName()).append(" Experience: Java, Spring Security, ...");return cv.toString();
}private String getUserName() {Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();String username;if (principal instanceof UserDetails) {username = ((UserDetails)principal).getUsername();} else {username = principal.toString();}return username;
}
該控制器直接返回String,而不是ModelView對象。 該字符串直接作為HttpServletResponse發送。
現在,我們有一個簡單的網站,可以返回已登錄用戶的簡歷 。 如果您嘗試訪問/ cvs資源,如果您未通過身份驗證, Spring Security將顯示一個登錄頁面,并且如果您已經登錄,則將返回您的工作經驗。 與使用Spring Security的任何其他網站一樣工作。
下一步是修改此項目,以允許外部站點可以使用OAuth 2身份驗證協議訪問受保護的資源。
在root-context.xml中:
<beans:bean id="tokenServices"class="org.springframework.security.oauth2.provider.token.InMemoryOAuth2ProviderTokenServices"><beans:property name="supportRefreshToken" value="true" />
</beans:bean><oauth:provider client-details-service-ref="clientDetails"token-services-ref="tokenServices"><oauth:verification-code user-approval-page="/oauth/confirm_access" />
</oauth:provider><oauth:client-details-service id="clientDetails"><oauth:client clientId="foo" authorizedGrantTypes="authorization_code" />
</oauth:client-details-service>
第一個bean是具有id tokenServices的OAuth2ProviderTokenServices接口實現。 OAuth2ProviderTokenServices接口定義了管理OAuth 2.0令牌所需的操作。 這些令牌應被存儲,以供后續訪問令牌引用。 對于此示例,InMemory存儲就足夠了。
下一個bean是<oauth:provider>。 此標記用于配置OAuth 2.0提供程序機制。 并且在這種情況下,配置了三個參數。 第一個是對定義客戶詳細信息服務的Bean的引用,將在下一段中進行解釋。 第二個是在前面的段落中說明的用于提供令牌的令牌服務,最后一個是將為授權令牌提供服務的URL。 這是通常的Authorize / Denny頁面,服務提供商在該頁面上詢問用戶是否允許消費者(在我們的情況下為fooCompany )訪問受保護的資源(其履歷表 )。
最后一個bean是<oauth:client-details-service>。 在此標簽中,您可以定義您授權哪些客戶端使用先前的身份驗證來訪問受保護的資源。 在這種情況下,由于CV公司已與foo公司達成協議,他們可以連接到其Curitaulum Vitae Service,因此使用id foo定義了一個客戶端。
現在,我們使用OAuth配置了應用程序。 最后一步是創建一個控制器,用于接收來自/ oauth / confirm_access URL的請求。
private ClientAuthenticationCache authenticationCache = new DefaultClientAuthenticationCache();
private ClientDetailsService clientDetailsService;@RequestMapping(value="/oauth/confirm_access")
public ModelAndView accessConfirmation(HttpServletRequest request, HttpServletResponse response) {ClientAuthenticationToken clientAuth = getAuthenticationCache().getAuthentication(request, response);if (clientAuth == null) {throw new IllegalStateException("No client authentication request to authorize.");}ClientDetails client = getClientDetailsService().loadClientByClientId(clientAuth.getClientId());TreeMap<String, Object> model = new TreeMap<String, Object>();model.put("auth_request", clientAuth);model.put("client", client);return new ModelAndView("access_confirmation", model);
}
該控制器返回帶有客戶信息的ModelAndView對象,應顯示哪個頁面以授予對受保護資源的權限。 這個JSP頁面稱為access_confirmation.jsp ,最重要的部分是:
<div id="content"><% if (session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null && !(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) instanceof UnapprovedClientAuthenticationException)) { %><div class="error"><p>Access could not be granted. (<%= ((AuthenticationException) session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).getMessage() %>)</p></div><% } %><c:remove scope="session" var="SPRING_SECURITY_LAST_EXCEPTION"/><authz:authorize ifAllGranted="ROLE_USER"><h2>Please Confirm</h2><p>You hereby authorize <c:out value="${client.clientId}"/> to access your protected resources.</p><form id="confirmationForm" name="confirmationForm" action="<%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%>" method="post"><input name="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%>" value="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%>" type="hidden"/><label><input name="authorize" value="Authorize" type="submit"/></label></form><form id="denialForm" name="denialForm" action="<%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%>" method="post"><input name="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%>" value="not_<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%>" type="hidden"/><label><input name="deny" value="Deny" type="submit"/></label></form></authz:authorize></div>
如您所見, Spring Security OAuth提供了用于創建確認表單和拒絕表單的幫助程序類。 提交結果后,將調用URL / cv / oauth / user / authorize (內部管理), OAuth會根據用戶選擇的選項來決定是否將受保護的資源(由loadCV ()方法返回的字符串)返回給調用者。
這就是使用Spring Security OAuth創建OAuth 2系統的全部內容。 但是我想您想知道如何對其進行測試,因此以同樣的價格,我還將解釋如何使用Spring Security OAuth編寫客戶端部分(Consumer)。
客戶端應用程序(稱為fooCompany )也是具有Spring Security的SpringMVC Web應用程序。
Spring Security部分在這里將被忽略。
客戶端應用程序包含一個主頁( home.jsp ),該主頁具有指向Spring Controller的鏈接,該鏈接將負責從CV站點下載Curriculum Vitae ,并將內容重定向到視圖( show.jsp )。
@RequestMapping(value="/cv")
public ModelAndView getCV() {String cv = cvService.getCVContent();Map<String, String> params = new HashMap<String, String>();params.put("cv", cv);ModelAndView modelAndView = new ModelAndView("show", params);return modelAndView;}
如您所見,它是一個簡單的Controller,它調用了Curitaulum Vitae服務。 此服務將負責連接到簡歷網站,并下載所需的簡歷 。 當然,它也處理OAuth通信協議。
服務外觀:
public String getCVContent() {byte[] content = (getCvRestTemplate().getForObject(URI.create(cvURL), byte[].class));return new String(content);
}
建議的訪問這些資源的方法是使用Rest。 為此, Spring Security OAuth提供了RestTemplate的擴展,用于處理OAuth協議。 此類( OAuth2RestTemplate )管理與所需資源的連接,還管理令牌, OAuth授權協議等。
OAuth2RestTemplate被注入到CVService中,并被配置到root-context.xml中:
<oauth:client token-services-ref="oauth2TokenServices" /><beans:bean id="oauth2TokenServices"class="org.springframework.security.oauth2.consumer.token.InMemoryOAuth2ClientTokenServices" /><oauth:resource id="cv" type="authorization_code"clientId="foo" accessTokenUri="http://localhost:8080/cv/oauth/authorize"userAuthorizationUri="http://localhost:8080/cv/oauth/user/authorize" /><beans:bean id="cvService" class="org.springsource.oauth.CVServiceImpl"><beans:property name="cvURL" value="http://localhost:8080/cv/cvs"></beans:property><beans:property name="cvRestTemplate"><beans:bean class="org.springframework.security.oauth2.consumer.OAuth2RestTemplate"><beans:constructor-arg ref="cv"/></beans:bean>
</beans:property>
<beans:property name="tokenServices" ref="oauth2TokenServices"></beans:property>
</beans:bean>
看到OAuth2RestTemplate是使用OAuth資源創建的,其中包含有關授權訪問受保護資源的連接位置的所有信息,并且在本例中是CV網站,請參見我們引用的是外部網站,盡管在此示例中,我們使用的是localhost。 還設置了服務提供者URL(http:// localhost:8080 / cvs / cv),因此RestTemplate可以建立與內容提供者的連接,并在授權過程成功結束的情況下,檢索請求的信息。
<oauth:resource>定義OAuth資源,在這種情況下,定義為客戶端的名稱(請記住,此值是在服務器端客戶端詳細信息標簽中配置的,用于授予對OAuth協議的訪問權限)。 還定義了userAuthorizationUri 。 這是URI到用戶是否曾經需要授權訪問的資源(這是Spring Security中的OAuth管理內部URI)的用戶將被重定向。 最后是accessTokenUri ,它是提供訪問令牌(也是內部URI)的URI OAuth提供者端點。
使用Spring Security OAuth創建使用者也很簡單。
現在,我將解釋當用戶希望授予foo公司以獲取其履歷的訪問權時發生的事件的順序。
首先,用戶連接到foo網站,然后單擊發布履歷鏈接。 然后調用控制器的getCV方法。 此方法調用cvService ,同時使用OAuth2RestTemplate創建與資源URI(CV)的 連接 。 這個類作為一個黑盒子 ,從客戶端,你不知道到底是什么這個類會做的,但它返回你的簡歷存儲在簡歷的網站。 可以想象,該類管理與OAuth有關的所有工作流程,例如管理令牌,執行所需的URL重定向以獲取權限等……并且,如果所有步驟都成功執行,則CV站點中存儲的Curitaulum Vitae將發送到foo公司站點。
這就是所有允許您的網站使用OAuth2授權協議充當服務提供商的步驟。 感謝Spring Security的人們,一開始您可能會想起來容易得多。
希望你覺得它有用。
下載ServerSide(CV)
下載ClientSide(fooCompany)
參考:來自我們JCG合作伙伴的 帶有Spring Security的OAuth ? 在一個罐子統治他們所有博客的亞歷克斯·索托。
翻譯自: https://www.javacodegeeks.com/2012/02/oauth-with-spring-security.html