页面重新加载后身份验证失败(Vue,带有Node.js的Expressjs)

时间:2020-03-14 10:26:02

标签: node.js express authentication cors vuex

当前情况

我正在开发nodejs后端服务器和vue前端应用程序,它们在不同的端口(localhost:3000和localhost:8080)下运行。为了启用CORS连接,我从vue.config.js文件配置了devServer代理。

vue.config.js

module.exports = {
    devServer: {
        proxy: {
            '/users': {
                target: 'http://127.0.0.1:3000/users',
                changeOrigin: true,
                pathRewrite: {
                    '^/users':''
                }
            },
            '/tasks': {
                target: 'http://127.0.0.1:3000/tasks',
                changeOrigin: true,
                pathRewrite: {
                    '^/tasks': ''
                }
            }
        }
    },
    outputDir: '../backend/public'
}

并在技术上使用cors.js启用对后端服务器的请求,该请求由expressjs实现。 我正在发送带有vue组件的请求,以从后端服务器检索数据。通过从服务器获取数据,它可以正常工作,我的目标是在重新加载页面时做出相同的行为。但是,每当我重新加载同一页面时,它始终显示由我自己编写的后端代码设置的401 http响应状态。

enter image description here

立即进行研究和试用

在继续尝试之前,我应该先介绍必须执行的强制性代码。不知何故,这至少是对其中vuex使用axios动作,axios最终使用后端路由器的解释。

tasks.module.js

import axios from "axios"
import authHeader from '../../services/auth-header'

export const tasks = {
    state: {
        tasks: []
    },

    getters: {
        allTasks: (state) => state.tasks
    },

    actions: {
        async fetchTasks({ commit }) {
            const response = await axios.get('http://127.0.0.1:3000/tasks', {headers: authHeader()})

            commit('setTasks', response.data)

            axios.defaults.headers.common['Authorization'] = authHeader()
        },

        async addTask({ commit }, description) {
            const response = await axios.post('http://127.0.0.1:3000/tasks', { description, completed: false}, {headers: authHeader()})

            commit('newTask', response.data)
        },

        async updateTask({ commit }, updTask) {
            const response = await axios.patch('http://127.0.0.1:3000/tasks/'+updTask.id, updTask, {headers: authHeader()})

            commit('updateTask', response.data)
        }
    },

    mutations: {
        setTasks: (state, tasks) => (state.tasks = tasks),
        newTask: (state, task) => state.tasks.unshift(task),
        updateTask: (state, updTask) => {
            let updates = Object.keys(updTask)

            updates.forEach((update) => {
                state.task[update] = updTask[update]
            })
        }
    }
}

TaskManager.vue

<template>
  <div class="container">
    <div class="jumbotron">
      <h3>Task Manager</h3>
      <AddTask/>
      <Tasks/>
    </div>
  </div>
</template>

<script>
import Tasks from './components/Tasks'
import AddTask from './components/AddTask'

export default {
  name:'TaskManager',
  components: {
    Tasks,
    AddTask
  }
}
</script>

Tasks.vue

<template>
<div>
    <div>
        <div class="legend">
            <span>Double click to mark as complete</span>
            <span>
                <span class="incomplete-box"></span> = Incomplete
            </span>
            <span>
                <span class="complete-box"></span> = Complete
            </span>
        </div>
    </div>
    <div class="tasks">
        <div
            @dblclick="onDblClick(task)"
            v-for="task in allTasks"
            :key="task.id"
            class="task"
            v-bind:class="{'is-completed':task.completed}">
            {{task.description}}
        </div>
    </div>
</div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex'
export default {
    name: "Tasks",
    methods:{
        ...mapActions(['fetchTasks', 'updateTask']),
        onDblClick(task) {
            const updTask = {
                id: task._id,
                description: task.description,
                completed: !task.completed
            }
            console.log(updTask)
            this.updateTask(updTask)
        }
    },
    computed: {
        ...mapGetters(['allTasks']),
    },
    created() {
        this.fetchTasks()
    }
}

现在我需要介绍解决问题的方法

  1. 配置CORS选项

    由于此错误页面没有显示应该在请求标头中设置的任何授权标头,因此我弄清楚了启用cors连接的方式,并且我相信这可以启用预检请求。这是我从后端代码配置的中间件行为。 task.js(表达路由器文件)

const router = new express.Router()
const auth = require('../middleware/auth')
const cors = require('cors')
const corsOptions = {
    origin: 'http://127.0.0.1:3000',
    allowedHeaders: 'content-Type, Authorization',
    maxAge:3166950
}
router.options(cors(corsOptions))

router.get('/tasks', auth, async (req, res) => {
    const match = {}
    const sort = {}

    if(req.query.completed) {
        match.completed = req.query.completed === 'true'
    }

    if(req.query.sortBy) {
        const parts = req.query.sortBy.split('_')
        sort[parts[0]] = parts[1] === 'desc' ? -1:1             // bracket notation
    }

    try {
        await req.user.populate({
            path: 'tasks',
            match,
            options: {
                limit: parseInt(req.query.limit),
                skip: parseInt(req.query.skip),
                sort
            }
        }).execPopulate()
        console.log(req.user.tasks)
        res.status(200).send(req.user.tasks)
    } catch (e) {
        res.status(500).send(e)
    }
})
module.exports = router

auth.js(中间件)

const jwt = require('jsonwebtoken')
const User = require('../models/user')

const auth = async (req, res, next) => {
    try {
        const token = req.header('Authorization').replace('Bearer ','')
        const decoded = jwt.verify(token, 'thisisnewcourse')
        console.log('decoded token passed')
        const user = await User.findOne({ _id: decoded._id, 'tokens.token': token})
        console.log('user found')

        if(!user) {
            throw new Error()
        }
        req.token = token
        req.user = user
        next()

    } catch (error) {
        console.log('error caught')
        res.status(401).send({error: 'please authenticate'})
    }
}

module.exports = auth
  1. 登录后将“授权”标头设置为axios默认标头

auth.module.js(由于登录正常,我仅复制登录操作部分)

actions: {
        async login ({ commit }, user){
            try {
                const response = await axios.post('http://127.0.0.1:3000/users/login', user)

                if(response.data.token){
                    localStorage.setItem('user', JSON.stringify(response.data))

                    axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.token}`
                }

                commit('loginSuccess', response.data)
            } catch (e) {
                console.log(e)
            }


        }
  1. 中间件在快递路线上的链接(cors,auth)

我在同一个后端代码(task.js)上尝试了两种不同的中间件

router.get('/tasks', [cors(corsOptions), auth], async (req, res) => {
    // same as previously attached code
}

现在,我相信引用another post with similar issue会对我有所帮助,但这是有关启用CORS的问题,而不是没有通过预检请求或其他类型的请求发送标头的问题。

1 个答案:

答案 0 :(得分:0)

您尚未包含authHeader的代码,但我认为它只是返回Authorization标头的值。

这对我来说似乎很可疑:

async fetchTasks({ commit }) {
  const response = await axios.get('http://127.0.0.1:3000/tasks', {headers: authHeader()})

  commit('setTasks', response.data)

  axios.defaults.headers.common['Authorization'] = authHeader()
},

最后一行似乎试图全局设置Authorization头,以便将其包含在所有后续axios请求中。很好,但是不尽快做似乎很奇怪。您可以在login动作中添加类似的代码行,这很有意义,但我认为刷新页面时不会调用该行。

然后有这个位:

{headers: authHeader()}

如果authHeader返回Authorization标头的 value ,则此方法将无效。相反,您需要:

{headers: { Authorization: authHeader() }}

理想情况下,您无需在此处设置任何标头,只需在尝试此请求之前设置全局标头即可。

虽然这不是导致问题的直接原因,但您似乎对CORS感到困惑。您已经配置了代理,这意味着您没有使用CORS。您提出的请求来自同一来源,因此CORS不适用。如果您没有发出跨域请求,则无需包含CORS响应标头。如果您确实要发出跨域请求,请不要使用代理。您应该尝试在开发过程中模仿生产环境,因此,如果要在生产中使用CORS,则应在开发过程中使用它。否则,请坚持使用代理。