在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代码如下

 syntax = "proto3";

 option csharp_namespace = "GRPCDemo01Entity";

 package Goods;

 service Goodser {
//查询
rpc GetGoods (QueryRequest) returns (QueryResponse);
//登录
rpc Login (LoginRequest) returns (LoginResponse);
}
//查询参数
message QueryRequest {
string name = ;
}
//查询反回值
message QueryResponse {
string name = ;
int32 quantity=;
}
//登录参数
message LoginRequest{
string username=;
string password=;
}
//登录返回值
message LoginResponse{
bool result=;
string message=;
string token=;
}

.csproj中配置

  <ItemGroup>
<Protobuf Include="Protos\goods.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

 namespace GRPCDemo01Service
{
/// <summary>
/// 用户或角色或其他凭据实体
/// </summary>
public class Permission
{
/// <summary>
/// 用户或角色或其他凭据名称
/// </summary>
public virtual string Name
{ get; set; }
/// <summary>
/// 请求Url
/// </summary>
public virtual string Url
{ get; set; }
}
}

JwtToken.cs

 using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; namespace GRPCDemo01Service
{
public class JwtToken
{
/// <summary>
/// 获取基于JWT的Token
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
public static dynamic BuildJwtToken(Claim[] claims, PermissionRequirement permissionRequirement)
{
var now = DateTime.UtcNow;
var jwt = new JwtSecurityToken(
issuer: permissionRequirement.Issuer,
audience: permissionRequirement.Audience,
claims: claims,
notBefore: now,
expires: now.Add(permissionRequirement.Expiration),
signingCredentials: permissionRequirement.SigningCredentials
);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new
{
Status = true,
access_token = encodedJwt,
expires_in = permissionRequirement.Expiration.TotalMilliseconds,
token_type = "Bearer"
};
return response;
}
}
}

PermissionHandler.cs

 using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System;
using Microsoft.AspNetCore.Routing; namespace GRPCDemo01Service
{
/// <summary>
/// 权限授权Handler
/// </summary>
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{ /// <summary>
/// 验证方案提供对象
/// </summary>
public IAuthenticationSchemeProvider Schemes { get; set; } /// <summary>
/// 构造
/// </summary>
/// <param name="schemes"></param>
public PermissionHandler(IAuthenticationSchemeProvider schemes)
{
Schemes = schemes;
}
/// <summary>
/// 验证每次请求
/// </summary>
/// <param name="context"></param>
/// <param name="requirement"></param>
/// <returns></returns>
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
if (context.Resource is RouteEndpoint route && route != null)
{
var questUrl = route.RoutePattern.RawText?.ToLower();
if (requirement.Permissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > )
{
var name = context.User.Claims.SingleOrDefault(s => s.Type == requirement.ClaimType)?.Value;
//验证权限
if (requirement.Permissions.Where(w => w.Name == name && w.Url.ToLower() == questUrl).Count() > )
{ //判断过期时间
if (DateTime.Parse(context.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration).Value) >= DateTime.Now)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
}
context.Fail();
return Task.CompletedTask;
}
}
}

PermissionRequirement.cs

 using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic; namespace GRPCDemo01Service
{
/// <summary>
/// 必要参数类
/// </summary>
public class PermissionRequirement : IAuthorizationRequirement
{
/// <summary>
/// 用户权限集合
/// </summary>
public List<Permission> Permissions { get; private set; }
/// <summary>
/// 无权限action
/// </summary>
public string DeniedAction { get; set; } /// <summary>
/// 认证授权类型
/// </summary>
public string ClaimType { internal get; set; }
/// <summary>
/// 请求路径
/// </summary>
public string LoginPath { get; set; } = "/Api/Login";
/// <summary>
/// 发行人
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// 订阅人
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 过期时间
/// </summary>
public TimeSpan Expiration { get; set; }
/// <summary>
/// 签名验证
/// </summary>
public SigningCredentials SigningCredentials { get; set; } /// <summary>
/// 构造
/// </summary>
/// <param name="deniedAction">无权限action</param>
/// <param name="userPermissions">用户权限集合</param> /// <summary>
/// 构造
/// </summary>
/// <param name="deniedAction">拒约请求的url</param>
/// <param name="permissions">权限集合</param>
/// <param name="claimType">声明类型</param>
/// <param name="issuer">发行人</param>
/// <param name="audience">订阅人</param>
/// <param name="signingCredentials">签名验证实体</param>
public PermissionRequirement(string deniedAction, List<Permission> permissions, string claimType, string issuer, string audience, SigningCredentials signingCredentials, TimeSpan expiration)
{
ClaimType = claimType;
DeniedAction = deniedAction;
Permissions = permissions;
Issuer = issuer;
Audience = audience;
Expiration = expiration;
SigningCredentials = signingCredentials;
}
}
}

设置Startup.cs

 using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens; namespace GRPCDemo01Service
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
//读取配置文件
var audienceConfig = Configuration.GetSection("Audience");
var symmetricKeyAsBase64 = audienceConfig["Secret"];
var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
var signingKey = new SymmetricSecurityKey(keyByteArray);
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidIssuer = audienceConfig["Issuer"],//发行人
ValidateAudience = true,
ValidAudience = audienceConfig["Audience"],//订阅人
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
RequireExpirationTime = true,
};
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
//这个集合模拟用户权限表,可从数据库中查询出来
var permission = new List<Permission> {
new Permission { Url="/Goods.Goodser/GetGoods", Name="admin"},
new Permission { Url="systemapi", Name="system"}
};
//如果第三个参数,是ClaimTypes.Role,上面集合的每个元素的Name为角色名称,如果ClaimTypes.Name,即上面集合的每个元素的Name为用户名
var permissionRequirement = new PermissionRequirement(
"/api/denied", permission,
ClaimTypes.Role,
audienceConfig["Issuer"],
audienceConfig["Audience"],
signingCredentials,
expiration: TimeSpan.FromSeconds()//设置Token过期时间
); services.AddAuthorization(options =>
{
options.AddPolicy("Permission", policy => policy.AddRequirements(permissionRequirement));
}).
AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o =>
{
//不使用https
o.RequireHttpsMetadata = true;
o.TokenValidationParameters = tokenValidationParameters;
});
//注入授权Handler
services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
services.AddSingleton(permissionRequirement);
services.AddGrpc();
} public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GoodsService>(); endpoints.MapGet("/", async context =>
{
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");
});
});
}
}
}

GoodsService.cs

 using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Grpc.Core;
using GRPCDemo01Entity;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging; namespace GRPCDemo01Service
{
[Authorize("Permission")]
public class GoodsService : Goodser.GoodserBase
{
private readonly ILogger<GoodsService> _logger;
readonly PermissionRequirement _requirement;
public GoodsService(ILogger<GoodsService> logger, PermissionRequirement requirement)
{
_requirement = requirement;
_logger = logger;
}
public override Task<QueryResponse> GetGoods(QueryRequest request, ServerCallContext context)
{
return Task.FromResult(new QueryResponse
{
Name = "Hello " + request.Name,
Quantity =
});
}
[AllowAnonymous]
public override Task<LoginResponse> Login(LoginRequest user, ServerCallContext context)
{
//todo 查询数据库核对用户名密码
var isValidated = user.Username == "gsw" && user.Password == "";
if (!isValidated)
{
return Task.FromResult(new LoginResponse()
{
Message = "认证失败"
});
}
else
{
//如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
var claims = new Claim[] {
new Claim(ClaimTypes.Name, user.Username),
new Claim(ClaimTypes.Role, "admin"),
new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString())
}; var token = JwtToken.BuildJwtToken(claims, _requirement);
return Task.FromResult(new LoginResponse()
{
Result = true,
Token = token.access_token
}); }
}
}
}

控制台程序调用gRPC

添加引用 GRPCDemo01Entity项目

安装NuGet包

Grpc.Net.Client

Program.cs

 using Grpc.Core;
using Grpc.Net.Client;
using GRPCDemo01Entity;
using System;
using System.Threading.Tasks; namespace GRPCDemo01Test
{
class Program
{
static async Task Main(string[] args)
{
while (true)
{
Console.WriteLine("用户名:");
var username = Console.ReadLine();
Console.WriteLine("密码:");
var password = Console.ReadLine();
var tokenResponse = await Login(username, password);
if (tokenResponse.Result)
{
await Query(tokenResponse.Token);
}
else
{
Console.WriteLine("登录失败");
}
}
}
/// <summary>
/// 查询
/// </summary>
/// <param name="token">token</param>
/// <returns></returns>
static async Task Query(string token)
{
token = $"Bearer {token }";
var headers = new Metadata { { "Authorization", token } };
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Goodser.GoodserClient(channel);
var query = await client.GetGoodsAsync(
new QueryRequest { Name = "桂素伟" }, headers);
Console.WriteLine($"返回值 Name:{ query.Name},Quantity:{ query.Quantity}");
}
/// <summary>
/// 登录
/// </summary>
/// <param name="userName">userName</param>
/// <param name="password">password</param>
/// <returns></returns>
static async Task<LoginResponse> Login(string userName, string password)
{
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Goodser.GoodserClient(channel);
var response = await client.LoginAsync(
new LoginRequest() { Username = userName, Password = password });
return response;
}
}
}

 

webapi调用gRPC

添加引用 GRPCDemo01Entity项目

安装NuGet包

Grpc.Net.ClientFactory

Starup的ConfigureServices添加如下代码

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

调用gRPC

 using System.Threading.Tasks;
using Grpc.Core;
using GRPCDemo01Entity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; namespace GRPCDemo01WebTest.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
/// <summary>
/// 客户端
/// </summary>
private readonly Goodser.GoodserClient _client;
public WeatherForecastController(ILogger<WeatherForecastController> logger, Goodser.GoodserClient client)
{
_client = client;
_logger = logger;
} [HttpGet]
public async Task<string> Get()
{
//登录
var tokenResponse = await _client.LoginAsync(new LoginRequest { Username = "gsw", Password = "" });
var token = $"Bearer {tokenResponse.Token }";
var headers = new Metadata { { "Authorization", token } };
var request = new QueryRequest { Name = "桂素伟" };
//查询
var query = await _client.GetGoodsAsync(request, headers);
return $"Name:{query.Name},Quantity:{query.Quantity}";
}
}
}

 

gRPC调用gRPC

添加引用 GRPCDemo01Entity项目

Starup的ConfigureServices添加如下代码

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

调用gRPC

 using System;
using System.Threading.Tasks;
using Grpc.Core;
using GRPCDemo01Entity;
using Microsoft.Extensions.Logging; namespace GRPCDemo01GRPCTest
{
public class OrderService : Orderer.OrdererBase
{
private readonly ILogger<OrderService> _logger;
private readonly Goodser.GoodserClient _client;
public OrderService(ILogger<OrderService> logger, Goodser.GoodserClient client)
{
_client = client;
_logger = logger;
}
public override async Task<OrderResponse> GetGoods(OrderRequest request, ServerCallContext context)
{
//登录
var tokenResponse = await _client.LoginAsync(
new LoginRequest() {
Username = "gsw",
Password = ""
});
if (tokenResponse.Result)
{
var token = $"Bearer {tokenResponse.Token }";
var headers = new Metadata { { "Authorization", token } };
//查询
var query = await _client.GetGoodsAsync(
new QueryRequest { Name = "桂素伟" }, headers);
Console.WriteLine($"返回值 Name:{ query.Name},Quantity:{ query.Quantity}");
return new OrderResponse { Name = query.Name, Quantity = query.Quantity };
}
else
{
Console.WriteLine("登录失败");
return null;
}
}
}
}

gRPC asp.net core自定义策略认证的更多相关文章

  1. asp.net core 自定义认证方式--请求头认证

    asp.net core 自定义认证方式--请求头认证 Intro 最近开始真正的实践了一些网关的东西,最近写几篇文章分享一下我的实践以及遇到的问题. 本文主要介绍网关后面的服务如何进行认证. 解决思 ...

  2. ASP.NET Core的身份认证框架IdentityServer4--入门

    ASP.NET Core的身份认证框架IdentityServer4--入门 2018年08月11日 10:09:00 qq_42606051 阅读数 4002   https://blog.csdn ...

  3. asp.net core自定义端口

    asp.net Core 自定义端口 官方文档 aspnet内库源码: https://github.com/aspnet dotnet系统内库源码:https://github.com/dotnet ...

  4. asp.net core 2.1认证

    asp.net core 2.1认证 这篇文章基于asp.net core的CookieAuthenticationHandler来讲述. 认证和授权很相似,他们的英文也很相似,一个是Authenti ...

  5. Asp.Net Core基于JWT认证的数据接口网关Demo

    近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对 ...

  6. 如何在ASP.NET Core自定义中间件中读取Request.Body和Response.Body的内容?

    原文:如何在ASP.NET Core自定义中间件中读取Request.Body和Response.Body的内容? 文章名称: 如何在ASP.NET Core自定义中间件读取Request.Body和 ...

  7. asp.net core 自定义异常处理中间件

    asp.net core 自定义异常处理中间件 Intro 在 asp.net core 中全局异常处理,有时候可能不能满足我们的需要,可能就需要自己自定义一个中间件处理了,最近遇到一个问题,有一些异 ...

  8. asp.net core 自定义 Policy 替换 AllowAnonymous 的行为

    asp.net core 自定义 Policy 替换 AllowAnonymous 的行为 Intro 最近对我们的服务进行了改造,原本内部服务在内部可以匿名调用,现在增加了限制,通过 identit ...

  9. asp.net core 自定义基于 HttpContext 的 Serilog Enricher

    asp.net core 自定义基于 HttpContext 的 Serilog Enricher Intro 通过 HttpContext 我们可以拿到很多有用的信息,比如 Path/QuerySt ...

随机推荐

  1. 5分钟读懂Linux权限管理

    权限管理: 本文用于初学者对Linux文件系统权限的快速了解!! 进程安全上下文:   进程对文件的访问权限应用模型:     进程的属主与文件的属主是否相同:如果相同,则应用属主权限:      否 ...

  2. 2.Linux Bash认识

    虚拟机快照操作 1.什么是Bash shell? 它就是命令解释器,将用户输入的指令翻译给内核程序,内核处理完成之后将结果返回给Bash 2.Bash shell的用途? 几乎能完成所有的操作: 文件 ...

  3. springMVC初学简单例子

    新建web项目,保留web.xml. 配置web.xml文件(/WEB-INF/下): <?xml version="1.0" encoding="UTF-8&qu ...

  4. C++ 大作业资料总结

    一般 C++ 大作业都是用 Qt 来写,Qt 本身带了很多例子,详见:https://doc.qt.io/qt-5/qtexamples.html# 如果你想偷懒的话,直接拿来改就好,或者去 Gith ...

  5. solr学习篇(四) java使用solr简单查询(初识solrj)

    使用java实现solr查询 目录:1:导入jar包 2:核心类介绍 3:代码实现 4:注意事项 一 导入jar包 solrj可以使Java应用程序很方便的访问与操作solr. solrj有几个核心类 ...

  6. C++ 构造函数的执行过程(一) 无继承

      引言 C++ 构造函数的执行过程(一) 无继承 本篇介绍了在无继承情况下, C++构造函数的执行过程, 即成员变量的构建先于函数体的执行, 初始化列表的数量和顺序并不对构造函数执行顺序造成任何影响 ...

  7. Java基础(十八)集合(5)Queue集合

    队列是只能在尾部添加元素,同时只能在头部删除元素的数据结构.队列的原则就是“先进先出”. Queue接口是Collection接口的最后一个子接口. Queue接口是队列接口,而Deque接口是Que ...

  8. day3,用户交互,input的应用

    1.与用户交互 输入:input() python2.x版本 input后面家的东西要声明输入的类型       >>> input(">>:")   ...

  9. 启动elasticsearch报错的几种原因及解决方法

    ERROR: [1] bootstrap checks failed [1]: max virtual memory areas vm.max_map_count [65530] is too low ...

  10. LeetCode 二叉树的层次遍历

    第102题 给定一个二叉树,返回其按层次遍历的节点值. (即逐层地,从左到右访问所有节点). 例如: 给定二叉树: [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 ...