Skip to content

Firebase Authentication ​

背景 ​

在现代应用中,用户身份验证是确保系统安全性的重要环节。Firebase Authentication 提供了简单易用的身份验证服务,支持多种身份验证方法,包括电子邮件密码登录、第三方登录(如 Google、Facebook)、匿名登录等。为了确保操作的安全性,如文件上传,通常需要验证用户的身份,并验证其所携带的 Token 是否有效。

在本指南中,我们将展示如何使用 Firebase Authentication 验证用户的 Token,并将其集成到一个 Java 应用程序中,以确保只有经过身份验证的用户才能执行敏感操作。

验证 Token ​

在 Firebase 中,我们可以使用 FirebaseAuth 类来验证用户的 Token。以下示例展示了如何在 Java 中进行 Token 验证:

简单的验证示例 ​

以下是一个简单的测试示例,用于验证 Token 的有效性:

java
import java.io.IOException;

import org.junit.Before;
import org.junit.Test;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseToken;
import nexus.io.tio.utils.environment.EnvUtils;

public class FirebaseVerifyTokenTest {

  @AopBefore
  public void before() {
    EnvUtils.load();
    try {
      new FirebaseAppConfiguration().config();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Test
  public void verify() {
    String idToken = "YOUR_ID_TOKEN_HERE";
    try {
      FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
      String uid = decodedToken.getUid();
      System.out.println("Token is valid. User ID: " + uid);
    } catch (Exception e) {
      e.printStackTrace();
      System.out.println("Token is invalid: " + e.getMessage());
    }
  }
}

在这个示例中,我们使用 FirebaseAuth.getInstance().verifyIdToken(idToken) 方法来验证用户的 Token。如果 Token 有效,我们可以获取用户的 UID,并允许用户执行接下来的操作。

使用拦截器验证 Token ​

为了在实际应用中集成 Token 验证,我们可以创建一个拦截器来自动验证请求中的 Token。以下是实现方法:

java
package nexus.io.open.chat.interceptor;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseToken;
import com.jfinal.kit.StrKit;
import nexus.io.tio.boot.http.TioHttpContext;
import nexus.io.tio.http.common.HttpRequest;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.tio.http.common.HttpResponseStatus;
import nexus.io.tio.http.common.RequestLine;
import nexus.io.tio.http.server.intf.HttpRequestInterceptor;
import nexus.io.tio.utils.resp.RespBodyVo;

public class AuthInterceptor implements HttpRequestInterceptor {

  @Override
  public HttpResponse doBeforeHandler(HttpRequest request, RequestLine requestLine, HttpResponse responseFromCache) {
    String authorization = request.getHeader("authorization");

    if (StrKit.isBlank(authorization)) {
      return fail("Authorization header cannot be empty");
    }
    String[] split = authorization.split(" ");
    if (split.length < 2) {
      return fail("Failed to extract token");
    }
    String idToken = split[1];
    try {
      FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
      String uid = decodedToken.getUid();
      TioHttpContext.setUserId(uid);
    } catch (Exception e) {
      return fail("Failed to validate token: " + e.getMessage());
    }
    return null;
  }

  private HttpResponse fail(String msg) {
    HttpResponse response = TioHttpContext.getResponse();
    response.setStatus(HttpResponseStatus.C401);
    response.setJson(RespBodyVo.fail(msg));
    return response;
  }

  @Override
  public void doAfterHandler(HttpRequest request, RequestLine requestLine, HttpResponse response, long cost) {
    // Implement if needed
  }
}

配置拦截器 ​

将拦截器配置到服务器中,确保它能够拦截所有需要验证的请求:

java
package nexus.io.max.blog.config;

import nexus.io.annotation.AConfiguration;
import nexus.io.annotation.Initialization;
import nexus.io.max.blog.inteceptor.AuthInterceptor;
import nexus.io.tio.boot.http.interceptor.HttpInteceptorConfigure;
import nexus.io.tio.boot.http.interceptor.HttpInterceptorModel;
import nexus.io.tio.boot.server.TioBootServer;
import nexus.io.tio.http.server.intf.HttpRequestInterceptor;

@AConfiguration
public class InterceptorConfiguration {

  @Initialization
  public void config() {
    HttpRequestInterceptor UserTokenInterceptor = new AuthInterceptor();
    HttpInterceptorModel model = new HttpInterceptorModel();
    model.setInterceptor(UserTokenInterceptor);
    model.addBlockUrl("/**"); // 拦截所有路由
    model.addAllowUrls("", "/"); // 允许的路由

    HttpInteceptorConfigure serverInteceptorConfigure = new HttpInteceptorConfigure();
    serverInteceptorConfigure.add(model);
    TioBootServer.me().setHttpInteceptorConfigure(serverInteceptorConfigure);
  }
}

通过以上配置,我们确保每个请求都会经过 Token 验证,只有在验证通过的情况下才会继续处理请求。

安装的拦截器示例 ​

java
package nexus.io.max.blog.inteceptor;

import java.util.Date;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseToken;
import nexus.io.constatns.ServerConfigKeys;
import nexus.io.model.body.RespBodyVo;
import nexus.io.tio.boot.http.TioRequestContext;
import nexus.io.tio.http.common.HttpRequest;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.tio.http.common.HttpResponseStatus;
import nexus.io.tio.http.common.RequestLine;
import nexus.io.tio.http.server.intf.HttpRequestInterceptor;
import nexus.io.tio.utils.environment.EnvUtils;
import nexus.io.tio.utils.hutool.StrUtil;
import nexus.io.tio.utils.network.IpUtils;
import nexus.io.tio.utils.notification.LarksuiteNotificationUtils;
import nexus.io.tio.utils.notification.NotifactionWarmModel;

import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;

@Slf4j
public class AuthInterceptor implements HttpRequestInterceptor {

  private Object body = null;

  public AuthInterceptor() {

  }

  public AuthInterceptor(Object body) {
    this.body = body;
  }

  @Override
  public HttpResponse doBeforeHandler(HttpRequest request, RequestLine requestLine, HttpResponse responseFromCache) {
    String authorization = request.getHeader("authorization");

    if (StrUtil.isBlank(authorization)) {
      return fail("authorization can not be empty");
    }

    String[] split = authorization.split(" ");
    if (split.length < 2) {
      return fail("Failed to extra token");
    }
    String idToken = split[1];
    if (EnvUtils.getStr("admin.token").equals(idToken)) {
      TioRequestContext.setUserId("0");
      return null;
    }

    FirebaseToken decodedToken = null;
    try {
      decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
    } catch (Exception e) {
      e.printStackTrace();
      NotifactionWarmModel model = new NotifactionWarmModel();

      String localIp = IpUtils.getLocalIp();
      model.setAppEnv(EnvUtils.env());
      model.setAppGroupName("tio-boot");
      model.setAppName(EnvUtils.get(ServerConfigKeys.APP_NAME));
      model.setWarningName("AuthInterceptor");
      model.setLevel("普通级别");
      model.setContent("fetch " + request.getRequestLine().toString() + "\nfailed to validate token:" + idToken);
      model.setDeviceName(localIp);
      model.setTime(ZonedDateTime.now());

      try (Response response = LarksuiteNotificationUtils.sendWarm(EnvUtils.get("warm.notification.webhook.url"), model)) {
        if (!response.isSuccessful()) {
          log.error("Failed to push message to lark");
        }
      }
      log.error("Failed to validate token:", idToken);
      return fail("Failed to validate token");
    }

    String uid = decodedToken.getUid();
    TioRequestContext.setUserId(uid);
    return null;

  }

  private HttpResponse fail(String msg) {
    HttpResponse response = TioRequestContext.getResponse();
    response.setStatus(HttpResponseStatus.C401);
    if (body != null) {
      RespBodyVo respVo = RespBodyVo.fail(msg);
      respVo.setData(body);
      response.setJson(respVo);
    }
    return response;
  }

  @Override
  public void doAfterHandler(HttpRequest request, RequestLine requestLine, HttpResponse response, long cost) throws Exception {

  }
}

用户操作 ​

获取 Firebase Authentication 上的所有用户 ​

要从 Firebase Authentication 中获取所有用户,可以使用 Firebase Admin SDK。以下是一个 Java 示例,展示如何分页检索所有用户:

java
package nexus.io.firebase;

import java.io.IOException;

import org.junit.Test;

import com.google.firebase.auth.ExportedUserRecord;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.ListUsersPage;
import nexus.io.open.chat.config.FirebaseAppConfiguration;
import nexus.io.tio.utils.environment.EnvUtils;

public class FirebaseUsers {

  @Test
  public void listUser() {

    EnvUtils.load();
    try {
      new FirebaseAppConfiguration().config();
    } catch (IOException e) {
      e.printStackTrace();
      return;
    }

    ListUsersPage page = null;

    try {
      page = FirebaseAuth.getInstance().listUsers(null);
    } catch (FirebaseAuthException e) {
      e.printStackTrace();
      return;
    }
    while (page != null) {
      for (ExportedUserRecord user : page.getValues()) {
        System.out.println("User: " + user.getUid());
      }
      page = page.getNextPage();
    }
  }

}

这个代码段将遍历并打印出所有 Firebase Authentication 中的用户 UID。此操作对于管理用户、统计分析或执行批量操作非常有用。

MIT Licensed