0

我有两个 Node.js 服务器应用程序。第一个在 localhost:8081 上运行 Koa.JS,而第二个在 localhost:8080 上运行 Vue.JS。在两个服务器中,我使用的是 HTTP 而不是 HTTPS。

Koa.JS 服务器使用 Passport.JS 执行 oAuth2 登录流程,并提供端点以从 API 获取数据并将承载令牌传递给授权标头。

Vue.js 服务器负责客户端代码。它使用 axios 库调用 Koa 端点。

如果我打开浏览器,并针对 Koa 服务器测试登录流程,一切都很好并且工作正常。以下是步骤:

  1. 本地主机:8081/api/oauth/authenticate

    router.get(
      '/authenticate',
      passport.authenticate(
        'oauth2',
        { scope: config.scope }
      ))
    
  1. 用户登录并授予访问权限
  2. 完成后,回调被称为 localhost:8081/api/oauth/callback

    router.get(
      '/callback',
      ctx => {
        return passport.authenticate(
          'oauth2',
          async (err, user) => {
            if (err) ctx.throw(err)
            const tokenSession = new token(ctx.session)
            await ctx.login(user)
            tokenSession.setPublicCredentials(user)
            ctx.redirect(`${config.vuehost}/auth?isUserLoggedIn=true`)
          })(ctx)
      })
    
  1. 会话与用户信息一起保存
  2. 用户打开新选项卡以转到 localhost:8081/api/user/profile

    router.get(
      '/user/profile',
      async (ctx) => {
        if (ctx.isAuthenticated) {
          const options = {
            headers: { Authorization: `Bearer ${ctx.session.passport.user.access_token}` },
            json: true,
            method: 'GET',
            uri: 'https://developer.mycoolapi.com/userprofile/v1/users/@me'
          }
          const response = await rp(options)
          ctx.body = JSON.stringify(response)
        } else {
          ctx.throw(401)
        }
      }
    )
    
  1. Koa 服务器调用另一个 API 来检索用户配置文件数据,Vue.js 应用程序得到一个正确的 JSON 响应

但是,如果我执行以下操作, ctx.session 会丢失:

  1. 导航到 localhost:8080(Vue.js 服务器)
  2. 通过重定向到 Koa 端点 localhost:8081/api/oauth/authenticate 来执行登录
  3. 登录并授予访问权限
  4. 在 Koa 上 /callback 重定向回 l​​ocalhost:8080/auth?isUserLoggedIn=true
  5. 在 Vue 应用程序中,使用 this.$route.query.isUserLoggedIn 检索查询参数,如果为 true,则调用 Koa 端点以获取用户配置文件数据 localhost:8081/api/user/profile

    axios.get('http://localhost:8081/api/user/profile')
        .then (response => {
          console.info(`\nsetUserData response: ${JSON.stringify(response)}\n`)
        })
        .catch (err => {
          console.info(`\nsetUserData error: ${JSON.stringify(err)}\n`)
        })
    

最后一步返回 401 Unauthorized。

经过进一步调查,具有配置文件端点的 Koa 路由位于 appRoutes 中间件中。该中间件紧跟在 app.use(requireLogin) 中间件之后,该中间件检查会话是否经过身份验证 (ctx.isAuthenticated())。

'use strict'

const requireLogin = async (ctx, next) => {
  if (ctx.isAuthenticated()) {
    await next()
  } else {
    ctx.status = 401
    ctx.body = {
      errors: [{ title: 'Login required', status: 401 }]
    }
  }
}
module.exports = requireLogin

这是发生 401 错误的地方,因为此时 ctx 会话为空。

// Add routing
app.use(authRoutes.routes())
app.use(authRoutes.allowedMethods())
app.use(requireLogin)
app.use(appRoutes.routes())
app.use(appRoutes.allowedMethods())

我在处理某种时间问题吗?我尝试在我的 Koa server.js 文件中注释掉头盔中间件,但没有帮助。

请注意,如果我在同一个浏览器会话中打开一个新选项卡并转到 localhost:8081/api/user/profile 它工作正常。仅当从 Vue.js 调用此端点时,它才会由于 ctx.sesssion 为空而失败。

知道是什么将 ctx.session 重置为 null 吗?

4

1 回答 1

1

仔细阅读这篇文章:https ://medium.com/@xgwang/a-practical-guide-to-cors-51e8fd329a1f ,我终于能够解决问题了。

在 server.js 中,将 {credentials:true} 传递给 cors 选项。

app.use(cors({credentials:true}))

在 Vue.js 中,将 axios 默认设置为:

axios.defaults.baseURL = 'http://localhost:8081'
axios.defaults.withCredentials = true
axios.defaults.crossDomain = true

最后,当使用 axios 从 Vue.js 调用 Koa 端点时,传递给 headers { 'Access-Control-Allow-Credentials': true }

this.$axios({
        headers: { 'Access-Control-Allow-Credentials': true },
        method: 'GET',
        url: `${config.koahost}/api/user/profile`
      })

于 2018-08-20T22:29:07.797 回答