Next.js 14 TS 中使用jwt 和 App Router 进行管理

        jwt是一个很基础的工作。但是因为架构不一样,就算是相同的架构,版本不一样,加jwt都会有一定的差别。现在我们的项目是Next.js 14 TS 的 App Router项目(就是没有pages那种),添加jwt的步骤:

1、安装所需的依赖:

npm install jsonwebtoken bcryptjs
npm install -D @types/jsonwebtoken @types/bcryptjs


2、配置环境变量

//在项目根目录(package.json所在目录)下创建一个.env.local文件,用于存储环境变量,例如我们的 //JWT 秘密密钥:
JWT_SECRET=my_super_secret_key
JWT_EXPIRES_IN=1h


3、我们在 app/api 文件夹中创建两个 API 路由:一个用于登录,一个用于保护的数据获取。

1. 登录 API (app/api/login/route.ts)
为实现登录功能,我们需要处理用户输入的用户名和密码,验证它们,创建 JWT 并返回给客户端。import { NextRequest, NextResponse } from 'next/server';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';const users = [{ id: 1, username: '1', password: await bcrypt.hash('1', 10) },{ id: 2, username: '2', password: await bcrypt.hash('2', 10) },
];export async function POST(request: NextRequest) {const { username, password } = await request.json();const user = users.find(u => u.username === username);if (!user || !(await bcrypt.compare(password, user.password))) {return NextResponse.json({ error: 'Invalid username or password' }, { status: 401 });}const token = jwt.sign({ userId: user.id, username: user.username }, process.env.JWT_SECRET!, { expiresIn: process.env.JWT_EXPIRES_IN });return NextResponse.json({ token });
}
2. 受保护的 API (app/api/protected/route.ts)
这个路由将在请求时检查并验证 JWT,并返回受保护的数据。
import { NextRequest, NextResponse } from 'next/server';
import jwt from 'jsonwebtoken';export function GET(request: NextRequest) {const authHeader = request.headers.get('authorization');if (!authHeader || !authHeader.startsWith('Bearer ')) {return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });}const token = authHeader.split(' ')[1];try {const decodedToken = jwt.verify(token, process.env.JWT_SECRET!);return NextResponse.json({ message: 'This is protected data', user: decodedToken });} catch (err) {return NextResponse.json({ error: 'Invalid or expired token' }, { status: 401 });}
}


4、配置中间件

如果有多个受保护的路由,建议使用中间件来验证 JWT。这可以避免在每个受保护的路由中重复相同的验证逻辑。
在 app/middleware.ts 文件中:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import jwt from 'jsonwebtoken';export const middleware = (req: NextRequest) => {const token = req.cookies.get('token');if (req.nextUrl.pathname.startsWith('/api/protected')) {if (!token) {return NextResponse.json({ message: 'Authorization token missing' }, { status: 401 });}try {jwt.verify(token, process.env.JWT_SECRET!);return NextResponse.next();} catch (error) {return NextResponse.json({ message: 'Invalid token' }, { status: 401 });}}if (req.nextUrl.pathname.startsWith('/app/test')) {if (!token) {return NextResponse.redirect(new URL('/login', req.url));}try {jwt.verify(token, process.env.JWT_SECRET!);return NextResponse.next();} catch (error) {return NextResponse.redirect(new URL('/login', req.url));}}return NextResponse.next();
};export const config = {matcher: ['/api/protected/:path*', '/app/test/:path*'],
};


5、创建 AuthGuard 组件

创建 AuthGuard 组件 (app/components/AuthGuard.tsx)
我们使用一个高阶组件来实现路由保护逻辑。
'use client';// app/components/AuthGuard.tsx
import { ReactNode, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '../context/auth';const AuthGuard = ({ children }: { children: ReactNode }) => {const router = useRouter();const { isAuthenticated } = useAuth();useEffect(() => {if (!isAuthenticated) {router.push('/login');}}, [isAuthenticated, router]);if (!isAuthenticated) {return null; // 或者一个加载动画}return <>{children}</>;
};export default AuthGuard;


6、高阶组件将封装所有重复的逻辑:

创建 AuthGuardwithAuth高阶组件 (app/components/AuthGuardwithAuth.tsx)
// app/components/withAuth.tsx
import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '../context/auth';
import AuthGuard from './AuthGuard';const withAuth = (WrappedComponent: React.ComponentType<any>) => {return (props: any) => {const [message, setMessage] = useState('');const { isAuthenticated, logout } = useAuth();const router = useRouter();//console.log("withAuth启动了");useEffect(() => {const fetchData = async () => {const token = localStorage.getItem('token');const res = await fetch('/api/protected', {headers: {'Authorization': `Bearer ${token}`,},});const data = await res.json();if (res.ok) {setMessage(data.message);} else {setMessage(data.error);logout(); // 如果token无效,注销用户router.push('/login');}};if (isAuthenticated) {fetchData();}}, [isAuthenticated, logout, router]);return (<AuthGuard><WrappedComponent {...props} message={message} /></AuthGuard>);};
};export default withAuth;

7、使用 React Context 管理登录状态 

使用 React Context 管理登录状态 (app/context/auth.tsx)
"use client"
// app/context/auth.tsx
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';interface AuthContextType {isAuthenticated: boolean;login: (token: string) => void;logout: () => void;
}const AuthContext = createContext<AuthContextType | undefined>(undefined);export const AuthProvider = ({ children }: { children: ReactNode }) => {const [isAuthenticated, setIsAuthenticated] = useState(false);useEffect(() => {const token = localStorage.getItem('token');if (token) {// Optionally, you can verify the token on the client side heresetIsAuthenticated(true);}}, []);const login = (token: string) => {localStorage.setItem('token', token);setIsAuthenticated(true);};const logout = () => {localStorage.removeItem('token');setIsAuthenticated(false);};return (<AuthContext.Provider value={{ isAuthenticated, login, logout }}>{children}</AuthContext.Provider>);
};export const useAuth = () => {const context = useContext(AuthContext);if (context === undefined) {throw new Error('useAuth must be used within an AuthProvider');}return context;
};

8、配置全局 AuthProvider

配置全局 AuthProvider (app/layout.tsx)
接下来,在 app/layout.tsx 中配置全局的 AuthProvider:
// src/app/layout.tsx
import type { Metadata } from 'next';
import { Roboto } from 'next/font/google';
import '@progress/kendo-theme-bootstrap/dist/all.css';
import './globals.css';
import IntlProviderWrapper from './IntlProviderWrapper';
import { AuthProvider } from './context/auth';export const metadata: Metadata = {title: 'Create Next App',description: 'Generated by create next app',
};const roboto = Roboto({weight: '400',subsets: ['latin'],
});export default function RootLayout({children,
}: {children: React.ReactNode
}) {return (<html lang="en"><body className={roboto.className}><AuthProvider><IntlProviderWrapper>{children}</IntlProviderWrapper></AuthProvider></body></html>);
}

9、登录页示例

登录页示例 (app/login/page.tsx)
'use client';import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '../context/auth';export default function Login() {const [username, setUsername] = useState('');const [password, setPassword] = useState('');const [error, setError] = useState('');const { login } = useAuth();const router = useRouter();const handleLogin = async () => {setError(''); // Reset error messageconst res = await fetch('/api/login', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({ username, password }),});const data = await res.json();if (res.ok) {login(data.token); // 使用 context 中的 login 方法router.push('/protected');} else {setError(data.error);}};return (<div><h1>Login</h1><inputtype="text"placeholder="Username"value={username}onChange={(e) => setUsername(e.target.value)}/><inputtype="password"placeholder="Password"value={password}onChange={(e) => setPassword(e.target.value)}/><button onClick={handleLogin}>Login</button>{error && <p style={{ color: 'red' }}>{error}</p>}</div>);
}

10、受保护页

import withAuth from '../components/AuthGuardwithAuth';
export default  withAuth(App)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/893977.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端——js高级25.1.27

复习&#xff1a;对象 问题一&#xff1a; 多个数据的封装提 一个对象对应现实中的一个事物 问题二&#xff1a; 统一管理多个数据 问题三&#xff1a; 属性&#xff1a;组成&#xff1a;属性名属性值 &#xff08;属性名为字符串&#xff0c;属性值任意&#xff09; 方…

[创业之路-270]:《向流程设计要效率》-2-企业流程架构模式 POS架构(规划、业务运营、支撑)、OES架构(业务运营、使能、支撑)

目录 一、POS架构 二、OES架构 三、POS架构与OES架构的差异 四、各自的典型示例 POS架构典型示例 OES架构典型示例 示例分析 五、各自的典型企业 POS架构典型企业 OES架构典型企业 分析 六、各自典型的流程 POS架构的典型流程 OES架构的典型流程 企业流程架构模式…

计算机的错误计算(二百二十二)

摘要 利用大模型化简计算 实验表明&#xff0c;虽然结果正确&#xff0c;但是&#xff0c;大模型既绕了弯路&#xff0c;又有数值计算错误。 与前面相同&#xff0c;再利用同一个算式看看另外一个大模型的化简与计算能力。 例1. 化简计算摘要中算式。 下面是与一个大模型的…

【现代深度学习技术】深度学习计算 | 参数管理

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上&#xff0c;结合当代大数据和大算力的发展而发展出来的。深度学习最重…

C语言,无法正常释放char*的空间

问题描述 #include <stdio.h> #include <stdio.h>const int STRSIZR 10;int main() {char *str (char *)malloc(STRSIZR*sizeof(char));str "string";printf("%s\n", str);free(str); } 乍一看&#xff0c;这块代码没有什么问题。直接书写…

【开源免费】基于Vue和SpringBoot的在线文档管理系统(附论文)

本文项目编号 T 038 &#xff0c;文末自助获取源码 \color{red}{T038&#xff0c;文末自助获取源码} T038&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

忘记宝塔的访问地址怎么找

在linux中安装宝塔面板后会生成网址、账号和密码 如果网址忘记了那将进不去宝塔面板该怎么办呢&#xff1f; bt命令 我们输入 bt 命令的时候&#xff0c;是在根目录里面进行操作的。 / bt 我们根据自己的需要&#xff0c;选择对应的数字就可以了。 bt 14 输入 14 查看面板默…

力扣hot100-->滑动窗口、贪心

你好呀&#xff0c;欢迎来到 Dong雨 的技术小栈 &#x1f331; 在这里&#xff0c;我们一同探索代码的奥秘&#xff0c;感受技术的魅力 ✨。 &#x1f449; 我的小世界&#xff1a;Dong雨 &#x1f4cc; 分享我的学习旅程 &#x1f6e0;️ 提供贴心的实用工具 &#x1f4a1; 记…

【蓝桥杯嵌入式入门与进阶】2.与开发板之间破冰:初始开发板和原理图2

个人主页&#xff1a;Icomi 专栏地址&#xff1a;蓝桥杯嵌入式组入门与进阶 大家好&#xff0c;我是一颗米&#xff0c;本篇专栏旨在帮助大家从0开始入门蓝桥杯并且进阶&#xff0c;若对本系列文章感兴趣&#xff0c;欢迎订阅我的专栏&#xff0c;我将持续更新&#xff0c;祝你…

Spring Boot - 数据库集成02 - 集成JPA

集成JPA 文章目录 集成JPA一&#xff1a;JPA概述1&#xff1a;JPA & JDBC2&#xff1a;JPA规范3&#xff1a;JPA的状态和转换关系 二&#xff1a;Spring data JPA1&#xff1a;JPA_repository1.1&#xff1a;CurdRepostory<T, ID>1.2&#xff1a;PagingAndSortingRep…

从ai产品推荐到利用cursor快速掌握一个开源项目再到langchain手搓一个Text2Sql agent

目录 0. 经验分享&#xff1a;产品推荐 1. 经验分享&#xff1a;提示词优化 2. 经验分享&#xff1a;使用cursor 阅读一篇文章 3. 经验分享&#xff1a;使用cursor 阅读一个完全陌生的开源项目 4. 经验分享&#xff1a;手搓一个text2sql agent &#xff08;使用langchain l…

【Java-数据结构】Java 链表面试题下 “最后一公里”:解决复杂链表问题的致胜法宝

我的个人主页 我的专栏&#xff1a;Java-数据结构&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 引言&#xff1a; Java链表&#xff0c;看似简单的链式结构&#xff0c;却蕴含着诸多有趣的特性与奥秘&#xff0c;等待我们去挖掘。它就像一…

智慧园区系统的类型及其在企业管理效率提升中的关键作用解析

内容概要 在智慧园区的建设中&#xff0c;各类系统的采用是提升管理效率的关键所在。快鲸智慧园区(楼宇)管理系统&#xff0c;通过其全面数字化的管理手段&#xff0c;已经成为了企业管理的新标杆。这一系统能够有效整合租赁管理、资产管理、招商管理和物业管理等功能&#xf…

多级缓存(亿级并发解决方案)

多级缓存&#xff08;亿级流量&#xff08;并发&#xff09;的缓存方案&#xff09; 传统缓存的问题 传统缓存是请求到达tomcat后&#xff0c;先查询redis&#xff0c;如果未命中则查询数据库&#xff0c;问题如下&#xff1a; &#xff08;1&#xff09;请求要经过tomcat处…

第27篇 基于ARM A9处理器用C语言实现中断<三>

Q&#xff1a;基于ARM A9处理器怎样设计C语言工程&#xff0c;同时使用按键中断和定时器中断在红色LED上计数&#xff1f; A&#xff1a;基本原理&#xff1a;设置HPS Timer 0和按键中断源&#xff0c;主程序调用set_A9_IRQ_stack( )函数设置中断模式的ARM堆栈指针&#xff0c…

C++ 中用于控制输出格式的操纵符——setw 、setfill、setprecision、fixed

目录 四种操纵符简要介绍 setprecision基本用法 setfill的基本用法 fixed的基本用法 setw基本用法 以下是一些常见的用法和示例&#xff1a; 1. 设置字段宽度和填充字符 2. 设置字段宽度和对齐方式 3. 设置字段宽度和精度 4. 设置字段宽度和填充字符&#xff0c;结合…

【1.安装ubuntu22.04】

目录 参考文章链接电脑参数安装过程准备查看/更改引导方式查看/更改磁盘的分区格式关闭BitLocker加密压缩分区关闭独显直连制作Ubuntu安装盘下载镜像制作启动盘 进入BIOS模式进行设置Secure Boot引导项顺序try or install ubuntu 进入安装分区启动引导器个人信息和重启 参考文章…

代码随想录算法【Day34】

Day34 62.不同路径 思路 第一种&#xff1a;深搜 -> 超时 第二种&#xff1a;动态规划 第三种&#xff1a;数论 动态规划代码如下&#xff1a; class Solution { public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m, vector<int>(n,…

计算机毕业设计PySpark+hive招聘推荐系统 职位用户画像推荐系统 招聘数据分析 招聘爬虫 数据仓库 Django Vue.js Hadoop

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

强化学习数学原理(三)——迭代算法

一、值迭代过程 上面是贝尔曼最优公式&#xff0c;之前我们说过&#xff0c;f(v)v&#xff0c;贝尔曼公式是满足contraction mapping theorem的&#xff0c;能够求解除它最优的策略和最优的state value&#xff0c;我们需要通过一个最优v*&#xff0c;这个v*来计算状态pi*&…