• gRPC asp.net core自定义策略认证


    在GitHub上有个项目,本来是作为自己研究学习.net core的Demo,没想到很多同学在看,还给了很多星,所以觉得应该升成3.0,整理一下,写成博分享给学习.net core的同学们。

    项目名称:Asp.NetCoreExperiment

    项目地址:https://github.com/axzxs2001/Asp.NetCoreExperiment

    本案例Github代码库

    https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/GRPC

    关于gRPC参考https://grpc.io/

    在 gRPC asp.net core项目模板下引入自定义策略认证,代码如下

    创建共享proto

    创建.NET Standard库项目GRPCDemo01Entity

    安装NuGet包

    Google.Protobuf

    Grpc.Core

    Grpc.Tools 

    goods.proto代码如下

     1 syntax = "proto3";
     2 
     3 option csharp_namespace = "GRPCDemo01Entity";
     4 
     5 package Goods;
     6 
     7 service Goodser {
     8   //查询
     9   rpc GetGoods (QueryRequest) returns (QueryResponse);
    10   //登录
    11   rpc Login (LoginRequest) returns (LoginResponse);
    12 }
    13 //查询参数
    14 message QueryRequest {
    15   string name = 1;
    16 }
    17 //查询反回值
    18 message QueryResponse {
    19   string name = 1;
    20   int32 quantity=2;
    21 }
    22 //登录参数
    23 message LoginRequest{
    24     string username=1;
    25     string password=2;
    26 }
    27 //登录返回值
    28 message LoginResponse{
    29     bool result=1;
    30     string message=2;
    31     string token=3;
    32 }

    .csproj中配置

      <ItemGroup>
        <Protobuf Include="Protosgoods.proto" />
      </ItemGroup>

    创建GRPC asp.net core service

    安装NuGet包

    Grpc.AspNetCore

    Microsoft.AspNetCore.Authentication.JwtBearer

    添加引用 GRPCDemo01Entity项目

    设置配置文件appsettings.json

    {
      "Logging": {
        "LogLevel": {
          "Default": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      },
      "AllowedHosts": "*",
      "Kestrel": {
        "EndpointDefaults": {
          "Protocols": "Http2"
        }
      },
      "Audience": {
        "Secret": "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
        "Issuer": "gsw",
        "Audience": "everone"
      }
    }

    添加四个自定义策略认证相关文件

    Permission.cs

     1 namespace GRPCDemo01Service
     2 {
     3     /// <summary>
     4     /// 用户或角色或其他凭据实体
     5     /// </summary>
     6     public class Permission
     7     {
     8         /// <summary>
     9         /// 用户或角色或其他凭据名称
    10         /// </summary>
    11         public virtual string Name
    12         { get; set; }
    13         /// <summary>
    14         /// 请求Url
    15         /// </summary>
    16         public virtual string Url
    17         { get; set; }
    18     }
    19 }

    JwtToken.cs

     1 using System;
     2 using System.IdentityModel.Tokens.Jwt;
     3 using System.Security.Claims;
     4 
     5 namespace GRPCDemo01Service
     6 {
     7     public class JwtToken
     8     {
     9         /// <summary>
    10         /// 获取基于JWT的Token
    11         /// </summary>
    12         /// <param name="username"></param>
    13         /// <returns></returns>
    14         public static dynamic BuildJwtToken(Claim[] claims, PermissionRequirement permissionRequirement)
    15         {
    16             var now = DateTime.UtcNow;
    17             var jwt = new JwtSecurityToken(
    18                 issuer: permissionRequirement.Issuer,
    19                 audience: permissionRequirement.Audience,
    20                 claims: claims,
    21                 notBefore: now,
    22                 expires: now.Add(permissionRequirement.Expiration),
    23                 signingCredentials: permissionRequirement.SigningCredentials
    24             );
    25             var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
    26             var response = new
    27             {
    28                 Status = true,
    29                 access_token = encodedJwt,
    30                 expires_in = permissionRequirement.Expiration.TotalMilliseconds,
    31                 token_type = "Bearer"
    32             };
    33             return response;
    34         }
    35     }
    36 }

    PermissionHandler.cs

     1 using Microsoft.AspNetCore.Authentication;
     2 using Microsoft.AspNetCore.Authorization;
     3 using System.Linq;
     4 using System.Security.Claims;
     5 using System.Threading.Tasks;
     6 using System;
     7 using Microsoft.AspNetCore.Routing;
     8 
     9 namespace GRPCDemo01Service
    10 {
    11     /// <summary>
    12     /// 权限授权Handler
    13     /// </summary>
    14     public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
    15     {
    16 
    17         /// <summary>
    18         /// 验证方案提供对象
    19         /// </summary>
    20         public IAuthenticationSchemeProvider Schemes { get; set; }
    21 
    22         /// <summary>
    23         /// 构造
    24         /// </summary>
    25         /// <param name="schemes"></param>
    26         public PermissionHandler(IAuthenticationSchemeProvider schemes)
    27         {
    28             Schemes = schemes;
    29         }
    30         /// <summary>
    31         /// 验证每次请求
    32         /// </summary>
    33         /// <param name="context"></param>
    34         /// <param name="requirement"></param>
    35         /// <returns></returns>
    36         protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
    37         {
    38             if (context.Resource is RouteEndpoint route && route != null)
    39             {
    40                 var questUrl = route.RoutePattern.RawText?.ToLower();
    41                 if (requirement.Permissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)
    42                 {
    43                     var name = context.User.Claims.SingleOrDefault(s => s.Type == requirement.ClaimType)?.Value;
    44                     //验证权限
    45                     if (requirement.Permissions.Where(w => w.Name == name && w.Url.ToLower() == questUrl).Count() > 0)
    46                     {                        //判断过期时间
    47                         if (DateTime.Parse(context.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration).Value) >= DateTime.Now)
    48                         {
    49                             context.Succeed(requirement);
    50                             return Task.CompletedTask;
    51                         }
    52                     }
    53                 }
    54             }
    55             context.Fail();
    56             return Task.CompletedTask;
    57         }
    58     }
    59 }

    PermissionRequirement.cs

     1 using Microsoft.AspNetCore.Authorization;
     2 using Microsoft.IdentityModel.Tokens;
     3 using System;
     4 using System.Collections.Generic;
     5 
     6 namespace GRPCDemo01Service
     7 {
     8     /// <summary>
     9     /// 必要参数类
    10     /// </summary>
    11     public class PermissionRequirement : IAuthorizationRequirement
    12     {
    13         /// <summary>
    14         /// 用户权限集合
    15         /// </summary>
    16         public List<Permission> Permissions { get; private set; }
    17         /// <summary>
    18         /// 无权限action
    19         /// </summary>
    20         public string DeniedAction { get; set; }
    21 
    22         /// <summary>
    23         /// 认证授权类型
    24         /// </summary>
    25         public string ClaimType { internal get; set; }
    26         /// <summary>
    27         /// 请求路径
    28         /// </summary>
    29         public string LoginPath { get; set; } = "/Api/Login";
    30         /// <summary>
    31         /// 发行人
    32         /// </summary>
    33         public string Issuer { get; set; }
    34         /// <summary>
    35         /// 订阅人
    36         /// </summary>
    37         public string Audience { get; set; }
    38         /// <summary>
    39         /// 过期时间
    40         /// </summary>
    41         public TimeSpan Expiration { get; set; }
    42         /// <summary>
    43         /// 签名验证
    44         /// </summary>
    45         public SigningCredentials SigningCredentials { get; set; }
    46 
    47         /// <summary>
    48         /// 构造
    49         /// </summary>
    50         /// <param name="deniedAction">无权限action</param>
    51         /// <param name="userPermissions">用户权限集合</param>
    52 
    53         /// <summary>
    54         /// 构造
    55         /// </summary>
    56         /// <param name="deniedAction">拒约请求的url</param>
    57         /// <param name="permissions">权限集合</param>
    58         /// <param name="claimType">声明类型</param>
    59         /// <param name="issuer">发行人</param>
    60         /// <param name="audience">订阅人</param>
    61         /// <param name="signingCredentials">签名验证实体</param>
    62         public PermissionRequirement(string deniedAction, List<Permission> permissions, string claimType, string issuer, string audience, SigningCredentials signingCredentials, TimeSpan expiration)
    63         {
    64             ClaimType = claimType;
    65             DeniedAction = deniedAction;
    66             Permissions = permissions;
    67             Issuer = issuer;
    68             Audience = audience;
    69             Expiration = expiration;
    70             SigningCredentials = signingCredentials;
    71         }
    72     }
    73 }

    设置Startup.cs

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Security.Claims;
      4 using System.Text;
      5 using Microsoft.AspNetCore.Authentication.JwtBearer;
      6 using Microsoft.AspNetCore.Authorization;
      7 using Microsoft.AspNetCore.Builder;
      8 using Microsoft.AspNetCore.Hosting;
      9 using Microsoft.AspNetCore.Http;
     10 using Microsoft.Extensions.Configuration;
     11 using Microsoft.Extensions.DependencyInjection;
     12 using Microsoft.Extensions.Hosting;
     13 using Microsoft.IdentityModel.Tokens;
     14 
     15 namespace GRPCDemo01Service
     16 {
     17     public class Startup
     18     {
     19         public Startup(IConfiguration configuration)
     20         {
     21             Configuration = configuration;
     22         }
     23         public IConfiguration Configuration { get; }
     24         public void ConfigureServices(IServiceCollection services)
     25         {
     26             //读取配置文件
     27             var audienceConfig = Configuration.GetSection("Audience");
     28             var symmetricKeyAsBase64 = audienceConfig["Secret"];
     29             var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
     30             var signingKey = new SymmetricSecurityKey(keyByteArray);
     31             var tokenValidationParameters = new TokenValidationParameters
     32             {
     33                 ValidateIssuerSigningKey = true,
     34                 IssuerSigningKey = signingKey,
     35                 ValidateIssuer = true,
     36                 ValidIssuer = audienceConfig["Issuer"],//发行人
     37                 ValidateAudience = true,
     38                 ValidAudience = audienceConfig["Audience"],//订阅人
     39                 ValidateLifetime = true,
     40                 ClockSkew = TimeSpan.Zero,
     41                 RequireExpirationTime = true,
     42             };
     43             var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
     44             //这个集合模拟用户权限表,可从数据库中查询出来
     45             var permission = new List<Permission> {
     46                               new Permission {  Url="/Goods.Goodser/GetGoods", Name="admin"},
     47                               new Permission {  Url="systemapi", Name="system"}
     48                           };
     49             //如果第三个参数,是ClaimTypes.Role,上面集合的每个元素的Name为角色名称,如果ClaimTypes.Name,即上面集合的每个元素的Name为用户名
     50             var permissionRequirement = new PermissionRequirement(
     51                 "/api/denied", permission,
     52                 ClaimTypes.Role,
     53                 audienceConfig["Issuer"],
     54                 audienceConfig["Audience"],
     55                 signingCredentials,
     56                 expiration: TimeSpan.FromSeconds(10000)//设置Token过期时间
     57                 );
     58 
     59             services.AddAuthorization(options =>
     60             {
     61                 options.AddPolicy("Permission", policy => policy.AddRequirements(permissionRequirement));
     62             }).
     63             AddAuthentication(options =>
     64             {
     65                 options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
     66                 options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
     67             })
     68             .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o =>
     69             {
     70                 //不使用https
     71                 o.RequireHttpsMetadata = true;
     72                 o.TokenValidationParameters = tokenValidationParameters;           
     73             });
     74             //注入授权Handler
     75             services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
     76             services.AddSingleton(permissionRequirement);
     77             services.AddGrpc();
     78         }
     79 
     80    
     81         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
     82         {
     83             if (env.IsDevelopment())
     84             {
     85                 app.UseDeveloperExceptionPage();
     86             }
     87 
     88             app.UseRouting();
     89             app.UseAuthentication();
     90             app.UseAuthorization();
     91             app.UseEndpoints(endpoints =>
     92             {
     93                 endpoints.MapGrpcService<GoodsService>();
     94 
     95                 endpoints.MapGet("/", async context =>
     96                 {
     97                     await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
     98                 });
     99             });
    100         }
    101     }
    102 }

    GoodsService.cs

     1 using System;
     2 using System.Security.Claims;
     3 using System.Threading.Tasks;
     4 using Grpc.Core;
     5 using GRPCDemo01Entity;
     6 using Microsoft.AspNetCore.Authorization;
     7 using Microsoft.Extensions.Logging;
     8 
     9 namespace GRPCDemo01Service
    10 {
    11     [Authorize("Permission")]
    12     public class GoodsService : Goodser.GoodserBase
    13     {
    14         private readonly ILogger<GoodsService> _logger;
    15         readonly PermissionRequirement _requirement;
    16         public GoodsService(ILogger<GoodsService> logger, PermissionRequirement requirement)
    17         {
    18             _requirement = requirement;
    19             _logger = logger;
    20         }
    21         public override Task<QueryResponse> GetGoods(QueryRequest request, ServerCallContext context)
    22         {
    23             return Task.FromResult(new QueryResponse
    24             {
    25                 Name = "Hello " + request.Name,
    26                 Quantity = 10
    27             });
    28         }
    29         [AllowAnonymous]
    30         public override Task<LoginResponse> Login(LoginRequest user, ServerCallContext context)
    31         {
    32             //todo 查询数据库核对用户名密码
    33             var isValidated = user.Username == "gsw" && user.Password == "111111";
    34             if (!isValidated)
    35             {
    36                 return Task.FromResult(new LoginResponse()
    37                 {
    38                     Message = "认证失败"
    39                 });
    40             }
    41             else
    42             {
    43                 //如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
    44                 var claims = new Claim[] {
    45                     new Claim(ClaimTypes.Name, user.Username),
    46                     new Claim(ClaimTypes.Role, "admin"),
    47                     new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString())
    48                 };
    49 
    50                 var token = JwtToken.BuildJwtToken(claims, _requirement);
    51                 return Task.FromResult(new LoginResponse()
    52                 {
    53                     Result = true,
    54                     Token = token.access_token
    55                 });
    56 
    57             }
    58         }
    59     }
    60 }

    控制台程序调用gRPC

    添加引用 GRPCDemo01Entity项目

    安装NuGet包

    Grpc.Net.Client 

    Program.cs

     1 using Grpc.Core;
     2 using Grpc.Net.Client;
     3 using GRPCDemo01Entity;
     4 using System;
     5 using System.Threading.Tasks;
     6 
     7 namespace GRPCDemo01Test
     8 {
     9     class Program
    10     {
    11         static async Task Main(string[] args)
    12         {
    13             while (true)
    14             {
    15                 Console.WriteLine("用户名:");
    16                 var username = Console.ReadLine();
    17                 Console.WriteLine("密码:");
    18                 var password = Console.ReadLine();
    19                 var tokenResponse = await Login(username, password);
    20                 if (tokenResponse.Result)
    21                 {
    22                     await Query(tokenResponse.Token);
    23                 }
    24                 else
    25                 {
    26                     Console.WriteLine("登录失败");
    27                 }
    28             }
    29         }
    30         /// <summary>
    31         /// 查询
    32         /// </summary>
    33         /// <param name="token">token</param>
    34         /// <returns></returns>
    35         static async Task Query(string token)
    36         {
    37             token = $"Bearer {token }";
    38             var headers = new Metadata { { "Authorization", token } };
    39             var channel = GrpcChannel.ForAddress("https://localhost:5001");
    40             var client = new Goodser.GoodserClient(channel);
    41             var query = await client.GetGoodsAsync(
    42                               new QueryRequest { Name = "桂素伟" }, headers);
    43             Console.WriteLine($"返回值  Name:{ query.Name},Quantity:{ query.Quantity}");
    44         }
    45         /// <summary>
    46         /// 登录
    47         /// </summary>
    48         /// <param name="userName">userName</param>
    49         /// <param name="password">password</param>
    50         /// <returns></returns>
    51         static async Task<LoginResponse> Login(string userName, string password)
    52         {
    53             var channel = GrpcChannel.ForAddress("https://localhost:5001");
    54             var client = new Goodser.GoodserClient(channel);
    55             var response = await client.LoginAsync(
    56                               new LoginRequest() { Username = userName, Password = password });
    57             return response;
    58         }
    59     }
    60 }

     

    webapi调用gRPC

    添加引用 GRPCDemo01Entity项目

    安装NuGet包

    Grpc.Net.ClientFactory

    Starup的ConfigureServices添加如下代码

    1 //添加Grpc客户端
    2  services.AddGrpcClient<Goodser.GoodserClient>(o =>
    3  {
    4        o.Address = new Uri("https://localhost:5001");
    5  });

    调用gRPC

     1 using System.Threading.Tasks;
     2 using Grpc.Core;
     3 using GRPCDemo01Entity;
     4 using Microsoft.AspNetCore.Mvc;
     5 using Microsoft.Extensions.Logging;
     6 
     7 namespace GRPCDemo01WebTest.Controllers
     8 {
     9     [ApiController]
    10     [Route("[controller]")]
    11     public class WeatherForecastController : ControllerBase
    12     {
    13         private readonly ILogger<WeatherForecastController> _logger;
    14         /// <summary>
    15         /// 客户端
    16         /// </summary>
    17         private readonly Goodser.GoodserClient _client;
    18         public WeatherForecastController(ILogger<WeatherForecastController> logger, Goodser.GoodserClient client)
    19         {
    20             _client = client;
    21             _logger = logger;
    22         }
    23 
    24         [HttpGet]
    25         public async Task<string> Get()
    26         {
    27             //登录
    28             var tokenResponse = await _client.LoginAsync(new LoginRequest { Username = "gsw", Password = "111111" });
    29             var token = $"Bearer {tokenResponse.Token }";
    30             var headers = new Metadata { { "Authorization", token } };
    31             var request = new QueryRequest { Name = "桂素伟" };
    32             //查询
    33             var query = await _client.GetGoodsAsync(request, headers);
    34             return $"Name:{query.Name},Quantity:{query.Quantity}";
    35         }
    36     }
    37 }

     

    gRPC调用gRPC

    添加引用 GRPCDemo01Entity项目

    Starup的ConfigureServices添加如下代码

    1 //添加Grpc客户端
    2  services.AddGrpcClient<Goodser.GoodserClient>(o =>
    3  {
    4        o.Address = new Uri("https://localhost:5001");
    5  });

    调用gRPC

     1 using System;
     2 using System.Threading.Tasks;
     3 using Grpc.Core;
     4 using GRPCDemo01Entity;
     5 using Microsoft.Extensions.Logging;
     6 
     7 namespace GRPCDemo01GRPCTest
     8 {
     9     public class OrderService : Orderer.OrdererBase
    10     {
    11         private readonly ILogger<OrderService> _logger;
    12         private readonly Goodser.GoodserClient _client;
    13         public OrderService(ILogger<OrderService> logger, Goodser.GoodserClient client)
    14         {
    15             _client = client;
    16             _logger = logger;
    17         } 
    18         public override async Task<OrderResponse> GetGoods(OrderRequest request, ServerCallContext context)
    19         {
    20             //登录
    21             var tokenResponse = await _client.LoginAsync(
    22                              new LoginRequest() {
    23                                  Username = "gsw",
    24                                  Password = "111111" 
    25                              });
    26             if (tokenResponse.Result)
    27             {
    28                 var token = $"Bearer {tokenResponse.Token }";
    29                 var headers = new Metadata { { "Authorization", token } };
    30                 //查询
    31                 var query = await _client.GetGoodsAsync(
    32                                   new QueryRequest { Name = "桂素伟" }, headers);
    33                 Console.WriteLine($"返回值  Name:{ query.Name},Quantity:{ query.Quantity}");
    34                 return new OrderResponse { Name = query.Name, Quantity = query.Quantity };
    35             }
    36             else
    37             {
    38                 Console.WriteLine("登录失败");
    39                 return null;
    40             }
    41         }
    42     }
    43 }
  • 相关阅读:
    perl6中函数参数(2)
    perl6中函数参数(1)
    上传绕过(转载)
    perl6中的hash定义(1)
    mssql手工注入2
    mssql手工注入1
    mssql注入中的储存用法删除与恢复
    perl 复制exe文件的简单方法
    python shell
    perl中设置POST登录时的重定向
  • 原文地址:https://www.cnblogs.com/axzxs2001/p/11921424.html
Copyright © 2020-2023  润新知