从浏览器

时间:2017-04-17 14:13:55

标签: node.js authentication web browser token

类似的问题是由其他人(here)提出的,但没有得到正确答案。因为这对我来说是基本而重要的(也许也适用于其他人),我试着在这里问一下。我在服务器端使用Node.js + Express + EJS。我努力通过在服务器上使用jsonwebtoken和在Web浏览器上使用jQuery的ajax-jsonp来使令牌认证成功。现在,在令牌被授予并存储在浏览器端的sessionStorage中之后,我可以使用请求头中包含的令牌发起另一个ajax请求,以获取用户的配置文件并将其显示在“当前”页面的某个位置。但我想要的是显示一个新的网页来显示用户的个人资料,而不是在“当前”页面(网站的主/索引页面)中显示它。问题是:

  1. 如何发起这样的HTTP GET请求,包括HTTP头中的令牌;并将响应显示为新的网页?
  2. Node.js如何处理这个?如果我使用res.render然后在哪里放置js逻辑来验证令牌并访问数据库并生成页面内容?
  3. 或者,我们是否应该说令牌机制比普通网页身份验证(Web浏览器提供有限的API)更适合API身份验证?

    我认为如果我们想要将令牌机制用作一般身份验证,这个问题的答案很重要,因为在网站场景中,内容主要被组织为服务器上的网页,而客户端上的API则由浏览器。

    通过纯粹的猜测,可能有另一种方式,ajax成功回调从当前页面用服务器的响应创建一个新页面,但我不知道如何实现这一点。

    通过调用bellow代码成功返回 customer_profile.ejs 中的HTML内容,但客户端ajax(显然)拒绝了它。

    exports.customer_profile = function (req, res) {
      var token = req.headers.token;
      var public_key = fs.readFileSync(path.resolve() + '/cert/public_key.pem');
      var decoded = jwt.verify(token, public_key);
      var sql = 'SELECT * FROM  customer WHERE username = "' + decoded.sub + '"';
      util.conn.query(sql, function (err, rows) {
        if (!err) {
          for (var i = 0; i < rows.length; i++) {
            res.render('customer_profile', {customer_profile: rows[i]});
            break;
          }
        }
      });
    };
    

1 个答案:

答案 0 :(得分:0)

我也在尝试找到解决方案。请注意,我正在使用Firebase来实现某些功能,但是我会尽力记录逻辑。

到目前为止,我能弄清的是以下内容:

  1. 将自定义标头附加到HTTP请求客户端
// landing.js - main page script snippet
function loadPage(path) {
    // Get current user's ID Token
    firebase.auth().currentUser.getIdToken()
    .then(token => {
        // Make a fetch request to 'path'
        return fetch(`${window.location.origin}/${document.documentElement.lang}/${path}`, {
            method: 'GET',
            headers: {'X-Firebase-ID-Token': token} // Adds unverified token to a custom header
        });
    })
    .then(response => { 
        // As noted below, this part I haven't solved yet. 
        // TODO: Open response as new webpage instead of displaying as data in existing one
        return response.text();
    })
    .then(text => {
        console.log(text);
    })
    .catch(error => {
        console.log(error);
    });
}
  1. 通过检索服务器端相应的标头值,根据您的逻辑验证令牌
// app.js - main Express application server-side file
// First of all, I set up middleware on my application (and all other setup). 
// getLocale - language negotiation. 
// getContext - auth token verification if it is available and appends it to Request object for convenience

app.use('/:lang([a-z]{2})?', middleware.getLocale, middleware.getContext, routes);

// Receives all requests on optional 2 character route, runs middleware then passes to router "routes"
// middleware/index.js - list of all custom middleware functions (only getContext shown for clarity)
getContext: function(req, res, next) {
        const idToken = req.header('X-Firebase-ID-Token'); // Retrieves token from header
        if(!idToken) {
            return next(); // Passes to next middleware if no token, terminates further execution
        }
        admin.auth().verifyIdToken(idToken, true) // If token provided, verify authenticity (Firebase is kind enough to do it for you)
        .then(token => {
            req.decoded_token = token; // Append token to Request object for convenience in further middleware
            return next(); // Pass on further
        })
        .catch(error => {
            console.log('Request not authorized', 401, error)
            return next(); // Log error to server console, pass to next middleware (not interested in failing the request here as app can still work without token)
        });
    }
  1. 呈现并发送回数据
// routes/index.js - main router for my application mounted on top of /:lang([a-z]{2})? - therefore routes are now relative to it
// here is the logic for displaying or not displaying the page to the user

router.get('/console', middleware.getTranslation('console'), (req, res) => {
    if(req.decoded_token) { // if token was verified successfully and is appended to req
        res.render('console', responseObject); // render the console.ejs with responseObject as the data source (assume for now that it contains desired DB data)
    } else {
        res.status(401).send('Not authorized'); // else send 401 to user
    }
});

如您所见,我能够对代码进行模块化,并使之简洁明了,并且可以使用自定义中间件。现在是一个有效的API,使用身份验证和受限访问从服务器返回数据

我尚未解决的问题:

如上所述,该解决方案使用提取API,请求的结果是来自服务器(html)的数据,而不是新页面的数据(即,在跟随锚链接时)。这意味着现在使用此代码的唯一方法是使用DOM操作并将响应设置为该页面的innerHTML。 MDN建议您设置“ Location”标头,该标头将在浏览器中显示一个新URL(您希望指示的URL)。这意味着您实际上已经实现了您和我都想要的东西,但是如果您知道我的意思,那么我仍然无法像浏览器跟随链接时一样展示如何展示它。

无论如何,请让我知道您对此的想法,以及您是否能够从我还没有解决的部分中解决它