func StreamServerInterceptor(authFunc AuthFunc) grpc.StreamServerInterceptor
func UnaryServerInterceptor(authFunc AuthFunc) grpc.UnaryServerInterceptor
type AuthFunc
type AuthFunc func(ctx context.Context) (context.Context, error) 验证方法
客户端请求添加bearer token
实现和上篇的自定义认证方法大同小异。gRPC 中默认定义了 PerRPCCredentials
,是提供用于自定义认证的接口,它的作用是将所需的安全认证信息添加到每个RPC方法的上下文中。其包含 2 个方法:
GetRequestMetadata
:获取当前请求认证所需的元数据RequireTransportSecurity
:是否需要基于 TLS 认证进行安全传输
接下来我们实现这两个方法
// Token token认证
type Token struct {
Value string
}
//这里要以authorization为头部,和服务端对应
const headerAuthorize string = "authorization"
// GetRequestMetadata 获取当前请求认证所需的元数据
func (t *Token) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{headerAuthorize: t.Value}, nil
}
// RequireTransportSecurity 是否需要基于 TLS 认证进行安全传输
func (t *Token) RequireTransportSecurity() bool {
return true
}
发送请求时添加token,注意:Token中的Value的形式要以bearer token值
形式。因为我们服务端使用了bearer token
验证方式。
//从输入的证书文件中为客户端构造TLS凭证
creds, err := credentials.NewClientTLSFromFile("../tls/server.pem", "go-grpc-example")
if err != nil {
log.Fatalf("Failed to create TLS credentials %v", err)
}
//构建Token
token := auth.Token{
Value: "bearer grpc.auth.token",
}
// 连接服务器
conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(&token))
新建grpc_auth服务端拦截器
// TokenInfo 用户信息
type TokenInfo struct {
ID string
Roles []string
}
// AuthInterceptor 认证拦截器,对以authorization为头部,形式为`bearer token`的Token进行验证
func AuthInterceptor(ctx context.Context) (context.Context, error) {
token, err := grpc_auth.AuthFromMD(ctx, "bearer")
if err != nil {
return nil, err
}
tokenInfo, err := parseToken(token)
if err != nil {
return nil, grpc.Errorf(codes.Unauthenticated, " %v", err)
}
//使用context.WithValue添加了值后,可以用Value(key)方法获取值
newCtx := context.WithValue(ctx, tokenInfo.ID, tokenInfo)
//log.Println(newCtx.Value(tokenInfo.ID))
return newCtx, nil
}
//解析token,并进行验证
func parseToken(token string) (TokenInfo, error) {
var tokenInfo TokenInfo
if token == "grpc.auth.token" {
tokenInfo.ID = "1"
tokenInfo.Roles = []string{"admin"}
return tokenInfo, nil
}
return tokenInfo, errors.New("Token无效: bearer " + token)
}
//从token中获取用户唯一标识
func userClaimFromToken(tokenInfo TokenInfo) string {
return tokenInfo.ID
}
GetRequestMetadata
:获取当前请求认证所需的元数据RequireTransportSecurity
:是否需要基于 TLS 认证进行安全传输
接下来我们实现这两个方法
// Token token认证
type Token struct {
Value string
}
//这里要以authorization为头部,和服务端对应
const headerAuthorize string = "authorization"
// GetRequestMetadata 获取当前请求认证所需的元数据
func (t *Token) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{headerAuthorize: t.Value}, nil
}
// RequireTransportSecurity 是否需要基于 TLS 认证进行安全传输
func (t *Token) RequireTransportSecurity() bool {
return true
}
发送请求时添加token,注意:Token中的Value的形式要以bearer token值
形式。因为我们服务端使用了bearer token
验证方式。
//从输入的证书文件中为客户端构造TLS凭证
creds, err := credentials.NewClientTLSFromFile("../tls/server.pem", "go-grpc-example")
if err != nil {
log.Fatalf("Failed to create TLS credentials %v", err)
}
//构建Token
token := auth.Token{
Value: "bearer grpc.auth.token",
}
// 连接服务器
conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(&token))
把grpc_auth拦截器添加到服务端
grpcServer := grpc.NewServer(cred.TLSInterceptor(),
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
grpc_auth.StreamServerInterceptor(auth.AuthInterceptor),
grpc_zap.StreamServerInterceptor(zap.ZapInterceptor()),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc_auth.UnaryServerInterceptor(auth.AuthInterceptor),
grpc_zap.UnaryServerInterceptor(zap.ZapInterceptor()),
)),
)
例子:
client
package main
import (
"context"
"fmt"
"google.golang.org/grpc/credentials"
"time"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
pb "test/grpcLB/protos"
)
// Token token认证
type Token struct {
Value string
}
const headerAuthorize string = "authorization"
// GetRequestMetadata 获取当前请求认证所需的元数据
func (t *Token) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{headerAuthorize: t.Value}, nil
}
// RequireTransportSecurity 是否需要基于 TLS 认证进行安全传输
func (t *Token) RequireTransportSecurity() bool {
return true
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
//TLS连接
creds, err := credentials.NewClientTLSFromFile("./keys/server.pem", "go-grpc-example")
if err != nil {
panic(err.Error())
}
//构建Token
token := Token{
Value: "bearer grpc.auth.toke",
}
grpc.WithInsecure()
// authority是自己随便起的,不是必须的,但是r.Scheme()+"://authority/"+*svc这种格式是必须的
conn, err := grpc.DialContext(ctx, "localhost:50000", grpc.WithTransportCredentials(creds),grpc.WithPerRPCCredentials(&token))
if err != nil {
panic(err)
}
client := pb.NewGreeterClient(conn)
resp, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "world "})
if err == nil {
logrus.Infof("Reply is %s\n", resp.Message)
}else {
fmt.Println(err)
}
}
service
package main
import (
"context"
"fmt"
"github.com/grpc-ecosystem/go-grpc-middleware"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/status"
"net"
"time"
"google.golang.org/grpc"
"github.com/sirupsen/logrus"
"github.com/grpc-ecosystem/go-grpc-middleware/auth"
pb "test/grpcLB/protos"
"errors"
)
const DialTimeout = time.Second*5
// TokenInfo 用户信息
type TokenInfo struct {
ID string
Roles []string
}
// AuthInterceptor 认证拦截器,对以authorization为头部,形式为`bearer token`的Token进行验证
// 返回值 context.Context 会传到具体的service
func AuthInterceptor(ctx context.Context) (context.Context, error) {
token, err := grpc_auth.AuthFromMD(ctx, "bearer")
if err != nil {
return ctx, err
}
tokenInfo, err := parseToken(token)
if err != nil {
return ctx, status.Errorf(codes.Unauthenticated, " %v", err)
}
//使用context.WithValue添加了值后,可以用Value(key)方法获取值
newCtx := context.WithValue(ctx, "id",tokenInfo.ID)
newCtx = context.WithValue(newCtx, "role",tokenInfo.Roles)
//log.Println(newCtx.Value(tokenInfo.ID))
return newCtx, nil
}
//解析token,并进行验证
func parseToken(token string) (TokenInfo, error) {
var tokenInfo TokenInfo
if token == "grpc.auth.token" {
tokenInfo.ID = "1"
tokenInfo.Roles = []string{"admin"}
return tokenInfo, nil
}
return tokenInfo, errors.New("Token无效: bearer " + token)
}
//从token中获取用户唯一标识
func userClaimFromToken(tokenInfo TokenInfo) string {
return tokenInfo.ID
}
func main() {
// 端口监听
lis, err := net.Listen("tcp", ":50000")
if err != nil {
panic(err)
}
defer lis.Close()
//TLS认证
creds, err := credentials.NewServerTLSFromFile("./keys/server.pem", "./keys/server.key")
if err != nil {
grpclog.Fatal("加载在证书文件失败", err)
}
s := grpc.NewServer(grpc.Creds(creds),grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
grpc_auth.StreamServerInterceptor(AuthInterceptor),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc_auth.UnaryServerInterceptor(AuthInterceptor),
)),)
pb.RegisterGreeterServer(s, &server{})
s.Serve(lis)
}
// server is used to implement helloworld.GreeterServer.
type server struct{}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
fmt.Println(ctx.Value("id"))
fmt.Println(ctx.Value("role"))
logrus.Infof("%v: Receive is %s\n", time.Now(), in.Name)
return &pb.HelloReply{Message: "Hello " + in.Name }, nil
}
成功:
失败:
client不携带token,或token值给个错的