业界 作者:SegmentFault 2023-03-27 18:10:23 阅读:843
今天小编为大家带来的是社区作者 江南一点雨 的文章,让我们一起来学习 gRPC 。



├── grpc_api
│ ├── pom.xml
│ └── src
├── grpc_client
│ ├── pom.xml
│ └── src
├── grpc_server
│ ├── pom.xml
│ └── src
└── pom.xml
<dependencies>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.52.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.52.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.52.1</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>annotations-api</artifactId>
<version>6.0.53</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.21.7:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.51.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
syntax = "proto3";
option java_multiple_files = true;
option java_package = "org.javaboy.grpc.api";
option java_outer_classname = "LoginProto";
import "google/protobuf/wrappers.proto";
package login;
service LoginService {
rpc login (LoginBody) returns (LoginResponse);
}
service HelloService{
rpc sayHello(google.protobuf.StringValue) returns (google.protobuf.StringValue);
}
message LoginBody {
string username = 1;
string password = 2;
}
message LoginResponse {
string token = 1;
}
public interface AuthConstant {
SecretKey JWT_KEY = Keys.hmacShaKeyFor("hello_javaboy_hello_javaboy_hello_javaboy_hello_javaboy_".getBytes());
Context.Key<String> AUTH_CLIENT_ID = Context.key("clientId");
String AUTH_HEADER = "Authorization";
String AUTH_TOKEN_TYPE = "Bearer";
}
public class LoginServiceImpl extends LoginServiceGrpc.LoginServiceImplBase {
@Override
public void login(LoginBody request, StreamObserver<LoginResponse> responseObserver) {
String username = request.getUsername();
String password = request.getPassword();
if ("javaboy".equals(username) && "123".equals(password)) {
System.out.println("login success");
//登录成功
String jwtToken = Jwts.builder().setSubject(username).signWith(AuthConstant.JWT_KEY).compact();
responseObserver.onNext(LoginResponse.newBuilder().setToken(jwtToken).build());
responseObserver.onCompleted();
}else{
System.out.println("login error");
//登录失败
responseObserver.onNext(LoginResponse.newBuilder().setToken("login error").build());
responseObserver.onCompleted();
}
}
}
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
@Override
public void sayHello(StringValue request, StreamObserver<StringValue> responseObserver) {
String clientId = AuthConstant.AUTH_CLIENT_ID.get();
responseObserver.onNext(StringValue.newBuilder().setValue(clientId + " say hello:" + request.getValue()).build());
responseObserver.onCompleted();
}
}
public class AuthInterceptor implements ServerInterceptor {
private JwtParser parser = Jwts.parser().setSigningKey(AuthConstant.JWT_KEY);
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata, ServerCallHandler<ReqT, RespT> serverCallHandler) {
String authorization = metadata.get(Metadata.Key.of(AuthConstant.AUTH_HEADER, Metadata.ASCII_STRING_MARSHALLER));
Status status = Status.OK;
if (authorization == null) {
status = Status.UNAUTHENTICATED.withDescription("miss authentication token");
} else if (!authorization.startsWith(AuthConstant.AUTH_TOKEN_TYPE)) {
status = Status.UNAUTHENTICATED.withDescription("unknown token type");
} else {
Jws<Claims> claims = null;
String token = authorization.substring(AuthConstant.AUTH_TOKEN_TYPE.length()).trim();
try {
claims = parser.parseClaimsJws(token);
} catch (JwtException e) {
status = Status.UNAUTHENTICATED.withDescription(e.getMessage()).withCause(e);
}
if (claims != null) {
Context ctx = Context.current()
.withValue(AuthConstant.AUTH_CLIENT_ID, claims.getBody().getSubject());
return Contexts.interceptCall(ctx, serverCall, metadata, serverCallHandler);
}
}
serverCall.close(status, new Metadata());
return new ServerCall.Listener<ReqT>() {
};
}
}
public class LoginServer {
Server server;
public static void main(String[] args) throws IOException, InterruptedException {
LoginServer server = new LoginServer();
server.start();
server.blockUntilShutdown();
}
public void start() throws IOException {
int port = 50051;
server = ServerBuilder.forPort(port)
.addService(new LoginServiceImpl())
.addService(ServerInterceptors.intercept(new HelloServiceImpl(), new AuthInterceptor()))
.build()
.start();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LoginServer.this.stop();
}));
}
private void stop() {
if (server != null) {
server.shutdown();
}
}
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
}
public class LoginClient {
public static void main(String[] args) throws InterruptedException {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
.usePlaintext()
.build();
LoginServiceGrpc.LoginServiceStub stub = LoginServiceGrpc.newStub(channel);
login(stub);
}
private static void login(LoginServiceGrpc.LoginServiceStub stub) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
stub.login(LoginBody.newBuilder().setUsername("javaboy").setPassword("123").build(), new StreamObserver<LoginResponse>() {
@Override
public void onNext(LoginResponse loginResponse) {
System.out.println("loginResponse.getToken() = " + loginResponse.getToken());
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onCompleted() {
countDownLatch.countDown();
}
});
countDownLatch.await();
}
}
public class JwtCredential extends CallCredentials {
private String subject;
public JwtCredential(String subject) {
this.subject = subject;
}
@Override
public void applyRequestMetadata(RequestInfo requestInfo, Executor executor, MetadataApplier metadataApplier) {
executor.execute(() -> {
try {
Metadata headers = new Metadata();
headers.put(Metadata.Key.of(AuthConstant.AUTH_HEADER, Metadata.ASCII_STRING_MARSHALLER),
String.format("%s %s", AuthConstant.AUTH_TOKEN_TYPE, subject));
metadataApplier.apply(headers);
} catch (Throwable e) {
metadataApplier.fail(Status.UNAUTHENTICATED.withCause(e));
}
});
}
@Override
public void thisUsesUnstableApi() {
}
}
public class LoginClient {
public static void main(String[] args) throws InterruptedException {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
.usePlaintext()
.build();
LoginServiceGrpc.LoginServiceStub stub = LoginServiceGrpc.newStub(channel);
sayHello(channel);
}
private static void sayHello(ManagedChannel channel) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
HelloServiceGrpc.HelloServiceStub helloServiceStub = HelloServiceGrpc.newStub(channel);
helloServiceStub
.withCallCredentials(new JwtCredential("eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJqYXZhYm95In0.IMMp7oh1dl_trUn7sn8qiv9GtO-COQyCGDz_Yy8VI4fIqUcRfwQddP45IoxNovxL"))
.sayHello(StringValue.newBuilder().setValue("wangwu").build(), new StreamObserver<StringValue>() {
@Override
public void onNext(StringValue stringValue) {
System.out.println("stringValue.getValue() = " + stringValue.getValue());
}
@Override
public void onError(Throwable throwable) {
System.out.println("throwable.getMessage() = " + throwable.getMessage());
}
@Override
public void onCompleted() {
countDownLatch.countDown();
}
});
countDownLatch.await();
}
}
上面的登录与校验只是松哥给小伙伴们展示的一个具体案例而已,在此案例基础之上,我们还可以扩展出来更多写法,但是万变不离其宗,其他玩法就需要小伙伴们自行探索啦~
点击左下角阅读原文,到 SegmentFault 思否社区 和文章作者展开更多互动和交流,“公众号后台“回复“ 入群 ”即可加入我们的技术交流群,收获更多的技术文章~
- END -

