浏览器存储技术 Cookie

服务器将少量数据交于浏览器存储管理

解决 http 无状态协议的问题(无法区分多次请求是否发送自同一客户端

一个网页一般最多 20个的 cookie,每个 cookie 一般 4KB

第一次访问 url,服务器会创建一个 Cookie 给浏览器

浏览器会保存 Cookie,以后每次请求,都会携带所有 Cookie,一并发送给服务器

  • 登录 Cookie

1. 浏览器发送请求给服务器,请求登录

  • app.get('/cookie1', (request, response)=>{
    response.cookie('userid', '123456', {masAge: 100*3600*24});
    response.send('cookie1 的 get 请求 的响应');
    });

2. 服务器返回响应给浏览器,会创建并发送 Cookie(包含了当前用户的唯一标识)

3. 浏览器接受响应,会将 Cookie 保存下来

4. 浏览器下一次发送请求时,都会自动携带上 Cookie

  • npm install cookie-parser
  • const cookieParser = require('cookie-parser');
    app.use(cookieParser()); // 应用中间件,解析 Cookie 的数据,挂载到 request.cookie 上
    app.get('/cookie2', (request, response)=>{
    response.send(request.cookies);
    });

5. 服务器接受到请求,解析 Cookie,从而判断是哪个用户发送的请求(解决 http 协议无状态问题)

6.  主动清除 Cookie

  • app.get('/cookie3', (request, response)=>{
    response.clearCookie('userid');
    response.send('key 为 userid 的 cookie 已经清除');
    });
  • 前端操作 Cookie
  • 清除 Cookie
  • document.cookie = 'hello=123; expires=' + new Date(Data.now()-1000);
  • 判断用户到底登录没?哪个用户登录的?

不能将用户的隐私直接暴露

数据加密 或者 数据库文档 id 装进 Cookie

res.cookie('userid', user.id, {maxAge: 1000*24*3600*7});

  • const cookieParser = require('cookie-parser');
    
    indexRouter.use(cookieParser());    // 应用中间件,解析 Cookie 的数据,挂载到 request.cookie 上
    
    indexRouter.get('/user_center',async (request, response)=>{
    const {userId} = request.cookies;
    if(userId){ // 用户已经登录
    const findRet = await userInfoModel.findOne({"_id":userId});
    if(findRet){
    let serverInfo = {uName:findRet.userName};
    response.render('user_center.ejs', {serverInfo});
    }else{ // 恶意 Cookie
    response.clearCookie("userId");
    response.redirect('/login');
    };
    }else{ // 用户未登录
    response.redirect('/login');
    };
    });
  • 登录注册实例

package.json

  • {
    "name": "node_express",
    "version": "1.0.0",
    "main": "index.js",
    "license": "MIT",
    "dependencies": {
    "connect-mongo": "^2.0.3",
    "cookie-parser": "^1.4.3",
    "ejs": "^2.6.1",
    "express": "^4.16.4",
    "mongoose": "^5.4.0",
    "sha1": "^1.1.1"
    }
    }

public/register.html

  • <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8"/>
    <title>用户注册</title> <link rel="stylesheet" type="text/css" href="css/index.css"/>
    </head> <body>
    <div id="outer_box" class="register">
    <h2>用户注册</h2>
    <form action="http://localhost:3000/register" method="post">
    <div class="clothes">
    <label for="input_name">用&nbsp;户&nbsp;名</label>
    <input id="input_name" type="text" name="user_name" placeholder="请输入用户名" />
    </div> <div class="clothes">
    <label for="input_pwd">密&nbsp;&nbsp;&nbsp;码</label>
    <input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" />
    </div> <div class="clothes">
    <label for="input_repeat_pwd">确认密码</label>
    <input id="input_repeat_pwd" type="password" name="user_repeat_pwd" placeholder="请再次输入密码" />
    </div> <div class="clothes">
    <label for="input_email">注册邮箱</label>
    <input id="input_email" type="text" name="user_email" placeholder="请输入邮箱地址" />
    </div> <div class="clothes">
    <button class="register btn" type="submit">注册</button>
    <a class="btn" href="http://localhost:3000/login">
    <button type="button">登录</button>
    </a>
    </div>
    </form>
    </div>
    </body>
    </html>

public/login.html

  • <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8"/>
    <title>用户登录</title> <link rel="stylesheet" type="text/css" href="css/index.css"/>
    </head> <body>
    <div id="outer_box" class="login">
    <h2>用户登录</h2>
    <form action="http://localhost:3000/login" method="post">
    <div class="clothes">
    <label for="input_name">用&nbsp;户&nbsp;名</label>
    <input id="input_name" type="text" name="user_name" placeholder="请输入用户名" />
    </div> <div class="clothes">
    <label for="input_pwd">密&nbsp;&nbsp;&nbsp;码</label>
    <input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" />
    </div> <div class="clothes">
    <a class="btn" href="http://localhost:3000/register">
    <button type="button">注册</button>
    </a>
    <button class="login btn" type="submit">登录</button>
    </div>
    </form>
    </div>
    </body>
    </html>

public/user_center.html

  • <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8"/>
    <title>用户中心</title> <link rel="stylesheet" type="text/css" href="css/index.css"/>
    </head> <body>
    <div id="outer_box" class="login">
    <h2>个人空间</h2>
    </div>
    </body>
    </html>

public/css/index.css

  • body {
    width: 100%;
    height: 100%; color: #000;
    background: #b9c2a4;
    background-size: cover; /* 指定背景图片大小 */
    } /*************************************************/
    #outer_box {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: #1a45c3;
    } #outer_box.login {
    color: #9e098b;
    } #outer_box.register {
    color: #1a45c3;
    } #outer_box>h2{
    padding-bottom: 40px;
    margin-left: -50px;
    } .clothes {
    position: relative;
    width: 260px;
    display: flex;
    justify-content: space-between;
    margin: 20px 0;
    font-size: 18px;
    line-height: 32px;
    } .tips {
    position: absolute;
    top: 0;
    right: -100%;
    margin-left: -50%;
    margin-top: 2px;
    width: 252px;
    height: 32px;
    text-align: center;
    color: #f00;
    } .clothes>label{
    width: 80px;
    text-align: center;
    } .clothes>input{
    width: 170px;
    height: 32px;
    } button {
    width: 100%;
    height: 100%; font-size: 16px;
    background-color: #c4ceda;
    cursor: pointer;
    } .clothes .btn{
    width: 64px;
    height: 32px;
    margin: 0 20px;
    } .clothes button.register{
    background-color: #1a45c3;
    color: #fff;
    } .clothes button.login{
    background-color: #9e098b;
    color: #fff;
    } #outer_box>h2 {
    position: relative;
    } #server_info {
    display: block;
    width: 139px;
    height: 20px;
    border-radius: 10px; position: absolute;
    top: 0;
    right: 0; color: red;
    font-size: 14px;
    text-align: center;
    line-height: 20px;
    background-color: #cecece;
    }

views/register.ejs

  • <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8"/>
    <title>用户注册</title> <link rel="stylesheet" type="text/css" href="css/index.css"/>
    </head> <body>
    <div id="outer_box" class="register">
    <h2>用户注册</h2>
    <form action="http://localhost:3000/register" method="post">
    <div class="clothes">
    <label for="input_name">用&nbsp;户&nbsp;名</label>
    <input id="input_name" type="text" name="user_name" value="<%= serverInfo.uName %>" placeholder="请输入用户名" />
    <div class="tips"><%= serverInfo.nameErr %></div>
    </div> <div class="clothes">
    <label for="input_pwd">密&nbsp;&nbsp;&nbsp;码</label>
    <input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" />
    <div class="tips"><%= serverInfo.passwordErr %></div>
    </div> <div class="clothes">
    <label for="input_repeat_pwd">确认密码</label>
    <input id="input_repeat_pwd" type="password" name="user_repeat_pwd" placeholder="请再次输入密码" />
    <div class="tips"><%= serverInfo.repeatErr %></div>
    </div> <div class="clothes">
    <label for="input_email">注册邮箱</label>
    <input id="input_email" type="text" name="user_email" value="<%= serverInfo.uEmail %>" placeholder="请输入邮箱地址" />
    <div class="tips"><%= serverInfo.emailErr %></div>
    </div> <div class="clothes">
    <button class="register btn" type="submit">注册</button>
    <a class="btn" href="http://localhost:3000/login">
    <button type="button">登录</button>
    </a>
    </div>
    </form>
    </div>
    </body>
    </html>

views/login.ejs

  • <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8"/>
    <title>用户登录</title> <link rel="stylesheet" type="text/css" href="css/index.css"/>
    </head> <body>
    <div id="outer_box" class="login">
    <h2>
    用户登录
    <div id="server_info">
    <%= serverInfo.tips %>
    </div>
    </h2>
    <form action="http://localhost:3000/login" method="post">
    <div class="clothes">
    <label for="input_name">用&nbsp;户&nbsp;名</label>
    <input id="input_name" type="text" name="user_name" value="<%= serverInfo.uName %>" placeholder="请输入用户名" />
    </div> <div class="clothes">
    <label for="input_pwd">密&nbsp;&nbsp;&nbsp;码</label>
    <input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" />
    </div> <div class="clothes">
    <a class="btn" href="http://localhost:3000/register">
    <button type="button">注册</button>
    </a>
    <button class="login btn" type="submit">登录</button>
    </div>
    </form>
    </div>
    </body>
    </html>

views/user_center.ejs

  • <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8"/>
    <title>用户中心</title> <link rel="stylesheet" type="text/css" href="css/index.css"/>
    </head> <body>
    <div id="outer_box" class="login">
    <h2><%= serverInfo.uName%> - 个人空间</h2>
    </div>
    </body>
    </html>

db/connectDB.js

  • const mongoose = require('mongoose');
    
    module.exports = new Promise((resolve, reject)=>{
    mongoose.connect('mongodb://localhost:27017/user_database', {useNewUrlParser:true})
    mongoose.connection.once('open', err=>{
    if(err){
    console.log(err);
    reject(err);
    }else{
    resolve('数据库已连接');
    };
    });
    });

models/userModel.js

  • const mongoose = require('mongoose');
    
    const Schema = mongoose.Schema;
    const fieldSchema = new Schema({
    "userName": {
    "type": String,
    "unique": true,
    "required": true
    },
    "userPassword": {
    "type": String,
    "required": true
    },
    "userEmail": {
    "type": String,
    "unique": true,
    "required": true
    },
    "createTime": {
    "type": Date,
    "default": Date.now()
    }
    }); module.exports = mongoose.model("user_info", fieldSchema);

routers/get/index_router.js

  • const express = require('express');
    
    const promiseConnect = require('../../db/connectDB.js');
    const userInfoModel = require('../../models/userModel.js'); const {resolve} = require('path');
    const cookieParser = require('cookie-parser'); const indexRouter = new express.Router();
    indexRouter.use(cookieParser());
    /************************ get ***********************/
    indexRouter.get('/', (request, response)=>{
    response.sendFile(resolve(__dirname, '../../public/login.html'));
    }); indexRouter.get('/login', (request, response)=>{
    response.sendFile(resolve(__dirname, '../../public/login.html'));
    }); indexRouter.get('/register', (request, response)=>{
    response.sendFile(resolve(__dirname, '../../public/register.html'));
    }); promiseConnect.then(result=>{
    console.log("index_router.js - "+result); indexRouter.get('/user_center',async (request, response)=>{
    const {userId} = request.cookies;
    if(userId){ // 用户已经登录
    const findRet = await userInfoModel.findOne({"_id":userId});
    if(findRet){
    let serverInfo = {uName:findRet.userName};
    response.render('user_center.ejs', {serverInfo});
    }else{ // 恶意 Cookie
    response.clearCookie("userId");
    response.redirect('/login');
    };
    }else{ // 用户未登录
    response.redirect('/login');
    };
    });
    }).catch(err=>console.log(err)); module.exports = indexRouter;

routers/post/form_router.js

  • const express =  require('express');
    const sha1 = require('sha1'); const promiseConnect = require('../../db/connectDB.js');
    const userInfoModel = require('../../models/userModel.js'); const formRouter = new express.Router(); /************************ post ***********************/
    let logged = false ;
    promiseConnect.then(result=>{
    console.log("form_router.js - "+result); formRouter.post('/register', async (request, response)=>{
    const {
    user_name:uName,
    user_pwd:uPwd,
    user_repeat_pwd:urePwd,
    user_email:uEmail,
    } = request.body; /**** 解构赋值 ****/ userInfo = {
    "userName": uName,
    "userPassword": uPwd,
    "userEmail": uEmail
    }; let serverInfo = {uName, uEmail}; if(urePwd !== uPwd){
    serverInfo.repeatErr = '密码两次输入不一致';
    };
    if(!(/^[a-zA-Z][a-zA-Z0-9_]{5,20}$/.test(uName))){
    serverInfo.nameErr = '用户名不合法';
    };
    if(!(/^[a-zA-Z0-9_]{6,20}$/.test(uPwd))){
    serverInfo.passwordErr = '密码不合法';
    };
    if(!(/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(uEmail))){
    serverInfo.emailErr = '邮箱不合法';
    }; const fond = await userInfoModel.findOne({"userName": uName});
    if(fond){
    serverInfo.nameErr = '用户名已被注册';
    }; const badEmail = await userInfoModel.findOne({"userEmail": uEmail});
    if(badEmail){
    serverInfo.emailErr = '邮箱已被注册';
    }; if(serverInfo.repeatErr || serverInfo.nameErr || serverInfo.passwordErr || serverInfo.emailErr){
    response.render('register.ejs', {serverInfo}); // 渲染错误信息
    return;
    }else{
    userInfo.userPassword = sha1(userInfo.userPassword);
    await userInfoModel.create(userInfo);
    response.redirect('/login'); // 跳转到登录
    };
    }); formRouter.post('/login',async (request, response)=>{
    logged = false;
    let uName = request.body['user_name'];
    let uPwd = request.body['user_pwd'];
    userInfo = {
    "userName": uName,
    "userPassword": uPwd
    }; if(!(/^[a-zA-Z][a-zA-Z0-9_]{5,20}$/.test(uName))){
    logged = false;
    }else if(!(/^[a-zA-Z0-9_]{6,20}$/.test(uPwd))){
    logged = false;
    };
    try{
    const findName = await userInfoModel.findOne({"userName": uName});
    if(findName && (findName.userPassword===sha1(uPwd)) ){
    logged = true;
    }; let serverInfo = {uName};
    if(logged){
    response.cookie('userId', findName.id, {maxAge: 1000*24*3600*7});
    response.redirect('/user_center'); // 跳转到用户 个人空间 页面
    }else{
    serverInfo.tips='用户名或密码错误';
    response.render('login.ejs', {serverInfo}); // 渲染错误信息
    };
    }catch(err) {
    cosole.log(err);
    }
    });
    }).catch(err=>console.log(err)); module.exports = formRouter;

index.js

  • const express =  require('express');
    const app = express(); const indexRouter = require('./routers/get/index_router.js');
    const formRouter = require('./routers/post/form_router.js'); app.set('views', 'views'); // 1. 配置模板路径
    app.set('view engine', 'ejs'); // 2. 配置模板引擎 /*********************** 中间件 **********************/
    // 暴露路由 login.html register.html
    app.use(express.static('public')); // 默认调用 next(); // 将 用户输入的数据 挂载到 请求体 request.body 上
    app.use(express.urlencoded({extended: true})); // 默认调用 next(); app.use(indexRouter);
    app.use(formRouter); /**************** 端口号 3000, 启动服务器 ***************/
    app.listen(3000, err=>console.log(err?err:'\n\n服务器已启动: http://localhost:3000\n\t\tHunting Happy!'));

Node.js_express_浏览器存储技术 Cookie(服务器将少量数据交于浏览器存储管理)的更多相关文章

  1. 计算机浏览器存储技术cookie、sessionStorage、localStorage

    HTTP无状态协议是指协议对于事务处理没有记忆能力.会话跟踪协议的状态是指下一次传输可以"记住"这次传输信息的能力,无状态是指同一个会话(注意什么叫同一个会话)的连续两个请求互相不 ...

  2. http 协议_DNS_域名解析 DNS 服务器_内容分发网络 CDN_缓存机制_HTML5 浏览器存储技术_cookie_sessionStorage_localStorage

    TCP/IP 协议族 是按层次去划分的 应用层    决定了向用户提供应用服务时通信的活动. FTP 协议(文件传输协议)DNS(域名协议)HTTP(超文本传输协议) 传输层    提供处于网络连接中 ...

  3. 浏览器存储:cookie

    Cookie是什么:cookie是指存储在用户本地终端上的数据,同时它是与具体的web页面或者站点相关的.Cookie数据会自动在web浏览器和web服务器之间传输,也就是说HTTP请求发送时,会把保 ...

  4. 浅谈浏览器存储(cookie、localStorage、sessionStorage)

    今天我们从前端的角度了解一下浏览器存储,我们常见且常用的存储方式主要由两种:cookie.webStorage(localStorage和sessionStorage).下面我们来一一认识它们. Co ...

  5. Ajax交互,浏览器接收不到服务器的Json数据(跨域问题)

    该问题的情景如下: 问题描述 Ajax的请求代码放在一台机器上,而服务器的java 路由程序放在另一个机子上,所以Ajax的url填写的是带"http://"  的地址,而不是相对 ...

  6. 深入了解浏览器存储:对比Cookie、LocalStorage、sessionStorage与IndexedDB

    摘要: 对比Cookie.LocalStorage.sessionStorage与IndexedDB 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 随着移动网络的发展与演化,我 ...

  7. LocalStorage存储和cookie存储

    localStorage是H5的新特性,主要用来本地存储,一般浏览器支持的大小是5M,不同浏览器会有所不同,解决了cookie存储空间不足的问题. 2.使用:     ⑴.存 if(!window.l ...

  8. 会话技术cookie与session

    目录 会话技术cookie 会话技术 cookie 服务器怎样把Cookie写 给客户端 服务器如何获取客户端携带的cookie session session简介 Session如何办到在一个ser ...

  9. 会话跟踪技术 - Cookie 和 Session 快速上手 + 登陆注册案例

    目录 1. 会话跟踪技术概述 2. Cookie 2.1 Cookie的概念和工作流程 2.2 Cookie的基本使用 2.3 Cookie的原理分析 2.4 Cookie的使用细节 2.4.1 Co ...

随机推荐

  1. 【转】Reflector、reflexil、De4Dot、IL相关操作指令合集

    PS:CTRL+F 输入你需要的内容,可以快速查找页面上的内容. 名称 说明 Add 将两个值相加并将结果推送到计算堆栈上. Add.Ovf 将两个整数相加,执行溢出检查,并且将结果推送到计算堆栈上. ...

  2. 【搞事情】VS2015下的openGL初始化

    环境:glfw+glew+visual studio 2015 原材料下载链接: glfw 下载 glew 下载 glm库 下载 cmake 下载 (我下载的时候有些官网戳不开(大概校园网问题)... ...

  3. 使用echarts-for-react 绘制折线图 报错:`series.type should be specified `

    解决办法: 在动态获取值的函数前面加 访问器属性  get ,去获取对象的属性 @inject('commonStore', 'reportUIStore') @observer class Line ...

  4. swoole简单demo测试

    测试代码 1.server.php: <?php $serv = new swoole_server("0.0.0.0", 9502); $serv->on('conn ...

  5. 2.解决虚拟机中centos联网的问题

    首先:打开虚拟机的编辑菜单,选择“虚拟机网络编辑器” 虚拟机网络编辑器 在虚拟机网络编辑器中选择还原默认设置 接下来开启CentOS7虚拟机 在这里需要注意的是必需以管理员身份来进行设置,所以要用管理 ...

  6. EasyUI datagrid 的多条件查询

    <script type="text/javascript">         $(function () {            $("#dg" ...

  7. javascript基础 之 保留关键字

    1,保留关键字 意思是:特定的字符串要么是已经有指代了要么是未来将要有指代,所以取名字不要用保留关键字里的字符串 js保留关键字 abstract arguments boolean break by ...

  8. 3D Slicer中文教程(四)—图像分割

    1.数据获取 (1)下载3D Slicer自带的样本数据 (2)选择自由的数据 (3)网上数据库等其他方式下载数据 2.分割工具 Segment Editor是一个用于分割的模块.细分(也称为轮廓)描 ...

  9. 第一章 Bootstrap简介

    一.Bootstrap简介 Bootstrap是基于 HTML.CSS.JAVASCRIPT 的前端框架,它简洁灵活,使得 Web 开发更加快捷.它由Twitter的设计师Mark Otto和Jaco ...

  10. Windows server 2012配置WebDeploy发布网站

    以前都是使用win2008r2服务器,最近更新了操作系统到WINDOWS 2012,发现以前的做法已经不适用了. win2008r2配置WebDeploy: https://www.cnblogs.c ...