You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React应用中同时实现Azure AD认证与数据库登录

实现数据库登录功能的可行方案

1. 统一认证状态管理

扩展现有认证上下文,让它同时兼容Azure AD和数据库登录的状态同步。假设你用React Context管理认证状态,修改Auth.tsx:

import { createContext, useContext, useState, useEffect, ReactNode } from 'react';

interface AuthContextType {
  isAuthenticated: boolean;
  user: any | null;
  loginWithAzure: () => Promise<void>;
  loginWithDb: (username: string, password: string) => Promise<void>;
  logout: () => void;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [user, setUser] = useState<any | null>(null);

  // 页面刷新时恢复数据库登录状态
  useEffect(() => {
    const token = localStorage.getItem('db-auth-token');
    if (token) {
      // 可选:调用后端接口校验token有效性
      setIsAuthenticated(true);
      // 可从token解析用户信息,或调用接口获取完整用户数据
      setUser({ type: 'db' });
    }
    // 保留原有Azure AD的自动登录逻辑
    // ...(你的Azure AD刷新状态代码)
  }, []);

  // 保留原有Azure AD登录逻辑
  const loginWithAzure = async () => {
    // ...你的Azure AD登录实现
    setIsAuthenticated(true);
    setUser({ type: 'azure' });
  };

  // 新增数据库登录逻辑
  const loginWithDb = async (username: string, password: string) => {
    try {
      const response = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
      });
      if (!response.ok) throw new Error('登录失败');
      const data = await response.json();
      // 存储token到本地存储
      localStorage.setItem('db-auth-token', data.token);
      setIsAuthenticated(true);
      setUser({ ...data.user, type: 'db' });
    } catch (err) {
      console.error(err);
      // 这里可添加用户可见的错误提示,比如弹窗或文字提示
    }
  };

  const logout = () => {
    setIsAuthenticated(false);
    setUser(null);
    localStorage.removeItem('db-auth-token');
    // 同时处理Azure AD的登出流程
    // ...(你的Azure AD登出代码)
  };

  return (
    <AuthContext.Provider value={{ isAuthenticated, user, loginWithAzure, loginWithDb, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth必须在AuthProvider范围内使用');
  return context;
};

2. 修改登录页面(Auth.tsx)

添加数据库登录表单,和Azure AD登录按钮并列展示:

import { useState } from 'react';
import { useAuth } from './Auth';

const Auth = () => {
  const { loginWithAzure, loginWithDb } = useAuth();
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleDbLoginSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    await loginWithDb(username, password);
  };

  return (
    <div className="login-container">
      <h2>登录</h2>
      {/* 数据库登录表单 */}
      <form onSubmit={handleDbLoginSubmit}>
        <div>
          <label>用户名:</label>
          <input
            type="text"
            value={username}
            onChange={(e) => setUsername(e.target.value)}
            required
          />
        </div>
        <div>
          <label>密码:</label>
          <input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
          />
        </div>
        <button type="submit">数据库登录</button>
      </form>

      {/* 原有Azure AD登录按钮 */}
      <button onClick={loginWithAzure}>Azure AD 登录</button>
    </div>
  );
};

export default Auth;

3. 适配受保护路由(Router.tsx)

修改原有受保护路由的判断逻辑,不再依赖单一的Azure AD状态,改用统一的isAuthenticated标识:

import { Routes, Route, Navigate } from 'react-router-dom';
import { useAuth } from './Auth';
import Auth from './Auth';
import Dashboard from './Dashboard';

const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
  const { isAuthenticated } = useAuth();
  return isAuthenticated ? <>{children}</> : <Navigate to="/" />;
};

const Router = () => {
  return (
    <Routes>
      <Route path="/" element={<Auth />} />
      <Route
        path="/dashboard"
        element={
          <ProtectedRoute>
            <Dashboard />
          </ProtectedRoute>
        }
      />
      {/* 其他受保护路由按此逻辑修改 */}
    </Routes>
  );
};

export default Router;

4. 后端登录接口实现(必要前提)

你需要后端提供数据库登录的验证接口,以下是Node.js/Express的示例:

// 后端/api/login接口示例
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require('./models/User');

app.post('/api/login', async (req, res) => {
  const { username, password } = req.body;
  // 从数据库查询用户
  const user = await User.findOne({ username });
  if (!user) return res.status(401).json({ message: '用户不存在' });
  // 验证密码(注意:数据库中必须存储哈希后的密码)
  const isPasswordValid = await bcrypt.compare(password, user.password);
  if (!isPasswordValid) return res.status(401).json({ message: '密码错误' });
  // 生成JWT token
  const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
  res.json({ token, user: { id: user._id, username: user.username } });
});

5. 额外优化建议

  • token过期处理:前端请求接口时,若返回401错误,自动清除本地token并跳转登录页
  • 表单验证:添加用户名、密码的格式校验(比如长度限制、非法字符过滤)
  • 错误提示:登录失败时给用户显示友好的文字提示(比如用Toast组件)
  • 安全加固:接口启用HTTPS,后端密码必须用bcrypt等算法哈希存储,禁止明文存储

内容的提问来源于stack exchange,提问作者Avanish Srivastava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 08:15:52