我是 Node.js 新手,我创建了一个登录系统。我的应用程序位于域的子目录中,例如:https://domain.tld/nrjs/。该应用程序中有两个包含静态文件的文件夹。一个文件夹称为
public
,另一个文件夹称为 protected
。当用户通过 https://domain.tld/nrjs/ 连接到节点应用程序时,他们将获得静态文件夹 public
并定向到 login.html
页面。一旦通过身份验证,它们就会被提供给静态文件夹 protected
,并发送到 https://domain.tld/nrjs/index.html,该文件夹存储在 protected
文件夹中。问题是,似乎 app.js 是否向他们发送文件夹(无论他们是否有身份验证),用户都可以直接链接到位于 https://domain.tld/nrjs/protected/index 的文件夹。 html 或任何其他实际文件,它完全忽略我的 app.js 文件中的任何路由处理程序。我正在使用 cPanel。下面是我的 app.js,但似乎用户可以直接链接到该文件,而 app.js 甚至不涉及。我需要能够阻止用户访问 protected
文件夹,直到他们成功登录,或者完全阻止他们访问它也可以,因为我将在 /index.html
而不是 /protected/index.html
向他们发送文件
.
结构:
public_html/
│
└───nrjs/ (node.js app is in subdirectory)
│ app.js
│
├───public/
│ login.html (Publicly accessible login page)
│
└───protected/
index.html (Private home page, should not be directly accessible)
这是我的app.js:
const express = require('express');
const fs = require('fs');
const path = require('path');
const session = require('express-session');
const bodyParser = require('body-parser');
const bcrypt = require('bcryptjs');
const app = express();
const basePath = '/nrjs';
const saltRounds = 10;
let users = [
{ username: 'admin', password: 'password' },
{ username: 'user1', password: 'password1' },
{ username: 'user2', password: 'password2' }
];
Promise.all(users.map(user =>
bcrypt.hash(user.password, saltRounds).then(hash => {
user.password = hash;
})
)).then(() => {
console.log('Users initialized with hashed passwords');
});
app.use(session({
secret: 'KAL783BB4HJK1P90V3',
resave: false,
saveUninitialized: true
}));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(basePath, express.static(path.join(__dirname, 'public')));
function checkAuthenticated(req, res, next) {
if (!req.session.user && req.path !== '/login.html' && req.path !== '/login') {
return res.redirect(basePath + '/login.html');
}
next();
}
app.use(basePath, checkAuthenticated, express.static(path.join(__dirname, 'protected')));
app.post(basePath + '/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (user) {
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) {
console.error('Error during password comparison:', err);
return res.redirect(basePath + '/login.html?error=An error occurred');
}
if (isMatch) {
req.session.user = user.username;
const loginLog = `Successful login by ${user.username} at ${new Date().toLocaleString()}\n`;
fs.appendFile('logs/success_login_attempts.log', loginLog, (err) => {
if (err) {
console.error('Error writing to login log file:', err);
}
});
res.redirect(basePath + '/index.html');
} else {
const failedLoginLog = `Failed login attempt with username: ${username} at ${new Date().toLocaleString()}\n`;
fs.appendFile('logs/failed_login_attempts.log', failedLoginLog, (err) => {
if (err) {
console.error('Error writing to failed login log file:', err);
}
});
res.redirect(basePath + '/login.html?error=Incorrect username or password');
}
});
} else {
const failedLoginLog = `Failed login attempt with username: ${username} at ${new Date().toLocaleString()}\n`;
fs.appendFile('logs/failed_login_attempts.log', failedLoginLog, (err) => {
if (err) {
console.error('Error writing to failed login log file:', err);
}
});
res.redirect(basePath + '/login.html?error=Incorrect username or password');
}
});
app.get(basePath + '/login', (req, res) => {
res.redirect(basePath + '/login.html');
});
app.get(basePath + '/logout', (req, res) => {
req.session.destroy();
res.redirect(basePath + '/login.html');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
我尝试将各种路由处理程序放在页面顶部,并做了一个包罗万象的路由处理程序,它仍然让用户直接导航到文件。
示例:
app.get('/nrjs/protected', (req, res) => {
res.status(403).send('Access Denied');
});
我还尝试使用路线通配符:
app.get('/nrjs/protected/*', (req, res) => {
res.status(403).send('Access Denied');
});
并且当尝试捕获所有内容时,他们仍然可以导航到受保护的文件:
app.use((req, res, next) => {
res.status(404).send("Sorry, the page you're looking for doesn't exist.");
});
我还尝试使用
protected
阻止对 .htaccess
文件夹的访问,并在 protected
文件夹之外的路由上收到 503 错误。
编辑:我已将节点应用程序移至 Web 目录之外,因此用户无法再直接导航到应用程序静态文件,从而解决了问题。但是,我仍然想知道如果我确实将节点应用程序存储在 Web 目录中,是否有办法阻止用户导航文件。当用户成功导航到受保护目录时,我可以确认没有设置会话。谢谢!
可以在浏览器中访问
.jpg
或 .css
目录中的所有可通过 HTTP 访问的文件,例如 .js
、.txt
、.html
、public
或 protected
)只需在 url 中导航到它们即可。这是因为无论从 public_html/
目录(可能是 Apache)提供文件,都是提供 protected/index.html
而不是 Express。这是一个插图。
这是您的域名:
https://domain.tld
用户向此文件发出
GET
请求:
https://domain.tld/pancakes.html
Apache(为了论证)从这里提供文件:
/var/www/html/public_html/pancakes.html
用户然后向此路由发出
GET
请求,例如:
https://domain.tld/nrjs/login
那么,您必须有一些东西侦听端口 443 上到
https://domain.tld/nrjs
的流量,并将其路由到端口 3000,以便 Express 可以接收它。
当 Express 在端口 3000 上收到该请求时,它会向
redirect
发送 nrjs/login.html
。那么,您正在使用 app.use(basePath, express.static(path.join(__dirname, 'public')));
中间件,因此 Express 会在 login.html
文件夹中看到 public
,然后将 html 发送回用户。
接下来,用户向此处发送
POST
请求:
https://domain.tld/nrjs/login
再次重新路由到端口 3000,并经过几次密码验证等后将
redirects
表示为 nrjs/index.html
。它通过static
中间件查看public
目录,找不到它。然后它通过 static
中间件查看 protected
目录和宾果游戏,它提供了 index.html
。
现在您拥有的是一个
checkAuthenticated
中间件,确保它是正在寻找 index.html
的经过身份验证的用户,但这仅适用于在端口 3000 上向 https://domain.tld/nrjs/index.html
发出的请求。
如果用户导航至:
https://domain.tld/nrjs/protected/index.html
然后看起来 Apache 只是在端口 443 上接收并提供服务。 Express 没有任何
protected/index.html
的路线处理程序,它只有 nrjs/index.html
。
我的建议是:
protected
文件夹。index.html
移动到与 app.js
相同的目录。res.redirect(basePath + '/index.html');
到此
res.sendFile(__dirname + '/index.html')
app.use(basePath, checkAuthenticated, express.static(path.join(__dirname, 'protected')));
app.get(basePath + '/index', checkAuthenticated, (req, res) => {
res.sendFile(__dirname + '/index.html')
});
现在,用户访问
index.html
的唯一方法是通过登录过程或通过 https://domain.tld/nrjs/login
路由,他们需要首先通过 checkAuthenticated
中间件。