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

如何实现单管理员登录限制?多设备同账号登录拦截方案求助

Solution for Single Admin Session Login with Firebase & React

Alright, let's implement the single-device-only login for your admin account using Firebase (since that's what your code is already using). The core idea is to track active sessions in Firestore, so we can check if the admin is already logged in when a new login attempt happens.

Step 1: Set Up Firestore Collection

First, create a Firestore collection named activeSessions (you can name it anything, just keep it consistent). Each document in this collection will represent an active admin session, using the user's UID as the document ID (so we can quickly look up if a session exists).

Step 2: Modify Your Login Component Code

Here's the updated Login component with the single-session logic integrated:

import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import firebase from './config/database';
import Config from './config/app';
import * as firebaseCLASS from 'firebase';
require("firebase/firestore");

class Login extends Component {
  constructor(props) {
    super(props);
    this.state = {
      username: '',
      password: '',
      error: '',
    };
    this.handleChangeUsername = this.handleChangeUsername.bind(this);
    this.handleChangePassword = this.handleChangePassword.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.authenticate = this.authenticate.bind(this);
    this.endActiveSession = this.endActiveSession.bind(this);
  }

  componentDidMount() {
    // Listen for auth state changes to clean up session on logout
    firebase.auth().onAuthStateChanged(user => {
      if (!user) {
        // User logged out, remove their active session from Firestore
        this.endActiveSession();
      }
    });
  }

  handleChangeUsername(event) {
    this.setState({ username: event.target.value });
  }

  handleChangePassword(event) {
    this.setState({ password: event.target.value });
  }

  handleSubmit(event) {
    this.authenticate(this.state.username, this.state.password);
    event.preventDefault();
  }

  async authenticate(username, password) {
    const displayError = (error) => {
      this.setState({ error: error });
    }

    // First check if user is allowed
    if (Config.adminConfig.allowedUsers != null && Config.adminConfig.allowedUsers.indexOf(username) === -1) {
      displayError("This user doesn't have access to this admin panel!");
      return;
    }

    try {
      // Step 1: Attempt Firebase Auth login
      const userCredential = await firebase.auth().signInWithEmailAndPassword(username, password);
      const user = userCredential.user;

      // Step 2: Check Firestore for existing active session
      const sessionDocRef = firebase.firestore().collection('activeSessions').doc(user.uid);
      const sessionDoc = await sessionDocRef.get();

      if (sessionDoc.exists) {
        // Session exists: log out current attempt and show error
        await firebase.auth().signOut();
        displayError("用户已登录,无法访问该账号");
        return;
      }

      // Step 3: No existing session, create new active session
      await sessionDocRef.set({
        userId: user.uid,
        email: user.email,
        loginTime: firebase.firestore.FieldValue.serverTimestamp(),
        sessionId: user.refreshToken // Use refresh token as unique session identifier
      });

      // Login successful - redirect or do your post-login logic here
      console.log("Yes, user is logged in (single session)");
      // Example: this.props.history.push('/dashboard');

    } catch (error) {
      console.log(error.message);
      displayError(error.message);
    }
  }

  async endActiveSession() {
    const currentUser = firebase.auth().currentUser;
    if (currentUser) {
      try {
        await firebase.firestore().collection('activeSessions').doc(currentUser.uid).delete();
      } catch (error) {
        console.error("Error ending session:", error);
      }
    }
  }

  authWithGoogle() {
    // Keep your existing Google auth code here, but you'll want to add the same session check logic
    var provider = new firebaseCLASS.auth.GoogleAuthProvider();
    firebase.auth().signInWithPopup(provider).then(async function (result) {
      const user = result.user;
      // Add session check logic here similar to authenticate() method
    }).catch(function (error) {
      console.log(error.message);
    });
  }

  showGoogleLogin() {
    if (Config.adminConfig.allowedUsers != null && Config.adminConfig.allowedUsers.length > 0 && Config.adminConfig.allowGoogleAuth) {
      return (<div>
        <p className="category text-center">
          <a onClick={this.authWithGoogle} className="btn btn-social btn-fill btn-google">
            <i className="fa fa-google"></i>&nbsp;&nbsp;&nbsp;Login with google
          </a>
        </p>
        <br />
        <p className="category text-center">Or login using email</p>
      </div>)
    } else {
      return (<div></div>)
    }
  }

  render() {
    // Keep your existing render code here
    return (
      <div>
        <nav className="navbar navbar-primary navbar-transparent navbar-absolute">
          <div className="container">
            <div className="navbar-header">
              <button type="button" className="navbar-toggle" data-toggle="collapse" data-target="#navigation-example-2">
                <span className="sr-only">Toggle navigation</span>
                <span className="icon-bar"></span>
                <span className="icon-bar"></span>
                <span className="icon-bar"></span>
              </button>
              <a className="navbar-brand" href="#">
                {Config.adminConfig.appName}
              </a>
            </div>
            <div className="collapse navbar-collapse">
              <ul className="nav navbar-nav navbar-right">
                <li className=" active ">
                  <a>
                    <i className="material-icons">fingerprint</i>Login
                  </a>
                </li>
              </ul>
            </div>
          </div>
        </nav>
        <div className="wrapper wrapper-full-page">
          <div className="full-page login-page" data-image="assets/img/lock.jpeg">
            <div className="content">
              <div className="container">
                <div className="row">
                  <div className="col-md-4 col-sm-6 col-md-offset-4 col-sm-offset-3">
                    <form onSubmit={this.handleSubmit}>
                      <div className="card card-login card-hidden">
                        <div className="card-header text-center" data-background-color="rose">
                          <h4 className="card-title">Login</h4>
                        </div>
                        <div className="card-content">
                          <h4>{this.state.error}</h4>
                          <div className="input-group">
                            <span className="input-group-addon">
                              <i className="material-icons">email</i>
                            </span>
                            <div className="form-group label-floating">
                              <label className="control-label">Email address</label>
                              <input type="email" value={this.state.username} onChange={this.handleChangeUsername} className="form-control" />
                            </div>
                          </div>
                          <div className="input-group">
                            <span className="input-group-addon">
                              <i className="material-icons">lock_outline</i>
                            </span>
                            <div className="form-group label-floating">
                              <label className="control-label">Password</label>
                              <input type="password" value={this.state.password} onChange={this.handleChangePassword} className="form-control" />
                            </div>
                          </div>
                        </div>
                        <div className="footer text-center">
                          <input type="submit" className="btn btn-rose btn-simple btn-wd btn-lg" value="Login" />
                        </div>
                      </div>
                    </form>
                  </div>
                </div>
              </div>
            </div>
            <footer className="footer">
              <div className="container">
                <nav className="pull-left">
                  <ul>
                  </ul>
                </nav>
                <p className="copyright pull-right">
                  &copy; <script>
                    document.write(new Date().getFullYear())
                  </script> {Config.adminConfig.appName}
                </p>
              </div>
            </footer>
          </div>
        </div>
      </div>
    );
  }
}

export default Login;

Key Changes Explained:

  • authenticate Method: Converted to async/await for cleaner async flow. After successful Firebase Auth login, we check Firestore for an existing activeSessions document for the user. If it exists, we log out the new attempt and show your desired error message. If not, we create the session document.
  • endActiveSession Method: Deletes the user's session from Firestore when they log out (triggered by the onAuthStateChanged listener in componentDidMount).
  • Auth State Listener: Ensures that if the user logs out (either intentionally or due to token expiration), their active session is cleaned up in Firestore.

Additional Notes:

  • Session Expiry: If you want to handle cases where the admin's session ends abruptly (like closing the browser without logging out), you can add a TTL (Time-To-Live) policy on the activeSessions collection in Firestore. This will automatically delete old sessions after a set time (e.g., 24 hours).
  • Google Auth: I left your Google auth code in place, but you'll need to add the same session check logic to the authWithGoogle method if you want to enforce single sessions for Google logins too.
  • Security Rules: Don't forget to set Firestore security rules to ensure only the logged-in user can modify their own session document. Example rule:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /activeSessions/{userId} {
          allow read, write: if request.auth != null && request.auth.uid == userId;
        }
      }
    }
    

That should do it! Now your admin account will only allow one active login at a time.

内容的提问来源于stack exchange,提问作者Farha khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:19:01