使用 Next.js + Remult 创建一个待办事项(六)
Yakim Zhang

增加 auth 校验

待办事项应用程序功能已接近完成,马上要大功告成了,还需要加一个登录角色校验。

Remult 提供了一种灵活的机制,可以在应用程序 API 的各个级别放置基于代码的授权规则。为了保持代码的高内聚性,实体和字段级授权代码应该放在实体类中。

Remult 在用户身份验证方面完全没有意见。您可以自由使用任何类型的身份验证机制,只需要向 Remult 提供一个实现 RemultUserInfo 接口的对象。

本教程,我们将使用 NextAuth.js (打开新窗口)用于身份验证。

配置需要登录,才能访问

Task @Entity 通过修改 allowApiCrud 属性的值,此规则在装饰器中实现。此属性可以设置为接受 Remult 参数并返回 boolean 值的函数。让我们使用 Allow.authenticatedRemult 中的函数。

src/shared/Task.ts

import of Allow from remult.

1
2
3
@Entity("tasks", {
allowApiCrud: Allow.authenticated
})

添加完成后,可以测试下是否生效。
预期是无法创建任务

1
curl -i http://localhost:3000/api/tasks

用户认证

让我们设置 NextAuth.js 以对我们的应用程序的用户进行身份验证。

后端设置

  1. 安装 next-auth:

npm i next-auth

  1. 创建以下[…nextauth].tsAPI 路由。

pages/api/auth/[…nextauth].ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";

const validUsers = [
{ id: "1", name: "Jane", roles: ["admin"] },
{ id: "2", name: "Steve", roles: [] },
];

const secret = process.env["NEXTAUTH_SECRET"] || "my secret";

export default NextAuth({
providers: [
CredentialsProvider({
name: "Username",
credentials: {
name: {
label: "",
type: "text",
placeholder: "Username, try Steve or Jane",
},
},
authorize(credentials) {
return (
validUsers.find((user) => user.name === credentials?.name) || null
);
},
}),
],
secret: secret,
});

这个(非常)简单的 NextAuth.js CredentialsProvider ,username 通过在预定义的有效用户列表中查找来授权用户。

前端设置

pages/index.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//... imports
import { signIn, signOut, useSession } from "next-auth/react";

//... fetchTasks

const Home: NextPage = () => {
const { data: session } = useSession();

//,,,

return (
<div>
<header>
{session
? (
<>
Hello {session?.user?.name}{" "}
<button onClick={() => signOut()}>Sign Out</button>
</>
)
: <button onClick={() => signIn()}>Sign In</button>}
</header>

//...
</div>
)
}

连接 Remult 中间件

建立身份验证流程后,将其与后端的 Remult 集成就像为 RemultgetUser 提供 UserInfo 从 Request.

  1. 将以下 getUserFromNextAuth 函数添加到[…nextauth].ts.

pages/api/auth/[…nextauth].ts

1
2
3
4
export async function getUserFromNextAuth(req: NextApiRequest) {
const token = await getToken({ req, secret }); // import getToken from 'next-auth/jwt'
return validUsers.find((u) => u.id === token?.sub);
}
  1. getUser 将选项对象的属性设置 createRemultServer 为 getUserFromNextAuth 函数:

src/server/api.ts

1
2
3
4
export const api = createRemultServer({
//...
getUser: getUserFromNextAuth,
});

待办事项应用程序现在支持登录和注销,所有访问仅限于登录用户。

到此,授权功能完成,todolist 小 demo 就算完成了。