从 Apollo Vue 发送 headers 到 Node

Send headers from Apollo Vue to Node

我正在使用 jwt、apollo-vue 和 graphql

对注册和身份验证做一些 api

我无法通过查询获取数据(或通过更改设置数据)from/to 我的后端。 但我可以从邮递员那里做到这一点,因为我知道如何在 headers.

中发送令牌

我也尝试调用 onLogin(apolloClient, token) 从 vuex 登录操作。无效

我是后端新手,我将不胜感激任何建议

另一个问题? : 如果在下面的函数中...

const authLink = setContext(async (_, { headers }) => {
  // add here console.log(localStorage.getItem('apollo-token'))
  const token = await localStorage.getItem('apollo-token')
  // and then console.log(token)

  return {...}
})

第一个控制台打印一个令牌,但第二个控制台打印null。这对我来说很奇怪。

这是我的vue-apollo.js

import Vue from 'vue'
import VueApollo from 'vue-apollo'
import { createApolloClient, restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client'
import { setContext } from 'apollo-link-context'

Vue.use(VueApollo)

const AUTH_TOKEN = 'apollo-token'

// Http endpoint
const httpEndpoint = process.env.VUE_APP_GRAPHQL_HTTP || 'http://localhost:3000/graphql'

const authLink = setContext(async (_, { headers }) => {
  const token = await localStorage.getItem(AUTH_TOKEN)
  return {
    ...headers,
    Authorization: token || ''
  }
})

// Files URL root
export const filesRoot = process.env.VUE_APP_FILES_ROOT || httpEndpoint.substr(0, httpEndpoint.indexOf('/graphql'))

Vue.prototype.$filesRoot = filesRoot

// Config
const defaultOptions = {
  httpEndpoint,
  wsEndpoint: null,
  tokenName: AUTH_TOKEN,
  websocketsOnly: false,
  ssr: false,
  link: authLink
}

export const { apolloClient } = createApolloClient({
  ...defaultOptions,
})
 

export function createProvider(options = {}) {
  const { apolloClient, wsClient } = createApolloClient({
    ...defaultOptions,
    ...options,
  })
  apolloClient.wsClient = wsClient


  const apolloProvider = new VueApollo({
    defaultClient: apolloClient,
    defaultOptions: {
      $query: {
        // fetchPolicy: 'cache-and-network',
      },
    },
    errorHandler(error) {
      // eslint-disable-next-line no-console
      console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message)
    },
  })

  return { apolloProvider, apolloClient }
}

// Manually call this when user log in
export async function onLogin(apolloClient, token) {
  if (typeof localStorage !== 'undefined' && token) {
    localStorage.setItem(AUTH_TOKEN, token)
  }
  if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
  try {
    await apolloClient.resetStore()
  } catch (e) {
    // eslint-disable-next-line no-console
    console.log('%cError on cache reset (login)', 'color: orange;', e.message)
  }
}

// Manually call this when user log out
export async function onLogout(apolloClient) {
  if (typeof localStorage !== 'undefined') {
    localStorage.removeItem(AUTH_TOKEN)
  }
  if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
  try {
    await apolloClient.resetStore()
  } catch (e) {
    // eslint-disable-next-line no-console
    console.log('%cError on cache reset (logout)', 'color: orange;', e.message)
  }
}

main.js 来自 vue

// HTTP connection to the API
const httpLink = createHttpLink({
  // You should use an absolute URL here
  uri: 'http://localhost:3000/graphql',
})
// Cache implementation
const cache = new InMemoryCache()

// Create the apollo client
const apolloClient = new ApolloClient({
  link: httpLink,
  cache,
})


Vue.config.productionTip = false
Vue.use(VueScreen)
  .use(VueApollo)

const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
})


new Vue({
  router,
  store,
  vuetify,
  apolloProvider,
  render: h => h(App)
}).$mount('#app')

EDIT: more code

这是查询,在 vue 的视图中

import gql from "graphql-tag";
export default {
  name: "Home",
  apollo: {
    Users: gql`
      {
        Users {
          _id
          username
          email
          password
          token
          createdAt
        }
      },
    `,   
  },
};

我收到的错误是:

bundle.esm.js:75 POST http://localhost:3000/graphql 500 (Internal Server Error)

Error sending the query 'Users' ServerError: Response not successful: Received status code 500 at throwServerError

在后端,这是我的查询

Query: {
    async Users(_, req, context) {
        const auth = checkAuth(context)
        if (auth.id) {
            const users = await User.find()
            users.forEach(e => {
                e.password = null
            })
            return users
        } else {
            return new Error("must be logged.")
        }
    },

这是我的 checkAuth.js

import jwt from 'jsonwebtoken'
import { AuthenticationError } from 'apollo-server'
import 'dotenv/config'


module.exports = (context) => {
    const authHeader = context.headers.authorization;
    console.log("headers: ",context.headers)
    if (authHeader) {
        const token = authHeader.split('Bearer ')[1];
        if (token) {
            try {
                const user = jwt.verify(token, process.env.SECRET_KEY);
                return user
            } catch (err) {
                return new AuthenticationError("Invalid token.")
            }
        }
        return new Error("Token must be 'Bearer [token]'")
    }
    return new Error("I need a token bro!")
}

EDIT 2

后端收到的context.header

headers: {
    host: 'localhost:3000',
    connection: 'keep-alive',
    'content-length': '160',
    'sec-ch-ua': '"Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"',
    accept: '*/*',
    'sec-ch-ua-mobile': '?0',
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
    'content-type': 'application/json',
    origin: 'http://localhost:8081',
    'sec-fetch-site': 'same-site',
    'sec-fetch-mode': 'cors',
    'sec-fetch-dest': 'empty',
    referer: 'http://localhost:8081/',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'es-419,es;q=0.9,en;q=0.8'
  },

据我所知,您只在授权中发送令牌 header。

const authLink = setContext(async (_, { headers }) => {
  const token = await localStorage.getItem(AUTH_TOKEN)
  return {
    ...headers,
    Authorization: token || ''
  }
})

但您希望在后端找到不记名令牌:


module.exports = (context) => {
    const authHeader = context.headers.authorization;
    console.log("headers: ",context.headers)
    if (authHeader) {
        const token = authHeader.split('Bearer ')[1]; << Your code is breaking here
        if (token) {
            try {
                const user = jwt.verify(token, process.env.SECRET_KEY);
                return user
            } catch (err) {
                return new AuthenticationError("Invalid token.")
            }
        }
        return new Error("Token must be 'Bearer [token]'")
    }
    return new Error("I need a token bro!")
}

您必须发送 'Bearer [token]' 而不仅仅是令牌。像这样:

const authLink = setContext(async (_, { headers }) => {
  const token = await localStorage.getItem(AUTH_TOKEN)
  return {
    ...headers,
    Authorization: `Bearer ${token}`
  }
})

documentation 中,setContext 是这样使用的:

const setAuthorizationLink = setContext((request, previousContext) => ({
  headers: {authorization: "1234"}
}));

The setContext function takes a function that returns either an object or a promise that returns an object to set the new context of a request.

在下面的代码中你只有 return headers。当你应该 return 上下文时。

const authLink = setContext(async (_, { headers }) => {
  const token = await localStorage.getItem(AUTH_TOKEN)
  return {
    ...headers,
    Authorization: token || ''
  }
})

试试这个

const authLink = setContext(async (_, { headers }) => {
  const token = await localStorage.getItem(AUTH_TOKEN)
  return {
    headers: {
      ...headers,
      Authorization: token || ''
    }
  }
})

未使用vue-apollo.js文件。

在您的 main.js 中,您在 Vue 中注入的 apolloClientmain.js 中声明并且不包含 authLink你在 vue-apollo.js 中的所有代码都没有被调用

所以不是这个:

// HTTP connection to the API
const httpLink = createHttpLink({
  // You should use an absolute URL here
  uri: 'http://localhost:3000/graphql',
})
// Cache implementation
const cache = new InMemoryCache()

// Create the apollo client
const apolloClient = new ApolloClient({
  link: httpLink,
  cache,
})


Vue.config.productionTip = false
Vue.use(VueScreen)
  .use(VueApollo)

const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
})


new Vue({
  router,
  store,
  vuetify,
  apolloProvider,
  render: h => h(App)
}).$mount('#app')

试试这个:

import { createProvider } from 'vue-apollo.js';

Vue.config.productionTip = false
Vue.use(VueScreen)
  .use(VueApollo)

const { apolloProvider, apolloClient } = createProvider();

new Vue({
  router,
  store,
  vuetify,
  apolloProvider,
  render: h => h(App)
}).$mount('#app')