前后端分离项目实现登录(登录)
现在大部分的项目都是前后端分离项目,在前后端分离项目中的身份验证我们经常采用JWT认证。关于JWT认证的详细内容,请移步上一篇博客。最近做的项目恰好用到了JWT的身份验证,今天拿出来说一下:login.vue<template><input type="text" v-model="loginForm.username" placeholder="用户名"/><inp
现在大部分的项目都是前后端分离项目,在前后端分离项目中的身份验证我们经常采用JWT认证。关于JWT认证的详细内容,请移步上一篇博客。
最近做的项目恰好用到了JWT的身份验证,今天拿出来说一下:
login.vue
<template>
<input type="text" v-model="loginForm.username" placeholder="用户名"/>
<input type="text" v-model="loginForm.password" placeholder="密码"/>
<input type="text" v-model="loginForm.code" placeholder="验证码"/>
<img :src="codeUrl" @click="getCode">
<checkbox v-model="loginForm.rememberMe">记住我</checkbox>
<button @click="login">登录</button>
</template>
<script>
//引入获取验证码的接口
import {getCode} from '../../api/login.js'
//注意哦,Cookie.set和Cookie.get的方法都要引入js-cookie才能用哦
import Cookies from 'js-cookie'
//要引入encrypt才能使用rsa算法对密码进行加密
import {encrypt} from '../../utils/rsaEncrypt.js'
import Config from '../../settings.js'
export default{
created() {
//利用生命周期钩子在页面构建之前获取验证码
this.getCodes();
//利用生命周期钩子在页面构建之前获取用户名密码等Cookie
this.getCookie()
},
data(){
return{
loginForm: {
username: '',
password: '',
rememberMe: false,
code: '',
uuid: ''
},
}
},
methods:{
//获取验证码的方法
getCodes() {
//调用获取验证码的接口
getCode().then(
res => {
this.codeUrl = res.data.img
this.loginForm.uuid = res.data.uuid
}
)
},
getCookie() {
const username = Cookies.get('username')
let password = Cookies.get('password')
const rememberMe = Cookies.get('rememberMe')
// 保存cookie里面的加密后的密码
this.cookiePass = password === undefined ? '' : password
this.loginForm = {
username: username === undefined ? this.loginForm.username : username,
password: password === undefined ? this.loginForm.password : password,
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
code: ''
}
},
login() {
const user = {
username: this.loginForm.username,
password: this.loginForm.password,
rememberMe: this.loginForm.rememberMe,
code: this.loginForm.code,
uuid: this.loginForm.uuid
}
//如果从cookie里面拿到的password和用户输入的不一样,就加密
if (user.password !== this.cookiePass) {
//注意:这里我们使用了rsa非对称加密,要记得引入啊
user.password = encrypt(user.password)
}
//检验填写是否规范
if (this.loginForm.username.length == 0 || this.loginForm.password.length == 0 || this.loginForm.code
.length == 0) {
//填写不规范
uni.showToast({
icon: 'none',
title: "请填写完整"
})
return;
} else {
//填写规范
if (user.rememberMe) {
Cookies.set('username', user.username, {
//这里用到了config,记得引入
expires: Config.passCookieExpires
})
Cookies.set('password', user.password, {
//这里用到了config,记得引入
expires: Config.passCookieExpires
})
Cookies.set('rememberMe', user.rememberMe, {
//这里用到了config,记得引入
expires: Config.passCookieExpires
})
} else {
Cookies.remove('username')
Cookies.remove('password')
Cookies.remove('rememberMe')
}
//这里使用了异步触发vuex中的Login方法,并且将user对象作为参数传入
this.$store.dispatch('Login', user).then(
(res) => {
uni.navigateTo({
url: '/pages/companyExam/companyExam?deptId=' + res.data.user.user.dept.id
})
}
).catch((error) => {
uni.showToast({
icon: 'none',
title: "密码或验证码不正确"
})
this.getCodes()
})
}
},
}
}
</script>
store下的index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//引入登录的login的接口
import {login} from '../api/login.js'
//引入拿token、设token、删token的方法
import {getToken, setToken, removeToken } from '../utils/auth.js'
const store = new Vuex.Store({
state: {
token: getToken(),
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
}
},
actions: {
// 登录
Login({
commit
}, userInfo) {
const rememberMe = userInfo.rememberMe
return new Promise((resolve, reject) => {
login(userInfo.username, userInfo.password, userInfo.code, userInfo.uuid).then(res => {
//登录成功的操作
//设置cookie中的token
setToken(res.data.token, rememberMe)
//触发vuex中的SET_TOKEN方法,设置vuex仓库中的token
commit('SET_TOKEN', res.data.token)
resolve(res)
}).catch(error => {
//登录失败的操作
reject(error)
})
})
},
}
})
//记得将store暴露出去
export default store
util下的auth.js
import Cookies from 'js-cookie'
import Config from '@/settings'
const TokenKey = Config.TokenKey
export function getToken() {
//cookie是键值对格式,这里通过键名来拿token的值
return Cookies.get(TokenKey)
}
export function setToken(token, rememberMe) {
if (rememberMe) {
return Cookies.set(TokenKey, token, { expires: Config.tokenCookieExpires })
} else return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
全局位置的setting.js
module.exports = {
/**
* @description 记住密码状态下的token在Cookie中存储的天数,默认1天
*/
tokenCookieExpires: 1,
/**
* @description 记住密码状态下的密码在Cookie中存储的天数,默认1天
*/
passCookieExpires: 1,
/**
* @description token key
*/
TokenKey: 'SMPE-ADMIN-TOEKN'
}
utils下的request.js
import axios from 'axios';
import {getToken} from './auth.js'
// 添加请求拦截器
axios.interceptors.request.use(
(config) => {
//用到了getToken的方法,需要引入
let token = getToken();
config.headers['Authorization'] = token;
config.headers['Content-type'] = 'application/json';
return config;
}, err => {
return err;
}
);
// 添加响应拦截器
axios.interceptors.response.use(
(response) => {
console.log('来到了response拦截success中');
return response.data;
},
(err) => {
console.log('来到了response拦截failure中');
if (err && err.response) {
switch (err.response.status) {
case 400:
err.message = '参数错误(400)';
break;
case 401:
err.message = '未授权访问(401)';
let pages = getCurrentPages(); //拿到页面的栈
console.log('401了,去登录界面', pages);
if (pages[pages.length - 1].$page.fullPath === '/pages/releaseNotice/index') {
uni.redirectTo({
url: '/pages/releaseNotice/fakeIndex',
});
} else {
uni.redirectTo({
//当前页面的地址编码给callback
url: '/pages/login/index?callback=' + encodeURIComponent(pages[pages.length - 1].$page.fullPath),
});
}
break;
case 403:
err.message = '权限错误(403)';
break;
case 404:
err.message = '访问资源错误(404)';
break;
case 500:
err.message = '服务器错误(500)';
break;
}
} else if (err.response.status < 200 || err.response.status > 300) {
err.message = '请求失败';
}
return Promise.reject(err);
}
);
utils下的rsaEncrypt.js
该文件用于对密码的加密
//jsencrypt需要引入,不同端可以采用不同的方式引入
//uniapp端可以使用插件(查看官网使用详情),vue端可以使用 npm install jsencrypt 来引入,这里我们使用vue端来展示
import JSEncrypt from 'jsencrypt/bin/jsencrypt'
// 密钥对生成 http://web.chacuo.net/netrsakeypair
const publicKey = '公钥'
const privateKey = '私钥'
// 加密
export function encrypt(txt) {
const encryptor = new JSEncrypt()
encryptor.setPublicKey(publicKey) // 设置公钥
return encryptor.encrypt(txt) // 对需要加密的数据进行加密
}
// 解密
export function decrypt(txt) {
const encryptor = new JSEncrypt()
encryptor.setPrivateKey(privateKey)
return encryptor.decrypt(txt)
}
注意:我们本文采用的是cookie来存储token,还可以将token存储到localStorage或者SessionStorage中。
这里有一篇关于存储到localStorage的博客供大家参考。
Vue实现用户登录及token验证
那么,这三者有什么区别呢?
- 储存在 localStorage 中,每次调用接口时放在http请求头里面,长期有效
- 储存在 sessionStorage 中,每次调用接口时,把它当为一个字段传给后台,浏览器关闭自动清除
- 储存在 cookie 中,每次调用接口会自动发送,不过缺点是不能跨域
深入了解
- 将 Token 存储在 webStorage(localStorage,sessionStorage) 中可以通过同域的js访问,这样导致很容易受到 xss 攻击,特别是项目中引入很多第三方js库的情况下,如果js脚本被盗用,攻击者就可以轻易访问你的网站。
xss攻击:是一种注入代码攻击,通过在网站里注入script代码,当访问者浏览网站的时候通过注入的script代码窃取用户信息,盗用用户身份等
- 将 Token 存储在 cookie 中,可以指定 httponly 来防止 js 被读取,也可以指定 secure 来保证 Token 只在 HTTPS 下传输,缺点是不符合 RestFul 最佳实践,容易受到 CSRF 攻击。
CSRF: 跨站点请求伪造,攻击者盗用已经认证过的用户信息,以用户信息的名义进行操作(转账,购买商品等),由于身份已经认证过了,所以网站会认为此操作是用户本人操作。 CSRF 并不能拿到用户信息,但它可以盗用用户的凭证进行操作。
不过,一般我们还是使用cookie来存储token,因为web Storage太容易受到攻击了。
更多推荐
所有评论(0)