一段时间以来,我一直想为我的个人网站添加身份验证,以了解它在 Astro 中的工作原理。由于 Prisma 和 PlanetScale 已经在我的博客上
征求意见,我决定将我的帐户信息存储在 PlanetScale 中。 因为它只是用于我自己的帐户,并且我没有在我的数据库中存储任何其他敏感信息,所以我决定暂时以纯文本形式存储凭据。 我更改了我的 Prisma 模式以使其成为可能:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>// schema.prisma
model Account {
id Int @id @default(autoincrement())
username String @unique
password String
}
</code></span></span>
在代码中更新模型后,运行npx prisma db push
会将更改传播到 PlanetScale,因此架构会在实际数据库中更新。
我使用一个现有的包@astro-auth
来处理我网站上的所有身份验证。
为此,我需要向我的应用程序添加 2 个环境变量:(ASTROAUTH_URL
托管我网站的 URL)和ASTROAUTH_SECRET
(一个自选密钥)。
因为我将凭据存储在 PlanetScale 中,所以我需要使用 来CredentialProvider
启用使用用户名和密码登录。
上还有许多其他提供程序可用@astro-auth
,如果您有兴趣,请查看软件包。
设置它所需的代码@astro-auth
如下所示:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)">// /pages/api/auth/[...astroauth].ts</span>
<span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-name-color)">AstroAuth</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">@astro-auth/core</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">CredentialProvider</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">@astro-auth/providers</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">prisma</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">../../../lib/prisma</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">export</span> <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">all</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">AstroAuth</span><span style="color:var(--syntax-text-color)">({</span>
<span style="color:var(--syntax-name-color)">authProviders</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">[</span>
<span style="color:var(--syntax-name-color)">CredentialProvider</span><span style="color:var(--syntax-text-color)">({</span>
<span style="color:var(--syntax-name-color)">authorize</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-name-color)">properties</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">account</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">prisma</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">account</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">findFirst</span><span style="color:var(--syntax-text-color)">({</span>
<span style="color:var(--syntax-name-color)">where</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">username</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">properties</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">username</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-name-color)">AND</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">password</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">properties</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">password</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">},</span>
<span style="color:var(--syntax-text-color)">},</span>
<span style="color:var(--syntax-name-color)">select</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">id</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">true</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">},</span>
<span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">account</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">id</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">properties</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">username</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">null</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">},</span>
<span style="color:var(--syntax-text-color)">}),</span>
<span style="color:var(--syntax-text-color)">],</span>
<span style="color:var(--syntax-text-color)">});</span>
</code></span></span>
创建登录页面非常简单。
我刚刚创建了一个表单,在提交时调用了该signIn()
方法@astro-auth
,然后 BOOM:已登录!
登录页面的代码:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>// /pages/login.astro
<html>
<head>
<title>Login</title>
<script>
import { signIn } from '@astro-auth/client';
document.addEventListener('DOMContentLoaded', () => {
document.querySelector('form')?.addEventListener('submit', async e => {
e.preventDefault();
const form = e.target;
if (form) {
const formData = new FormData(form as HTMLFormElement);
const data = Object.fromEntries(formData);
await signIn({
provider: 'credential',
login: data,
});
window.location.href = '/';
}
});
});
</script>
</head>
<body>
<form>
<label for="username">Name</label>
<input type="text" name="username" />
<label for="password">Password</label>
<input type="password" name="password" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
</code></span></span>
提交表单后,用户登录并被重定向到主页。使用身份验证保护页面很容易,只需使用来自的函数
检查登录用户即可。 这是我使用此检查的页面示例:getUser()
@astro-auth
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>// /pages/comment-overview.astro
---
import { getUser } from '@astro-auth/core';
import Layout from '../layouts/Layout.astro';
import { prisma } from '../lib/prisma';
import CommentsOverviewWrapper from '../components/CommentOverviewWrapper';
const user = getUser({ client: Astro });
if (!user) {
return Astro.redirect('/', 307);
}
const commentsWithPost = await prisma?.comment.findMany({
include: {
post: {
select: {
url: true,
},
},
},
});
---
<Layout
description="Overview of comments"
title="Thomas Ledoux | Comment overview"
>
<CommentsOverviewWrapper commentsWithPost={commentsWithPost} client:load />
</Layout>
</code></span></span>
如果用户未登录,用户将被重定向到带有 307状态代码的主页。
我还有一个 API 路由来删除我的博客文章上的评论,我想将其隔离,以便只有经过身份验证的用户才能使用此 API。也可以为此
使用getUser()
函数 from ,但这次我们将传递而不是对象。 使用此代码的示例:@astro-auth
request
Astro
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)">// /pages/api/comments.ts</span>
<span style="color:var(--syntax-declaration-color)">export</span> <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">del</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">APIRoute</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">({</span> <span style="color:var(--syntax-name-color)">request</span> <span style="color:var(--syntax-text-color)">})</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">user</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">getUser</span><span style="color:var(--syntax-text-color)">({</span> <span style="color:var(--syntax-name-color)">server</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">request</span> <span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">user</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">body</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">request</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">json</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">deleteComment</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">prisma</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">comment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-declaration-color)">delete</span><span style="color:var(--syntax-text-color)">({</span>
<span style="color:var(--syntax-name-color)">where</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">id</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">body</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">id</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">},</span>
<span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">Response</span><span style="color:var(--syntax-text-color)">(</span>
<span style="color:var(--syntax-name-color)">JSON</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">stringify</span><span style="color:var(--syntax-text-color)">({</span>
<span style="color:var(--syntax-name-color)">message</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">`Comment with id </span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">deleteComment</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">id</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)"> deleted`</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">}),</span>
<span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">status</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">200</span> <span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">Response</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">null</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">status</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">403</span> <span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-text-color)">};</span>
</code></span></span>
所以当用户未通过身份验证时,将返回 403 响应。
希望这对您有所帮助!源代码可以一如既往地在我的 Github
上找到。