项目实战(连载):基于Angular2+Mongodb+Node

作者: fwytech | 来源:发表于2017-02-26 11:48 被阅读698次

    本章主要讲什么(一句话)?

    本章主要讲解:重构Node后台加入Session支持及前台跨域访问配置

    一、前言

    上一章主要对项目的路由功能进行完善:登录路由,注册路由,发表文章路由,获取用户信息路由等,本章会继续对于本项目的Node部分功能进行完善。本将也将会是最后一章关于Node,Mongose相关技术及代码的讲解,下一章起,将正式启动angular2的前端部分,敬请期待 :)

    二、本章技术关健词

    Node、MongoDB、Angular2、Mongoose、Route、Session、跨域

    三、本章涉及核心技术点

    四、内容

    4.1、Session

    4.1.1、为什么需要Session?

         cookie虽然很方便,但是使用cookie有一个很大的弊端,cookie中的所有数据在客户端就可以被修改,数据非常容易被伪造,那么一些重要的数据就不能存放在cookie中了,而且如果cookie中数据字段太多会影响传输效率。为了解决这些问题,就产生了session,session中的数据是保留在服务器端的。HTTP协议(http://www.w3.org/Protocols/)是“一次性单向”协议。服务端不能主动连接客户端,只能被动等待并答复客户端请求。客户端连接服务端,发出一个HTTP Request,服务端处理请求,并且返回一个HTTP Response给客户端,本次HTTP Request-Response Cycle结束。 我们看到,HTTP协议本身并不能支持服务端保存客户端的状态信息。于是,Web Server中引入了session的概念,用来保存客户端的状态信息。

    4.1.2、什么是Session?

    Session:在计算机中,尤其是在网络应用中,称为“会话”。Session直接翻译成中文比较困难,一般都译成时域。在计算机专业术语中,Session是指一个终端用户与交互系统进行通信的时间间隔,通常指从注册进入系统到注销退出系统之间所经过的时间。具体到Web中的Session指的就是用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间。因此从上述的定义中我们可以看到,Session实际上是一个特定的时间概念。

    4.1.3、Session的工作原理

    一个session就是一系列某用户和服务器间的通讯。服务器有能力分辨出不同的用户。一个session的建立是从一个用户向服务器发第一个请求开始,而以用户显式结束或session超时为结束。

    其工作原理是这样的:

    1.当一个用户向服务器发送第一个请求时,服务器为其建立一个session,并为此session创建一个标识号;

    2.这个用户随后的所有请求都应包括这个标识号。服务器会校对这个标识号以判断请求属于哪个session。这种机制不使用IP作为标识,是因为很多机器是通过代理服务器方式上网,没法区分每一台机器。

    形象比喻:

         这里用一个形象的比喻来解释session的工作方式。假设Web Server是一个商场的存包处,HTTP Request是一个顾客,第一次来到存包处,管理员把顾客的物品存放在某一个柜子里面(这个柜子就相当于Session),然后把一个号码牌交给这个顾 客,作为取包凭证(这个号码牌就是Session ID)。顾客(HTTP Request)下一次来的时候,就要把号码牌(Session ID)交给存包处(Web Server)的管理员。管理员根据号码牌(Session ID)找到相应的柜子(Session),根据顾客(HTTP Request)的请求,Web Server可以取出、更换、添加柜子(Session)中的物品,Web Server也可以让顾客(HTTP Request)的号码牌和号码牌对应的柜子(Session)失效。顾客(HTTP Request)的忘性很大,管理员在顾客回去的时候(HTTP Response)都要重新提醒顾客记住自己的号码牌(Session ID)。这样,顾客(HTTP Request)下次来的时候,就又带着号码牌回来了。

    4.1.4、express中的Session

    express中操作session要用到express-session

    (https://github.com/expressjs/session )这个模块,主要的方法就是session(options),其中options中包含可选参数,主要有:

    Øname:设置cookie中,保存session的字段名称,默认为connect.sid。

    Østore:

    session的存储方式,默认存放在内存中,也可以使用redis,mongodb等。express生态中都有相应模块的支持。

    Øsecret:通过设置的secret字符串,来计算hash值并放在cookie中,使产生的signedCookie防篡改。

    Øcookie:设置存放session id的cookie的相关选项,默认为

    (default: { path: '/', httpOnly: true,secure: false, maxAge: null })

    genid:产生一个新的session_id时,所使用的函数,默认使用uid2这个npm包。

    Ørolling:每个请求都重新设置一个cookie,默认为false。

    Øresave:即使session没有被修改,也保存session值,默认为true。

    express-session默认使用内存来存session,对于开发调试来说很方便

    注意:在Express4.x中使用session时要额外安装express-session包:

    两种安装方式:

    第一种:cnpm install express-session

    第二种:package.json包中加入"express-session":"~1.14.1"

    到其对应目录下执行:npm install命令

    var app = express();

    var session= require('express-session');

    app.listen(5000);

    //按照上面的解释,设置session的可选参数

    app.use(session({

    secret: 'recommand 128 bytes random string', //建议使用128个字符的随机字符串

    cookie: { maxAge: 60 * 1000 }

    }));

    app.get('/', function (req, res) {

    //检查session中的isVisit字段

    //如果存在则增加一次,否则为session设置isVisit字段,并初始化为1。

    if(req.session.isVisit) {

    req.session.isVisit++;

    res.send('

    第' + req.session.isVisit + '次来此页面

    ');

    } else {

    req.session.isVisit = 1;

    res.send("欢迎第一次来这里");

    console.log(req.session);

    }

    });

    4.1.4、项目中Session的代码

        基本上跟上述的配置大同小异,在这里不累述,后面大家可以直接看代码!此处略

    4.2、跨域 

    4.2.1、什么是跨域?

    概念:只要协议、域名、端口有任何一个不同,都被当作是不同的域。

    URL                                               说明                      是否允许通信

    http://www.a.com/a.js

    http://www.a.com/b.js               同一域名下                       允许

    http://www.a.com/lab/a.js

    http://www.a.com/script/b.js     同一域名下不同文件夹     允许

    http://www.a.com:8000/a.js

    http://www.a.com/b.js                  同一域名,不同端口      不允许

    http://www.a.com/a.js

    https://www.a.com/b.js                   同一域名,不同协议     不允许

    http://www.a.com/a.js

    http://70.32.92.74/b.js                        域名和域名对应ip       不允许

    http://www.a.com/a.js

    http://script.a.com/b.js                        主域相同,子域不同     不允许

    http://www.a.com/a.js

    http://a.com/b.js                   同一域名,不同二级域名(同上)       不允许(cookie这种情况下也不允许访问)

    http://www.cnblogs.com/a.js

    http://www.a.com/b.js                                        不同域名               不允许

    4.2.2、为什么浏览器不支持跨域请求

    主要的原因还是安全性,防止CSRF攻击。

    CSRF是什么?

        CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

    CSRF可以做什么?

       你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。

    4.2.3、跨域的几种解决办法

    解决跨域访问的办法有N种,最常用的为以下几种:

    1> document.domain + iframe      (只有在主域相同的时候才能使用该方法)

    2> 动态创建script

    3> location.hash + iframe

    4> window.name + iframe

    5> postMessage(HTML5中的XMLHttpRequest Level 2中的API)

    6> CORS

    7> JSONP

    8> web sockets

    要想了解以上8种的具体实现,可以参考此篇文章:http://blog.csdn.net/joyhen/article/details/21631833

    但就像知道孔乙已的茴香豆的几种写法一样,我认为没有太大的意义,我们更看重的怎么样解决问题。

    根据我个人的工作经验,在解决跨域时无外乎两种,一种是客户端想办法,如使用JosnP(这个后面讲Angular2时我会给大家演示),另一种在服务器端想办法,做配置 。相比较而言,第二种会配置更灵活,功能更强大,所以这里我主要给大家演示第二种方式。

    4.2.4、Node中前端JS跨域配置代码

    好,重点来了,直接上代码:

    打开index.js,找到以下代码段:

    var crypto = require('crypto'),

    User = require('../models/user.js'),

    Post = require('../models/post.js');

    settings = require('../settings');

    module.exports = function(app) {

        //此处我们将加入跨域代码配置

    }

    在上述注释处加入以下代码:

    app.all('*', function(req, res, next) {

    res.header('Access-Control-Allow-Origin',settings.client);

    res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');

    res.setHeader("Access-Control-Max-Age", "3600");

    res.setHeader("Access-Control-Allow-Credentials", "true"); //是否支持cookie跨域

    next();

    });

    //跨域预检查所有的get请求

    app.all('/get/*', function(req, res, next) {

    res.header('Access-Control-Allow-Methods', 'GET,OPTIONS');

    if (req.method == 'OPTIONS') {

    res.send(200);

    } else {

    console.log(req.method);

    next();

    }

    });

    //跨域预检查所有的post请求

    app.all('/post/*', function(req, res, next) {

    res.header('Access-Control-Allow-Methods', 'POST,OPTIONS');

    if (req.method == 'OPTIONS') {

    res.send(200);

    } else {

    console.log(req.method);

    next();

    }

    });

    1. app.all('*', function(req, res, next) {。。。}

    app.all('/get/*', function(req, res, next) {。。。}

    app.all('/post/*', function(req, res, next) {。。。}

    注意:第一行 * 代表,所有前端路由请求,必须经过此“过滤器”拦截处理

    /get/*,代表所有以get打头的路由请求,必须经过此“过滤器”拦截处理,如:http://localhost:8800/get/user?name=zzz  ,这个请求就会被 此“过滤器”拦截

    /post/*,代表所有以post打头的路由请求,必须经过此“过滤器”拦截处理,如:http://localhost:8800/post/reg ,这个请求就会被 此“过滤器”拦截

    2. res.header('Access-Control-Allow-Origin',settings.client);

    这种代码的作用是:只有当目标页面的response中,包含了Access-Control-Allow-Origin这个header,并且它的值里有我们自己的域名时,浏览器才允许我们拿到它页面的数据进行下一步处理,即设置 “同源策略“, 如果它的值设为*,则表示谁都可以用:Access-Control-Allow-Origin: *,但一般我们不会这么干,出于安全性考虑,最好还是设定你允许访问的网站

    这名代码相当于设置了:

    Access-Control-Allow-Origin : http://localhost:3000

    注意settings.client的配置:

    settings.js

    module.exports = {

    cookieSecret: 'myblog',

    db: 'blog',

    host: 'localhost',

    port: 27017,

    client:'http://localhost:3000'  //用于设置跨域

    };

    3. res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');

    OPTIONS请求头部中会包含以下头部:Origin、Access-Control-Request-Method、Access-Control-Request-Headers,发送这个请求后,服务器可以设置如下头部与浏览器沟通来判断是否允许这个请求。yourHeaderFeild 这个表示你可以自定义与浏览器客户端沟通的信息。

    4. res.setHeader("Access-Control-Max-Age", "3600");

    Access-Control-Max-Age: 3600 // 表明在3600秒内,不需要再发送预检验请求,可以缓存该结果,即CORS缓存配置

    5. res.setHeader("Access-Control-Allow-Credentials", "true"); //是否支持cookie跨域,ture代表支持,注意这个配置很重要,不然后面我们和Angular2 Http请求时,发现不配置此项无法进行服务端的Session操作

    6. Access-Control-Allow-Methods : 表明它允许GET、POST、PUT、DELETE的外域请求

    注意app.all('/get/*'。。。)与 app.all('/post/*',。。。)里面的内容的区别,这样做的目的,主要还是从更高效和更安全的角度去考虑的

    7. 注意,上面各个 ” 过滤器“里的 next() 不可省

    五、后述

         好了,项目到了这里,关于Node与MongoDB操作部分内容,已经基本全部结束,希望大家能够有所收获,后继我们将继续Angular2部分,因近期工作比较忙,所以更新文章有些慢,大家见谅,大家支持的话,可以给我点赞,评论,转发,您的支持将会是持续下去的动力!

    本章代码下载:http://pan.baidu.com/s/1dFf22yt

    下章剧透:

    《项目实战:基于Angular2+Mongodb+Node技术实现的多用户博客系统教程(11)》

                                                                                               --  Angular2前台框架搭建

    相关文章

      网友评论

      • FD365:博主,项目下载下来,直接执行,报这个错是什么情况呢?
        > node-blog@0.0.0 start F:\blog\1-9_node-blog\node-blog
        > node ./bin/www

        module.js:457
        throw err;
        ^

        Error: Cannot find module 'F:\blog\1-9_node-blog\node-blog\bin\www'
        at Function.Module._resolveFilename (module.js:455:15)
        at Function.Module._load (module.js:403:25)
        at Module.runMain (module.js:590:10)
        at run (bootstrap_node.js:394:7)
        at startup (bootstrap_node.js:149:9)
        at bootstrap_node.js:509:3
        npm ERR! code ELIFECYCLE
        npm ERR! errno 1
        npm ERR! node-blog@0.0.0 start: `node ./bin/www`
        npm ERR! Exit status 1
        npm ERR!
        npm ERR! Failed at the node-blog@0.0.0 start script.
        npm ERR! This is probably not a problem with npm. There is likely additional log
        ging output above.
      • 静静前行:您好,我目前正在学习angular2.0,看到您的博文,感觉对我学习很有帮助。就是博主最近好像没在更新,想请教下,angular2的项目打包到express的public目录下,之后,nodejs应该怎么和前端进行交互呢。
      • 游戏化:MEAN可是个大工程 估计要连载二三十章吧( ̄▽ ̄)" 加油!
      • sunny2786:楼主,可以用markdown的语法黏贴代码
        fwytech:@雪覆2011 :+1:OK!
      • 泽拉丶::blush: 很实用,谢谢
      • 36020b860fed:博主您好,请问angular2搭建的项目对typescript的要求高不呢?
        36020b860fed:@fwytech 嗯嗯,好的
        fwytech:@ReySun 环境搭建倒不要求,但后期的代码要求,最好先去了解下typescript

      本文标题:项目实战(连载):基于Angular2+Mongodb+Node

      本文链接:https://www.haomeiwen.com/subject/ixjnwttx.html