上传文件并请求受保护的下载(Node.js)

3
我有一个使用node.js,express和multer上传文件的系统,文件存储在静态目录中。我希望它们存储在服务器上,并且只能在登录后才能查看。
问题:
我的上传系统很好,但是我需要保护目录/files/documents/hv.pdf中的文件,因为当我输入URL时,浏览器会保存历史记录并打开该文件,这是不应该发生的,如何避免用户未登录访问?
我尝试使用一个中间件来运行如果url字符串包含/files文件夹的名称,但很奇怪,如果我不放置文件名或者放置其他名称,例如/files/document/test.txt,它可以工作,但是当我访问静态文件夹中的链接时就不行了,我认为这是缓存,但绝对不是那样
这个中间件
module.exports = (req,res,next)=>{
    let regex = /^\/files\/.*$/;
    if (!regex.test(req.url)) { return next(); }

    // for test
    req.session.user = {name:"thaylor"}; //comment for not session
    //fin for test

    if(req.session.user){
        next();
    }else{
        res.end('You are not allowed!');
    }
}

更新,此解决方案2018-04-2017

中间件用于获取根路径和保护路由app.js

const protectedfile = require("./controllers/protectedfile");

app.use(function(req, res, next) {
    req.rootPath = __dirname;
    next();
});

app.use('/files', protectedfile);
app.use('/files', express.static(path.join(__dirname, 'files')) );

这个文件位于controllers/protectedfile.js。
const path = require('path'); 
module.exports = (req,res,next)=>{
    if(!req.session.user){
        res.send("Route protected");
    }else{          
        let file = path.join(req.rootPath, req.originalUrl);  
        res.download(file, function (err) {
            if (err) {
                console.log("Error");
                console.log(err);
            } else {
                console.log("success"); 
            }        
        });       
    }
}

感谢所有人


当然不会,一个 static 文件夹是……应该是静态的。也就是说,您不希望将文件上传到静态文件夹中(通常命名为 public)。您可以创建一个名为 upload 的文件夹,并设置 app.set('upload', upload_path),并使用 req.app.get('upload') - Leo Li
我不理解你的解决方案。 - Thaylor Mosquera Castro
请看下面我详细的解释。希望能解决你的问题 :) - Leo Li
2个回答

1
在深入细节之前,需要记住的一件事是:Express.js框架中的所有内容都被视为一个中间件。因此,您的代码顺序很重要(即您的app.use的连接顺序)。每次客户端访问您的应用程序时,都会从app.js文件的顶部开始,直到找到可以返回的内容。
首先,静态路由意味着通过给定路径(文件夹)传递的内容是静态的。通常,在app.js文件的顶部头部有:
app.use(express.static('./public', options));

在上述代码中,文件夹“public”被设置为静态。也就是说,放入该文件夹的任何内容(包括放入其子文件夹的文档)对公众来说都是完全透明的,因此您不需要指定已经放入该文件夹的内容。当客户端尝试向您的服务器发出HTTP请求时,Express会扫描该文件夹,并在找到所请求的文件后返回该文档;如果没有找到,则会继续执行下一个app.use。您可以分配多个静态路由。例如,如果您现在在上述代码之后附加以下代码:
app.use(express.static('./file', options));

您的服务器现在将扫描以“file”命名的文件夹,在“./public”路径中未找到任何内容之后,尝试查找所请求的文档。基本上,与上面的操作相同。
以下是可以替换上述代码的技巧:
app.use('/file', checkIfTheUserHaveLogIn);
app.use('/file', express.static('./file', options));

或者一行代码:
app.use('/file', checkIfTheUserHaveLogIn, express.static('./file', options));

在这里,我使用'/file'作为app.use的第一个参数来指定URL中必须匹配的特殊路径。注意,checkIfTheUserHaveLogIn是一个中间件函数,用作控制器(函数),以决定是否允许客户端访问下一级中间件(通过调用next()),即express.static('./file', options)。如果客户端没有获得该特权,则可以在checkIfTheUserHaveLogIn中将客户端重定向到登录页面或执行其他操作。
在您的代码中,您设置了一个路由来筛选出'/file'路由路径以执行身份验证。但是,由于中间件的顺序很重要。实际上会首先触发静态路由器,并且可以找到文档,因此文件已经返回给请求的客户端。您的中间件实际上从未被触及。为了避免这种情况,只需按照我之前所做的设置另一个静态路由,并指向另一个文件夹(不能是第一个透明静态路由的子文件夹,即不在./public下,就像在我的示例中一样)。然后它应该完美地工作。
希望我的解释澄清了你的问题。

1
过了一段时间后,我解决了几个问题,其中之一是获取根目录,因为我的安全管理文件在驱动程序文件夹中,所以使用此帮助链接作为最佳选项来解决它。非常感谢您的帮助,除此之外,您的解释帮助我理解了很多东西,并且我在我的代码中纠正了许多问题。 - Thaylor Mosquera Castro
很高兴听到你的进步!既然你提到了安全性,如果你想设置全局内容安全策略(CSP),可以编写一个中间件,并将其放置在app.js文件的顶部,这样所有客户端都将响应全局CSP规则。如果你不知道什么是CSP,可以通过谷歌搜索获取更多信息。 - Leo Li
如果用户已经通过身份验证并被授予访问公共文件夹/子文件夹的权限,那么他也将被授予访问其他用户文件的权限。对吧? - Hairi

0
var express = require("express");
var path = require( "path" );
var app = express();

app.use( '/upload', isLoggedIn, express.static( path.join( __dirname, '**your upload folder name**' ) ) );

app.listen( 3000 );

//Use this code if you are using passport.js for authentication mech.
function isLoggedIn(req, res, next) {
    if (req.user) {
        next();
    } else {
        res.redirect('/login');
    }
}

//Use this code for custom sign in implementation
function isLoggedIn(req, res, next) {
    //check if user is logged in
    // your business logic goes here
    if ( condition ) {
        next();
    } else {
        res.redirect('/login');
    }
}

通过这样做,每次调用localhost:3000/upload/*都会通过isLoggedIn函数。


当我将PDF文件发送给用户时,“如果用户已登录,则允许他下载”在文件位于静态路由中时没有效果,它总是让您查看它,无论是否已登录。 - Thaylor Mosquera Castro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接