首页 文章

如何在XHR之后将基本身份验证凭据存储在浏览器缓存中

提问于
浏览
1

Context

我有一个朋友,他的名字叫鲍勃 .

Bob有一个服务器,其中一个应用程序正在运行并且可以访问 only in local . 为了从外部世界访问此应用程序,Bob安装并配置了带有nginx的 reverse proxy 和模块 auth_basic .

每个请求都通过反向代理进行身份验证过程 . 两种情况:

  • 如果 HTTP GET request 包含有效的HTTP头参数 Authorization: Basic base64credentials ,则反向代理会相应地访问本地应用程序和响应 . 每个子请求都不需要新的身份验证,因为浏览器会自动缓存凭据并在每个请求中自动发送,直到我们关闭浏览器 .

  • 如果 HTTP GET request 不包含有效的HTTP头参数,则反向代理直接使用HTTP头 WWW-Authenticate: Basic realm="User Visible Realm" 进行响应 . 然后浏览器会自动显示一个对话框以输入凭据 .

一切正常,直到这里 . 这就像基本的auth规范所期望的那样 .

Problem

Bob不喜欢浏览器中的默认对话框,想要一个带有表单的漂亮html页面 . 他将nginx服务器配置为拥有自己的html表单 .

问题是默认情况下,表单的HTML提交过程不会发送 Authorization 标头参数 . 鲍勃需要使用 XMLHttpRequest . 当凭据良好时,他实现它并从服务器收到一个好的 200 HTTP response .

与默认表单行为不同,使用XMLHttpRequest时,如果成功,浏览器不会自动缓存凭据 . 因此每个子请求再次显示登录表单:'(

Bob无法更改本地应用程序的前端代码,以便自己发送每个请求中的凭据(与SPA一样) . 实际上,他无法访问此应用程序 . 他只需要访问nginx conf和他自己的html登录表单 . 所以存储在这里没用 .

Questions

Is it possible for bob to make the browser cache the credentials after receive the XHR response ?

(即使他使用XMLHttpRequest,目标也是默认行为)


编辑:

Further explanation

enter image description here

本地应用程序在localhost上运行 . 鲍勃没有't develop this app and he can' t编辑它 . 此应用程序不提供身份验证,Bob使用nginx的basic_auth模块作为 reverse proxy 来验证人员 .

它运行良好,但使用实现Basic Auth规范的浏览器的默认行为 . 此行为显示丑陋的表单并在成功时缓存凭据 . 如果Bob提供自己的表单行为go,这是正常的,因为Basic Auth规范需要HTML表单无法提供的特定标头参数 (Authorization: Basic ...) . Bob需要使用XHR来提供此参数 .

问题是,如何用XHR取回浏览器的良好行为?

我们只能在login.html上使用JS而不能在本地应用程序上使用JS . 这是工作流程:

  • HTTP GET请求到服务器

  • 服务器找不到 Authorization 参数或凭据错误

  • 服务器响应login.html

  • 用户按表单提供凭据 . 使用 Authorization 参数发出XHR .

  • 服务器查找 Authorization 参数AND凭证有效

  • Server返回本地应用程序条目文件(例如index.html)

  • 浏览器读取index.html并想要请求其他文件(img,css,js ......)

  • 这些子请求将失败,因为这些请求中没有提供凭据 .

  • 如果使用丑陋的默认表单,则会自动缓存凭据并且它可以正常工作 .

我还准确地说,一个解决方案是用一个真正的后端应用程序替换nginx基本auth反向代理和另一个身份验证系统(例如自动发送的cookie),它可以作为反向代理,但不是问题 .


编辑2:

Why Bob can't use storage solution ?

在ulgy表单场景中,他没有HTML登录文件 . 当浏览器客户端向服务器请求请求时,服务器仅响应带有 WWW-Authenticate 标头但没有HTML内容的HTTP响应 . 让此标头参数显示表单的简单事实 . 只需输入好的凭据就会发回200 HTTP响应,浏览器将缓存凭据并在每个请求中使用HTTP标头 Authorization: Basic 发送 .

在login.html方案中,成功登录后,我们需要在每个请求中发回HTTP标头 Authorization: Basicnot a cookie, because it's how work Basic Auth spec and Bob doesn't have any backend, just the nginx module ) . 它's possible to send this header from the login.html because we can attach JS on it. But then, the next pages respond by the server will be HTML files from the local app, where Bob doesn' t可以访问他们的HTML并且不能在它们上面附加JS来为下一个请求提供头 Authorization: Basic . 可以从login.html文件中存储cookie,但是需要从其他页面检索此cookie并用于发送 Headers Authorization: Basic ,这是不可能的,因为Bob无法访问这些页面的JS .

先感谢您 .

1 回答

  • 0

    由于您已经在使用ajax,因此只需设置javascript并读取cookie:

    (我在这里使用jQuery,为简单起见,如果你不使用jQuery,用适当的语法替换ajax调用):

    function getCookie(cookiename) {
        /* a function to find a cookie based on its name */
    
        var r = document.cookie.match('\\b' + cookiename + "=([^;]*)\\b");
        // document.cookie returns all cookies for this url
        return r ? r[1] : undefined;
        // return the regex capture if it has content, otherwise return undefined
    }
    
    function getData(auth_basic) {
        $.ajax({
            url: 'url_of_nginx...',
            headers: {
                'Authorization': 'Basic ' + auth_basic
            } // send auth header on xmlhttprequest GET
        }).next(function ajaxSuccess(data) {
            // data from the nginx
            document.cookie = '_auth_cookie=' + auth_basic;
            // store my auth in a cookie
        }, function ajaxFailed(jqXHR) {
            // do something on failure, like
            showLoginForm()
        });
    }
    
    function showLoginForm() {
        /* function to render your form */
    
        // attach an event handler to form submission
        $('#submit_button_id').click(function form_submitted(login_evt) {
            // I clicked login
            login_evt.preventDefault(); // don't really submit the form
    
            // get my field form values
            username = $('#username_input_field').val();
            password = $('#password_input_field').val();
    
            // I base64 the auth string
            var auth_basic = btoa(username + ':' + password);
    
            // try to auth
            getData(auth_basic);
    
        });
    }
    
    var auth_cookie = getCookie('_auth_cookie');
    if (auth_cookie === undefined) {
        // I have no cookie
        showLoginForm()       
    } else {
        getData(auth_cookie)
    }
    

相关问题