Next.js 14 踩坑:处理API中的JWT登录认证问题

前言

在使用 Next.js14 开发登录登出功能时遇到一个问题:可以设置 cookie,但无法清除 cookie。

在开发这个功能时,借助了 Route Handler 的特性,关于 cookie 的处理,官网提到了 next/headers 的 cookies API;同样可以通过 Request、Response 和 NextRequest、NextResponse 提供的相关方法处理。NextRequest、NextResponse 来自 next/server,它们分别拓展了 Web Request APIWeb Response API 的功能。

登录功能

我的 JWT 来自 API 接口,因此做了一些 BFF 的工作,下面是登录功能的实现:

ts 复制代码
// app/api/login/route.ts
import { api } from '@/config/api';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const body = await request.json();
  const { email, password } = body;

  const response = await fetch(`${api}/login`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      email,
      password,
    }),
  });

  if (response.status === 404) {
    return NextResponse.json({
      success: false,
      msg: '请求地址不存在',
    });
  }

  const json = await response.json(); // 解析出失败的 json 数据
  if (!response.ok) {
    return NextResponse.json(
      {
        success: false,
        msg: json.message,
      },
      { status: response.status || 500 }
    );
  }
  const token = json.data.token;

  return NextResponse.json(
    // 返回body内容
    {
      success: true,
      msg: json.message,
    },
    // 设置其他
    {
      headers: {
        'Set-Cookie': `token=${token};path=/;max-age=86400;HttpOnly`,
      },
    }
  );
}

调用 NextResponse.json 方法,第一个参数为返回给前端 json 数据,第二个参数用来配置请求头、状态码。在服务端设置响应头 Set-Cookie 后,发送给客户端,以后客户端就会自动带上请求头 Cookie。在上面代码中,Set-Cookie 中最重要的参数就是 token,它的值来自 API 中返回的 jwt 字符串。

  1. 登录时,响应头中加入了 Set-Cookie:
  1. Cookie 中存入了 token:
  1. 发请求时,请求头中存在 Cookie:

登出功能

登出时,需要把 cookie 删除,于是我使用了 NextRequest 和 NextResponse 中提供的 request.cookie.delete方法和 response.cookie.delete 方法:

ts 复制代码
request.cookies.delete('token');

let response = NextResponse.next();
response.cookies.delete('token');

但这并不起作用。

下面是有效的代码:

ts 复制代码
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';

export async function DELETE() {
  // 设置过期时间为0来删除cookie
  cookies().set('token', '', { maxAge: 0 });

  return NextResponse.json({
    success: true,
    msg: '登出成功',
  });
}

调用 next/headers 中的 cookies 成功地删除了 cookies,实现了登出功能。

路由中间件

在 Next.js 中的中间件是介于服务器和应用之间的一层介质。当一个请求到达应用时,它会首先通过中间件。然后中间件可以根据这个请求的信息(比如URL、headers、cookies等)执行一些动作,然后有选择的转发这个请求到你的页面或者api路由,或者直接返回一个响应。

有了中间件,你就可以在一个集中的地方处理跨越多个页面或API路由的逻辑。比如:

  • 认证和授权:检查请求的 cookie 或 header,来确认用户的身份,然后决定他是否有权限访问请求的资源。
  • 重定向和路由改写
  • 自定义的缓存规则

下面是检查认证和重定向的例子:

ts 复制代码
import { NextRequest, NextResponse } from 'next/server';

export function middleware(request: NextRequest) {
  // 检查 token(从cookie中获取token)
  const token = request.cookies.get('token')?.value;

  // 没有 token 时,跳回登录页
  if (!token) {
    const url = request.nextUrl.clone();
    url.pathname = '/login';
    return NextResponse.redirect(url);
  }
}

如果未登录,则重定向到登录页。

为什么使用 cookie?

要清楚 Route Handler 属于服务器层面,这里无法使用浏览器提供的window对象,也就无法使用HTML5的 localStorage 和 sessionStorage 作为 token 的存储方案。路由中间件 middleware.ts 也在服务器中,同样无法调用浏览器中的 localStorage 等 API。

综上,在客户端和服务端都能够使用的存储方案只有 cookie。


技术交流:

公众号:见嘉 Being Dev

v:with_his_x

相关推荐
来自旧金山的勇士几秒前
WSL->Ubunut安装Redis
后端
Struggler281几秒前
Chrome插件开发
前端
大葱白菜2 分钟前
Java Set 集合详解:从基础语法到实战应用,彻底掌握去重与唯一性集合
java·后端
大葱白菜3 分钟前
Java Map 集合详解:从基础语法到实战应用,彻底掌握键值对数据结构
java·后端
小猪乔治爱打球9 分钟前
[Golang修仙之路] 算法专题:回溯(递归)
后端·面试
前端 贾公子13 分钟前
Monorepo + vite 怎么热更新
前端
昵称为空C17 分钟前
SpringBoot数据存储时区选择,符合国际化和特定时区方案
spring boot·后端
然我1 小时前
不用 Redux 也能全局状态管理?看我用 useReducer+Context 搞个 Todo 应用
前端·javascript·react.js
前端小巷子1 小时前
Web 实时通信:从短轮询到 WebSocket
前端·javascript·面试
神仙别闹1 小时前
基于C#+SQL Server实现(Web)学生选课管理系统
前端·数据库·c#