赞
踩
目录
1、【username.trim() 】去除 username 字符串中的前导和尾随空白字符。
2、在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下:
简洁版: https://github.com/PanJiaChen/vue-admin-template 我们用这个
加强版: https://github.com/PanJiaChen/vue-element-admin(1)克隆项目代码到本地:
git clone https://github.com/PanJiaChen/vue-admin-template.git
(2)进入项目目录:
cd vue-admin-template
(3)引入项目依赖:
npm install
(4)启动开发环境:
npm run dev
- .
- ├── build/ # 构建脚本
- ├── mock/ # 模拟数据
- ├── public/ # 静态资源
- │ ├── favicon.ico # 网站图标
- │ └── index.html # 入口页面
- ├── src/ # 项目源码
- │ ├── api/ # API 请求封装
- │ ├── assets/ # 静态资源
- │ ├── components/ # 公共组件
- │ ├── directive/ # 自定义指令
- │ ├── filters/ # 过滤器
- │ ├── icons/ # 图标
- │ ├── layout/ # 布局组件
- │ ├── router/ # 路由配置
- │ ├── store/ # Vuex 状态管理
- │ ├── styles/ # 全局样式
- │ ├── utils/ # 工具库
- │ ├── vendor/ # 第三方库
- │ ├── views/ # 页面组件
- │ ├── App.vue # 根组件
- │ └── main.js # 入口文件
- ├── .editorconfig # 编辑器配置
- ├── .env # 环境变量
- ├── .eslintrc.js # ESLint 配置
- ├── .gitignore # Git 忽略文件
- ├── babel.config.js # Babel 配置
- ├── package-lock.json # 依赖锁定文件
- ├── package.json # 项目依赖和配置
- ├── postcss.config.js # PostCSS 配置
- ├── README.md # 项目说明文档
- ├── vue.config.js # Vue CLI 配置
- └── yarn.lock # 依赖锁定文件
-
参考《尚硅谷》项目
vue.config.js中将以下三项设置为false
// 是否在开发模式下保存文件时运行代码检查 // lintOnSave: process.env.NODE_ENV === 'development', //运行代码检查设置为false lintOnSave:false,
vue.config.js
- // 开发服务器的选项
- devServer: {
- port: port,
- open: true,
- overlay: {
- warnings: false,
- errors: true
- },
- // 配置代理跨域
- proxy: {
- '/dev-api': {
- target: 'http://gmall-h5-api.atguigu.cn',
- pathRewrite: { '^/dev-api': '' }
- },
- },
- //开启mock数据
- after: require('./mock/mock-server.js')
src/api/user.js
- // src/api/user.js
-
- // 对外暴露登录的接口函数
- export function login(data) {
- return request({
- url: 'dev-api/admin/acl/index/login',
- method: 'post',
- data
- })
- }
- // 对外暴露获取用户信息的接口函数
- export function getInfo(token) {
- return request({
- url: 'dev-api/admin/acl/index/info',
- method: 'get',
- params: { token }
- })
- }
- // 对外暴露退出登录的接口函数
- export function logout() {
- return request({
- url: 'dev-api/admin/acl/index/logout',
- method: 'post'
- })
- }
(1)修改为: 如果自定义的响应码不是20000或200,就判断为错误
if (res.code !== 20000 && res.code !== 200)(2)将'X-Token' 改为token
// 如果有token就在请求头中加上token
config.headers['token'] = getToken()
- import axios from 'axios'
- import { MessageBox, Message } from 'element-ui'
- import store from '@/store'
- import { getToken } from '@/utils/auth'
-
- // 创建一个axios实例
- const service = axios.create({
- baseURL: process.env.VUE_APP_BASE_API, // 接口的基础路径
- timeout: 5000 // 请求超时时间
- })
-
- // 请求拦截器
- service.interceptors.request.use(
- config => {
- // 在请求发送前做一些处理
- if (store.getters.token) {
- // 如果有token就在请求头中加上token
- config.headers['token'] = getToken()
- }
- return config
- },
- error => {
- // 对请求错误做些什么
- console.log(error)
- return Promise.reject(error)
- }
- )
-
- // 响应拦截器
- service.interceptors.response.use(
- response => {
- // 对响应数据做一些处理,这里只返回响应数据中的data部分
- const res = response.data
-
- // 如果自定义的响应码不是20000或200,就判断为错误
- if (res.code !== 20000 && res.code !== 200) {
- // 在页面上显示错误信息
- Message({
- message: res.message || 'Error',
- type: 'error',
- duration: 5 * 1000
- })
-
- // 以下是针对特定错误码的处理
- if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
- // 重新登录
- MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
- confirmButtonText: 'Re-Login',
- cancelButtonText: 'Cancel',
- type: 'warning'
- }).then(() => {
- store.dispatch('user/resetToken').then(() => {
- location.reload()
- })
- })
- }
- // 返回一个被拒绝的Promise对象,用来表示错误
- return Promise.reject(new Error(res.message || 'Error'))
- } else {
- // 如果没有错误,就返回响应数据中的data部分
- return res
- }
- },
- error => {
- // 对响应错误做些什么
- console.log('err' + error)
- Message({
- message: error.message,
- type: 'error',
- duration: 5 * 1000
- })
- return Promise.reject(error)
- }
- )
-
- export default service
原来代码为:
- import { login, logout, getInfo } from '@/api/user' // 引入用户相关的 API
- import { getToken, setToken, removeToken } from '@/utils/auth' // 引入 token 相关的工具函数
- import { resetRouter } from '@/router' // 引入路由重置函数
-
- const getDefaultState = () => {
- return {
- token: getToken(), // 获取本地存储的 token
- name: '',
- avatar: ''
- }
- }
-
- const state = getDefaultState()
-
- const mutations = {
- // 重置状态
- RESET_STATE: (state) => {
- Object.assign(state, getDefaultState())
- },
- // 设置 token
- SET_TOKEN: (state, token) => {
- state.token = token
- },
- // 设置用户名
- SET_NAME: (state, name) => {
- state.name = name
- },
- // 设置头像
- SET_AVATAR: (state, avatar) => {
- state.avatar = avatar
- }
- }
-
- const actions = {
- // 用户登录
- login({ commit }, userInfo) {
- const { username, password } = userInfo
- return new Promise((resolve, reject) => {
- login({ username: username.trim(), password: password }).then(response => {
- const { data } = response
- commit('SET_TOKEN', data.token) // 设置 token
- setToken(data.token) // 存储 token
- resolve()
- }).catch(error => {
- reject(error)
- })
- })
- },
-
- // 获取用户信息
- getInfo({ commit, state }) {
- return new Promise((resolve, reject) => {
- getInfo(state.token).then(response => {
- const { data } = response
-
- if (!data) {
- return reject('Verification failed, please Login again.')
- }
-
- const { name, avatar } = data
-
- commit('SET_NAME', name) // 设置用户名
- commit('SET_AVATAR', avatar) // 设置头像
- resolve(data)
- }).catch(error => {
- reject(error)
- })
- })
- },
-
- // 用户退出登录
- logout({ commit, state }) {
- return new Promise((resolve, reject) => {
- logout(state.token).then(() => {
- removeToken() // 移除 token
- resetRouter() // 重置路由
- commit('RESET_STATE') // 重置状态
- resolve()
- }).catch(error => {
- reject(error)
- })
- })
- },
-
- // 重置 token
- resetToken({ commit }) {
- return new Promise(resolve => {
- removeToken() // 移除 token
- commit('RESET_STATE') // 重置状态
- resolve()
- })
- }
- }
-
- export default {
- namespaced: true,
- state,
- mutations,
- actions
- }
(1)使用了async/await和try/catch语法,
(2)在 Vuex 的 action 中使用返回整个响应对象 替换 使用 {data} 来获取 axios 请求返回的响应数据。
【区别】
使用 {data} 来获取 axios 请求返回的响应数据对象中的 data 属性,只会返回响应数据对象中的 data 属性的值,而在 Vuex 的 action 中返回整个响应对象,则会返回包含响应数据、响应状态码、响应头等全部信息的完整的响应对象。
- // 引入需要使用的函数和模块
- import { login, logout, getInfo } from '@/api/user'
- import { getToken, setToken, removeToken } from '@/utils/auth'
- import { resetRouter } from '@/router'
-
- // 定义获取默认状态的函数
- const getDefaultState = () => {
- return {
- token: getToken(), // 使用 getToken 函数获取 token 值
- name: '',
- avatar: ''
- }
- }
-
- // 定义 Vuex 模块的状态
- const state = getDefaultState()
-
- // 定义 Vuex 模块的变更操作
- const mutations = {
- // 重置状态为默认状态
- RESET_STATE: (state) => {
- Object.assign(state, getDefaultState())
- },
- // 设置 token
- SET_TOKEN: (state, token) => {
- state.token = token
- },
- // 设置用户名
- SET_NAME: (state, name) => {
- state.name = name
- },
- // 设置用户头像
- SET_AVATAR: (state, avatar) => {
- state.avatar = avatar
- }
- }
-
- // 定义 Vuex 模块的异步操作
- const actions = {
- // 用户登录
- async login({ commit }, userInfo) {
- const { username, password } = userInfo
- try {
- // 发送登录请求
- const response = await login({ username: username.trim(), password: password }) // 发送登录请求
- // 设置 token
- commit('SET_TOKEN', response.data.token)
- // 将 token 值保存在浏览器的本地存储中
- setToken(response.data.token)
- // 返回完整的响应对象
- return response
- } catch (error) {
- return Promise.reject(error)
- }
- },
-
- async getInfo({ commit, state }) {
- try {
- // 发送请求获取用户信息
- const response = await getInfo(state.token)
- // 如果响应数据不存在,说明验证失败
- if (!response.data) {
- throw new Error('验证失败,请重新登录。')
- }
- // 获取用户名和头像
- const { name, avatar } = response.data
- // 设置用户名
- commit('SET_NAME', name)
- // 设置用户头像
- commit('SET_AVATAR', avatar)
- // 返回完整的响应对象
- return response
- } catch (error) {
- return Promise.reject(error)
- }
- },
- // 用户注销
- async logout({ commit, state }) {
- try {
- // 发送请求注销用户登录状态
- await logout(state.token)
- // 从本地存储中删除 token
- removeToken()
- // 重置路由
- resetRouter()
- // 重置状态
- commit('RESET_STATE')
- } catch (error) {
- return Promise.reject(error)
- }
- },
- // 重置 token 值
- async resetToken({ commit }) {
- try {
- // 从本地存储中删除 token
- removeToken()
- // 重置状态
- commit('RESET_STATE')
- } catch (error) {
- return Promise.reject(error)
- }
- }
- }
-
- // 导出 Vuex 模块
- export default {
- namespaced: true, // 开启命名空间
- state,
- mutations,
- actions
- }
【场景】有时候后端服务被部署在多个服务器上,且每个服务器的域名或 IP 地址都不相同,那么在前端项目中向这些不同的后端服务发起请求时就会遇到跨域问题。此时,可以通过配置多个代理来分别处理各个后端服务的跨域请求。
通过修改【
vue.config.js
】 文件来完成配置多个代理来实现跨域请求的设置:
- // vue.config.js
- // 配置代理跨域
- proxy: {
- // 关于用户的接口
- '/dev-api': {
- target: 'http://gmall-h5-api.atguigu.cn',
- pathRewrite: { '^/dev-api': '' }
- },
- // 关于文件上传的接口
- "/dev-upload": {
- target: "http://loaclhost:8989",
- pathRewrite: { "^/dev-upload": "" }
- },
- },
-
【方式一】:修改接口的基础路径 base:url+request,
- // 创建一个axios实例
-
- const service = axios.create({
-
- baseURL: '/', // 接口的基础路径 base: url+request,
-
- timeout: 5000 // 请求超时时间
-
- })
修改user.js 把访问路径增加 'dev-api'
即: /admin/acl/index/login 改为 '/dev-api/admin/acl/index/login'
- import request from '@/utils/request'
- // http://39.98.123.211:8170/swagger-ui.html
- export function login(data) {
- return request({
- url: '/dev-api/admin/acl/index/login',
- method: 'post',
- data
- })
- }
-
- export function getInfo(token) {
- return request({
- url: '/dev-api/admin/acl/index/info',
- method: 'get',
- params: { token }
- })
- }
-
- export function logout() {
- return request({
- url: '/dev-api/admin/acl/index/logout',
- method: 'post'
- })
- }
这种方式需要每一个接口都加上对应的基础访问路径 '/dev-api/ 或 /dev-upload
【方式二】:使用配置不同的request.js 和 baseURL
(1)修改.env.development 和.env.production,增加 # upload api VUE_APP_UPLOAD_API = '/upload-api'
- # just a flag
- ENV = 'development'
-
- # base api
- VUE_APP_BASE_API = '/dev-api'
-
- # upload api
- VUE_APP_UPLOAD_API = '/upload-api'
(2)复制request.js 重命名为request-upload.js
(3)修改 request-upload.js 文件中 baseURL: process.env.VUE_APP_BASE_API 改为 baseURL: process.env.VUE_APP_UPLOAD_API。
(4)引入对应的 import request from '@/utils/request-upload'
- //src/api/upload.js
- // 当前这个模块:api进行统一管理
- import request from '@/utils/request-upload'
-
- export function uploadFiles(formData) {
- return request({
- url: '/uploadFiles',
- method: 'post',
- data: formData,
- headers: {
- 'Content-Type': 'multipart/form-data'
- }
- });
- }
1、【username.trim() 】去除 username 字符串中的前导和尾随空白字符。
let result = await login({ username: username.trim(), password: password })
2、在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下:
(1)第一次登录的时候,前端调后端的登陆接口,发送用户名和密码;
(2)后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token;
(3)前端拿到token,将token存储到localStorage和vuex中,并跳转路由页面;
(4)前端每次跳转路由,就判断 localStroage 中有无 token ,没有就跳转到登录页面,有则跳转到对应路由页面;
(5)每次调后端接口,都要在请求头中加token;
(6)后端判断请求头中有无token,有token,就拿到token并验证token,验证成功就返回数据,验证失败(例如:token过期)就返回401,请求头中没有token也返回401;
(7)如果前端拿到状态码为401,就清除token信息并跳转到登录页面。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。