• asp.ner core 5.0 Grpc HttpApi 和jwt的集成 和跨域【https双向认证】


    书接上文 Go Grpc Jwt身份认证和Gateway集成以及HTTPS双向认证, 那么它在asp.net core 里面怎么实现了, 前面asp.ner core 5.0 Grpc双向认证 和 restful api包装 外加swagger启用【VSCode创建】已经完成了大部分, 我们只要引入jwt 和跨域就了, 网上很多文章 都是用IdentityServer4【个人实际生产中没有使用过, 感觉比较中】。本文保持和go一至的风格,增加一个login方法

    1.修改grpcserverProtosgreet.proto 文件如下【客户端的文件不需要option部分】:

    syntax = "proto3";
     
    import "google/api/annotations.proto";
    option csharp_namespace = "grpcserver";
     
    package greet;
     
    // The greeting service definition.
    service Greeter {
      rpc Login (LoginRequest) returns (LoginReply) {
        option (google.api.http) = {
          post: "/v1/greeter/login"
          body: "*"
      };
     
      }
      rpc SayHello (HelloRequest) returns (HelloReply) {
        option (google.api.http) = {
          get: "/v1/greeter/{name}"
        };
      }
    }
     
    // The request message containing the user's name.
    message HelloRequest {
      string name = 1;
    }
     
    // The response message containing the greetings.
    message HelloReply {
      string message = 1;
    }
     
    message LoginRequest{
      string username=1;
      string password=2;
    }
    message LoginReply{
      string status=1;
      string token=2;
    }

    客户端grpcclientProtosgreet.proto 如下:

    syntax = "proto3";
     
    option csharp_namespace = "grpcserver";
     
    package greet;
     
    // The greeting service definition.
    service Greeter {
        rpc Login (LoginRequest) returns (LoginReply);
      rpc SayHello (HelloRequest) returns (HelloReply);
    }
     
    // The request message containing the user's name.
    message HelloRequest {
      string name = 1;
    }
     
    // The response message containing the greetings.
    message HelloReply {
      string message = 1;
    }
     
    message LoginRequest{
      string username=1;
      string password=2;
    }
    message LoginReply{
      string status=1;
      string token=2;
    }

    2.要使用jwt 需要增加相应的包Microsoft.AspNetCore.Authentication.JwtBearer,和go相同, 我们同样创建一个 创建token 和通过token 获取用户名的方法, 整个grpcserverServicesGreeterService.cs如下:

    using System;
    using System.Collections.Generic;
    using System.IdentityModel.Tokens.Jwt;
    using System.Linq;
    using System.Security.Claims;
    using System.Text;
    using System.Threading.Tasks;
    using Grpc.Core;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.Extensions.Logging;
    using Microsoft.IdentityModel.Tokens;
     
    namespace grpcserver
    {
        public class GreeterService : Greeter.GreeterBase
        {
            private readonly ILogger<GreeterService> _logger;
            const string tokenSchema = "Bearer";
            const string tokenIssuer = "https://localhost:5001";
            const string tokenAudience = "grpc";
            const string tokenSecurityKey = "asp.netcore5.0grpcjwt";
            public GreeterService(ILogger<GreeterService> logger)
            {
                _logger = logger;
            }
            public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
            {
               var userName= CheckAuth(context)??string.Empty;
                return Task.FromResult(new HelloReply
                {
                    Message = $"Hello {request.Name } Request by:{userName}"
                });
            }
     
            public override Task<LoginReply> Login(LoginRequest request, ServerCallContext context)
            {
                LoginReply reply = new LoginReply { Status = "401", Token = "�û�������������Ч" };
               
                if (request.Username == "gavin" && request.Password == "gavin")
                {
                    reply.Token = CreateToken(request.Username);
                    reply.Status = "200";
                }
                Console.WriteLine($"call login: username:{request.Username} ,password:{request.Password}, return token:{reply.Token}");
                return Task.FromResult(reply);
            }
     
            string CreateToken(string userName) {          
                var claim = new Claim[] {
                        new Claim(ClaimTypes.Name,userName)
                    };
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenSecurityKey));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                var token = new JwtSecurityToken(tokenIssuer, tokenAudience, claim, DateTime.Now,  DateTime.Now.AddMinutes(60),creds);
                var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
                return tokenStr;
            }
            string CheckAuth(ServerCallContext context) {
              string tokenStr=  context.GetHttpContext().Request?.Headers["Authorization"];
                if (string.IsNullOrEmpty(tokenStr)) {
                    return string.Empty;
                }
                if (tokenStr.StartsWith(tokenSchema)) {
                    tokenStr = tokenStr.Split(' ')[1];
                }
                SecurityToken token;
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenSecurityKey));
                ClaimsPrincipal claims= new JwtSecurityTokenHandler().ValidateToken(tokenStr, new TokenValidationParameters {
                    ValidAudience=tokenAudience,
                    ValidIssuer=tokenIssuer ,
                    IssuerSigningKey=key
     
                }, out token);
                var userName = claims.FindFirstValue(ClaimTypes.Name);
                return userName;
            }
        }
     
    }

    3.修改客户端的调用如下【由于客户端 这次调用的是https 所以grpcserverProgram.cs的方法CreateHostBuilder 在监听5001的时候 需要这只 协议  listenOptions.Protocols = HttpProtocols.Http1AndHttp2;】:

    using System;
    using System.Net.Http;
    using System.Threading.Tasks;
    using grpcserver;
    using Grpc.Net.Client;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Authentication;
    using Grpc.Core;
    using Newtonsoft.Json;
     
    namespace grpcclient
    {
        class Program
        {
            const string url = "https://localhost:5001";
            const string tokenSchema = "Bearer";
            static void Main(string[] args)
            {
                GrpcCall();
                Console.WriteLine("http start................");
                ///
                HttpCall();
            }
            static void GrpcCall() {
                var channel = GrpcChannel.ForAddress(url, new GrpcChannelOptions { HttpHandler = GetHttpHandler() });
                var client = new Greeter.GreeterClient(channel);
                var loginReplay = client.Login(new LoginRequest {  Username="gavin", Password="gavin"});
                string token = loginReplay.Token;
                //Console.WriteLine("get token:" + token);
             
                var headers = new Metadata();
                headers.Add("Authorization", $"{tokenSchema} {token}");
                var reply = client.SayHello(new HelloRequest { Name = "GreeterClient" },headers);
                Console.WriteLine("SayHello 1: " + reply.Message);
                ///
     
                client = new Greeter.GreeterClient(GetChannel(url, token));
                reply = client.SayHello(new HelloRequest { Name = "GreeterClient2" });
                Console.WriteLine("SayHello 2: " + reply.Message);
            }
            static void HttpCall() {
                var httpclient = new HttpClient(GetHttpHandler());
                var loginRequest = "{"username":"gavin","password":"gavin"}";
                HttpContent content = new StringContent(loginRequest);
                content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
                var response=  httpclient.PostAsync(url + "/v1/greeter/login", content).Result;
                response.EnsureSuccessStatusCode();//用来抛异常的
                var loginResponseStr=  response.Content.ReadAsStringAsync().Result;
                var token=  JsonConvert.DeserializeObject<LoginReply>(loginResponseStr).Token;
                //
                HttpRequestMessage request = new HttpRequestMessage();
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(tokenSchema, token);
                request.RequestUri = new Uri(url + "/v1/greeter/gavin");
     
             
                var helloResponse = httpclient.Send(request);
                var helloResponseStr = helloResponse.Content.ReadAsStringAsync().Result;
               Console.WriteLine(helloResponseStr);
            }
            static HttpClientHandler GetHttpHandler() {
                var handler = new HttpClientHandler()
                {
                    SslProtocols = SslProtocols.Tls12,
                    ClientCertificateOptions = ClientCertificateOption.Manual,
                    ServerCertificateCustomValidationCallback = (message, cer, chain, errors) =>
                    {
                        return chain.Build(cer);
                    }
                };
                var path = AppDomain.CurrentDomain.BaseDirectory + "cert\client.pfx";
                var crt = new X509Certificate2(path, "123456789");
                handler.ClientCertificates.Add(crt);
                return handler;
            }
     
          static  GrpcChannel GetChannel(string address, string token) {
                var credentials = CallCredentials.FromInterceptor((context, metadata) =>
                {
                    if (!string.IsNullOrEmpty(token))
                    {
                        metadata.Add("Authorization", $"{tokenSchema} {token}"); 
                    }
                    return Task.CompletedTask;
                });
                
                var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
                {
                    Credentials = ChannelCredentials.Create(new SslCredentials(), credentials),
                    HttpHandler=GetHttpHandler()
                });
                return channel;
            }
        }
    }

    4.为了保证跨域请求, 我们在grpcserverStartup.cs的ConfigureServices方法 添加 如下:

     services.AddCors(o => o.AddPolicy("AllowAll", builder =>
                {
                    builder.AllowAnyOrigin()
                           .AllowAnyMethod()
                           .AllowAnyHeader()
                           .WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
                }));

    在Configure 方法调用  app.UseCors("AllowAll");

    5.运行结果:

    D:UsersgavinDocumentsDotNetCoreSampleasp.netgrpccertgrpcclient>dotnet run
    SayHello 1: Hello GreeterClient Request by:gavin
    SayHello 2: Hello GreeterClient2 Request by:gavin
    http start................
    { "message": "Hello gavin Request by:gavin" }

     下载地址:

    https://github.com/dz45693/asp.netgrpccert.git

  • 相关阅读:
    Codevs1684 垃圾陷阱
    Codevs1540银河英雄传说[并查集]
    Poj1182食物链[并查集]
    树的顺序遍历的应用
    树的顺序遍历
    ARTS打卡
    定位iOS代码中崩溃的位置
    leetcode 24
    leetcode 24
    Drafter简单介绍
  • 原文地址:https://www.cnblogs.com/majiang/p/14240807.html
Copyright © 2020-2023  润新知