0

使用 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 上使用除基本身份验证之外的其他东西会使这更容易吗?

4

1 回答 1

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

于 2013-04-01T16:43:56.927 回答