首页 技术 正文
技术 2022年11月18日
0 收藏 982 点赞 3,239 浏览 13032 个字

在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;
}
}
}
}
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,023
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,513
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,361
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,143
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,774
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,853