节点,快速-限制对可下载文件的访问

时间:2018-09-04 13:42:55

标签: node.js express

我正在将express用作微服务rest api的服务器。端点是从目录结构构建的。当前在客户端上几乎没有可下载的pdf文件。即使用户未登录到门户网站,它也可以下载(使用href URL)。因此,我将所有pdf文件都放入了服务器。

服务器上的目录结构: directory structure on server

pdf文件位于docs目录中。请在下面找到服务器的代码:

/* global __dirname */
import morgan from 'morgan';
import logger, { webStream } from './services/logger';
import { socket } from './services';

// set env variables before all else
import { GATEWAY_PORT, CORS_ORIGINS } from './config';

const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser')();
const version = require('./services/utils').version();
const authentication = require('./services/authentication');
const utils = require('./services/utils');


// set up app and middleware
const app = express();

app.use(morgan('User::req[user-id] Correlation::req[x-correlation-id] Method::method URL::url Status::status :res[content-length] - :response-time ms', { stream: webStream }));

logger.info('Starting...');

app.use(cookieParser);
app.use(bodyParser.json({ limit: '50mb' }));
app.disable('x-powered-by');

// CORS headers to allow running client/server on different ports
app.use((req, res, next) => {
  // Check if the origin is whitelisted in the env vars
  const actual = req.headers.origin || '';
  if (utils.matchCors(actual, CORS_ORIGINS.split(','))) {
    res.set({ 'Access-Control-Allow-Origin': actual });
  }

  res.set({
    // standard CORS headers
    'Access-Control-Allow-Headers': 'Content-Type, Authorization, Accept, Accept-Language',
    'Access-Control-Allow-Credentials': true,
    'Access-Control-Allow-Methods': 'PATCH,POST,GET,DELETE',

    // addresses security issues identified by automated pen testing
    'X-Frame-Options': 'DENY',
    'X-Content-Type-Options': 'nosniff',
    'X-XSS-Protection': 1,
  });
  next();
});

// set the user property of the request object
app.use((req, res, next) => {
  const token = req.cookies[authentication.cookieName];
  if (!token) {
    req.user = false;
  } else {
    req.user = authentication.decodeJWT(token);
    authentication.setCookie(res, token, req.user);
  }
  utils.setCorrelationId(req.headers['x-correlation-id']);
  req.correlationId = req.headers['x-correlation-id'];
  next();
});

// helper function returning middleware to reject unauthorised users
function requiredRoles(roles, abcOnly) {
  return function requireRolesHandler(req, res, next) {
    if (
      !req.user
      || (abcOnly && !req.user.isabc)
      || !authentication.hasRole(req.user, roles)) {
      const error = new Error('UNAUTHORISED');
      error.status = 403;
      next(error);
    } else {
      next();
    }
  };
}

// Add the endpoints to express.
// Reversed to get literal routes before @ capture groups.
utils.parseDirectory(`${__dirname}/rest`, [], true).reverse().forEach((endpoint) => {
  const { auth, functions } = endpoint.handler;
  if (auth) {
    functions.unshift(requiredRoles(auth.roles, auth.abcOnly));
  }
  app[endpoint.method](
    endpoint.url,
    functions,
  );
});


// setup server
const server = app.listen(GATEWAY_PORT, () => {
  logger.info(`Allowed CORS: ${CORS_ORIGINS}`);
  logger.info(`Started ${version.name} (${version.number}) listening on ${GATEWAY_PORT}`);
});

socket.createServer(server);

当用户单击页面上的链接时,如何仅将服务器上的pdf文件仅提供给授权用户?

1 个答案:

答案 0 :(得分:1)

具有下载文件的路径,例如GET /api/download?file=abc.pdf

现在在中间件中,

  1. 检查req.user是否存在。

  2. 检查user是否具有下载文件的足够权限,或者 不是

  3. 如果满足1和2,则投放文件


代码或多或少看起来像这样:

app.get('/api/download', (req, res, next) => {
  // Check if the request had valid token or not
  if(!req.user) {
    const error = new Error('UNAUTHORISED');
    error.status = 403;
    return next(error);
  }

  const { user } = req;
  const { file } = req.query;

  // If you want to have some additional logic wherein
  // you want to restrict the download of the file, 
  // you can put that logic in this function
  const isAllowed = canDownload(user, file);

  if(isAllowed) {
    return res.sendFile(path.join(__dirname, 'docs', path.sep, file));
  }
  const error = new Error('UNAUTHORISED');
  error.status = 403;
  return next(error);

})

由于使用path,您可能需要要求canDownload,实现__dirname或解决没有此类文件或目录错误。所有这些都是微不足道的。如果您也需要帮助,请在评论中告诉我。


这里是对response.sendFile()的引用
而且this可能也有帮助。