Firebase Authentication
背景
在现代应用中,用户身份验证是确保系统安全性的重要环节。Firebase Authentication 提供了简单易用的身份验证服务,支持多种身份验证方法,包括电子邮件密码登录、第三方登录(如 Google、Facebook)、匿名登录等。为了确保操作的安全性,如文件上传,通常需要验证用户的身份,并验证其所携带的 Token 是否有效。
在本指南中,我们将展示如何使用 Firebase Authentication 验证用户的 Token,并将其集成到一个 Java 应用程序中,以确保只有经过身份验证的用户才能执行敏感操作。
验证 Token
在 Firebase 中,我们可以使用 FirebaseAuth
类来验证用户的 Token。以下示例展示了如何在 Java 中进行 Token 验证:
简单的验证示例
以下是一个简单的测试示例,用于验证 Token 的有效性:
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 com.litongjava.tio.utils.environment.EnvUtils;
public class FirebaseVerifyTokenTest {
@Before
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。以下是实现方法:
package com.litongjava.open.chat.interceptor;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseToken;
import com.jfinal.kit.StrKit;
import com.litongjava.tio.boot.http.TioHttpContext;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.common.HttpResponseStatus;
import com.litongjava.tio.http.common.RequestLine;
import com.litongjava.tio.http.server.intf.HttpRequestInterceptor;
import com.litongjava.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
}
}
配置拦截器
将拦截器配置到服务器中,确保它能够拦截所有需要验证的请求:
package com.litongjava.max.blog.config;
import com.litongjava.annotation.AConfiguration;
import com.litongjava.annotation.Initialization;
import com.litongjava.max.blog.inteceptor.AuthInterceptor;
import com.litongjava.tio.boot.http.interceptor.HttpInteceptorConfigure;
import com.litongjava.tio.boot.http.interceptor.HttpInterceptorModel;
import com.litongjava.tio.boot.server.TioBootServer;
import com.litongjava.tio.http.server.intf.HttpRequestInterceptor;
@AConfiguration
public class InterceptorConfiguration {
@Initialization
public void config() {
HttpRequestInterceptor authTokenInterceptor = new AuthInterceptor();
HttpInterceptorModel model = new HttpInterceptorModel();
model.setInterceptor(authTokenInterceptor);
model.addBlockUrl("/**"); // 拦截所有路由
model.addAllowUrls("", "/"); // 允许的路由
HttpInteceptorConfigure serverInteceptorConfigure = new HttpInteceptorConfigure();
serverInteceptorConfigure.add(model);
TioBootServer.me().setHttpInteceptorConfigure(serverInteceptorConfigure);
}
}
通过以上配置,我们确保每个请求都会经过 Token 验证,只有在验证通过的情况下才会继续处理请求。
安装的拦截器示例
package com.litongjava.max.blog.inteceptor;
import java.util.Date;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseToken;
import com.litongjava.constatns.ServerConfigKeys;
import com.litongjava.model.body.RespBodyVo;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.common.HttpResponseStatus;
import com.litongjava.tio.http.common.RequestLine;
import com.litongjava.tio.http.server.intf.HttpRequestInterceptor;
import com.litongjava.tio.utils.environment.EnvUtils;
import com.litongjava.tio.utils.hutool.StrUtil;
import com.litongjava.tio.utils.network.IpUtils;
import com.litongjava.tio.utils.notification.LarksuiteNotificationUtils;
import com.litongjava.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(new Date());
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 示例,展示如何分页检索所有用户:
package com.litongjava.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 com.litongjava.open.chat.config.FirebaseAppConfiguration;
import com.litongjava.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。此操作对于管理用户、统计分析或执行批量操作非常有用。