gRPC内置了以下身份验证机制:
- SSL / TLS:gRPC具有SSL / TLS集成,并促进使用SSL / TLS对服务器进行身份验证,并加密客户端和服务器之间交换的所有数据。可选机制可供客户端提供相互身份验证的证书。
- 使用Google进行基于令牌的身份验证:gRPC提供了一种通用机制(如下所述),用于将基于元数据的凭据附加到请求和响应。
- gRPC提供了一个基于Credentials对象统一概念的简单身份验证API,可以在创建整个gRPC频道或单个调用时使用。
所有的gRPC stubs都可以通过withCallCredentials(CallCredentials credentials)
方法返回一个新的stub,用于简单身份验证。
继承CallCredentials
实现自己的身份认证令牌
public class BearerToken extends CallCredentials {
private String value;
public BearerToken(String value) {
this.value = value;
}
@Override
public void applyRequestMetadata(RequestInfo requestInfo, Executor executor, MetadataApplier metadataApplier) {
executor.execute(() -> {
try {
Metadata headers = new Metadata();
headers.put(Constants.AUTHORIZATION_METADATA_KEY, String.format("%s %s", Constants.BEARER_TYPE, value));
metadataApplier.apply(headers);
} catch (Throwable e) {
metadataApplier.fail(Status.UNAUTHENTICATED.withCause(e));
}
});
}
@Override
public void thisUsesUnstableApi() {
// noop
}
}
applyRequestMetadata
方法用将身份认证信息通过MetadataApplier
传递给请求的metadata。
通过拦截器进行身份认证
public class Constants {
public static final String JWT_SIGNING_KEY = "L8hHXsaQOUjk5rg7XPGv4eL36anlCrkMz8CJ0i/8E/0=";
public static final String BEARER_TYPE = "Bearer";
public static final Metadata.Key<String> AUTHORIZATION_METADATA_KEY = Metadata.Key.of("Authorization", ASCII_STRING_MARSHALLER);
public static final Context.Key<String> CLIENT_ID_CONTEXT_KEY = Context.key("clientId");
private Constants() {
throw new AssertionError();
}
}
public class AuthorizationServerInterceptor implements ServerInterceptor {
private JwtParser parser = Jwts.parser().setSigningKey(Constants.JWT_SIGNING_KEY);
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata, ServerCallHandler<ReqT, RespT> serverCallHandler) {
String value = metadata.get(Constants.AUTHORIZATION_METADATA_KEY);
Status status;
if (value == null) {
status = Status.UNAUTHENTICATED.withDescription("Authorization token is missing");
} else if (!value.startsWith(Constants.BEARER_TYPE)) {
status = Status.UNAUTHENTICATED.withDescription("Unknown authorization type");
} else {
try {
String token = value.substring(Constants.BEARER_TYPE.length()).trim();
Jws<Claims> claims = parser.parseClaimsJws(token);
Context ctx = Context.current().withValue(Constants.CLIENT_ID_CONTEXT_KEY, claims.getBody().getSubject());
return Contexts.interceptCall(ctx, serverCall, metadata, serverCallHandler);
} catch (Exception e) {
status = Status.UNAUTHENTICATED.withDescription(e.getMessage()).withCause(e);
}
}
serverCall.close(status, metadata);
return new ServerCall.Listener<ReqT>() {
// noop
};
}
}
客户端发起调用是通过withCallCredentials(token)
方法带上身份认证信息,可以为单个调用设置,也可以全局设置.