Node.js_express_临时会话对象 session

临时会话对象 session

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

npm install express-session

npm install connect-mongo

  • 基本使用
  • const session = require('express-session');
    const MongoStore = require('connect-mongo')(session);
  • app.use(express.session({
          secret: 'keyboard cat',    //  加密字符串,参与 sessionid 加密
          saveUninitialized: false, // 在存储某东东之前,不会创建 session 对象
          resave: false, // 如果没有修改 session 对象,就不会重新保存
          store: new MongoStore({
                url: 'mongodb://localhost: 27017/test-app',    // 连接数据库的地址
                touchAfter: 24 * 3600 // 24 小时更新一次
          })
    }));
    
    ...
        // 设置 session    会在数据库中创建 session 对象
        // 保存 userId=findRet.id 到数据库
        request.session.userId = findRet.id;
    ...
        // 解析 cookie 中的 session 去数据库中找对应 sessionId 的数据
        // 返回一个 cookie
        const {userId} = request.session;
    ...
  • session 优势

读写二合一

存储数据近乎无限大,取决于 服务器 的存储容量

传输流量小(数据传输过程中 cookie 更小更少)

  • 私有变量 _name
  • 一定要在最开始 激活 session    
    app.use(session({... ...});

登录/注册 实例

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",
        "express-session": "^1.15.6",
        "mongoose": "^5.4.0",
        "sha1": "^1.1.1"
      }
    }

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/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/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/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/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/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/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)=>{
            // s4: 解析 cookie 中的 session 去数据库中找对应 sessionId 的数据
            const {userId} = request.session;    // 返回一个 Cookie 对象
            
            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){
                    // s3: 设置 session    会在数据库中创建 session 对象
                    // 保存 userId=findRet.id 到数据库
                    request.session.userId = findName.id;
                    
                    response.redirect('/user_center');    // 跳转到用户 个人空间 页面
                }else{
                    serverInfo.tips='用户名或密码错误';
                    response.render('login.ejs', {serverInfo});    // 渲染错误信息
                };
            }catch(err) {
                console.log(err);
            }
        });
    }).catch(err=>console.log(err));
    
    module.exports = formRouter;

index.js

  • const express =  require('express');
    // s1: 导入 session 模块
    const session = require('express-session');
    const mongoSession = require('connect-mongo')(session);
    
    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. 配置模板引擎
    
    /*********************** 中间件 **********************/
    // s2: 通过中间件,激活 session
    app.use(session({
        secret: 'myMongoSession',
        saveUninitialized: false,
        resave: false,
        store: new mongoSession({
            url: 'mongodb://localhost:27017/user_database',
            touchAfter: 1000*3600*24
        })
    }));
    
    // 暴露路由 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!'));

 

posted @ 2018-12-24 12:09  耶梦加德  阅读(745)  评论(0编辑  收藏  举报