Ktor项目JWT认证调用接口遇401 Unauthorized错误求助
我在Ktor项目中实现JWT认证功能,登录时可成功获取JWT Token,但使用该Token调用DailySalesReportCreateRoute接口时,返回401 Unauthorised错误。
相关代码
JwtService实现
class JwtService { private val issuer = "testServer" private val jwtSecretTest = System.getenv("JWT_SECRET_TEST") private val algorithm = Algorithm.HMAC512(jwtSecretTest) val verifier: JWTVerifier = JWT .require(algorithm) .withIssuer(issuer) .build() fun generateToken(user: User): String { return JWT.create() .withSubject("TestAuthentication") .withIssuer(issuer) .withClaim("email", user.email) .sign(algorithm) } }
Application.kt认证配置
val jwtService = JwtService() install(Authentication) { jwt ("jwt") { verifier(jwtService.verifier) realm = "Test Server" validate { val payload = it.payload val email = payload.getClaim("email").toString() val user = db.findUserByEmail(email) user } } }
路由代码
fun Route.dailySalesReportRoutes( db: Repo, jwtService: JwtService, hashFunction: (String) -> String ) { authenticate("jwt") { post<DailySalesReportCreateRoute> { val dailySalesReport = try { call.receive<DailySalesReport>() } catch (e: Exception) { call.respond(HttpStatusCode.BadRequest, SimpleResponse(false, "Missing fields")) return@post } try { //val email = call.principal<User>()!!.email //db.addDailySalesReport(dailySalesReport, email) dailySalesReport.loggedInUserEmail.let { it1 -> db.addDailySalesReport(dailySalesReport, it1) } call.respond(HttpStatusCode.OK, SimpleResponse(true, "Note added successfully")) } catch (e: Exception) { call.respond(HttpStatusCode.Conflict, SimpleResponse(false, e.message ?: "Some problem occurred")) } } } }
问题原因及解决方法
1. Claim解析格式错误
这是最可能的原因:在认证的validate块中,使用payload.getClaim("email").toString()获取邮箱时,返回的字符串会包含双引号(例如"user@example.com"),而数据库中存储的是不带引号的邮箱,导致db.findUserByEmail(email)无法找到用户,认证失败返回401。
解决:将toString()替换为asString()方法获取纯字符串值:
validate { val payload = it.payload val email = payload.getClaim("email").asString() // 替换toString()为asString() val user = db.findUserByEmail(email) user }
2. 请求Token传递格式错误
确保调用接口时,Token通过Authorization请求头传递,格式必须为Bearer <你的Token>,例如:
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9...
如果直接传递Token字符串而不带Bearer 前缀,Ktor的JWT认证插件无法识别,会返回401。
3. JWT密钥不一致
生成Token和验证Token时使用的JWT_SECRET_TEST环境变量值必须完全一致。如果生成Token时的密钥和验证时的密钥不同,会导致签名验证失败,返回401。
验证:可以在JwtService初始化时打印jwtSecretTest的值,确认生成和验证阶段的密钥相同。
4. 路由认证策略名称不匹配
确认路由中的authenticate("jwt")和install(Authentication)中配置的jwt("jwt")的名称完全一致,名称不匹配会导致认证策略不生效。
5. Token过期(可选)
当前代码未设置Token过期时间,但如果后续添加了过期逻辑,请求时Token已过期也会返回401。如果需要添加过期时间,可在生成Token时补充:
fun generateToken(user: User): String { return JWT.create() .withSubject("TestAuthentication") .withIssuer(issuer) .withClaim("email", user.email) .withExpiresAt(Date(System.currentTimeMillis() + 3600000)) // 设置1小时过期 .sign(algorithm) }
内容的提问来源于stack exchange,提问作者BRDroid

