使用 Firebase Admin SDK 进行匿名用户管理与自定义状态标记

在现代应用程序中,匿名用户管理是一个常见的需求,尤其是在需要为未登录用户提供部分功能的场景中。通过 Firebase Admin SDK,开发者可以方便地管理匿名用户,并为用户设置自定义的状态标记,例如记录用户是否使用过某项特定功能。

本文将介绍如何使用 Firebase Admin SDK 实现匿名用户的管理,并通过自定义声明(Custom Claims)来标记用户是否使用过 AI 问答功能。我们将结合一个实际的代码示例来展示如何实现这一功能。

一、匿名用户与 getProviderData() 方法

在 Firebase 中,用户可以通过多种身份验证方式登录,如电子邮件和密码、Google、Facebook 等。当用户未通过任何提供程序登录时,他们被视为匿名用户。判断用户是否为匿名用户的关键在于 getProviderData() 方法。

1.1 getProviderData() 方法

getProviderData() 方法返回的是与用户关联的身份验证提供程序的数据列表。每个身份验证提供程序(如 Google、Facebook)对应一个 UserInfo 对象。这个方法返回一个 UserInfo[] 数组,其中每个 UserInfo 对象包含以下信息:

  • getProviderId(): 返回身份提供程序的唯一标识符。
  • getUid(): 返回与该提供程序关联的用户标识符。
  • getDisplayName(): 返回用户的显示名称。
  • getPhotoUrl(): 返回用户的头像 URL。
  • getEmail(): 返回用户的电子邮件地址。
  • getPhoneNumber(): 返回用户的电话号码。

1.2 区分匿名用户

匿名用户通常没有绑定任何身份验证提供程序,因此 getProviderData() 通常返回一个空数组或仅包含默认的 Firebase 提供程序。通过检查 getProviderData() 的长度,我们可以判断用户是否为匿名用户:

public boolean isAnonymous(UserRecord userRecord) {
    UserInfo[] providerData = userRecord.getProviderData();
    return providerData.length < 1;
}

在这个方法中,如果 providerData 的长度小于 1,那么该用户就是匿名用户。

二、自定义声明与状态标记

Firebase 提供了自定义声明(Custom Claims)功能,允许开发者为用户设置自定义的元数据。在我们的场景中,可以利用这一功能来记录用户是否使用过 AI 问答。

2.1 更新用户状态

通过 UserRecord.UpdateRequest,我们可以为用户设置自定义声明。在以下代码中,我们将自定义声明 usedAIQuestioning 设置为 true,表示用户已经使用过 AI 问答功能:

public void updateUsing(String uid) {
    FirebaseAuth auth = FirebaseAuth.getInstance();
    UserRecord.UpdateRequest request = new UserRecord.UpdateRequest(uid)
        .setCustomClaims(Collections.singletonMap("usedAIQuestioning", true));
    try {
        auth.updateUser(request);
    } catch (FirebaseAuthException e) {
        e.printStackTrace();
    }
}

2.2 检查用户状态

要检查用户是否使用过 AI 问答功能,可以通过读取用户的自定义声明来判断:

public boolean isUsing(UserRecord userRecord) {
    Map<String, Object> customClaims = userRecord.getCustomClaims();
    return customClaims.get("usedAIQuestioning") != null && (boolean) customClaims.get("usedAIQuestioning");
}

在这个方法中,我们通过读取 customClaims 来判断 usedAIQuestioning 是否存在以及是否为 true,以此来确定用户是否已经使用过 AI 问答功能。

三、综合应用

以下是一个综合应用示例,展示了如何结合匿名用户的判断与自定义状态标记,来管理用户的访问权限:

import java.util.Collections;
import java.util.Map;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.UserInfo;
import com.google.firebase.auth.UserRecord;

public class FirebaseUserService {

  private String usedKey = "usedAIQuestioning";

  public void updateUsing(String uid) {
    FirebaseAuth auth = FirebaseAuth.getInstance();
    UserRecord.UpdateRequest request = new UserRecord.UpdateRequest(uid).setCustomClaims(Collections.singletonMap(usedKey, true));
    try {
      auth.updateUser(request);
    } catch (FirebaseAuthException e) {
      e.printStackTrace();
    }
  }

  public boolean isUsing(UserRecord userRecord) {
    Map<String, Object> customClaims = userRecord.getCustomClaims();

    return customClaims.get(usedKey) != null && (boolean) customClaims.get(usedKey);
  }

  public UserRecord getUser(String uid) {
    FirebaseAuth auth = FirebaseAuth.getInstance();
    UserRecord userRecord = null;
    try {
      userRecord = auth.getUser(uid);
    } catch (FirebaseAuthException e) {
      e.printStackTrace();
      return null;
    }
    return userRecord;
  }

  public boolean isAnonymous(UserRecord userRecord) {
    UserInfo[] providerData = userRecord.getProviderData();
    if (providerData.length < 1) {
      return true;
    }
    return false;
  }
}

public void handleUser(String userId) {
    boolean isAdmin = "0".equals(userId);

    UserRecord userRecord = null;
    if (!isAdmin) {
        FirebaseUserService firebaseUserService = Aop.get(FirebaseUserService.class);
        userRecord = firebaseUserService.getUser(userId);
        boolean isAnonymous = firebaseUserService.isAnonymous(userRecord);
        if (isAnonymous) {
            boolean used = firebaseUserService.isUsing(userRecord);
            if (used) {
                // 需要用户登录
                SsePacket ssePacket = new SsePacket(AiChatEventName.need_login, ("").getBytes());
                Tio.send(channelContext, ssePacket);
                closeSeeConnection(channelContext);
                return RespVo.fail("need login");
            } else {
                firebaseUserService.updateUsing(userId);
            }
        }
    }
    //....
}

在这个方法中,首先判断用户是否为管理员(admin)。如果用户不是管理员,程序会进一步判断用户是否为匿名用户,以及是否已经使用过 AI 问答功能。如果用户是匿名用户且已经使用过 AI 问答功能,则要求用户登录;否则,更新用户的状态为已使用 AI 问答功能。程序并不会终止,并且会继续向下运行. 下次再调用该方法时 used 为 true 会系统用户登录

四、总结

通过本文的示例,我们展示了如何使用 Firebase Admin SDK 来管理匿名用户,并通过自定义声明来记录用户的特定状态。这一功能在为用户提供定制化体验时尤为有用。通过合理利用这些工具,开发者可以更好地管理用户数据,并根据用户的状态提供个性化的服务。