如何防止浏览器调用基本身份验证弹出和处理401错误使用 Jquery?

我需要使用基本身份验证发送授权请求。我已经成功地使用 jquery 实现了这一点。但是,当我得到401错误基本认证浏览器弹出打开和 jquery Ajax 错误回调不被调用。

122396 次浏览

我最近也遇到了这个问题。因为你不能改变浏览器在 401(基本的消化身份验证)情况下弹出窗口的默认行为,有两种方法可以解决这个问题:

  • 更改服务器响应以不返回 401。而是返回一个 200代码并在 jQuery 客户机中处理它。
  • 将用于授权的方法更改为标头中的自定义值。浏览器将显示 基本的消化的弹出窗口。您必须在客户机和服务器上更改这一点。

    headers : {
    "Authorization" : "BasicCustom"
    }
    

Please also take a look at this for an example of using jQuery with Basic Auth.

或者,如果可以自定义服务器响应,则可以返回一个403禁区。

浏览器不会打开身份验证弹出窗口,将调用 jquery 回调。

返回一个通用的400状态代码,然后处理该客户端。

或者您可以保留401,而不返回 WWW-Authenticate 头,这实际上是浏览器通过身份验证弹出窗口响应的内容。如果 WWW-Authenticate 标头丢失,浏览器将不会提示输入凭据。

Make an /login url, than accept "user" and "password" parameters via GET and don't require basic auth. Here, use php, node, java, whatever and parse your passwd file and match parameters (user/pass) against it. If there is a match then redirect to http://user:pass@domain.com/ (this will set credential on your browser) if not, send 401 response (without WWW-Authenticate header).

如果您正在使用 IIS 服务器,您可以设置 IIS URL 重写(v2) ,以便在请求的 URL 上将 WWW-Authentication头重写为 None

指南。

要更改的值为 response_www_authenticate

If you need more info, add a comment and I'll post the web.config file.

您可以使用请求 url 来禁止基本的授权弹出,如下所示:

https://username:password@example.com/admin/...

如果您得到401错误(错误的用户名或密码) ,它将被正确处理与 jquery 错误回调。它可能会导致一些安全问题(在使用 http 协议而不是 https 的情况下) ,但它是有效的。

UPD: 此解决方案将在 Chrome59中删除

对请求头使用 X- 请求-With: XMLHttpRequest。 因此响应头不包含 WWW-Authenticate: Basic。

beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', ("Basic "
.concat(btoa(key))));
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
},

正如其他人指出的那样,改变浏览器行为的唯一方法是确保响应不包含401状态码,或者如果包含,则不包含 WWW-Authenticate: Basic头。由于更改状态代码不是非常语义化和不受欢迎的,所以一个好的方法是删除 WWW-Authenticate标头。如果您不能或不想修改 Web 服务器应用程序,您可以通过 Apache 提供服务或代理服务(如果您还没有使用 Apache)。

下面是 Apache 重写响应以删除 WWW-Authenticate 头 IFF 的配置,请求包含头 X-Requested-With: XMLHttpRequest(默认情况下由 JQuery/AngularJS 等主要 Javascript 框架设置)。.)响应包含头 WWW-Authenticate: Basic

在 Apache 2.4上测试(不确定是否可以与2.2一起使用)。 这取决于正在安装的 mod_headers模块。 (在 Debian/Ubuntu,sudo a2enmod headers和重启 Apache 上)

    <Location />
# Make sure that if it is an XHR request,
# we don't send back basic authentication header.
# This is to prevent the browser from displaying a basic auth login dialog.
Header unset WWW-Authenticate "expr=req('X-Requested-With') == 'XMLHttpRequest' && resp('WWW-Authenticate') =~ /^Basic/"
</Location>

If WWW-Authenticate header is removed, then you wont get the caching of credentials and wont get back the Authorization header in request. That means now you will have to enter the credentials for every new request you generate.

在 Safari 中,可以使用同步请求来避免浏览器显示弹出窗口。当然,在这种情况下,同步请求只能用于检查用户凭据... ... 您可以在发送实际请求之前使用这样的请求,如果内容(发送或接收)相当重,这可能会导致糟糕的用户体验。

    var xmlhttp=new XMLHttpRequest;
xmlhttp.withCredentials=true;
xmlhttp.open("POST",<YOUR UR>,false,username,password);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

在 Spring Boot 的后面,我使用了自定义 BasicAuthenticationEntryPoint:

@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().authorizeRequests()
...
.antMatchers(PUBLIC_AUTH).permitAll()
.and().httpBasic()
//    https://www.baeldung.com/spring-security-basic-authentication
.authenticationEntryPoint(authBasicAuthenticationEntryPoint())
...


@Bean
public BasicAuthenticationEntryPoint authBasicAuthenticationEntryPoint() {
return new BasicAuthenticationEntryPoint() {
{
setRealmName("pirsApp");
}


@Override
public void commence
(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
throws IOException, ServletException {
if (request.getRequestURI().equals(PUBLIC_AUTH)) {
response.sendError(HttpStatus.PRECONDITION_FAILED.value(), "Wrong credentials");
} else {
super.commence(request, response, authEx);
}
}
};
}

还没有探索修复的原因或范围,但我发现,如果我正在做一个 fetch请求,并添加头 x-requested-with: 'XMLHttpRequest',我不再得到在 Chrome 中的弹出授权框,不需要服务器更改。它正在与节点 http库交互。看起来像 WWW-Authenticate头从服务器返回,但 Chrome 处理它的方式不同。可能是特制的。

例如:

fetch(url, {
headers: {
Authorization: `Basic ${auth}`,
'x-requested-with': 'XMLHttpRequest'
},
credentials: 'include'
})