我刚刚使用 App Router v4 身份验证和授权创建了我的第一个 Next JS 14 应用程序。
我也很自豪,一切都很好! 只有一个问题我似乎无法解决:服务器操作。 它们应该取代 API 路由,使事情变得更加顺利和容易。 它们确实工作得很好,但问题是:我无法保护它们。
应用程序设置: 应用程序文件夹结构,其中“actions”文件夹与应用程序文件夹并排。它包含所有服务器操作。
其中一个文件是 db.js,包含所有数据库相关逻辑,包括这段代码:
export async function executeQuery(query, values = []) {
// Ensure that the connection pool is initialized
await connectDatabase();
// Execute the query using the connection pool
const connection = await pool.getConnection();
try {
const [results] = await connection.execute(query, values);
return results;
} finally {
connection.release(); // Release the connection back to the pool
}
}
这基本上使用普通的 mysql2 数据库执行所有数据库查询,我没有使用适配器。
所有相关路由都在中间件中受到保护(这里只是一个例子):
export { default } from "next-auth/middleware";
export const config = { matcher: ["/profile" "/dashboard" ] };
现在,当我像这样在公共路线“home”上调用“executeQuery”时(只是为了测试这个问题):
import {useSession} from "next-auth/react";
import { executeQuery } from "../actions/db";
import { useState } from "react";
export default /*async*/ function Home() {
const session = useSession();
const [data, setData] = useState(null);
return (
<div className="base-container" style={{ textAlign: "center", flexDirection: "column" }}>
<h1>Home</h1>
{session?.user?.firstname}, {session?.user?.lastname}
<input type="text" name="id" className="base-input" style={{border: "1px solid white"}}></input>
<button onClick={async () => {
let id = document.querySelector('input[name="id"]').value;
const result = await executeQuery("SELECT * FROM users WHERE id = ?", [id]);
setData(JSON.stringify(result[0]));
}}> Get email for input user</button>
<label style={{border: "1px solid white"}}>{data}</label>
</div>
);
}
这里,名字/姓氏仅在登录时显示,一切都很好。
我原本期望 Next Auth 至少在未登录时禁止使用服务器操作,但事实并非如此。由于服务器功能也在登录之前使用,并且也只能以某种方式使用,所以这也没有完全帮助。
将随机 ID 放入输入字段(与数据库中的 ID 匹配;是的,我可以使用 UUID,但我不想仅依赖于此,或者人们会这样做吗?)我可以提取所有数据与数据库中的“用户”相关;我什至可以输入更详细的查询并获得我想要的一切。
这当然是一个不可接受的安全问题。
我现在能想到的唯一方法是手动检查每个服务器操作调用是否可以完成。但这不是正确的方法,因为这也容易出错。
如何正确保护服务器操作(这一项以及其他可能敏感的操作)?或者我毕竟必须使用 API,因为 API 路由可以轻松地通过 Next Auth 进行保护...?
我在这里有点困惑,所以我真的很感激一些关于它应该如何工作的意见。
next js 官方团队不鼓励你使用中间件来保护路由是有原因的。所以最好的方法是检查每个
page.jsx
上的用户会话
那么您将如何保护您的服务器操作呢?引用自官方下一个文档,
Verifying user authorization for each action is crucial
。例如:
'use server'
// ...
export async function serverAction() {
const session = await getSession()
const userRole = session?.user?.role
// Check if user is authorized to perform the action
if (userRole !== 'admin') {
throw new Error('Unauthorized access: User does not have admin privileges.')
}
// Proceed with the action for authorized users
// ... implementation of the action
}
现在,如果用户无权使用服务器,您将如何保护服务器?同样的原则也适用,您只需要让您的客户端处理服务器操作的返回即可:
'use client'
import { useRouter } from 'next/navigation'
function handleSubmit(values) {
const router = useRouter()
// Call your server actions from your client handler
const serverResponse = await serverActions(values);
if (serverResponse.error === 'Unauthorize') {
toast("Your are not authorize for this actions")
// Redirect if not authorize
return router.push('/login')
}
}
<form onSubmit={handleSubmit}>
...
</form>