书接上文 Go Grpc Jwt身份认证 ,本文我们尝试把gateway也加进来,有关gatewa大家可以参考 go学习笔记 grpc-gateway和swagger。直接开干吧
Grpc Jwt GateWay的集成【包含跨域问题的解决】
1.修改api/api.proto文件
syntax = "proto3";
package api;
// 1 导入 gateway 相关的proto 以及 swagger 相关的 proto
import "google/api/annotations.proto";
import "protoc-gen-swagger/options/annotations.proto";
// 2 定义 swagger 相关的内容
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
info: {
title: "grpc gateway sample";
version: "1.0";
license: {
name: "MIT";
};
};
schemes: HTTP;
consumes: "application/json";
produces: "application/json";
};
service Ping {
rpc Login (LoginRequest) returns (LoginReply) {
option (google.api.http) = {
post: "/login"
body: "*"
};
}
rpc SayHello(PingMessage) returns (PingMessage) {
option (google.api.http) = {
post: "/sayhello"
body: "*"
};
}
}
message LoginRequest{
string username=1;
string password=2;
}
message LoginReply{
string status=1;
string token=2;
}
message PingMessage {
string greeting = 1;
}
2.编译api/api.proto
protoc -ID:\Go\include -I. --go_out=plugins=grpc:. ./api/api.proto
protoc -ID:\Go\include -I. --grpc-gateway_out=logtostderr=true:. ./api/api.proto
3. 这次我们吧server 和client 分开, 分成两个文件夹,上文中获取token 用的是metadata.FromIncomingContext(ctx)方法, 这次我们该用metautils.ExtractIncoming(ctx).Get(headerAuthorize)方法比较简单。修改后的的authtoken.go 如下:
package api
import (
"context"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
)
var (
headerAuthorize = "authorization"
)
func CreateToken(userName string) (tokenString string) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"iss": "lora-app-server",
"aud": "lora-app-server",
"nbf": time.Now().Unix(),
"exp": time.Now().Add(time.Hour).Unix(),
"sub": "user",
"username": userName,
})
tokenString, err := token.SignedString([]byte("verysecret"))
if err != nil {
panic(err)
}
return tokenString
}
// AuthToekn 自定义认证
type AuthToekn struct {
Token string
}
func (c AuthToekn) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
headerAuthorize: c.Token,
}, nil
}
func (c AuthToekn) RequireTransportSecurity() bool {
return false
}
// Claims defines the struct containing the token claims.
type Claims struct {
jwt.StandardClaims
// Username defines the identity of the user.
Username string `json:"username"`
}
// Step1. 从 context 的 metadata 中,取出 token
func getTokenFromContext(ctx context.Context) string {
val := metautils.ExtractIncoming(ctx).Get(headerAuthorize)
return val
}
func CheckAuth(ctx context.Context) (username string) {
tokenStr := getTokenFromContext(ctx)
if len(tokenStr) == 0 {
panic("get token from context error")
}
var clientClaims Claims
token, err := jwt.ParseWithClaims(tokenStr, &clientClaims, func(token *jwt.Token) (interface{}, error) {
if token.Header["alg"] != "HS256" {
panic("ErrInvalidAlgorithm")
}
return []byte("verysecret"), nil
})
if err != nil {
panic("jwt parse error")
}
if !token.Valid {
panic("ErrInvalidToken")
}
return clientClaims.Username
}
4.server的main.go 我们增加了跨域请求的设置,同时也罢 grpc server 和http 的server整合在一起【原理很简单 就是整合一个handler 监听一个端口, 判断进来的是grpc 还是json,grpc交由grpc 服务处理】,server/main.go代码如下:
package main
import (
"context"
"fmt"
"log"
"net/http"
"strings"
"jwtdemo/api"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"google.golang.org/grpc"
)
const