尝试使用Dojo捕获HTTP 401

时间:2013-03-08 00:34:10

标签: javascript http dojo

使用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的其他内容会使这更容易吗?

1 个答案:

答案 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),无论用户名密码如何,并传递浏览器将捕获的无效用户名和密码。希望这一切对某人有所帮助。