You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ktor项目JWT认证调用接口遇401 Unauthorized错误求助

Ktor JWT认证返回401 Unauthorised问题排查

我在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 00:12:03