RaisFastRaisFast
Full-Stack Development

认证集成

在前端实现登录、注册、路由守卫和令牌刷新。

认证原理

RaisFast 使用 JWT 短期访问令牌 + 长期刷新令牌

  1. 用户用邮箱/密码登录 → 获得 access_token(15 分钟)+ refresh_token(7 天)
  2. 前端存储令牌,在 Authorization 请求头中发送 access_token
  3. access_token 过期时,用 refresh_token 获取新的令牌对
  4. 刷新令牌存储在数据库中 — 可以随时撤销

登录流程

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 操作

On this page