设为首页 加入收藏

TOP

Akka-CQRS(16)- gRPC用JWT进行权限管理(一)
2019-08-15 00:10:42 】 浏览:213
Tags:Akka-CQRS gRPC JWT 进行 权限 管理

   前面谈过gRPC的SSL/TLS安全机制,发现设置过程比较复杂:比如证书签名:需要服务端、客户端两头都设置等。想想实际上用JWT会更加便捷,而且更安全和功能强大,因为除JWT的加密签名之外还可以把私密的用户信息放在JWT里加密后在服务端和客户端之间传递。当然,最基本的是通过对JWT的验证机制可以控制客户端对某些功能的使用权限。

通过JWT实现gRPC的函数调用权限管理原理其实很简单:客户端首先从服务端通过身份验证获取JWT,然后在调用服务函数时把这个JWT同时传给服务端进行权限验证。客户端提交身份验证请求返回JWT可以用一个独立的服务函数实现,如下面.proto文件里的GetAuthToken:

message PBPOSCredential {
    string userid = 1;
    string password = 2;
}
message PBPOSToken {
    string jwt = 1;
}

service SendCommand {
    rpc SingleResponse(PBPOSCommand) returns (PBPOSResponse) {};
    rpc GetTxnItems(PBPOSCommand) returns (stream PBTxnItem) {};
    rpc GetAuthToken(PBPOSCredential) returns (PBPOSToken) {};

}

比较棘手的是如何把JWT从客户端传送至服务端,因为gRPC基本上骑劫了Request和Response。其中一个方法是通过Interceptor来截取Request的header即metadata。客户端将JWT写入metadata,服务端从metadata读取JWT。

我们先看看客户端的Interceptor设置和使用:

  class AuthClientInterceptor(jwt: String) extends ClientInterceptor {
    def interceptCall[ReqT, RespT](methodDescriptor: MethodDescriptor[ReqT, RespT], callOptions: CallOptions, channel: io.grpc.Channel): ClientCall[ReqT, RespT] =
      new ForwardingClientCall.SimpleForwardingClientCall[ReqT, RespT](channel.newCall(methodDescriptor, callOptions)) {
        override def start(responseListener: ClientCall.Listener[RespT], headers: Metadata): Unit = {
          headers.put(Key.of("jwt", Metadata.ASCII_STRING_MARSHALLER), jwt)
          super.start(responseListener, headers)
        }
      }
  }

...


    val unsafeChannel = NettyChannelBuilder
      .forAddress("192.168.0.189",50051)
      .negotiationType(NegotiationType.PLAINTEXT)
      .build()

   val securedChannel = ClientInterceptors.intercept(unsafeChannel, new AuthClientInterceptor(jwt))

    val securedClient = SendCommandGrpc.blockingStub(securedChannel)

    val resp = securedClient.singleResponse(PBPOSCommand())

身份验证请求即JWT获取是不需要Interceptor的,所以要用没有Interceptor的unsafeChannel: 

    //build connection channel
    val unsafeChannel = NettyChannelBuilder
      .forAddress("192.168.0.189",50051)
      .negotiationType(NegotiationType.PLAINTEXT)
      .build()


    val authClient = SendCommandGrpc.blockingStub(unsafeChannel)
    val jwt = authClient.getAuthToken(PBPOSCredential(userid="johnny",password="p4ssw0rd")).jwt
    println(s"got jwt: $jwt")
 

JWT的构建和使用已经在前面的几篇博文里讨论过了: 

package com.datatech.auth

import pdi.jwt._
import org.json4s.native.Json
import org.json4s._
import org.json4s.jackson.JsonMethods._
import pdi.jwt.algorithms._
import scala.util._

object AuthBase {
  type UserInfo = Map[String, Any]
  case class AuthBase(
                       algorithm: JwtAlgorithm = JwtAlgorithm.HMD5,
                       secret: String = "OpenSesame",
                       getUserInfo: (String,String) => Option[UserInfo] = null) {
    ctx =>

    def withAlgorithm(algo: JwtAlgorithm): AuthBase = ctx.copy(algorithm = algo)

    def withSecretKey(key: String): AuthBase = ctx.copy(secret = key)

    def withUserFunc(f: (String, String) => Option[UserInfo]): AuthBase = ctx.copy(getUserInfo = f)

    def authenticateToken(token: String): Option[String] =
      algorithm match {
        case algo: JwtAsymmetricAlgorithm =>
          Jwt.isValid(token, secret, Seq((algorithm.asInstanceOf[JwtAsymmetricAlgorithm]))) match {
            case true =&g
首页 上一页 1 2 3 4 5 6 7 下一页 尾页 1/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇restapi(0)- 平台数据维护,写.. 下一篇restapi(2)- generic restful C..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目