Clerk 验证
tio-boot 整合 Clerk 进行 Token 验证
本文档介绍如何将 Clerk SDK for Java 与 tio-boot 框架集成,实现对 token 的验证功能。主要内容包括 Clerk 简介、Token 格式说明以及整合步骤和代码示例。
1. Clerk 简介
Clerk 是一款用于处理身份验证和授权的工具包,支持对 JSON Web Token (JWT) 进行验证。通过 Clerk 后台提供的 JWT 公钥,可以对客户端传入的 token 进行解析和验证,从而确保用户身份的真实性。整合 Clerk 后,你可以使用标准方法获取 token 中包含的用户信息,例如 sub
字段(通常表示用户标识符)。
2. Token 格式
在整合 Clerk 时,Token 通常采用 JWT 格式。常见的字段包括:
- sub:JWT 标准声明,表示用户的唯一标识符(user id)。
- 其他自定义字段:例如
user_id
或其他业务相关数据。
验证过程中,建议使用 getSubject()
方法获取用户 ID,或直接获取 token 中其他自定义的字段。
3. 整合步骤
3.1 添加依赖
在项目的 Maven 配置中添加 Clerk SDK 和 Jackson 核心库的依赖,示例配置如下:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.clerk</groupId>
<artifactId>backend-api</artifactId>
<version>1.5.0</version>
</dependency>
3.2 配置 Token 验证工具类
创建工具类 ClerkVerifyTokenUtils
,使用 Clerk 后台提供的 JWT 公钥来配置验证选项。代码示例如下:
package com.litongjava.open.chat.utils;
import java.net.URL;
import com.clerk.backend_api.helpers.jwks.TokenVerificationException;
import com.clerk.backend_api.helpers.jwks.VerifyToken;
import com.clerk.backend_api.helpers.jwks.VerifyTokenOptions;
import com.clerk.backend_api.helpers.jwks.VerifyTokenOptions.Builder;
import com.litongjava.tio.utils.environment.EnvUtils;
import com.litongjava.tio.utils.hutool.FileUtil;
import com.litongjava.tio.utils.hutool.ResourceUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ClerkVerifyTokenUtils {
public static VerifyTokenOptions options = null;
static {
URL resource = null;
if (EnvUtils.isProd()) {
resource = ResourceUtil.getResource("CLERK_JWT_KEY_prod.key");
} else {
resource = ResourceUtil.getResource("CLERK_JWT_KEY_test.key");
}
if (resource == null) {
log.error("Failed to read CLERK_JWT_KEY.key");
}
StringBuilder readURLAsString = FileUtil.readURLAsString(resource);
Builder builder = VerifyTokenOptions.jwtKey(readURLAsString.toString());
options = builder.build();
}
public static Claims verifyToken(String token) throws TokenVerificationException {
// 配置验证选项,比如使用 Clerk 后台提供的 JWT 公钥
return VerifyToken.verifyToken(token, options);
}
}
该工具类中,通过
ResourceUtil
和FileUtil
获取存放 JWT 公钥的文件,并构造出验证选项。调用VerifyToken.verifyToken
方法可以验证 token 并返回对应的Claims
对象。
3.3 在 AuthInterceptor 中集成 Token 验证
在 tio-boot 的 AuthInterceptor
中,通过解析 HTTP 请求头中的 authorization
字段获取 token,并利用 ClerkVerifyTokenUtils.verifyToken
方法进行验证。验证成功后,提取 token 中的用户标识符(使用 getSubject()
方法)并保存到上下文中。
以下是完整代码示例:
package com.litongjava.open.chat.intecerptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import com.clerk.backend_api.helpers.jwks.TokenVerificationException;
import com.jfinal.kit.StrKit;
import com.litongjava.constants.ServerConfigKeys;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.model.body.RespBodyVo;
import com.litongjava.open.chat.notification.TokenNotificationUtils;
import com.litongjava.open.chat.services.NewUserService;
import com.litongjava.open.chat.utils.ClerkVerifyTokenUtils;
import com.litongjava.tio.boot.admin.services.TokenPredicate;
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.common.utils.HttpIpUtils;
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.NotifactionWarmModel;
import com.litongjava.tio.utils.notification.NotificationTemplate;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AuthInterceptor implements HttpRequestInterceptor {
TokenPredicate tokenPredicate = Aop.get(TokenPredicate.class);
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 (StrKit.isBlank(authorization)) {
String content = "authorization can not be empty";
sendError(request, null, content);
return fail(content);
}
String[] split = authorization.split(" ");
if (split.length < 2) {
String content = "Failed to extra token";
sendError(request, null, content);
return fail(content);
}
String idToken = split[1];
boolean test = tokenPredicate.test(idToken);
if (!test) {
Claims decodedToken = null;
try {
decodedToken = ClerkVerifyTokenUtils.verifyToken(idToken);
} catch (TokenVerificationException e) {
String content = "failed to validate token:" + idToken;
sendError(request, e, content);
return fail("Failed to validate token");
}
// 使用 getSubject() 获取 token 中的用户标识符
String uid = decodedToken.getSubject();
TioRequestContext.setUserId(uid);
Aop.get(NewUserService.class).save(uid);
}
return null;
}
private void sendError(HttpRequest request, Exception e, String content) {
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("II");
model.setContent(content);
model.setDeviceName(localIp);
String requestId = request.getChannelContext().getId();
String realIp = HttpIpUtils.getRealIp(request);
model.setRequestId(requestId);
model.setUserIp(realIp);
model.setHost(request.getHost());
model.setRequestLine(request.getRequestLine().toString());
model.setRequestBody(request.getBodyString());
if (e != null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String stackTrace = sw.toString();
model.setStackTrace(stackTrace);
}
model.setTime(new Date());
String msg = NotificationTemplate.format(model);
TokenNotificationUtils.sendAsync(msg);
}
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 {
// 处理完请求后的操作
}
}
代码说明:
- 从请求头中提取
authorization
字段,并解析出 token(格式通常为 "Bearer token")。- 调用
TokenPredicate
进行初步校验;如果校验不通过,则进入 Clerk 验证流程。- 利用
ClerkVerifyTokenUtils.verifyToken
方法验证 token,验证通过后调用getSubject()
方法获取用户 ID。- 将用户 ID 存入上下文中,并调用新用户服务保存用户信息。
总结
本文档详细描述了如何在 tio-boot 框架中整合 Clerk 进行 token 验证。通过添加依赖、配置验证工具类以及在拦截器中处理 token 验证流程,你可以实现一个高效且安全的认证方案。详细参考 Clerk SDK 的官方文档获取更多信息和最新更新。
希望本篇文档对你在整合过程中有所帮助。