【问题标题】:Scalatra response HMAC calulationScalatra 响应 HMAC 计算
【发布时间】:2014-10-17 11:05:49
【问题描述】:

我正在使用 Scalatra 开发 Web 服务,我想使用 HMAC 进行双向身份验证。

到目前为止,我已经实现了对服务器的客户端身份验证:客户端(Android 应用程序)使用以下参数为每个请求计算 HMAC/SHA512:共享密钥、HTTP 方法、URL、一些标头(时间戳、clientId 等)和请求正文(如果是 POST 或 PUT)。然后将此 HMAC 添加到特定标头,并将请求发送到服务器(服务器使用与客户端计算相同的 HMAC 验证请求标头中的 HMAC)。

现在,我想做相反的事情:让服务器使用存储的共享密钥、请求 HTTP 方法、URL 和响应 body 对客户端进行身份验证。

到目前为止,我发现我可以覆盖 renderResponse(actionResult: Any)renderResponseBody(actionResult: Any) 甚至 renderPipeline,我决定使用覆盖 renderPipeline,因为它似乎是最容易处理的。

在我覆盖的renderPipeline 中,我将响应正文转换为字节数组(如果提供File,则将服务的File 加载到内存中),计算HMAC 并将其添加到response 标头中。

我想知道的是:在这种情况下覆盖renderPipeline 是否会破坏上面提供的身份验证功能(例如renderPipeline 未被调用或被多次调用或在@987654331 之前已发送标头@ 被调用来渲染主体)或Scalatra中的一些其他功能?

请注意,当操作返回 Unit 并且响应输出由操作直接写入时,我不会计算 HMAC。

【问题讨论】:

    标签: scala servlets hmac scalatra


    【解决方案1】:

    我有完全相同的问题要解决。我使用了扩展Handler 的特征,就像在GZipSupport.scala 中所做的一样,并使用此answer 作为参考实现。

    我构建了一个ServletOutputStreamCopier,其中包含原始OutputStream 的副本以及两个流的每个字节:

    class ServletOutputStreamCopier(orig: ServletOutputStream) extends ServletOutputStream {
        val copy: ByteArrayOutputStream = new ByteArrayOutputStream(1024)
    
        override def write(b: Int): Unit = {
          orig.write(b)
          copy.write(b)
        }
        override def setWriteListener(writeListener: WriteListener): Unit = orig.setWriteListener(writeListener)
    
        override def isReady: Boolean = orig.isReady
    
        def getCopy: Array[Byte] = copy.toByteArray
    }
    

    然后是ResponseCopier,它是一个HttpServletResponseWrapper,前面定义了ServletOutputStreamCopier,并将copy暴露给外部:

    class ResponseCopier(res: HttpServletResponse, sos:   ServletOutputStreamCopier, w: PrintWriter) extends HttpServletResponseWrapper(res) {
        override def getOutputStream: ServletOutputStream = new ServletOutputStreamCopier(sos)
    
        override def getWriter: PrintWriter = w
    
        override def setContentLength(i: Int) = {}
    
        def getCopy: Array[Byte] = sos.getCopy
    }
    

    最后,handle 方法通过使用回调 ScalatraBase.onRenderedComplete 在 Scalatra 操作完成后负责添加标头。

    trait SignedResponseSupport extends Handler {
      self: ScalatraBase =>
    
      abstract override def handle(req: HttpServletRequest, res: HttpServletResponse): Unit = {
        withRequestResponse(req, res) {
          val sosc = new ServletOutputStreamCopier(res.getOutputStream)
          val w = new PrintWriter(sosc)
          val wrapped = new ResponseCopier(response,sosc ,w)
    
          ScalatraBase.onRenderedCompleted { _ =>
            w.flush()
            w.close()
            val password = "secret-password"
            val signature = signResponseBody(wrapped.getCopy, password)
            wrapped.addHeader("X-Response-Signature", signature)
            }
          }
          super.handle(req, wrapped)
        }
    
      def signResponseBody(body: Array[Byte], password: String): String = {
        /*signing goes here*/
      }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-03
      • 2011-10-21
      相关资源
      最近更新 更多