2014-10-17 3 views
1

Scalatra를 사용하여 웹 서비스를 개발 중이며 양방향 인증에 HMAC를 사용하고 싶습니다.Scalatra response HMAC calulation

지금까지 클라이언트 (Android 앱)는 공유 된 비밀번호, HTTP 메소드, URL, 일부 헤더 (예 : 쿠키) 매개 변수를 사용하여 요청마다 HMAC/SHA512를 계산합니다. timestamp, clientId 등) 및 요청 본문 (POST 또는 PUT 인 경우)이 포함됩니다. 그런 다음이 HMAC가 특정 헤더에 추가되고 요청이 서버로 전송됩니다 (요청 헤더의 HMAC가 클라이언트와 동일한 것으로 계산되는 HMAC로 유효성을 검사 함).

이제 반대의 작업을 수행하고 싶습니다. 저장된 공유 비밀, 요청 HTTP 메소드, URL 및 응답 본문을 사용하여 서버가 클라이언트에 인증되도록하십시오.

지금까지, 나는 renderResponse(actionResult: Any), renderResponseBody(actionResult: Any) 또는 renderPipeline을 무시할 수 있다는 것을 발견했고 나는 그것을 처리 할 수있는 가장 쉬운 것으로 보인다 최우선 renderPipeline로 이동하기로 결정했습니다. 제 renderPipeline 재정의에서

는 I 바이트 배열 (a File 속한 경우 메모리에 제공 File 로딩)은 HMAC을 계산하고 response 헤더에 추가 응답 본체 변환.

내가 알고 싶은 것은 : 거기 사례를 위에 제시된 인증 기능 중 하나를 끊을이 방법을 renderPipeline 오버라이드 (override) 할 때 (같은 renderPipeline하지 호출되는 또는 렌더링하기 위해 호출됩니다 여러 번 또는 renderPipeline befor를 전송 된 헤더를 호출되고 시체) 또는 Scalatra의 다른 기능?

메모는 동작이 Unit을 반환하고 작업에 의해 직접 응답 결과가 기록 될 때 HMAC를 계산하지 않습니다.

답변

1

나는 똑같은 문제를 해결해야했습니다. Handler이라는 특성을 사용했는데 마치 GZipSupport.scala에서 수행 한 것처럼이 answer을 참조 구현으로 사용했습니다.

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, 이전 ServletOutputStreamCopier 정의와 HttpServletResponseWrapper이고 외부로 copy 노출 :

난 양 스트림에 원래 OutputStream의 복사본과 모든 바이트를 보유 ServletOutputStreamCopier 내장 다음 Scalatra 동작이 완료 한 후

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을 사용하십시오.

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*/ 
    } 

}