利用Vue.js实现登录/登出以及JWT认证
JSON Web Token 入门教程:http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
后端代码地址:https://github.com/lison16/vue-cource/commit/c0341e8ea79d876ae552281ad8c1f1da5049434f
代码结构:


uer.js:封装了登录和授权两个接口。
import axios from "./index";
export const getUserInfo = ({ userId }) => {
debugger;
return axios.request({
url: "/getUserInfo",
method: "post",
data: {
userId
}
});
};
export const login = ({ userName, password }) => {
return axios.request({
url: "/index/login",
method: "post",
data: {
userName,
password
}
});
};
export const authorization = () => {
return axios.request({
url: "/users/authorization",
method: "get"
});
};
/src/store/module/user.js:由于是异步操作,所以都放在了actions中。
import { login, authorization } from "@/api/user";
import { setToken } from "@/lib/util";
const state = {
//
userName: "Caoqi"
};
const getters = {
firstLetter: state => {
return state.userName.substr(0, 1);
}
};
const mutations = {
//
SET_USER_NAME(state, params) {
state.userName = params.userName;
}
};
const actions = {
login({ commit }, { userName, password }) {
return new Promise((resolve, reject) => {
login({ userName, password })
.then(res => {
debugger;
if (res.data.code === 200 && res.data.data.token) {
setToken(res.data.data.token);
resolve();
} else {
reject(new Error("错误"));
}
})
.catch(error => {
reject(error);
});
});
},
authorization({ commit }, token) {
return new Promise((resolve, reject) => {
authorization()
.then(res => {
if (parseInt(res.data.code) === 401) {
reject(new Error("token error"));
} else {
setToken(res.data.data.token);
resolve();
}
})
.catch(error => {
reject(error);
});
});
},
logout() {
setToken("");
}
};
export default {
//namespaced:true,//有利于模块更加密闭,不受外界的干扰
state,
getters,
mutations,
actions
};
/src/lib/axios.js:
import axios from "axios";
import { baseURL } from "@/config";
import { getToken } from '@/lib/util';
//ES6类的写法
class HttpRequest {
//ES6默认参数
constructor(baseUrl = baseURL) {
this.baseUrl = baseUrl;
this.queue = {}; //将请求都放到队列中
}
getInsideConfig() {
const config = {
baseURL: this.baseUrl,
hearders: {
//
}
};
return config;
}
//封装拦截器
interceptors(instance, url) {
instance.interceptors.request.use(
config => {
//添加全局的loading
//Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组
if (!Object.keys(this.queue).length) {
//spin.show
}
this.queue[url] = true;
config.headers['Authorization'] = getToken();
return config;
},
error => {
return Promise.reject(error);
}
);
instance.interceptors.response.use(
res => {
delete this.queue[url];
const { data, status } = res;
return { data, status };
},
error => {
delete this.queue[url];
return Promise.reject(error);
}
);
} request(options) {
const instance = axios.create();
/**
* Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
* const target = { a: 1, b: 2 };
* const source = { b: 4, c: 5 };
* const returnedTarget = Object.assign(target, source);
* console.log(target);
* expected output: Object { a: 1, b: 4, c: 5 }
*/
options = Object.assign(this.getInsideConfig(), options);
this.interceptors(instance, options.url);
return instance(options);
}
} export default HttpRequest;
login.vue:
<template>
<div>
<div>
<input v-model="userName">
</div>
<div>
<input type="password" v-model="password">
</div>
<div>
<button @click="handleSubmit">登录</button>
</div>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
name: "login_page",
data() {
return {
userName: "",
password: ""
};
},
methods: {
...mapActions(["login"]),
handleSubmit() {
this.login({
userName: this.userName,
password: this.password
})
.then(() => {
console.log("success!!");
this.$router.push({
name: "home"
});
})
.catch(error => {
console.log(error);
});
}
}
};
</script>
/src/router/index.js:跳转到任何页面前都要验证token
import Vue from "vue";
import Router from "vue-router";
import routes from "./router";
import store from "@/store";
import { setTitle, setToken, getToken } from "@/lib/util"; Vue.use(Router); const router = new Router({
routes
}); const HAS_LOGINED = false;
//全局前置守卫
/*
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子
*/
//模拟登陆验证逻辑:当跳转页面为登陆页面且已经登陆时,直接跳转到home页面,如果跳转页面不为登录页且已经登陆,则继续执行,否则直接跳转到登录页
router.beforeEach((to, from, next) => {
to.meta && setTitle(to.meta.title);
debugger
const token = getToken();
if (token) {
store
.dispatch("authorization", token)
.then(() => {
if (to.name == "login") {
next({ name: "home" });
} else {
next();
}
})
.catch(() => {
setToken("");
next({ name: "login" });
});
} else {
if (to.name === "login") next();
else next({ name: "login" });
}
}); export default router;
src/views/Home.vue:包含登出效果:清除cookie,页面跳转到login.vue
<template>
<div class="home">
<b>{{ food }}</b>
<button @click="handleClick('back')">返回上一页</button>
<button @click="handleClick('push')">跳转到parent</button>
<button @click="handleClick('replace')">替换到parent</button>
<button @click="getInfo">请求数据</button>
<button @click="handleLogout">退出登录</button>
</div>
</template> <script>
// @ is an alias to /src
import HelloWorld from "@/components/HelloWorld.vue";
import { getUserInfo } from '@/api/user'
import { mapActions } from 'vuex'
export default {
name: "home",
components: {
HelloWorld
},
props: {
food: {
type: String,
default: "apple"
}
},
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
next(vm => {
//若想使用实例,可使用这种方法
console.log(vm);
});
},
beforeRouteLeave(to, from, next) {
// const leave = confirm('您确定要离开吗?')
// if (leave) next()
// else next(false)
next();
},
methods: {
...mapActions([
'logout'
]),
handleClick(type) {
if (type === "back") {
//this.$router.back();
this.$router.go(-1);
} else if (type === "push") {
const name = "caoqi";
//使用push会在浏览器中加入一个记录
//使用路径跳转
//this.$router.push("/parent");
//还可以使用命名路由的方式:
this.$router.push({
// name: "parent",
// //加入name参数,http://localhost:8080/#/parent?name=caoqi
// query: {
// name: 'caoqi'
// } // name: "argu",
// //加入name参数,http://localhost:8080/#/argu/caoqi
// params: {
// name: 'caoqi'
// } //ES6写法:
path: `/argu/${name}`
});
} else if (type === "replace") {
//使用replace不会在浏览历史中加入记录
this.$router.replace({
name: "parent"
});
}
},
getInfo() {
getUserInfo({ userId: 21 }).then(res => {
console.log("res: ", res);
});
},
handleLogout () {
this.logout()
this.$router.push({
name: 'login'
})
}
}
};
</script>
效果图:(一开始登录失败,因为后台设置了密码为123,登录成功后,如果在cookie有效的时间段内,登录系统其它页面则无需登录,若把cookie清除则再查看系统其它页面,则直接跳转到登录页进行登录操作)

利用Vue.js实现登录/登出以及JWT认证的更多相关文章
- Vue 登录/登出以及JWT认证
1. 后端代码概览 server/router/index.js 请求 router.get('/getUserInfo', function (req, res, next) { // 登录请求 r ...
- Vue登录登出以及JWT认证
数据模型 主要用户名,密码,邮箱,头像,身份 const mongoose = require('mongoose') const schema = new mongoose.Schema({ use ...
- ThinkPHP---案例1登录登出和添加部门
配置文件分3类:系统配置文件,分组配置文件,应用配置文件 ①系统配置文件ThinkPHP/Conf/convention.php: ②分组 / 模块 /平台配置文件Home/Conf/config.p ...
- flask 实现登录 登出 检查登录状态 的两种方法的总结
这里我是根据两个项目的实际情况做的总结,方法一(来自项目一)的登录用的是用户名(字符串)和密码,前后端不分离,用form表单传递数据:方法二用的是手机号和密码登录,前后端分离,以json格式传递数据, ...
- Django项目: 4.用户登录登出功能
用户登录登出功能 一.功能需求分析 1. 登录退出功能分析 流程图 功能 登录页面 登录功能 退出功能 二.登录页面 1. 接口设计 接口说明 类目 说明 请求方法 GET url定义 /user/l ...
- SpringBoot登录登出切面开发
阅读本文约“2.5分钟” 本文开发环境是SpringBoot2.X版本. 对于系统而言(这里多指管理系统或部分具备登录登出功能的系统),登录登出是一个类权限验证的过程,现在一般是以token进行校验, ...
- Struts2学习第六课 实现登录登出功能
关于Struts2请求的扩展名问题: 1).org.apache.struts2包下的default.properties中配置了struts2应用的一些常量 2).struts.action.ext ...
- ASP.NET Core 登录登出 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 登录登出 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 登录登出 上一章节我们总算完善了注册的功能,而且也添加了一个用户,现 ...
- jquery ajax常用的登录登出
整理jquery+ajax的登录登出方法. //登录 var currentUserId = -1; $(function() { var timestamp = (new Date()).value ...
随机推荐
- IT兄弟连 JavaWeb教程 使用Servlet实现在页面中显示随机数
在com.xdl.servlet包下定义RandomServlet类并HttpServlet类,在该类中生成随机数并发送给客户端.RandomServlet类详细代码如下: package com.x ...
- 更新常用的js工具函数
在手机调试时打印代码<script src="https://cdn.bootcss.com/vConsole/3.3.0/vconsole.min.js"></ ...
- MySQL创建用户+授权+备份
======权限管理====== 我们知道我们的最高权限管理者是root用户,它拥有着最高的权限操作.包括select.update.delete.update.grant等操作. 那么一般情况在公司 ...
- netty~引用对象引用
从InBound里读取的ByteBuf要手动释放,还有自己创建的ByteBuf要自己负责释放.这两处要调用这个release方法. write Bytebuf到OutBound时由netty负责释放, ...
- Linux (Windows Linux子系统)
Linux (Windows Linux子系统) 如果想体验Linux环境下开发和运行.NET Core应用,我们有多种选择.一种就是在一台物理机上安装原生的Linux,我们可以根据自身的喜好选择某种 ...
- c++11 右值的学习
从4行代码看右值引用 概述 简单的左值和右值的判断就是 看是否可以取得地址 可取得地址 是左值 不能则 是右值! c++98/03中的左值引用是这样的: int i = 0; int& ...
- Sticky Footer的实现
Sticky Footer即绝对底部,是一种常用的布局方式,页脚在内容区未超出窗口高度时一直保持在最底部显示,而超过窗口高度时则保持在内容区最底部. 有两种实现方法: 第一种:经典的实现方式 html ...
- POJ 1015 Jury Compromise dp分组
第一次做dp分组的问题,百度的~~ http://poj.org/problem?id=1015 题目大意:在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定.陪审团是由法官从公众中挑选的.先随机挑 ...
- (转)linux dumpe2fs命令
linux dumpe2fs命令 命令名称 dumpe2fs - 显示ext2/ext3/ext4文件系统信息. dumpe2fs命令语法 dumpe2fs [ -bfhixV ] [ -o supe ...
- Eclipse Debug时出现Source not found错误
今天在Debug Java代码时报出了Source not found这个错误,如下图所示,经过查询资料得知这是由于缺少Hadoop源程序代码所导致的错误. 在此我建议了两种方法,可以先采用方法一,这 ...