使用Dojo与服务器通信时,是否可以在浏览器拦截之前捕获401状态代码?
我正在使用Dojo 1.8与使用SSL进行基本身份验证的RESTful服务器进行通信。客户端捕获客户端的用户名和密码,然后将它们包含在对服务器的请求中,如下所示:
request.get(this.url, {
handleAs: "json",
user: creds.username,
password: creds.password
}).then(
function(user) {
console.log("user", user);
},
function(error) {
console.log("error", error);
});
当客户端凭据正确时,这可以正常工作。但是如果凭据错误,浏览器会在我的错误函数获取之前拦截返回的401状态代码。
我见过有关此问题的其他讨论,包括服务器在身份验证错误时返回401以外的其他内容的解决方法。然而,在我走这条路之前,我想知道是否有办法让我的代码在浏览器之前获得401状态。
或者,使用基本身份验证而非SSL的其他内容会使这更容易吗?
答案 0 :(得分:0)
我找不到阻止浏览器拦截401状态代码的方法,但我解决了这个问题。以下是我做过的事情,以防其他人提出这个问题。只有在需要进行身份验证时才能控制服务器返回的内容时,此方法才有效。
首先,我需要在需要身份验证时返回400状态代码。我正在使用Apache Shiro,所以这只涉及创建BasicHttpAuthenticationFilter的子类并覆盖sendChallenge(),如下所示:
protected boolean sendChallenge(ServletRequest request, ServletResponse response) {
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
String authcHeader = getAuthcScheme() + " realm=\"" + getApplicationName() + "\"";
httpResponse.setHeader(AUTHENTICATE_HEADER, authcHeader);
return false;
}
在我的情况下,我不希望注销重定向到JSP页面,因此我将Shiro的LogoutFilter preHandle()方法子类化为:
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
Subject subject = getSubject(request, response);
try {
subject.logout();
}
catch (SessionException ise) {
// Log exception
}
return false;
}
然后我将过滤器添加到我的shiro.ini文件中:
[main]
myAuth = com.example.filters.MyBasicAuthFilter
noRedirectLogout = com.example.filters.NoRedirectLogoutFilter
[urls]
/protected/** = myAuth
/logout = noRedirectLogout
您可以使用不同的状态代码,只需确保在客户端上检查相同的状态代码。
接下来,我创建了一个LoginController来处理登录和注销:
define([], function() {
var string = {};
string.getBytes = function(str) {
console.log("string.getBytes:str", str);
var bytes = [];
for (var i = 0; i < str.length; ++i) {
bytes.push(str.charCodeAt(i));
}
return bytes;
};
return string;
});
...
_authorize: function(url, username, password) {
var namepw = new String(username + ":" + password);
var authstr = "Basic " + base64.encode(utilString.getBytes(namepw));
var def = new Deferred();
request.get(url, {
handleAs: "json",
headers : {
Authorization: authstr
}
}).then(
function(data) {
def.resolve(data);
},
function(err) {
if (err.response.status == 400) {
def.progress();
}
else {
def.reject(err);
}
});
return def;
},
login: function(url, user, passwd) {
return this._authorize(url, user, passwd);
},
logout: function(url) {
return this._authorize(url, "#fakeuser#", "#fakepw#");
};
使用控制器登录的代码会返回Deferred,并且可以执行相应的操作:
var auth = this.controller.login(creds);
Deferred.when(auth,
function(data) {
if (data != null) {
// Successful login
}
else {
// Non-authentication error
}
},
function(err) {
// Non-authentication error
},
function(updt) {
// Invalid username or password
}
);
调用注销的代码也会返回Deferred:
controller.logout("/logout").then(function(val) {
// Successful logut
}, function(err) {
// Error attempting to logout
});
注销的工作方式是使注销过滤器返回成功(状态== 200),无论用户名密码如何,并传递浏览器将捕获的无效用户名和密码。希望这一切对某人有所帮助。