一. 证书管理
1. 如何生成证书
(1). 关于阿里云证书和证书的相关概念
详见:https://www.cnblogs.com/yaopengfei/p/10648151.html (抽时间重新配置一遍)
(2). 本地生成测试证书
详见:https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps
这里使用案例9:【New-SelfSignedCertificate -Subject "localhost" -TextExtension @("2.5.29.17={text}DNS=localhost&IPAddress=127.0.0.1&IPAddress=::1")】
2. 本地计算机证书管理
(1).查看计算已有的证书
cmd命令行→输入certmgr→可以查看当前用户下的证书以及受信任的颁发机构
(2).证书导入和添加信任
cmd命令行→输入mmc→进入控制台页面(默认空白)→选中文件中的‘添加/删除管理单元’→将'证书'一项添加进去
导入步骤:
A.以管理员的身份运行powershell,运行证书生成指令【New-SelfSignedCertificate -Subject "ypf" -TextExtension @("2.5.29.17={text}DNS=localhost&IPAddress=127.0.0.1&IPAddress=::1")】生成成功,如下图: 并在控制台下→个人→证书 找到刚才生成的证书。
B.将该证书导出:选择需要私钥→输入密码123455→输入名称ypfCert进行导出
C.对该证书添加信任:受信任的根证书颁发机构→证书→进行导入
二. 基于证书的认证和授权
1. 项目准备
GrpcServer3:服务端
MyClient3:客户端(控制台)
2. 服务端配置
(1).新建cert.proto文件,声明获取证书信息的方法GetCertificateInfo,并对其添加链接引用。
代码如下:
syntax = "proto3";
import "google/protobuf/empty.proto";
package certify;
//Certifier对应CertifierService实现类
service Certifier {
//获取证书信息的方法
rpc GetCertificateInfo (google.protobuf.Empty) returns (CertificateInfoResponse);
//获取证书信息的方法(测试不加校验)
rpc GetCertificateInfoNoAuth (google.protobuf.Empty) returns (CertificateInfoResponse);
}
message CertificateInfoResponse {
bool hasCertificate = 1;
string name = 2;
}
(2).新建CertifierService,重写GetCertificateInfo方法,并添加授权校验 [Authorize(AuthenticationSchemes = "Certificate")]
代码如下:
/// <summary>
/// cert.proto实现类
/// </summary>
public class CertifierService: CertifierBase
{
[Authorize(AuthenticationSchemes = "Certificate")]
public override Task<CertificateInfoResponse> GetCertificateInfo(Empty request, ServerCallContext context)
{
var httpContext = context.GetHttpContext();
var clientCertificate = httpContext.Connection.ClientCertificate;
Console.WriteLine(clientCertificate);
var name = string.Join(',', context.AuthContext.PeerIdentity.Select(i => i.Value));
var certificateInfo = new CertificateInfoResponse
{
HasCertificate = context.AuthContext.IsPeerAuthenticated,
Name = name
};
return Task.FromResult(certificateInfo);
}
/// <summary>
/// 不加校验
/// 和上面比较不加校验的情况
/// </summary>
/// <param name="request"></param>
/// <param name="context"></param>
/// <returns></returns>
public override Task<CertificateInfoResponse> GetCertificateInfoNoAuth(Empty request, ServerCallContext context)
{
var certificateInfo = new CertificateInfoResponse
{
Name = "看看能不能通过哦"
};
return Task.FromResult(certificateInfo);
}
}
(3).通过nuget安装程序集【Microsoft.AspNetCore.Authentication.Certificate】,在ConfigureService注册授权和认证中间件,在Configure开启认证和授权中间件,并映射CertifierService服务。
代码如下:
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
//认证
services.AddAuthentication("Certificate").AddCertificate(options =>
{
// Not recommended in production environments. The example is using a self-signed test certificate.
options.RevocationMode = X509RevocationMode.NoCheck;
options.AllowedCertificateTypes = CertificateTypes.All;
});
//授权
services.AddAuthorization();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
//认证
app.UseAuthentication();
//授权
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
//映射grpc实现类
endpoints.MapGrpcService<CertifierService>();
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");
});
});
}
(4).在Program类中配置允许所有证书:httpsOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
代码如下:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
//允许所有证书
webBuilder.ConfigureKestrel(kestrelOptions =>
{
kestrelOptions.ConfigureHttpsDefaults(httpsOptions =>
{
httpsOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
});
});
webBuilder.UseStartup<Startup>();
});
(PS:此处也可以配置 httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate; 对于没有证书请求,直接中断链接,而不是403未授权了)
3. 客户端配置
(1).对cert.proto文件添加服务链接引用,会自动安装相应的程序集(版本可能不是最新的,需要手动更新一下)。
(2).把前面导出来的证书ypfCert.pfx复制进来,并将属性改为"始终复制"。
(3).编写无证书的调用代码 和 有证书的调用代码.
代码如下:
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("----------------------------下面是无证书的请求------------------------------------");
try
{
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Certifier.CertifierClient(channel);
var certificateInfo = await client.GetCertificateInfoAsync(new Empty());
Console.WriteLine($"返回信息HasCertificate={certificateInfo.HasCertificate},certificate name={certificateInfo.Name}");
}
catch (RpcException ex)
{
Console.WriteLine($"gRPC error from calling service: {ex.Status.Detail}");
}
catch (Exception ex)
{
Console.WriteLine($"无证书:{ex.Message}");
}
Console.WriteLine("----------------------------下面是有证书的请求------------------------------------");
try
{
//证书相关配置
var handler = new HttpClientHandler();
var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
var certPath = Path.Combine(basePath!, "MyCerts", "ypfCert.pfx");
var clientCertificate = new X509Certificate2(certPath, "123456");
handler.ClientCertificates.Add(clientCertificate);
//携带证书创建通道
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
HttpClient = new HttpClient(handler)
});
var client = new Certifier.CertifierClient(channel);
var certificateInfo = await client.GetCertificateInfoAsync(new Empty());
Console.WriteLine($"请求成功,返回信息:HasCertificate={certificateInfo.HasCertificate},certificate name={certificateInfo.Name}");
}
catch (RpcException ex)
{
Console.WriteLine($"gRPC error from calling service: {ex.Status.Detail}");
}
catch (Exception ex)
{
Console.WriteLine($"有证书:{ex.Message}");
}
Console.WriteLine("----------------------------下面是不加校验 不传证书的测试------------------------------------");
try
{
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Certifier.CertifierClient(channel);
var certificateInfo = await client.GetCertificateInfoNoAuthAsync(new Empty());
Console.WriteLine($"返回信息certificate name={certificateInfo.Name}");
}
catch (RpcException ex)
{
Console.WriteLine($"gRPC error from calling service: {ex.Status.Detail}");
}
catch (Exception ex)
{
Console.WriteLine($"无证书:{ex.Message}");
}
Console.ReadKey();
}
}
4. 测试
将GrpcServer3和MyClient3配置同时启动,查看结果,然后把服务端配置改为 httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate,再次运行查看结果。
测试1:(ClientCertificateMode.AllowCertificate)
测试2:(ClientCertificateMode.RequireCertificate)
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
来源:oschina
链接:https://my.oschina.net/u/4280951/blog/4459987