如何解决编辑:找到的解决方案嗨,csrf 令牌仅在一个 POST 路由上不起作用
它在所有路由中都有效,仅在 POST 添加产品时无效,因为即使我为它分配了中间件和本地令牌,令牌也未定义。
您知道为什么令牌仅在一条路线上未定义吗?一周以来,我一直在尝试任何事情,但没有任何效果。
下面有一个最小的可重现示例,提前致谢
app.js
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
// generating token on every view so that fake websites cannot trick the user into a fraud (log in into their account and make them do stuff)
const csrf = require('csurf');
// including error messages with connect flash
const flash = require('connect-flash');
// upload files
const multer = require('multer');
const errorController = require('./controllers/error');
const User = require('./models/user');
const MONGODB_URI = 'mongodb+srv://mauro:password@cluster0.kyrqs.mongodb.net/shop';
const app = express();
const store = new MongoDBStore({
uri: MONGODB_URI,collection: 'sessions',});
// handling files
const fileStorage = multer.diskStorage({
destination: (req,file,cb) => {
// 1st argument error and 2nd where we want to store the files
cb(null,'./images/');
},filename: (req,cb) => {
// 1st argument is the error,2nd name of the file. with this line we make sure that there are no duplicate names and that the file is stored correctly
// this line will take out : from the name of the file
cb(null,new Date().toISOString().replace(/[-T:\.Z]/g,"") + '-' + file.originalname);
}
});
// we can filter the files
const fileFilter = (req,cb) => {
if(file.mimetype === 'image/png' || file.mimetype === 'image/jpg' || file.mimetype === 'image/jpeg') {
// "true" stores the file "false" won't
cb(null,true)
} else {
cb(null,false);
}
};
app.set('view engine','ejs');
app.set('views','views');
const adminRoutes = require('./routes/admin');
const shopRoutes = require('./routes/shop');
const authRoutes = require('./routes/auth');
app.use(multer({ storage: fileStorage,fileFilter: fileFilter }).single('image'));
// setting up the sessions
app.use(
session({
secret: 'my secret',resave: false,saveUninitialized: false,store: store,})
);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname,'./public')));
app.use(csrf());
// telling node to use the same "variables" in every path (this is in order to get a token for each page)
app.use((req,res,next) => {
// locals allows us to set local variables
res.locals.isAuthenticated = req.session.isLoggedIn;
res.locals.csrftoken = req.csrftoken();
next();
});
// handling a single file (input named image). {dest: 'images'} will create a folder named images and store into it the file (I don't think that we need it if we use storage).
// { storage: fileStorage } makes sure that the name of the file is stored correctly
// fileFilter (line 55)
// including error messages with connect flash
app.use(flash());
app.use((req,next) => {
if (!req.session.user) {
return next();
}
User.findById(req.session.user._id)
.then(user => {
if(!user) {
return next();
}
req.user = user;
next();
})
.catch(err => {
next(new Error(err));
});
});
app.use('/admin',adminRoutes);
app.use(shopRoutes);
app.use(authRoutes);
app.use(errorController.get404);
// app.use(errorController.get500);
// see next(error) on admin controller (this one is a error handling middleware)
// so when we call next(error) on line 87 in the admin controller we skip all the middlewares that are not error middlewares
// if we have more than one error middleware node will execute them from top to bottom (as a normal middleware)
app.use((error,req,next) => {
console.log('error middleware ' + error)
res.status(500).render('500',{
pageTitle: 'Something Went Wrong',path: '/500',isAuthenticated: req.session.isLoggedIn,});
});
mongoose
.connect(MONGODB_URI,{ useNewUrlParser: true })
.then(result => {
app.listen(3000);
})
.catch(err => {
console.log(err);
});
管理控制器
exports.postAddProduct = (req,next) => {
const title = req.body.title;
// with multer we changed this variable so that we can handle files that the users upload
const price = req.body.price;
const image = req.file;
const description = req.body.description;
if(!image) {
return res.status(422).render('admin/edit-product',{
pageTitle: 'Add Product',path: '/admin/add-product',editing: false,hasError: true,product: {
title: title,price: price,description: description
},errorMessage: "file is not an image",validationErrors: []
});
}
// getting all the errors that we might get from the auth routes (validation)
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.log('errors post add pro',errors);
return res.status(422).render('admin/edit-product',description: description,},errorMessage: errors.array()[0].msg,validationErrors: errors.array()
});
}
const imageUrl = image.path;
const product = new Product({
title: title,imageUrl: imageUrl,userId: req.user,});
product
.save()
.then(result => {
// console.log(result);
console.log('Created Product');
res.redirect('/admin/products');
})
.catch(err => {
const error = new Error('error:' + err);
error.httpStatusCode = 500;
return next(error);
});
};
edit-product.ejs
<%- include('../includes/head.ejs') %>
<link rel="stylesheet" href="/css/forms.css">
<link rel="stylesheet" href="/css/product.css">
</head>
<body>
<%- include('../includes/navigation.ejs') %>
<main>
<% if (errorMessage) { %>
<div class="centered"><%= errorMessage %></div>
<% } %>
<form class="product-form" action="/admin/<% if (editing) { %>edit-product<% } else { %>add-product<% } %>" method="POST" enctype="multipart/form-data">
<input type="hidden" name="_csrf" value="<%= csrftoken %>">
<div class="form-control">
<label for="title">Title</label>
<input
class="<%= validationErrors.find(e => e.param === 'title') ? 'invalid' : '' %>"
type="text"
name="title"
id="title"
value="<% if (editing || hasError) { %><%= product.title %><% } %>">
</div>
<div class="form-control">
<label for="image">Image</label>
<input
type="file"
name="image"
id="image"
>
</div>
<div class="form-control">
<label for="price">Price</label>
<input
class="<%= validationErrors.find(e => e.param === 'price') ? 'invalid' : '' %>"
type="number"
name="price"
id="price"
step="0.01"
value="<% if (editing || hasError) { %><%= product.price %><% } %>">
</div>
<div class="form-control">
<label for="description">Description</label>
<textarea
class="<%= validationErrors.find(e => e.param === 'description') ? 'invalid' : '' %>"
name="description"
id="description"
rows="5"><% if (editing || hasError) { %><%= product.description %><% } %></textarea>
</div>
<% if (editing) { %>
<input type="hidden" value="<%= product._id %>" name="productId">
<% } %>
<button class="btn" type="submit"><% if (editing) { %>Update Product<% } else { %>Add Product<% } %></button>
</form>
</main>
<%- include('../includes/end.ejs') %>
我相信我正在以下几行设置令牌:
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname,'./public')));
app.use(csrf());
app.use((req,next) => {
res.locals.isAuthenticated = req.session.isLoggedIn;
res.locals.csrftoken = req.csrftoken();
next();
});
我想我正在用这一行检查令牌
<input type="hidden" name="_csrf" value="<%= csrftoken %>">
表格内
解决方法
解决方案:
如果有人在做和我一样的课程,请确保中间件的顺序没问题,并且你改变了这一行 new Date().toISOString().replace(/[-T:.Z]/g,"") + '-' + file.originalname) 到 uuidv4() + '-' + file.originalname 如果你使用的是 windows(不要忘记安装并需要这一行 const { v4: uuidv4 } = require('uuid '); 在 app.js 中)。请参阅 app.js 以查看中间件的顺序(我基本上移动了 app.use(multer({ storage: fileStorage,fileFilter: fileFilter }).single('image')); 在会话的顶部)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。