Nuxt3 server api 实现jwt登录接口,token签发&校验
Nuxt3 是什么?
Nuxt 3是一个基于Vue 3的服务器端渲染(SSR)框架,它继承了Nuxt.js的核心概念,并利用Vue 3的最新特性,如组合式API(Composition API)和<script setup>
语法,来提供更加现代化的开发体验。以下是Nuxt 3的一些关键特性:
- 性能优化:Nuxt 3在性能方面做了很多优化,例如服务器端渲染(SSR)、预渲染(prerendering)等,这些都有助于提高应用程序的加载速度和运行效率。
- 模块化:Nuxt 3是基于Vue.js的模块化架构,这意味着你可以将应用程序分解为多个独立的模块,从而更好地组织和管理代码。
- 自动化:Nuxt 3提供了许多内置的功能,如自动化代码生成、热重载、单元测试等,这些功能可以帮助你更快速地开发和调试应用程序。
- 支持所有渲染模式:Nuxt.js可以自己选择浏览器还是服务器渲染模式。在服务端渲染的SSR,在客户端渲染的CSR,以及一个静态的SSG,Nuxt.js可以用一套代码生产多类型环境,而且模式的切换也非常简单。
- 基于Vue 3的高性能开发:Nuxt 3全面拥抱Vue 3,Vue 3不仅引入了类似React Hooks的Composition API以及一些TypeScript支持,同时提升了性能,减小了bundle size。
- 高度集成Vite、Vue Router等实用库:Nuxt 3集成了Vite、Vue Router等实用库,提供了更快的构建速度和更好的开发体验。
- 更轻、更快:服务器部署缩小至75倍以及针对现代浏览器的较小的客户捆绑包,优化了冷启动与动态服务器代码拆分,由nitro提供。
- 混合型渲染:现在可以实现增量静态生成和其他高级模式。
- Suspense:在导航之前或之后,在任何组件中获取数据。
- 组合式API:使用Composition API和Nuxt 3的composables来实现真正的代码可重用性。
- Nuxt CLI:全新的零依赖性体验,方便脚手架和模块集成。
- Nuxt 开发工具:在浏览器中直接使用信息和快速修复,工作更快。
- Nuxt Kit:全新的模块开发,采用TypeScript以及跨版本兼容性。
- webpack 5:构建时间更快,捆绑尺寸更小,不需要配置。
- Vite:通过使用Vite作为您的捆绑器,体验闪电般的HMR速度。
- TypeScript:使用本地TypeScript和ESM构建 – 不需要额外的步骤。
Nuxt 3是一个开源的框架,使Web开发变得简单而强大,帮助开发者构建高性能的单页应用程序(SPA),并提供完整的开发、生产、热更新和预渲染功能。
如何实现 jwt token签发&校验
开发一个网页端的项目,必不可少的功能莫过于系统登录,非常普遍。下面我们来实现一个简单的登录token签发&校验接口
这里我们准备采用 nuxt3、prisma、mysql等实现,这些环境搭建请看扩展阅读的文档。官方文档写得都非常清楚。
- 实现一个prisma数据模型
model User {
id Int @id @default(autoincrement())
userName String @unique
password String
}
- 实现登录接口
prisma实例
import { PrismaClient } from "@prisma/client";
const prismaClientSingleton = () => {
return new PrismaClient({
omit: {
sys_user: {
password: true // 全局屏蔽 password 字段返回
}
}
});
};
type PrismaClientSingleton = ReturnType<typeof prismaClientSingleton>;
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClientSingleton | undefined;
};
const prisma = globalForPrisma.prisma || prismaClientSingleton();
export default prisma;
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
密码数据保存,一般都会加密存储,这里采用 crypto-js 模块 做 MD5 加密。
import CryptoJS from 'crypto-js';
const MD5_KEY = 'ifrontend.net';
export const encryptAES = (value: string) => {
if (value == '') return '';
const keyHex = CryptoJS.enc.Utf8.parse(MD5_KEY);
const encrypted = CryptoJS.AES.encrypt(value, keyHex, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return encrypted.toString();
}
jwt 签发&校验,这里采用的是 jsonwebtoken
import jwt from 'jsonwebtoken';
const secretKey = 'ifrontend.net';
export const signToken = (username: string, expiresIn: string = '1h') => {
return jwt.sign({ username }, secretKey, { expiresIn });
}
export const verifyToken = (token: string) => {
try {
const decoded = jwt.verify(token, secretKey);
return decoded;
} catch (err) {
return null;
}
}
注意:token失效时间,expiresIn,支持时间格式 "2 days"
, "10h"
, "7d"
expiresIn:以秒或描述时间跨度vercel/ms的字符串表示。
例如:60,“2天”,“10小时”,“7天”。数值被解释为秒数。如果使用字符串,请确保提供时间单位(天、小时等),否则默认使用毫秒单位(“120”等于“120ms”)。
import prisma from '@/server/lib/prisma';
import { encryptAES } from '@/server/lib/md5';
import { signToken } from '@/server/lib/jwt';
export default defineEventHandler(async (event) => {
try {
const body = await readBody(event);
const { userName, password } = body;
const user = await prisma.User.findMany({
where: {
userName,
password: encryptAES(password),
},
});
if (!user) {
return {
code: 200,
msg: '用户名或者密码有误',
};
} else {
return {
code: 200,
msg: '登录成功',
data: signToken(userName),
}
}
} catch (error) {
throw error;
} finally {
await prisma.$disconnect(); // 关闭连接
}
})
看着代码其实也不多,比较简单。也好像实现完成了,是的,非常简单。登录接口完全实现了,但是还有比较重要的一步,如果拦截住其它接口服务,必须登录获取了token才有权限访问,无token或者token失效,无法阴访问服务。
jwt token 校验
这里就要用到 Nuxt3 server 的接口中间件能力
Nuxt 将自动读取 ~/server/middleware
中的任何文件以创建您的项目的服务器中间件。
中间件处理程序将在任何其他服务器路由之前运行每个请求,以添加或检查标头、记录请求或扩展事件请求对象。
// auth.ts
import { verifyToken } from '@/server/lib/jwt';
export default defineEventHandler((event) => {
// 无需校验的token权限的服务
const notAuthUrls = [
'/api/user/login',
];
const currentUrl = event.node.req.url;
if (!notAuthUrls.includes(currentUrl as string)) {
// 获取headers
const headers = getHeaders(event);
const authorization = headers['authorization'] as string;
if (authorization) {
const token = authorization.split(' ')[1];
if (!verifyToken(token)) {
return {
code: 403,
msg: '登录失效'
}
}
} else {
return {
code: 404,
msg: '无权限'
}
}
}
})
接口无权限
接口有权限