scala - 如何在纯 Scala 中验证 JWT token 的 HMAC 签名?

标签 scala playframework jwt twitter-finagle

有一些不错的 JWT token 解码库,但我觉得我不需要任何库,因为它应该都是 boil down to base64 encode/decode and basic cryptography algorithms可以在标准库中找到。

我找到了 authentikat-jwt但它引入了 Apache 通用编解码器和 Json4s,我真的不想在我的项目中使用它们:例如,我已经使用了另一个 Json 库!

我找到了 jwt-scala并引入所有类型的 Play 框架 deps.. 同样,我只是在 Finagle 中制作一个微型微服务!

一遍又一遍,我觉得我需要的只是一根香蕉,而我得到的是一只拿着香蕉的 gorilla

最佳答案

我终于写了自己的验证器。此代码段中唯一的“依赖项”是我的 Json 库,它恰好是 rapture-json .你可以完全用你自己的库甚至正则表达式替换它(你只需要从一个小的 Json 对象中提取一个字段)

/**
  * Created by sscarduzio on 14/12/2015.
  */
object JWTSignatureValidator {

  import javax.crypto.Mac
  import javax.crypto.spec.SecretKeySpec


  def sign(algorithm: String, headerAndClaims: String, key: Array[Byte]): Array[Byte] = {
    val algo = algorithm match {
      case "HS256" => "HmacSHA256"
      case "HS348" => "HmacSHA348"
      case "HS512" => "HmacSHA512"
      case "none" => "NONE"
      case _ => throw new Exception("algo not found for verification of JWT: " + algorithm)
    }
    val scs = new SecretKeySpec(key, algo)
    val mac = Mac.getInstance(algo)
    mac.init(scs)
    mac.doFinal(headerAndClaims.getBytes)
  }

  def decodeBase64(str: String): String = new String(new sun.misc.BASE64Decoder().decodeBuffer(str), "UTF-8")

  def encodeBase64URLSafeString(bytes: Array[Byte]): String = {
    // the "url safe" part in apache codec is just replacing the + with - and / with _
    val s = new sun.misc.BASE64Encoder().encode(bytes).map(c => if (c == '+') '-' else c).map(c => if (c == '/') '_' else c)
    // We don't need the Base64 padding for JWT '='
    s.substring(0, s.size - 1)
  }

  import rapture.json._
  import jsonBackends.argonaut._

  def validate(jwt: String, key: String, keyIsBase64Encoded: Boolean): Boolean = {

    jwt.split("\\.") match {
      case Array(providedHeader, providedClaims, providedSignature) =>

        val headerJsonString = decodeBase64(providedHeader)
        val algorithm = Json.parse(headerJsonString).alg.as[String]
        val ourSignature = encodeBase64URLSafeString(sign(algorithm, providedHeader + "." + providedClaims, if (keyIsBase64Encoded) decodeBase64(key).getBytes("UTF-8") else key.getBytes("UTF-8")))
        providedSignature.contentEquals(ourSignature)
      case _ =>
        false
    }
  }
}

用法

验证函数支持 base64 或字符串键。这是如何使用本教程中的示例 token 和签名来使用它的演示。 https://scotch.io/tutorials/the-anatomy-of-a-json-web-token

val token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
val key = "c2VjcmV0=" // base64 of 'secret' 
println(validate(token, key, true))

免责声明/致谢

我无耻地从 authentikat-jwt 中窃取了一些代码,并将 apache 通用编解码器代码替换为相同内容的标准 Java 版本。

关于scala - 如何在纯 Scala 中验证 JWT token 的 HMAC 签名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34269064/

相关文章:

Scala - Spray.io - sbt-revolver - jrebel - 重新加载时没有看到 HttpService (或任何内容)的更改

scala - 如何跳出 Play Framework 模板循环?

json - 我应该使用spray 还是play 来进行高度可扩展和高效的rest json 处理?

security - JsonWebToken : activity-based expiration vs issuing time-based expiration

django 管理页面和 JWT

jwt - 在 Swagger UI 中,如何从 "anonymous"方法中删除挂锁图标?

java - Securesocial - 在自定义 View 中获取用户数据

scala - 将对象参数传递给父级时无法在类层次结构中生成 unpickler

scala - 为什么我们定义 `def hello() = "world "`, but we can invoke it as "hello”?

java - 由于嵌套 bean,无法在 playframework 中使用 GSON 解析 JSON