我看过一些有关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 existance
if (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 width
var 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 processing
return true;
}
步骤#5,#7 – Ajax登录表单使用以下Ajax登录:
function ajaxLogin(form, suffix){
var my_callback = form.my_callback.value; // The original function which accessed the protected resource
var 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.xsd
http://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() {
}
@Override
public 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 call
if ("true".equals(request.getHeader("X-Ajax-call"))) {
try {
response.getWriter().print("ok");//return "ok" string
response.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