我看过一些有关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