在我的angular2应用程序中,我有一个以下HTTP Post.
const headers = new Headers(); headers.append('Content-Type','application/json'); const data = { email: this.form.value.email }; this.http.post('http://localhost:8080/api/user/email',data,{ headers: headers })
现在我想确保只有我的angular 2应用程序可以对用户api进行post调用.我结合Express和Angular 2对csrf进行了一些研究.在我的Angular 2应用程序中,我对app.module.ts文件进行了以下实现.
import { HttpModule,XSRFStrategy,CookieXSRFStrategy } from '@angular/http'; providers: [ { provide: XSRFStrategy,useValue: new CookieXSRFStrategy('csrftoken','X-CSrftoken') } ]
我认为这是向Angular 2实现XSRFStrategy的正确方法吗?
对于Express中的实现我遵循了一些教程,但没有任何成功.我收到的大部分时间:
ForbiddenError: invalid csrf token
如何在Express api中实现CSRF部分.这是我的Express配置:
// call the packages we need var express = require('express'); // call express var app = express(); // define our app using express var bodyParser = require('body-parser'); var cookieParser = require('cookie-parser'); var csrf = require('csurf'); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); var port = process.env.PORT || 8080; // set our port // ROUTES FOR OUR API // ============================================================================= var router = express.Router(); router.use(function (req,res,next) { console.log('Something is happening.'); res.setHeader("Access-Control-Allow-Origin","http://localhost:4200"); res.setHeader("Access-Control-Allow-Credentials","true"); res.setHeader("Access-Control-Allow-Methods","POST"); res.setHeader("Access-Control-Allow-Headers","Access-Control-Allow-Headers,Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers"); res.setHeader('Content-Type','application/json'); next(); }); app.use('/api',router); router.post('/user/email',function (req,res) { ..... [how to make sure that this post can only be fired from my angular 2 application ] }
更新#1
做了一些研究并在Angular 2文档中找到了以下内容:
//By default,Angular will look for a cookie called `'XSRF-TOKEN'`,and set //* an HTTP request header called `'X-XSRF-TOKEN'` with the value of the cookie on each request,
所以在我的Express应用程序中添加了以下部分:
const cookieOptions = { key: 'XSRF-TOKEN',secure: false,httpOnly: false,maxAge: 3600000 } var csrfProtection = csrf({ cookie: cookieOptions })
在后期路线我实施了如下保护:
router.post('/user/email',csrfProtection,res) { console.log('post incomming'); }):
我收到了以下响应标头
Access-Control-Allow-Credentials:true Access-Control-Allow-Headers:Access-Control-Allow-Headers,Access-Control-Request-Headers Access-Control-Allow-Methods:POST Access-Control-Allow-Origin:http://localhost:4200 Connection:keep-alive Content-Length:1167 Content-Type:text/html; charset=utf-8 Date:Mon,21 Nov 2016 20:07:12 GMT set-cookie:XSRF-TOKEN=O4JKkjAZRik2H7ml0DoxDc8s; Max-Age=3600000; Path=/ X-Content-Type-Options:nosniff X-Powered-By:Express
并且请求标头:
Accept:*/* Accept-Encoding:gzip,deflate,br Accept-Language:en-US,en;q=0.8,nl;q=0.6 Connection:keep-alive Content-Length:38 content-type:application/json Host:localhost:8080 Origin:http://localhost:4200 Referer:http://localhost:4200/profile/users
解决方法
默认情况下,Angular将查找名为“XSRF-TOKEN”的cookie并进行设置
一个名为“X-XSRF-TOKEN”的HTTP请求标头,其中包含每个请求的cookie值.
为了确保我们的后端可以设置XSRF-TOKEN cookie,我们必须代理我们对端口8080上运行的api的调用.我们可以使用proxy.config.json文件来实现.
{ "/api/*" : { "target": "http://localhost:8080","secure": false,"logLevel": "debug" } }
在我们的package.json文件中,我们修改脚本/启动函数以使用我们的proxy.config.json文件:
"scripts": { "start": "ng serve --proxy-config proxy.config.json",}
现在每次运行npm时,我们对/ api的调用都代理到localhost:8080.现在我们准备对我们的api服务器进行调用.
在我们的组件中,我们进行http post调用,并设置标头以使用content-type application / json.
ourfunction() { const headers = new Headers(); headers.append('Content-Type','application/json'); data = { key:value } this.http.post('/api/user/email',{ headers: headers }).subscribe( (resp: any) => console.log('resp',resp)); }
这就是我们在Angular2方面需要做的一切.现在我们实施Express方面.
var express = require('express'); var app = express(); var bodyParser = require('body-parser'); var cookieParser = require('cookie-parser'); var csrf = require('csurf'); var cors = require('cors')
我们初始化我们的应用程序并定义一些中间件以在我们的应用程序中使用
const cookieOptions = { key: 'XSRF-TOKEN',maxAge: 3600 } const corsOptions = { origin: 'http://localhost:4200',optionsSuccessstatus: 200 // some legacy browsers (IE11,varIoUs SmartTVs) choke on 204 };
这里我们设置用于csrf和cors中间件的选项.
const port = process.env.PORT || 8080; // set our port const csrfProtection = csrf({ cookie: cookieOptions }) const router = express.Router();
实现middelware.订单对于获得正确的结果非常重要.
app.use(bodyParser.urlencoded({extended: false})); app.use(bodyParser.json()); app.use('/api',router); app.use(cors(corsOptions)); app.use(cookieParser()); app.use(csrfProtection); router.use(function (req,next) { res.setHeader('Content-Type','application/json'); next(); });
这就是我们需要在Express方面做的所有事情.现在我们可以使用CSRF令牌保护我们的帖子调用.
Compleet express服务器文件
var express = require('express'); var app = express(); var bodyParser = require('body-parser'); var cookieParser = require('cookie-parser'); var csrf = require('csurf'); var cors = require('cors') const cookieOptions = { key: 'XSRF-TOKEN',varIoUs SmartTVs) choke on 204 }; const port = process.env.PORT || 8080; // set our port const csrfProtection = csrf({ cookie: cookieOptions }) const router = express.Router(); app.use(bodyParser.urlencoded({extended: false})); app.use(bodyParser.json()); app.use('/api','application/json'); next(); }); router.post('/user/email',res) { console.log('post incomming'); console.log('req',req.body); res.send('testing..'); }); app.listen(port); console.log('Magic happens on port ' + port);