用户登录状态

http是一个无状态协议,比如无法识别用户,保持登录状态

服务器识别用户

使用session识别用户


index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>首页</h1>
    <form action="/logout" method="post">
        <button>登出</button>
    </form>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form action="/login" method="post">
        <input type="text" name="username" placeholder="用户名">
        <input type="password" name="password" placeholder="密码">
        <button>登录</button>
    </form>
</body>
</html>

home.js

'use strict';

const { Controller } = require('egg');

class HomeController extends Controller {
  async index() {
    const { ctx } = this;
    if(this.ctx.session.user){
      await ctx.render("index");
    }else{
      ctx.redirect("/login");
    }
  }

  async login(){
    await this.ctx.render("login");
  }

  async doLogin(){
    let username = this.ctx.request.body.username;
    let password = this.ctx.request.body.password;
    if(username == "admin" && password == "123456"){
      this.ctx.session.user = username;
      this.ctx.redirect("/");
    }else{
      this.ctx.redirect("/login");
    }
  }

  async logout(){
    this.ctx.session.user = "";
    this.ctx.redirect("/login");
  }
}

module.exports = HomeController;

router.js

'use strict';//严格模式

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => { 
  const { router, controller } = app;
  router.get('/', controller.home.index);
  router.get('/login', controller.home.login);
  router.post('/login', controller.home.doLogin);
  router.post('/logout', controller.home.Logout);
};

post拦截记得要去掉

使用jwt(json web token)识别用户



config.default.js

生成token和校验token

async index(){
        //这段用于教学
        let user = {
            username:"admin"
        }//json对象
        /*egg-jwt插件导入后能引用this.app里的jwt属性来获取jwt对象
        它的sign方法能将里面的对象签名以实现加密*/
        //用户登录服务器会生成一个token,然后把token发送给客户端
       let token = this.app.jwt.sign(user,this.app.config.jwt.secret);//对象,密钥
        /*    this.ctx.body = token;
        得到一串很长的字符串
        eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
        eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNjc1Njg5MDUxfQ.
        eC0L0YRrxTYKrpP3uNUJ0aaPIs8Sl4ZMcZhmDoKDKp4
        */

        try{
            //用户再次请求数据的时候都要带着生成的token给服务器校验
            let decode = this.app.jwt.verify(token,this.app.config.jwt.secret);
            /*解密token以校验,如果校验得出来就说明用户登录验证成功。
            如果token是伪造的,则会跳转到报错页
            如果不想跳转报错,想给反馈,就用捕获异常*/
            /*this.ctx.body = decode;//{"username":"admin","iat":1675689453}
            前面是token的信息,后面是时间戳
            */
        }catch(e){
            this.ctx.body = "token验证失败"
        }
    }

功能实现

以前后端分离的形式
后端egg

先行配置jwt、cors插件
jwt.js

const Controller = require("egg").Controller

class JwtController extends Controller {
    async index(){
        this.ctx.body = "hi,egg";
    }

    async doLogin(){//处理用户登录,验证成功就发个token
        let user = this.ctx.request.body.user;//前端传过来的user,包含username和password属性
        if(user.username === "admin" && user.password === "123456"){
            let user_jwt = {userjwt:user.username};//用user.username来签名生成token
            let token = this.app.jwt.sign(user_jwt,this.app.config.jwt.secret);
            this.ctx.body = {
                code:20000,
                token:token
            }//验证成功就给个响应,code和token是自己定义的,像msg那样
        }else{//登录失败的响应
            this.ctx.body = {
                code:40000,
                msg:"用户名或密码错误"
            }
        }
    }

    async getMessage(){//前端向后台获取信息时校验token
        let token = this.ctx.request.header.token;//获取请求头里的token属性的值
        try{
            //用户再次请求数据的时候都要带着生成的token给服务器校验
            let decode = this.app.jwt.verify(token,this.app.config.jwt.secret);
            /*解密token以校验,如果校验得出来就说明用户登录验证成功。
            如果token是伪造的,则会跳转到报错页
            如果不想跳转报错,想给反馈,就用捕获异常*/
            /*this.ctx.body = decode;//{"username":"admin","iat":1675689453}
            前面是token的信息,后面是时间戳
            */
           this.ctx.body = "hello jwt";
        }catch(e){
            this.ctx.body = "token验证失败"
        }
    }
}

module.exports = JwtController

router.js

'use strict';//严格模式

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => { 
  const { router, controller } = app;
  router.get('/', controller.home.index);
  router.get('/login', controller.home.login);
  router.post('/login', controller.home.doLogin);
  router.post('/logout', controller.home.logout);
  router.get('/jwt', controller.jwt.index);
  router.post('/jwtlogin', controller.jwt.doLogin);//处理前端/login提交的表单
  router.get('/jwtmessage', controller.jwt.getMessage);//处理响应数据
};

前端vue

Login.vue

<template>
    <h1>登录</h1>
    <form @submit.prevent="doLogin">
        <input type="text" name="username" v-model="user.username" placeholder="用户名">
        <input type="password" name="password" v-model="user.password" placeholder="密码">
        <button>登录</button>
    </form>
</template>

<script>
import axios from "axios";
export default {
    data(){
        return {
            user:{
                username:"",
                password:""
            }
        }
    },
    methods:{
        doLogin(){
            axios.post("http://127.0.0.1:7001/jwtlogin",{user:this.user})//跨域发送user对象给后台负责登录验证的地址/jwtlogin
            .then((res)=>{
                if(res.data.code === 20000){//后台响应的数据里有个code字段,值为20000则成功
                    localStorage.setItem("token",res.data.token);//把响应的token值存在本地储存的token字段中
                    console.log(res.data.token)
                    this.$router.push("/");
                }
            })
            
        }
    }
}
</script>

Home.vue

<template>
    <h1>首页</h1>
    <button @click="getMessage">获取数据</button>
    <button @click="logout">登出</button>
</template>

<script>
import axios from 'axios';
export default {
    methods:{
        getMessage(){
            let token = localStorage.getItem("token");//从本地存储中拿到token
            axios.get("http://127.0.0.1:7001/jwtmessage",{headers:{token:token}}).then((res)=>{
                /*axios的get请求,它的第二个参数能设置请求头响应头之类的信息。
                headers代表的是请求头,headers:{token:token}的意思是
                在请求头中设置一个字段为token(:左边)的属性,字段的值为token(:右边)
                如果是post请求,第二个参数是数据,第三个才算请求头*/
                console.log(res.data);
            })
        },
        logout(){
            localStorage.setItem("token","");
            location.reload();//js的方法,能自动刷新页面
        }
    }
}
</script>

App.vue

<template>
  <router-view></router-view><!--用router-view占位,换页后显示的组件放在这里-->
</template>

<script>

export default {
  name: 'App',
  
}
</script>

router.js

import { createRouter,createWebHashHistory } from "vue-router";//引入创建路由的方法和哈希模式
import Home from "./components/Home.vue"//要加上.vue不让显示不出来
import Login from "./components/Login.vue"

const  router = createRouter({
    history:createWebHashHistory(),//路由模式
    routes:[//配置路由
        {
            path:"/",//首页
            component:Home//跳转到Home组件
        },
        {
            path:"/login",
            component:Login
        }
    ]
});//创建路由

router.beforeEach((to,from,next) => {
    //只有存在token时才能跳转到内容页
    //localStorage的getItem方法能根据数据名获取它的值
    let token = localStorage.getItem("token");
    if(token || to.path === "/login"){//有token或者访问的是登录页
        next();
    }else {
        next("/login");//否则让路由跳转到登录页
    }
})

export default router;

main.js

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
import router from './router.js'//引入router

const app = createApp(App)
app.use(router);
app.mount('#app')

用户携带token的方式,把token放到请求头requestHeader中。后台也由此校验

结果

点击登录

点击获取数据

点击登出

路由模式拓展

上面的地址栏带#是因为路由模式是hash模式,router.js中history:createWebHashHistory()
路由模式有history和hash两种模式,默认是hash模式
history模式地址栏不带#,更加美观,但如果项目部署到后台,会有额外的配置

posted @ 2023-02-07 00:28  ben10044  阅读(57)  评论(0编辑  收藏  举报