我看過一些有關Spring Security 3 Ajax登錄的博客,但是我找不到解決如何調用基于Ajax的登錄的博客,匿名用戶正在Ajax中訪問受保護的資源。
問題 – Web應用程序允許匿名訪問某些部分,并且某些部分是受保護的資源,需要用戶登錄。
當匿名用戶(通過Http Get / Post)訪問受保護的資源時,Spring Security會自動調用登錄頁面,并在成功通過身份驗證后重定向到所需的資源/頁面。
但是,如果正在Ajax中訪問受保護的資源,則登錄頁面將無法正確顯示(將在頁面的一部分上進行設置)。 302代碼(重定向到登錄頁面)將無法在Ajax中正常運行。
請注意,這與啟動Ajax登錄屏幕不同(例如,當用戶按下登錄按鈕并調用帶有用戶/密碼字段的彈出窗口時)。
那么–我們如何讓Spring Security 3通過“常規” HTTP Post(基于FORM的身份驗證)和Ajax調用來處理對受保護資源的訪問,包括在成功身份驗證后重定向到所需資源?
因此,此博客文章包含兩個保護層/部分:
1. Spring Security 3標準基于FORM的身份驗證
2.配置/擴展Spring Security 3.并且該應用程序還支持Ajax對受保護資源的訪問。
關于第1部分-有關此問題的參考很多。 無需詳細說明。
關于第2部分–要求以下內容:
1.配置Spring Security 3以啟用基于Ajax的登錄。
2.將客戶端Ajax調用配置為受保護的資源,以處理身份驗證請求。
3.重新執行功能以模擬成功登錄后自動進行用戶原始方法的調用(這在基于FORM的登錄中發生)
下圖描述了詳細的流程,應有助于遵循客戶端/服務器通信。
![]() |
通過Ajax處理受保護的資源訪問 |
讓我們討論一下圖:
該流程始于對受保護資源(1)的匿名用戶Ajax請求。 在這種情況下,用戶希望將商品添加到購物車。
addItem方法是受保護的資源,它通過Spring Security(@pre_authorize(“ SOME_ROLE”))(2)受保護。 這使Spring Secutiry過濾器(3)發送帶有HTTP代碼302的登錄表單(即,重定向到該頁面)。
現在,由于這是一個Ajax調用,它將無法很好地處理請求,因此這里涉及到了登錄表單,將其放在一邊,然后調用基于Ajax的登錄(4):
客戶端Ajax方法(調用了Ajax addItem方法)檢查??它是基于表單的登錄名還是其他任何答復。 如果是基于FORM的登錄,它將調用一個對話框模式(5),該模式將嘗試登錄Ajax。 Spring將處理Ajax登錄認證(6)并將適當的消息返回給客戶端。 如果消息成功,則客戶端將重新執行原始功能,該功能試圖訪問受保護的資源(例如,本例中的addItem )。
讓我們看看它們如何適合我們的代碼:
步驟#1,#4 –客戶端訪問受保護的資源并檢查是否需要登錄
//JavaScript method - Ajax call to protected resource (#1 in flow diagram)
function addItem(itemId) { $.ajax({url: '/my_url/order/addItem',type: 'POST',data: ({orderItemId : itemId,...}), success: function(data) {//construct a callback string if user is not logged in.var cllbck = 'addItem('+itemId +')';//Client check if login required//(#4 in flow diagram)if (verifyAuthentication(data,cllbck)){// in here => access to protected resource was ok// show message to user, "item has been added..."}});}
步驟#2,#3 –是常規的Spring Security配置。 的大量資源 了 那里 。
步驟#4 –客戶端檢查是否需要登錄:
function verifyAuthentication(data, cllBackString){//naive check - I put a string in the login form, so I check for existanceif (isNaN(data) && (data.indexOf("login_hidden_for_ajax")!= -1)){//if got here then data is a loginform => login required//set callback in ajax login form hidden input $("#my_callback").val(cllBackString); //show ajax login//Get the window height and widthvar winH = $(window).height();var winW = $(window).width();//Set the popup window to center$("#ajaxLogin").css('top', winH/2-$("#ajaxLogin").height()/2);$("#ajaxLogin").css('left', winW/2-$("#ajaxLogin").width()/2);$("#ajaxLogin").fadeIn(2000); return false;} // data is not a login form => return true to continue with function processingreturn true;
}
步驟#5,#7 – Ajax登錄表單使用以下Ajax登錄:
function ajaxLogin(form, suffix){var my_callback = form.my_callback.value; // The original function which accessed the protected resourcevar user_pass = form.j_ajax_password.value;var user_name = form.j_ajax_username.value; //Ajax login - we send credentials to j_spring_security_check (as in form based login$.ajax({url: "/myContextURL/j_spring_security_check", data: { j_username: user_name , j_password: user_pass }, type: "POST",beforeSend: function (xhr) {xhr.setRequestHeader("X-Ajax-call", "true");},success: function(result) { //if login is success, hide the login modal and//re-execute the function which called the protected resource//(#7 in the diagram flow)if (result == "ok") {$("#ajax_login_error_"+ suffix).html(""); $('#ajaxLogin').hide();if (my_callback!=null && my_callback!='undefined' && my_callback!=''){eval(my_callback.replace(/_/g,'"'));}return true;}else { $("#ajax_login_error_"+ suffix).html('<span class="alert display_b clear_b centeralign">Bad user/password</span>') ;return false; }},error: function(XMLHttpRequest, textStatus, errorThrown){$("#ajax_login_error_"+ suffix).html("Bad user/password") ;return false; }
});
}
我們需要將Spring設置為支持Ajax登錄(#6):
設置Spring Security xml配置:
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/security" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.3.xsd"><http auto-config="false" use-expressions="true"><intercept-url access="hasRole('ROLE_ADMIN')" pattern="/admin**"><intercept-url filters="none" pattern="/**"><intercept-url access="permitAll" pattern="/signin/**"><form-login authentication-failure-handler-ref="ajaxAuthenticationFailureHandler" authentication-success-handler-ref="ajaxAuthenticationSuccessHandler" login-page="/common/authentication/login"> <logout invalidate-session="true" logout-success-url="/common/authentication/logout"><custom-filter before="LOGOUT_FILTER" ref="logoutFilter"></custom-filter></logout></form-login></intercept-url></intercept-url></intercept-url></http>...
</beans:beans>
定義成功登錄的處理程序:
@Component("ajaxAuthenticationSuccessHandler")
public class AjaxAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { public AjaxAuthenticationSuccessHandler() { }@Overridepublic void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response, Authentication authentication)throws IOException, ServletException { HttpSession session = request.getSession(); DefaultSavedRequest defaultSavedRequest = (DefaultSavedRequest) session.getAttribute(WebAttributes.SAVED_REQUEST);//check if login is originated from ajax callif ("true".equals(request.getHeader("X-Ajax-call"))) {try {response.getWriter().print("ok");//return "ok" stringresponse.getWriter().flush();} catch (IOException e) { //handle exception...}} else { setAlwaysUseDefaultTargetUrl(false); ...}}
}
為登錄失敗定義一個處理程序–與成功相同,但是字符串為“ not-ok”。
我知道這里的某些代碼不是最佳做法,所以我想聽聽您的想法。
如果您能看到改進流程或使其更通用的方法,請發給我。
鳴謝:通過gliffy完成了圖表-在線圖表工具
參考: Spring security 3 Ajax登錄–通過 Gal Levinsky博客博客中的JCG合作伙伴 Gal Levinsky 訪問受保護的資源 。
翻譯自: https://www.javacodegeeks.com/2012/08/spring-security-3-ajax-login-accessing.html