第八节:基于Core6.0中间件实现SignalR的安全校验 以及 部署IIS注意的问题
一. 中间件实现安全校验
(官方默认的方式详见:https://learn.microsoft.com/zh-cn/aspnet/core/signalr/authn-and-authz?view=aspnetcore-6.0 个人不喜欢)
1. SignalR的注册
代码如下:
//1. 注册SignalR
services.AddSignalR();
//2. 允许跨域
app.UseCors(options =>
{
string[] urls = new string[] { "http://11.12.216.184:8001", "http://127.0.0.1:8848"};
options.WithOrigins(urls)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials(); //signalR需要开启AllowCredentials
});
//3. 开启SignalR (这里/myCrewHub可以是任意值,对应着前端路径)
app.MapHub<CrewHub>("/myCrewHub");
剖析:
(1). SignalR要求必须开启 AllowCredentials,而AllowCredentials需要和WithOrigins配对出现。
(2). 这里/myCrewHub可以是任意值,对应着前端路径,
var myConnection = new signalR.HubConnectionBuilder().withUrl(`http://xxx.xx.xx.xx:xx/myCrewHub`).build();
2. 中间件 及 注册
中间件代码:
查看代码
/// <summary>
/// 校验SignalR的中间件
/// (效果:SignalR校验不通过,仅signalr无法正常使用,其它http请求不受影响)
/// </summary>
public class CheckHubMiddleware
{
private readonly RequestDelegate _next;
public CheckHubMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var requestPath = context.Request.Path.Value;
if (requestPath.Equals("/myCrewHub"))
{
var accessToken = context.Request.Query["access_token"].ToString();
if (string.IsNullOrEmpty(accessToken))
{
context.Response.StatusCode = 401;
return;
}
//校验token的正确性
var result = JWTHelp.JWTJieM(accessToken, ConfigHelp.GetString("JWTSecret"));
if (result == "expired")
{
context.Response.StatusCode = 401;
return;
}
else if (result == "invalid")
{
context.Response.StatusCode = 401;
return;
}
else if (result == "error")
{
context.Response.StatusCode = 401;
return;
}
else
{
//表示校验通过,直接跳过,继续运行即可
await _next.Invoke(context);
}
}
else
{
//表示非SignalR连接,直接跳过,继续运行即可
await _next.Invoke(context);
}
}
}
注册中间件
//Signalr校验中间件
app.UseMiddleware<CheckHubMiddleware>();
剖析:
(1). if (requestPath.Equals("/myCrewHub")) 表示该过滤器仅对SignalR请求起作用,此处需要注意,在项目的其它接口中,应该回避/myCrewHub这一路径。
(2). token值要从 Query["access_token"] 中获取,这是固定值,这对应这前端的赋值方式:通过accessTokenFactory方法进行赋值
var myConnection = new signalR.HubConnectionBuilder().withUrl(`${baseUrl}/myCrewHub`, {
accessTokenFactory: () => myUtils.sessionGet('token'),
skipNegotiation: true, //跳过协商,永远走websocket协议
transport: signalR.HttpTransportType.WebSockets
})
.withAutomaticReconnect([3000, 20000, 60000, 60000]) //依次再过3s、20s、60s、60s进行重连
.build();
3. 前端代码
代码分享:
//9.1 初始化连接
var myConnection = new signalR.HubConnectionBuilder()
.withUrl(`${baseUrl}/myCrewHub`, {
accessTokenFactory: () => myUtils.sessionGet('token'),
skipNegotiation: true, //跳过协商,永远走websocket协议
transport: signalR.HttpTransportType.WebSockets
})
.withAutomaticReconnect([3000, 20000, 60000, 60000]) //依次再过3s、20s、60s、60s进行重连
.build();
//9.2 进行连接
myConnection.start().then(() => {
console.log('------signalR连接成功------');
//调用方法,进行关联
myConnection.invoke("SaveConnectionId", myUtils.sessionGet('token'));
}).catch(err => console.error(err.toString()));
//9.3 监听方法-进行证书表格的刷新
myConnection.on("UpdateCertTables", () => {
console.log('-------我是UpdateCertTables我收到更新指示------')
pageUtils.initZsTable();
// 初始化自动resize的标识字段
pageUtils.initAutoResizeFlag();
});
// 9.4 掉线重连机制
//(默认4次重连),任何一次只要回调成功,调用
myConnection.onreconnected(connectionId => {
console.log('-------触发重连机制------')
//调用方法,进行关联
myConnection.invoke("SaveConnectionId", myUtils.sessionGet('token'));
});
剖析:
(1). 通过accessTokenFactory: () => myUtils.sessionGet('token') 实现对 access_token 字段的赋值。
4. 测试
(1). 当token值校验通过的时候,如下:
(2). 当token校验不通过的时,如下: (注:其它http请求不受影响)
二. IIS部署问题
IIS需要安装WebSocket模块。
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。