Nuxtjs Auth 模块在中间件中不起作用

Nuxtjs Auth module not working in the middleware

您好,我在 Whosebug 上发现了一个类似于我的老问题,但没有答案:nuxtjs/auth axios not sending cookie

也在 GitHub 上,没有有效的解决方案:https://github.com/nuxt-community/auth-module/issues/478

所以问题是,如果我在任何页面中调用 $auth.loggedIn,它就像一个魅力,但如果我在我的自定义身份验证中间件中这样做(或者如果我使用默认的 auth 中间件), 它总是 return false.

我在 nuxt.config.js

中的身份验证配置
auth: {
    strategies: {
      local: {
        endpoints: {
          login: {
            url: '/rest-auth/login/',
            method: 'post',
            propertyName: 'key',
          },
          logout: { url: '/rest-auth/logout/', method: 'post' },
          user: {
            url: '/rest-auth/user/',
            method: 'get',
            propertyName: false,
          },
        },
        tokenType: 'Token',
        tokenName: 'Authorization',
      },
      redirect: {
        login: '/user_dashboard',
        home: '/',
      },
    },
  },

我的自定义授权中间件

export default async function ({ $auth, redirect }) {
  const user = await $auth.loggedIn
  console.log(user) // <-- this always return false for some reason :(
  if (user) {
    // let the user see the page
  } else {
    // redirect to homepage
    redirect('/')
  }
}

编辑:

应要求,这是我的 package.json:

{
  "name": "<MY_APP_NAME>",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "lint:style": "stylelint **/*.{vue,css} --ignore-path .gitignore",
    "lint": "npm run lint:js && npm run lint:style",
    "test": "jest"
  },
  "lint-staged": {
    "*.{js,vue}": "eslint",
    "*.{css,vue}": "stylelint"
  },
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
      "pre-commit": "lint-staged"
    }
  },
  "dependencies": {
    "@nuxt/content": "^1.9.0",
    "@nuxtjs/auth": "^4.9.1",
    "@nuxtjs/axios": "^5.12.2",
    "@nuxtjs/pwa": "^3.0.2",
    "cookie-universal-nuxt": "^2.1.4",
    "core-js": "^3.6.5",
    "nuxt": "^2.14.7",
    "nuxt-buefy": "^0.4.3"
  },
  "devDependencies": {
    "@commitlint/cli": "^11.0.0",
    "@commitlint/config-conventional": "^11.0.0",
    "@nuxtjs/eslint-config": "^3.1.0",
    "@nuxtjs/eslint-module": "^3.0.0",
    "@nuxtjs/style-resources": "^1.0.0",
    "@nuxtjs/stylelint-module": "^4.0.0",
    "@vue/test-utils": "^1.1.0",
    "babel-core": "7.0.0-bridge.0",
    "babel-eslint": "^10.1.0",
    "babel-jest": "^26.5.0",
    "eslint": "^7.10.0",
    "eslint-config-prettier": "^6.12.0",
    "eslint-plugin-nuxt": "^1.0.0",
    "eslint-plugin-prettier": "^3.1.4",
    "husky": "^4.3.0",
    "jest": "^26.5.0",
    "lint-staged": "^10.4.0",
    "node-sass": "^4.14.1",
    "prettier": "^2.1.2",
    "sass-loader": "^10.0.3",
    "stylelint": "^13.7.2",
    "stylelint-config-prettier": "^8.0.2",
    "stylelint-config-standard": "^20.0.0",
    "vue-jest": "^3.0.4"
  }
}

不幸的是,我无法使 nuxtjs/auth 在中间件中工作,但我能够通过将 cookie-universal-nuxtnuxtjs/auth 结合使用来解决问题:

You can leave your axios version as it is, no need to downgrade for this solution

  1. npm install --save cookie-universal-nuxt
  2. 在您的 nuxt.config.js 文件中添加 cookie-universal-nuxt
modules: [
    // other modules ...
    '@nuxtjs/auth',
    'cookie-universal-nuxt',
  ],
  1. 创建自定义 auth 中间件。我在中间件文件夹中调用了我的auth-user
export default async function ({ app, redirect }) {
  // the following look directly for the cookie created by nuxtjs/auth
  // instead of using $auth.loggedIn
  const user = await app.$cookies.get('auth._token.local')
  if (user) {
    // let the user see the page
  } else {
    // redirect to homepage
    redirect('/')
  }
}
  1. 然后在您的应用程序的根页面中声明您希望仅供经过身份验证的用户访问的中间件:
<script>
export default {
  middleware: ['auth-user'],
}
</script>

If this doesn't work, check the cookie name where your user credential are saved by opening the developer tools / inspector in the browser.

有同样的问题,我现在通过大量试验和错误解决了它。

服务器端不知道登录状态,因为他们没有实现它(?)。

所以我创建了自己的中间件来设置服务器端的状态。

创建: middleware/auth-ssr.ts

import { Context, Middleware } from '@nuxt/types';
import { parse as parseCookie } from 'cookie';
import jsonwebtoken from 'jsonwebtoken';
import { IJwtPayload } from '../../api/_types/types';

/**
 * This middleware is needed when running with SSR
 * it checks if the token in cookie is set and injects it into the nuxtjs/auth module
 * otherwise it will redirect to login
 * @param context
 */
const debugAuthMiddleware: Middleware = async (context: Context) => {
  if (process.server && context.req.headers.cookie != null) {
    try {
      const cookies = parseCookie(context.req.headers.cookie);
      const token = cookies['auth._token.local'] || '';
      const tokenWithoutBearer = token.replace('Bearer ', '');
      // console.log('headers.cookie token', token);
      // console.log('debugAuthMiddleware $auth 1', context.$auth.$state);
      if (!token || token.includes('false')) {
        // sometimes it stores 'Bearer false' when it unsets
        return;
      }
      const jwt: IJwtPayload = (jsonwebtoken.decode(tokenWithoutBearer) as unknown) as IJwtPayload;
      // console.log('jwt payload', jwt);
      if (!jwt) {
        return;
      }
      // console.log('set token ✅', jwt);
      await context.$auth.setToken('locale', tokenWithoutBearer);
      await context.$auth.setUser(jwt);
      context.$auth.$state.loggedIn = true;
    } catch (e) {
      console.error('debugAuthMiddleware', e);
    }
    // console.log('debugAuthMiddleware $auth 2', context.$auth.$state);
  }
};

export default debugAuthMiddleware;

然后在 nuxt.config.ts 我有这个中间件:

router: {
    middleware: ['user-agent', 'auth-ssr', 'auth'],
},

这是我的身份验证配置:

auth: {
    redirect: {
      logout: '/?signedOut=1',
      home: '/dashboard',
    },
    strategies: {
      local: {
        endpoints: {
          login: { url: '/api/v1/auth/login', method: 'post', propertyName: 'token' },
          logout: { url: '/api/v1/auth/logout', method: 'post' },
          user: { url: '/api/v1/user', method: 'get', propertyName: 'user' },
        },
        autoFetchUser: false, // do not fetch automatically! user object is coming from login api call
        rewriteRedirects: true, // If enabled, user will redirect back to the original guarded route instead of redirect.home.
        fullPathRedirect: true, // If true, use the full route path with query parameters for redirect
      },
    },
  },

然后服务器端 nodejs/auth-middleware 获取正确的登录状态 + 用户(我的 JWT 令牌包括用户 ID、电子邮件、名称、范围:[])。

您是否尝试过使用不带 $ 符号的身份验证?那应该可以解决您的问题

它已经“旧”了 post 但我遇到了与您相同的问题。如我所见,您正在使用 DRF。因此,我根据 Django-documentation-authentication 更改了 urls.py 并使用 obtain_auth_token 创建了 mylogin url 或者您可以创建自己的 LoginView。 另外,我用关键字 Bearer 创建了 TokenAuthentication 的子类,终于成功了! 例子: urls.py

from rest_framework.authtoken import views
path('api/login/',views.obtain_auth_token)

创建类似 authtoken.py

的文件
from rest_framework.authentication import TokenAuthentication
class TokenAuthentication(TokenAuthentication):
    keyword = 'Bearer'

Settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'myapp.authtoken.TokenAuthentication'
]

现在 nuxt.config.js

 auth: {
    strategies: {
      local: {
        user: {
          property: 'username',
        },
        endpoints: {
          login: { url: 'api/login/', method: 'post' },
          logout: { url: 'rest-auth/logout/', method: 'post' },
          user: { url: 'rest-auth/user/', method: 'get' }
        },
        tokenRequired:true,
        tokenType:'Bearer',
        autoFetchUser: false
      }
    }
  }

就是这些,希望对您有所帮助!

更新

在我的 nuxt.config.js 上,我尝试了这个并且它在没有触及任何其他东西的情况下工作!

auth: {
    strategies: {
        local: {
          user: {
            property: 'username',
            autoFetch: true
          },

          endpoints: {

            login: { url: '/rest-auth/login/', method: 'post' },
            logout: { url: '/rest-auth/logout/', method: 'post' },
            user: { url: '/rest-auth/user/', method: 'get' },
          },
          token: {
            property: 'key',
            type: 'Token',
            name: 'Authorization',
          },
 
        }
      }
    }```