Full-Stack Development
认证集成
在前端实现登录、注册、路由守卫和令牌刷新。
认证原理
RaisFast 使用 JWT 短期访问令牌 + 长期刷新令牌:
- 用户用邮箱/密码登录 → 获得
access_token(15 分钟)+refresh_token(7 天) - 前端存储令牌,在
Authorization请求头中发送access_token - 当
access_token过期时,用refresh_token获取新的令牌对 - 刷新令牌存储在数据库中 — 可以随时撤销
登录流程
import { client } from "@/lib/client";
import { useState } from "react";
function LoginPage({ onLogin }) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
async function handleSubmit(e) {
e.preventDefault();
const result = await client.auth.login({ email, password });
localStorage.setItem("access_token", result.access_token);
localStorage.setItem("refresh_token", result.refresh_token);
client.setToken(result.access_token);
onLogin(result.user);
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="邮箱"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="密码"
/>
<button type="submit">登录</button>
</form>
);
}<script setup>
import { ref } from "vue";
import { client } from "@/lib/client";
const emit = defineEmits(["login"]);
const email = ref("");
const password = ref("");
async function handleLogin() {
const result = await client.auth.login({
email: email.value,
password: password.value,
});
localStorage.setItem("access_token", result.access_token);
localStorage.setItem("refresh_token", result.refresh_token);
client.setToken(result.access_token);
emit("login", result.user);
}
</script>
<template>
<form @submit.prevent="handleLogin">
<input v-model="email" type="email" placeholder="邮箱" />
<input v-model="password" type="password" placeholder="密码" />
<button type="submit">登录</button>
</form>
</template>import { RaisFast } from "@raisfast/sdk";
const client = new RaisFast({ baseUrl: "/api" });
async function login(email, password) {
const result = await client.auth.login({ email, password });
localStorage.setItem("access_token", result.access_token);
localStorage.setItem("refresh_token", result.refresh_token);
client.setToken(result.access_token);
return result.user;
}
// 使用
document.getElementById("loginForm").addEventListener("submit", async (e) => {
e.preventDefault();
const user = await login(
document.getElementById("email").value,
document.getElementById("password").value
);
console.log("已登录:", user.username);
});令牌刷新
访问令牌 15 分钟过期。自动刷新:
import { client } from "./client";
let refreshPromise: Promise<void> | null = null;
export async function getValidToken(): Promise<string> {
const token = localStorage.getItem("access_token");
if (token) {
const payload = JSON.parse(atob(token.split(".")[1]));
if (payload.exp * 1000 > Date.now()) {
return token;
}
}
if (!refreshPromise) {
refreshPromise = (async () => {
const refreshToken = localStorage.getItem("refresh_token");
const result = await client.auth.refresh(refreshToken!);
localStorage.setItem("access_token", result.access_token);
localStorage.setItem("refresh_token", result.refresh_token);
client.setToken(result.access_token);
refreshPromise = null;
})();
}
await refreshPromise;
return localStorage.getItem("access_token")!;
}路由守卫(React 示例)
import { useState, useEffect } from "react";
import { client } from "@/lib/client";
import { Navigate } from "react-router-dom";
function ProtectedRoute({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const token = localStorage.getItem("access_token");
if (!token) {
setLoading(false);
return;
}
client.setToken(token);
client.auth.me().then(setUser).catch(() => {
localStorage.removeItem("access_token");
}).finally(() => setLoading(false));
}, []);
if (loading) return <div>加载中...</div>;
if (!user) return <Navigate to="/login" />;
return children;
}登出
async function logout() {
const refreshToken = localStorage.getItem("refresh_token");
if (refreshToken) {
await client.auth.logout(refreshToken);
}
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
client.setToken("");
}注册
const user = await client.auth.register({
username: "newuser",
email: "user@example.com",
password: "MyStr0ngP@ss",
});下一步
认证完成后,继续学习数据与 CRUD 操作。
