前后端分离项目实现登录(登录)

现在大部分的项目都是前后端分离项目,在前后端分离项目中的身份验证我们经常采用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太容易受到攻击了。

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值