DRF Django 认证

使用Auth.js进行Django REST Framework身份验证

介绍如何实现基于Django REST的身份验证系统并将其与前端的Auth.js集成,涵盖了基于凭据的身份验证以及Google的社交身份验证。

2024年3月20日
分享

本教程介绍如何实现基于 Django REST 的身份验证系统(通过Django REST Framework)并将其与前端的Auth.js(以前称为 NextAuth.js)集成。它涵盖了基于凭据的身份验证以及 Google 的社交身份验证。

介绍

Django 的默认身份验证系统对于全栈 Django 项目来说是一个出色的开箱即用解决方案。但在使用 Django REST Framework 用于客户端-服务端架构时,利用 Django 的内置身份验证可能相当困难。最重要的是,Django 本身并不支持社交身份验证。

幸运的是,您可以将 Django 的身份验证系统公开为 API,并使用社区开发的django-allauthdj-rest-auth软件包添加社交身份验证支持。一旦您的身份验证系统公开,从前端连接到它就相对容易了。

虽然基于凭据的身份验证很容易在前端实现,但添加社交身份验证时会出现问题。每个社交提供商都是不同的,单独为每个社交提供商添加支持可能非常耗时。

因此,出现了Auth.js,它是 Next.js、SvelteKit 和 SolidStart 的标准化开源身份验证解决方案。该库提供基于凭据的身份验证、无密码身份验证、社交身份验证等。它还支持 60 多个社交提供商。

在本教程中,我们将了解如何实现基于 Django REST 的身份验证系统并从Next.js前端连接到它。在本教程结束时,您将拥有一个支持社交身份验证的完整可用的身份验证系统。我们将首先处理后端,然后再处理前端。

实现的解决方案具有以下身份验证流程:

django-rest-authjs-1

目标

学完本教程后,您将能够:

  1. 使用 Django REST Framework、django-allauth 和 dj-rest-auth 实现基于 Django REST 的身份验证系统。
  2. 通过 djangorestframework-simplejwt 启用并配置 JSON Web Token 身份验证。
  3. 设置 django-cors-headers 以避免从前端连接时出现 CORS 问题。
  4. 设置 Auth.js 以支持基于凭据的身份验证以及社交身份验证。
  5. 将基于 Django REST 的身份验证系统与 Auth.js 连接。
  6. 通过 Google 添加社交身份验证。

后端

在本节中,我们将创建一个新的 Django 项目,启动一个专用的身份验证应用程序,安装和配置所有依赖项,并通过cURL测试身份验证系统。

Django 设置

首先为您的项目和虚拟环境创建一个新目录:

$ mkdir django-rest-authjs && cd django-rest-authjs
$ python3.11 -m venv env
$ source env/bin/activate
(env)$

接下来,安装 Django 并创建一个新项目:

(env)$ pip install Django==4.2.3
(env)$ django-admin startproject core .

迁移数据库并运行服务器:

(env)$ python manage.py migrate
(env)$ python manage.py runserver

最后,打开您最喜欢的网络浏览器并导航到http://localhost:8000。您应该能够看到默认的 Django 登录页面。

认证应用程序

为了使我们的项目更有组织性,我们创建一个专门的应用程序进行身份验证。该应用程序将包括身份验证视图和 URL。

创建一个新应用程序:

(env)$ python manage.py startapp authentication

将其添加到core/settings.pyINSTALLED_APPS中:

# core/settings.py

INSTALLED_APPS = [
    # ...
    "authentication.apps.AuthenticationConfig",
]

在“authentication”文件夹中创建urls.py文件:

# authentication/urls.py

from django.urls import path
from . import views

urlpatterns = [
    # URLs will come here
]

注册:

# core/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('api/auth/', include('authentication.urls')),
    path("admin/", admin.site.urls),
]

这就是初始设置。

基于凭证的身份验证

在本节中,我们将设置Django REST Frameworkdjango-allauthdj-rest-auth以启用基于凭据的身份验证。此外,我们将设置django-cors-headers以避免从前端连接时出现 CORS 问题。

Django REST 框架

首先安装 Django REST Framework:

(env)$ pip install djangorestframework==3.14.0

接下来,导航到core/settings.py并将其和authtoken一起添加到INSTALLED_APPS

# core/settings.py

INSTALLED_APPS = [
    # ...
    "rest_framework",
    "rest_framework.authtoken",
]

authtoken应用程序是必需的,因为我们将使用基于令牌的身份验证而不是 Django 的默认SessionAuthentication。令牌身份验证更适合客户端-服务端架构。

我们可以选择两种基于令牌的身份验证系统:

建议您通过djangorestframework-simplejwt插件使用 JWT 身份验证。JWT 方法更好,因为它更安全、更容易处理数据库,并且具有标准化的 HTTP 响应。

要安装 Simple JWT 插件,请运行:

$(env) pip install djangorestframework-simplejwt==5.2.2

接下来,将其添加到INSTALLED_APPS中的rest_framework.authtoken下面:

# core/settings.py

INSTALLED_APPS = [
    # ...
    "rest_framework.authtoken",
    "rest_framework_simplejwt",
]

然后,将以下设置添加到core/settings.py的底部:

# core/settings.py

SIMPLE_JWT = {
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
    "REFRESH_TOKEN_LIFETIME": timedelta(days=7),
    "ROTATE_REFRESH_TOKENS": False,
    "BLACKLIST_AFTER_ROTATION": False,
    "UPDATE_LAST_LOGIN": True,
    "SIGNING_KEY": "complexsigningkey",  # generate a key and replace me
    "ALGORITHM": "HS512",
}

请根据您的需要随意修改这些设置。要了解每个变量的作用,请查看官方文档

不要忘记导入:

from datetime import timedelta

最后,更改默认的 DRF 身份验证类别:

# core/settings.py

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ]
}

Django-allauth

首先,安装 django-allauth:

(env)$ pip install django-allauth==0.52.0

接下来,导航到settings.py,将其与accountsocialaccount一起添加到INSTALLED_APPS

# core/settings.py

INSTALLED_APPS = [
    "django.contrib.sites",  # make sure 'django.contrib.sites' is installed
    # ...
    "allauth",
    "allauth.account",
    "allauth.socialaccount",  # add if you want social authentication
]

django-allauth 依赖于Django 的“sites”框架,因此请确保已安装它。最重要的是,请确保您设置了SITE_ID

# core/settings.py

SITE_ID = 1  # make sure SITE_ID is set

接下来,您应该关闭电子邮件验证功能:

# core/settings.py

ACCOUNT_EMAIL_REQUIRED = False
ACCOUNT_EMAIL_VERIFICATION = "none"

在此步骤中应关闭电子邮件验证,因为我们的 Django 项目未配置为发送电子邮件。最重要的是,我们没有允许用户验证其电子邮件地址的端点。本教程末尾有更多相关内容。

最后,再次迁移数据库:

(env)$ python manage.py migrate

dj-rest-auth

首先安装 dj-rest-auth 包:

(env)$ pip install "dj-rest-auth[with_social]==4.0.0"

我们需要使用with_social说明符,因为我们想要启用标准注册过程。此外,稍后当我们启用社交身份验证时,我们将使用此包。

接下来,导航到core/settings.py将其添加到INSTALLED_APPS

# core/settings.py

INSTALLED_APPS = [
    # ...
    "dj_rest_auth",
    "dj_rest_auth.registration",
]

然后将以下配置设置添加到settings.py

# core/settings.py

REST_AUTH = {
    "USE_JWT": True,
    "JWT_AUTH_HTTPONLY": False,
}
  1. USE_JWT是必需的,因为我们使用的是djangorestframework-simplejwt
  2. JWT_AUTH_HTTPONLY应该关闭,否则 dj-rest-auth 将不会发送刷新令牌。

对于所有REST_AUTH设置,请查看官方文档

在撰写本文时,官方安装指南告诉您注册dj_rest_auth.urls. 我不建议您这样做,因为如果配置不正确(例如密码重置、电子邮件验证),默认 URL 会包含损坏的 URL。

相反,请查看dj-rest-auth源代码并精心挑选您需要的 URL:

像这样更新authentication/urls.py

# authentication/urls.py

from dj_rest_auth.jwt_auth import get_refresh_view
from dj_rest_auth.registration.views import RegisterView
from dj_rest_auth.views import LoginView, LogoutView, UserDetailsView
from django.urls import path
from rest_framework_simplejwt.views import TokenVerifyView

urlpatterns = [
    path("register/", RegisterView.as_view(), name="rest_register"),
    path("login/", LoginView.as_view(), name="rest_login"),
    path("logout/", LogoutView.as_view(), name="rest_logout"),
    path("user/", UserDetailsView.as_view(), name="rest_user_details"),
    path("token/verify/", TokenVerifyView.as_view(), name="token_verify"),
    path("token/refresh/", get_refresh_view().as_view(), name="token_refresh"),
]

django-cors-headers

我们需要安装和配置的最后一个包是django-cors-headers。当我们的前端与后端通信时,该包将设置正确的 CORS 标头。

首先,通过运行以下命令来安装它:

$(env) pip install django-cors-headers==4.1.0

将其添加到core/settings.pyINSTALLED_APPS中:

# core/settings.py

INSTALLED_APPS = [
    # ...
    "corsheaders",
]

CorsMiddleware添加到MIDDLEWARE列表中:

# core/settings.py

MIDDLEWARE = [
    # ...
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    # ...
]

您应该把CorsMiddleware放到尽可能前面的位置。特别是在任何可以生成响应的中间件(例如 Django 的CommonMiddleware或 Whitenoise的WhiteNoiseMiddleware )之前。

最后,将以下设置添加到core/settings.py的底部:

# core/settings.py

if DEBUG:
    CORS_ALLOW_ALL_ORIGINS = True
else:
    CORS_ALLOWED_ORIGINS = [
        "http://localhost:3000/",
        "http://127.0.0.1:3000/",
    ]

太棒了!就是这样。

您现在拥有了一个完全可运行的基于凭据的身份验证系统,该系统使用 JSON Web 令牌。

测试

为了测试 API,我们将使用cURL。我们还将把响应通过管道传输到jq中,以自动格式化它们并用颜色突出显示它们。

首先运行 Django 开发服务:

(env)$ python manage.py runserver

注册

首先,创建一个用户:

$ curl -XPOST -H "Content-type: application/json" -d '{
      "username": "user1",
      "password1": "complexpassword123",
      "password2": "complexpassword123"
  }' 'http://localhost:8000/api/auth/register/' | jq

您应该得到类似的响应:

{
  "access": "eyJhb...",
  "refresh": "eyJhb...",
  "user": {
    "pk": 1,
    "username": "user1",
    "email": ""
  }
}

您现在可以使用access令牌来验证自己的身份。access令牌在core/settings.py中指定的时间内有效,然后必须使用refresh令牌刷新它。

登录

以新创建的用户身份登录:

$ curl -XPOST -H "Content-type: application/json" -d '{
      "username": "user1",
      "password": "complexpassword123"
  }' 'http://localhost:8000/api/auth/login/' | jq

响应将与此类似:

{
  "access": "eyJhb...",
  "refresh": "eyJhb...",
  "user": {
    "pk": 1,
    "username": "user1",
    "email": ""
  }
}

用户详细信息

通过获取到的access令牌获取用户详细信息:

$ curl -XGET -H 'Authorization: Bearer <your_access_token>' \
    -H "Content-type: application/json" 'http://localhost:8000/api/auth/user/' | jq

响应:

{
  "pk": 1,
  "username": "user1",
  "email": ""
}

令牌验证

要验证令牌是否仍然有效,您可以将其传递给/api/auth/token/verify/如下:

$ curl -XPOST -H "Content-type: application/json" -d '{
    "token": "<your_access_token>"
  }' 'http://localhost:8000/api/auth/token/verify/' | jq

响应:

{}

状态码:

  • 200表示令牌仍然有效。
  • 4xx意味着令牌不再有效。

令牌刷新

要获取刷新的access令牌,您必须像下面这样使用refresh令牌:

$ curl -XPOST -H "Content-type: application/json" -d '{
    "refresh": "<your_refresh_token>"
  }' 'http://localhost:8000/api/auth/token/refresh/' | jq

响应:

{
  "access": "eyJhb...",
  "access_expiration": "2023-07-11T17:42:22.480739Z"
}

响应将包含新access令牌和到期时间。

注销

要注销用户,请尝试发送以下请求:

$ curl -XPOST -H 'Authorization: Bearer <your_access-token>' \
    -H "Content-type: application/json" 'http://localhost:8000/api/auth/logout/' | jq

响应:

{
  "detail": "Neither cookies or blacklist are enabled. Delete the token on client."
}

由于我们禁用了 cookie 并且没有配置黑名单,因此该端点不会执行任何操作。注销用户时无需调用它。您唯一要做的就是从客户端删除令牌。

如果您想要更安全的方法,请通过JWT Blacklist 应用程序设置黑名单。

通过 Google 进行社交身份验证

在本节中,我们将了解如何设置社交身份验证。将指导您通过 Google 设置社交身份验证,但您也可以对任何其他社交提供商使用类似的步骤。

设置

要启用 Google 注册,您首先需要创建 OAuth 客户端密钥。导航到您的Google Cloud Console,选择您要使用的项目,然后搜索“API Credentials”:

django-rest-authjs-2

接下来,单击“创建凭据”按钮并在下拉列表中选择“OAuth 客户端 ID”:

django-rest-authjs-3

选择“Web 应用程序”,选择自定义名称,然后添加您的前端 URL 作为授权重定向 URI。为了更容易测试,我建议您还添加:

最后,单击“创建”以生成凭据:

django-rest-authjs-4

您将看到您的“客户端 ID”和“客户端密码”。记下它们,因为我们在下一步中需要它们。

接下来,返回您的 Django 项目并将以下应用程序添加到INSTALLED_APPS

# core/settings.py

INSTALLED_APPS = [
    # ...
    "allauth.socialaccount.providers.google",
]

接下来,将SOCIALACCOUNT_PROVIDERS设置添加到core/settings.py中,如下所示:

# core/settings.py

SOCIALACCOUNT_PROVIDERS = {
    "google": {
        "APP": {
            "client_id": "<your google client id>",  # replace me
            "secret": "<your google secret>",        # replace me
            "key": "",                               # leave empty
        },
        "SCOPE": [
            "profile",
            "email",
        ],
        "AUTH_PARAMS": {
            "access_type": "online",
        },
        "VERIFIED_EMAIL": True,
    },
}

确保将<your google client id><your google secret>替换为您的实际钥匙。将key属性留空,因为 Google 社交身份验证不需要该属性。

authentication/views.py中添加一个继承自SocialLoginView的新视图:

# authentication/views.py

from dj_rest_auth.registration.views import SocialLoginView
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Client


class GoogleLogin(SocialLoginView):
    adapter_class = GoogleOAuth2Adapter
    callback_url = "http://127.0.0.1:3000/"
    client_class = OAuth2Client

最后,将以下 URL 添加到authentication/urls.py

# authentication/urls.py

from allauth.socialaccount.views import signup
from authentication.views import GoogleLogin

urlpatterns = [
    # ...
    path("google/", GoogleLogin.as_view(), name="google_login"),
]

不要忘记导入:

from authentication.views import GoogleLogin

测试

要测试 API 端点是否工作,您首先需要获取id_token并将其 POST 到后端的端点。最简单的获取方法id_token是通过Google 的 OAuth 2.0 Playground

要获得令牌,您可以关注这篇精彩文章

接下来,将id_token作为access_token在 cURL 请求正文中传递:

$ curl -XPOST -H "Content-type: application/json" -d '{
    "access_token": "<your_id_token>"
  }' 'http://localhost:8000/api/auth/google/'

您应该得到类似的响应:

{
  "access": "eyJhb...",
  "refresh": "eyJhb...",
  "user": {
    "pk": 2,
    "username": "user2",
    "email": ""
  }
}

您现在可以使用access令牌来验证自己的身份。

前端

在本节中,我们将创建 Next.js 项目、设置Chakra UI、安装和配置 Auth.js,并启用基于凭据的社交身份验证。最后,我们还将测试身份验证系统。

Web 应用程序将具有以下端点:

  1. /提示用户登录。
  2. /profile显示用户的帐户信息。
  3. /api/auth/...由 Auth.js 动态 API 路由器处理。

Next.js 设置

首先使用create-next-app生成 Next.js 项目:

$ npx create-next-app@13.4.9 django-rest-authjs-frontend

√ Would you like to use TypeScript? ... Yes
√ Would you like to use ESLint? ... Yes
√ Would you like to use Tailwind CSS? ... No
√ Would you like to use `src/` directory? ... No
√ Would you like to use App Router? (recommended) ... No
√ Would you like to customize the default import alias? ... No

Creating a new Next.js app in ~\django-rest-authjs-frontend.

为了简单起见,我建议您只对 TypeScript 和 ESLint 说“是”。如果您更喜欢 TailwindCSS 而不是 Chakra UI,您也可以选择使用 TailwindCSS。

Chakra 用户界面设置

为了简化 UI 构建过程,我们将引入 Chakra UI——一个简约、模块化且可访问的组件库,用于快速构建 React UI。该库具有内置组件、专用挂钩、样式系统等等。

通过运行以下命令来安装它:

$ npm i @chakra-ui/react@2.5.5 @chakra-ui/next-js@2.1.4 @emotion/react@11.10.6 \
        @emotion/styled@11.10.6 framer-motion@10.12.4

接下来,通过extendTheme()创建一个新主题,并在pages/_app.tsx中用ChakraProviderComponent包装起来,如下所示:

// pages/_app.tsx

import type {AppProps} from "next/app";
import {ChakraProvider, extendTheme} from "@chakra-ui/react";

export const theme = extendTheme({});

export default function App({Component, pageProps: {session, ...pageProps}}: AppProps) {
  return (
    <ChakraProvider theme={theme}>
      <Component {...pageProps} />
    </ChakraProvider>
  );
}

不要忘记导入:

import {ChakraProvider, extendTheme} from "@chakra-ui/react";

我们创建了一个新主题,因为我们需要在下一步中将其传递给颜色模式脚本。此外,定义这样的主题可以让您将来轻松自定义 Chakra。

接下来,让我们处理颜色模式脚本。

颜色模式脚本确保本地存储同步正常工作。它必须添加在任何内容之前(在我们的例子中,在pages/_document.tsx文件中)。

像下面这样将ColorModeScript包含在您的pages/_document.tsx中:

// pages/_document.tsx

import {Head, Html, Main, NextScript} from "next/document";
import {ColorModeScript} from "@chakra-ui/react";
import {theme} from "./_app";

export default function Document() {
  return (
    <Html lang="en">
      <Head/>
      <body>
        <ColorModeScript initialColorMode={theme.config.initialColorMode}/>
        <Main/>
        <NextScript/>
      </body>
    </Html>
  );
}

再次强调,不要忘记导入:

import {ColorModeScript} from "@chakra-ui/react";
import {theme} from "./_app";

视图

让我们用登录提示替换默认的 Next.js 首页。

导航到pages/index.tsx并将其内容替换为以下内容:

// pages/index.tsx

import {Box, Button, Spinner, Text, VStack} from "@chakra-ui/react";

export default function Home() {
  return (
    <Box m={8}>
      <VStack>
        <Text>You are not authenticated.</Text>
        <Button colorScheme="blue" onClick={() => console.log("Beep boop")}>
          Sign in
        </Button>
      </VStack>
    </Box>
  );
}

启动开发服务:

$ npx next dev

打开您最喜欢的网络浏览器并导航至http://127.0.0.1:3000/。您应该会看到一个带有“您未经身份验证”消息和登录按钮的页面。单击该按钮应将调试消息打印到控制台。

django-rest-authjs-5

很好,这就是初始设置。

基于凭证的身份验证

在本节中,我们将安装 Auth.js 并处理基于凭据的身份验证。

Axios

在使用 Auth.js 之前,让我们安装 Axios——一个适用于浏览器和 Node.js 的简单的基于 Promise 的 HTTP 客户端。这个库将允许我们使用优雅而简洁的语法向后端发送 HTTP 请求。

通过 NPM 安装:

$ npm install axios@1.3.6

Auth.js

接下来,安装 Auth.js:

$ npm install next-auth@4.22.1

为了让 Auth.js 正常工作,我们首先必须设置一些环境变量。在 Next.js 中,环境变量是从项目根目录的.env.local自动加载的。

创建包含以下内容的.env.local

# .env.local

NEXTAUTH_URL=http://127.0.0.1:3000/
NEXTAUTH_SECRET=verycomplexsecretkey
NEXTAUTH_BACKEND_URL=http://127.0.0.1:8000/api/
NEXT_PUBLIC_BACKEND_URL=http://127.0.0.1:8000/api/

接下来,我们必须创建一个动态路由处理程序和 Auth.js 配置文件夹。在“pages/api”文件夹中创建一个“auth”文件夹,并在新创建的文件夹中创建一个[...nextauth].js文件。

pages/
├── api/
│   └── auth/
│       └── [...nextauth].js
├── _app.tsx
├── _document.tsx
└── index.tsx

在编写代码之前,我们先澄清一下 Auth.js 会话的工作原理。Auth.js 带有两种处理会话的内置方法:

  1. 使用数据库(会话存储在数据库中)
  2. 使用 JWT 令牌(会话存储在令牌中)

由于我们使用 Django 作为后端,因此我们必须采用第二种方法。这种方法的缺点是在 JWT 令牌中传递和存储accessTokenrefreshToken和其他敏感变量的繁琐参数。

将以下内容粘贴到[...nextauth].js中:

// pages/api/auth/[...nextauth].js

import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import axios from "axios";

// These two values should be a bit less than actual token lifetimes
const BACKEND_ACCESS_TOKEN_LIFETIME = 45 * 60;            // 45 minutes
const BACKEND_REFRESH_TOKEN_LIFETIME = 6 * 24 * 60 * 60;  // 6 days

const getCurrentEpochTime = () => {
  return Math.floor(new Date().getTime() / 1000);
};

const SIGN_IN_HANDLERS = {
  "credentials": async (user, account, profile, email, credentials) => {
    return true;
  },
};
const SIGN_IN_PROVIDERS = Object.keys(SIGN_IN_HANDLERS);

export const authOptions = {
  secret: process.env.AUTH_SECRET,
  session: {
    strategy: "jwt",
    maxAge: BACKEND_REFRESH_TOKEN_LIFETIME,
  },
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        username: {label: "Username", type: "text"},
        password: {label: "Password", type: "password"}
      },
      // The data returned from this function is passed forward as the
      // `user` variable to the signIn() and jwt() callback
      async authorize(credentials, req) {
        try {
          const response = await axios({
            url: process.env.NEXTAUTH_BACKEND_URL + "auth/login/",
            method: "post",
            data: credentials,
          });
          const data = response.data;
          if (data) return data;
        } catch (error) {
          console.error(error);
        }
        return null;
      },
    }),
  ],
  callbacks: {
    async signIn({user, account, profile, email, credentials}) {
      if (!SIGN_IN_PROVIDERS.includes(account.provider)) return false;
      return SIGN_IN_HANDLERS[account.provider](
        user, account, profile, email, credentials
      );
    },
    async jwt({user, token, account}) {
      // If `user` and `account` are set that means it is a login event
      if (user && account) {
        let backendResponse = account.provider === "credentials" ? user : account.meta;
        token["user"] = backendResponse.user;
        token["access_token"] = backendResponse.access;
        token["refresh_token"] = backendResponse.refresh;
        token["ref"] = getCurrentEpochTime() + BACKEND_ACCESS_TOKEN_LIFETIME;
        return token;
      }
      // Refresh the backend token if necessary
      if (getCurrentEpochTime() > token["ref"]) {
        const response = await axios({
          method: "post",
          url: process.env.NEXTAUTH_BACKEND_URL + "auth/token/refresh/",
          data: {
            refresh: token["refresh_token"],
          },
        });
        token["access_token"] = response.data.access;
        token["refresh_token"] = response.data.refresh;
        token["ref"] = getCurrentEpochTime() + BACKEND_ACCESS_TOKEN_LIFETIME;
      }
      return token;
    },
    // Since we're using Django as the backend we have to pass the JWT
    // token to the client instead of the `session`.
    async session({token}) {
      return token;
    },
  }
};

export default NextAuth(authOptions);
  1. 我们首先定义了后端令牌生命周期和获取 UNIX 时间的方法。
  2. 登录处理程序将仅与社交身份验证一起使用。当使用基于凭据的身份验证时,验证已在authorize()方法中执行。
  3. 然后我们定义了authOptions, 并添加CredentialsProvider到它的providers中。
  4. CredentialsProvider中,我们定义了登录表单应包含哪些字段,并定义了将凭据转发到后端以验证它们的authorize()方法。
  5. 然后,我们使用signIn()jwt()session()回调将数据附加到 JWT 令牌并将其传递给客户端。jwt()回调中实现了后端token刷新。

要了解有关 Auth.js 回调的更多信息,请查看Auth.js 回调

最后,将您的应用程序在pages/_app.tsx中用SessionProvider包装起来,如下所示:

// pages/_app.tsx

import type {AppProps} from "next/app";
import {SessionProvider} from "next-auth/react";
import {ChakraProvider, extendTheme} from "@chakra-ui/react";

export const theme = extendTheme({});

export default function App({Component, pageProps: {session, ...pageProps}}: AppProps) {
  return (
    <SessionProvider session={session}>
      <ChakraProvider theme={theme}>
        <Component {...pageProps} />
      </ChakraProvider>
    </SessionProvider>
  );
}

不要忘记导入:

import {SessionProvider} from "next-auth/react";

SessionProvider将确保您可以通过useSession()挂钩访问会话。

要注入Session类型,请在项目根目录中创建一个名为“types”的新文件夹。然后将名为next-auth.d.ts的文件添加到其中。

视图

继续,让我们研究视图。

首先,像这样修改pages/index.tsx

// pages/index.tsx

import {useRouter} from "next/router";
import {signIn, useSession} from "next-auth/react";
import {Box, Button, Spinner, Text, VStack} from "@chakra-ui/react";

export default function Home() {

  const router = useRouter();
  const {data: session, status} = useSession();

  if (status == "loading") {
    return <Spinner size="lg"/>;
  }

  // If the user is authenticated redirect to `/profile`
  if (session) {
    router.push("profile");
    return;
  }

  return (
    <Box m={8}>
      <VStack>
        <Text>You are not authenticated.</Text>
        <Button
            colorScheme="blue"
            onClick={() => signIn(undefined, {callbackUrl: "/profile"})}
        >
          Sign in
        </Button>
      </VStack>
    </Box>
  );
}
  1. 我们使用useSession()钩子从SessionProvider获取会话。
  2. 加载会话时,会向用户呈现该Spinner组件。
  3. 如果用户已经登录,我们会通过useRouter()重定向到/profile
  4. 登录按钮现在调用signIn()方法。

接下来,在“pages”文件夹中创建一个名为profile.tsx的新文件,其中包含以下内容:

// pages/profile.tsx

import {useState} from "react";
import {signOut, useSession} from "next-auth/react";
import {Box, Button, Code, HStack, Spinner, Text, VStack} from "@chakra-ui/react";
import axios from "axios";

export default function Home() {

  const {data: session, status} = useSession({required: true});
  const [response, setResponse] = useState("{}");

  const getUserDetails = async (useToken: boolean) => {
    try {
      const response = await axios({
        method: "get",
        url: process.env.NEXT_PUBLIC_BACKEND_URL + "auth/user/",
        headers: useToken ? {Authorization: "Bearer " + session?.access_token} : {},
      });
      setResponse(JSON.stringify(response.data));
    } catch (error) {
      setResponse(error.message);
    }
  };

  if (status == "loading") {
    return <Spinner size="lg"/>;
  }

  if (session) {
    return (
      <Box m={8}>
        <VStack>
          <Text>PK: {session.user.pk}</Text>
          <Text>Username: {session.user.username}</Text>
          <Text>Email: {session.user.email || "Not provided"}</Text>
          <Code>
            {response}
          </Code>
        </VStack>
        <HStack justifyContent="center" mt={4}>
          <Button colorScheme="blue" onClick={() => getUserDetails(true)}>
            User details (with token)
          </Button>
          <Button colorScheme="orange" onClick={() => getUserDetails(false)}>
            User details (without token)
          </Button>
          <Button colorScheme="red" onClick={() => signOut({callbackUrl: "/"})}>
            Sign out
          </Button>
        </HStack>
      </Box>
    );
  }

  return <></>;
}
  1. 我们使用useRouter()anduseSession()的方式与前面的代码片段相同。
  2. 我们添加了getUserDetails()方法获取用户信息并显示它。
  3. 单击注销按钮,调用signOut()方法。

测试

重新启动 Next 开发服务:

$ npx next dev

然后打开您最喜欢的网络浏览器并导航到http://127.0.0.1:3000。单击“登录”按钮查看您是否被重定向到登录表单。填写表格并单击“使用凭据登录”按钮。

django-rest-authjs-6

如果登录成功,您将被重定向到个人资料页面。尝试使用您的accessToken获取用户信息。

django-rest-authjs-7

通过 Google 进行社交身份验证

在本节中,我们将使用 Google 设置社交身份验证。将其他社交提供商与 Auth.js 集成实际上是类似的。只需确保您的 Django 后端可以正常工作,然后再转到前端部分。

设置

首先,将以下两个环境变量添加到.env.local

# .env.local

# ...
GOOGLE_CLIENT_ID=<your_google_client_id>
GOOGLE_CLIENT_SECRET=<your_google_secret>

确保将<your_google_client_id><your_google_secret>替换为您的实际钥匙。请记住,密钥必须与core/settings.py中提供的密钥匹配。

接下来,在[...nextauth].js中注册 Google 登录处理程序:

// pages/api/auth/[...nextauth].js

const SIGN_IN_HANDLERS = {
  // ...
  "google": async (user, account, profile, email, credentials) => {
    try {
      const response = await axios({
        method: "post",
        url: process.env.NEXTAUTH_BACKEND_URL + "auth/google/",
        data: {
          access_token: account["id_token"]
        },
      });
      account["meta"] = response.data;
      return true;
    } catch (error) {
      console.error(error);
      return false;
    }
  }
};

该处理程序的唯一目的是将用户的id_token转发到 Django 社交端点。然后 Django 会处理其他所有事情——例如,创建帐户、让用户登录等。

然后将GoogleProvider添加到[...nextauth].js中的providers数组底部:

// pages/api/auth/[...nextauth].js

export const authOptions = {
  // ...
  providers: [
    // ...
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      authorization: {
        params: {
          prompt: "consent",
          access_type: "offline",
          response_type: "code"
        }
      }
    }),
  ],
  // ...
};

不要忘记在文件顶部导入GoogleProvider

import GoogleProvider from "next-auth/providers/google";

这就是我们所要做的。

测试

在测试之前,请确保您的 Django 后端正在运行并且可以通过.env.local中定义的 URL 进行访问。之后,终止 Next 开发服务器进程并再次启动它:

$ npx next dev

在您喜欢的网络浏览器中导航至http://127.0.0.1:3000/,然后单击“登录”按钮。选择“使用 Google 登录”并测试社交身份验证是否有效。

django-rest-authjs-8

结论

在本教程中,我们成功实现了 Django REST 身份验证系统并将其与 Auth.js 链接。实施的系统支持基于凭据的身份验证以及 Google 的社交身份验证。它的构建方式使得可以轻松添加其他社交提供商,例如 Twitter、GitHub 等。

源代码可在django-rest-authjs GitHub 存储库中获取。

未来的步骤

  1. 确保为社交提供商实施刷新令牌轮换
  2. 默认情况下,Auth.js 不负责帐户创建。如果您希望启用用户注册,请在authorize()方法中处理它或创建专用的注册页面
  3. 要启用电子邮件验证和密码重置功能,请查看Django REST Framework 身份验证文章的“电子邮件验证和密码重置”部分。
  4. 要启用社交帐户链接功能,请查看“社交连接视图”

来自:https://testdriven.io/blog/django-rest-authjs/

更多文章

学习如何加载和预处理数据集,包括使用动态填充和整理器。实现您自己的模型微调和评估,实施一个较为底层的训练循环,调整您的训练循环使其适用于多个GPU或TPU。

2024年3月14日 · 微调 Transformer NLP
基于标准Web API构建的开源包,用于在任何JS运行时、任何平台上的任何框架的现代应用程序中进行身份验证。
2024年3月12日 · 身份认证 WEB
webcheck-1
用于分析任何网站的一体化OSINT工具,支持IP信息、SSL链、DNS记录、cookie、标头、域信息、爬虫规则、页面地图、服务器位置、重定向分类、开放端口、跟踪路由、DNS安全扩展、站点性能、跟踪器、关联主机名等等。
2024年3月4日 · 系统管理 安全工具 WEB
ente-1
完全开源、端到端加密的Google Photos和Apple Photos替代品。
2024年3月4日 · 视频 隐私 照片 加密