一、JWT

服务端在respose中设置好cookie,浏览器发请求都会自动带上,不需要做额外设置

但是如果客户端是非浏览器,或者要兼容多种客户端,这个方法就不行了

Js端

@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div class="container">
<div class="row">&nbsp;</div>
<div class="row">
<div class="col-6">&nbsp;</div>
<div class="col-6">
User..........<input type="text" id="userInput" />
<br />
Message...<input type="text" id="messageInput" />
<input type="button" id="sendButton" value="Send Message" />
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-6">&nbsp;</div>
<div class="col-6">
<ul id="messagesList"></ul>
</div>
</div>
</div>
<script src="~/lib/signalr/dist/browser/signalr.js"></script>
<script type="text/javascript">
"use strict";
var url = "/chatHub"; //本地站点可以直接写"/chat"
var loginToken = "token"; // JWT验证码。不带Bearer
var connection = new signalR.HubConnectionBuilder().withUrl(url, { accessTokenFactory: () => loginToken }).build();
//Disable send button until connection is established
document.getElementById("sendButton").disabled = true; connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
}); connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
}); document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
</script>
</body>
</html>

Startup.cs配置端

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Text;
using System.Threading.Tasks;
using test.Hubs; //3、引用 处理客户端 - 服务器通信的高级管道
namespace test
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//JWT令牌配置
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "JwtBearer";
options.DefaultChallengeScheme = "JwtBearer";
}).AddJwtBearer("JwtBearer", options =>
{
options.Audience = "Audience";
options.TokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("SecurityKey")),
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = "Issuer",
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = "Audience",
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain Account of clock drift, set that here
ClockSkew = TimeSpan.Zero
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = (context) => {
if (!context.HttpContext.Request.Path.HasValue)
{
return Task.CompletedTask;
}
//重点在于这里;判断是Signalr的路径
var accessToken = context.HttpContext.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!(string.IsNullOrWhiteSpace(accessToken)) && path.StartsWithSegments("/chatHub"))
{
context.Token = accessToken;
return Task.CompletedTask;
}
return Task.CompletedTask;
}
};
});
//1、添加服务
services.AddSignalR();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
//跨域
app.UseCors(builder =>
{
builder.SetIsOriginAllowed(origin => true)
.AllowAnyHeader()
.WithMethods("GET", "POST")
.AllowCredentials();
});
//默认静态资源路径wwwroot
app.UseStaticFiles();
//默认启动cookie
app.UseCookiePolicy();
//添加授权服务
app.UseAuthentication();
//配置
app.UseSignalR(routes => //2、引用
{
//SignalrHub为后台代码中继承Hub的类,"/chatHub"为请求路由地址;
routes.MapHub<ChatHub>("/chatHub");
});
//启动MVC
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}

使用场景,就是第一次链接的时候,传输手持令牌token,用于后台绑定Signalr用户标识(这里第一次握手链接肯定要检验token,然后映射关系好,每次发送消息,通过此标识,可以指定和识别用户)

二、实践

前端代码:

@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div class="container">
<div class="row">&nbsp;</div>
<div class="row">
<div class="col-6">&nbsp;</div>
<div class="col-6">
User..........<input type="text" id="userInput" />
<br />
Message...<input type="text" id="messageInput" />
<input type="button" id="sendButton" value="Send Message" />
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-6">&nbsp;</div>
<div class="col-6">
<ul id="messagesList"></ul>
</div>
</div>
</div>
<script src="~/lib/signalr/dist/browser/signalr.js"></script>
<script type="text/javascript">
"use strict";
var url = "/chatHub"; //本地站点可以直接写"/chat"
var loginToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiSm9obiIsImVtYWlsIjoiam9obi5kb2VAYmxpbmtpbmdjYXJldC5jb20iLCJleHAiOjE1NzU1MTUyNzYsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTMxMTEiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjUzMTExIn0.jSN25YSXRjDs184SRPdmvBrFzH8-UoGYHorUUE8ttl8"; // JWT验证码。不带Bearer
var connection = new signalR.HubConnectionBuilder().withUrl(url, { accessTokenFactory: () => loginToken }).build();
//Disable send button until connection is established
document.getElementById("sendButton").disabled = true; connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
}); connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
}); document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
</script>
</body>
</html>

appsettings.json(Jwt验证Key)

{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"Jwt": {
"Key": "veryVerySecretKey",
"Issuer": "http://localhost:53111"
},
"AllowedHosts": "*"
}

Startup.cs配置端

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Text;
using System.Threading.Tasks;
using test.Hubs; //3、引用 处理客户端 - 服务器通信的高级管道
namespace test
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//JWT令牌配置
services.AddAuthentication(options => //默认cookie 或者Bearer
{
options.DefaultAuthenticateScheme = "JwtBearer";//schemes 字意:方案即自定义方案,那么authorize也去指定JwtBearer
options.DefaultChallengeScheme = "JwtBearer";
}).AddJwtBearer("JwtBearer", options =>
{
options.Audience = "Audience";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,//是否验证Issuer
ValidateAudience = true,//是否验证Audience
ValidateLifetime = true,//是否验证失效时间
ValidateIssuerSigningKey = true,//是否验证SecurityKey
ValidIssuer = Configuration["Jwt:Issuer"],//appsettings.json文件中定义的Issuer
ValidAudience = Configuration["Jwt:Issuer"],//appsettings.json文件中定义的Audience
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = (context) =>
{
if (!context.HttpContext.Request.Path.HasValue)
{
return Task.CompletedTask;
}
//重点在于这里;判断是Signalr的路径
var accessToken = context.HttpContext.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!(string.IsNullOrWhiteSpace(accessToken)) && path.StartsWithSegments("/chatHub"))
{
context.Token = accessToken;
return Task.CompletedTask;
}
return Task.CompletedTask;
},
//此处为权限验证失败后触发的事件
OnChallenge = context =>
{
//此处代码为终止.Net Core默认的返回类型和数据结果,这个很重要哦,必须
context.HandleResponse();
//自定义自己想要返回的数据结果,我这里要返回的是Json对象,通过引用Newtonsoft.Json库进行转换
var payload = new { StatusCode = , Message = "身份认证失败!" };
//自定义返回的数据类型
context.Response.ContentType = "application/json";
//自定义返回状态码,默认为401 我这里改成 200
context.Response.StatusCode = StatusCodes.Status200OK;
//context.Response.StatusCode = StatusCodes.Status401Unauthorized;
//输出Json数据结果
context.Response.WriteAsync(Convert.ToString(payload));
return Task.FromResult();
} };
});
//1、添加服务
services.AddSignalR();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
//跨域
app.UseCors(builder =>
{
builder.SetIsOriginAllowed(origin => true)
.AllowAnyHeader()
.WithMethods("GET", "POST")
.AllowCredentials();
});
//默认静态资源路径wwwroot
app.UseStaticFiles();
//默认启动cookie
app.UseCookiePolicy();
//添加授权服务
app.UseAuthentication();
//配置
app.UseSignalR(routes => //2、引用
{
//SignalrHub为后台代码中继承Hub的类,"/chatHub"为请求路由地址;
routes.MapHub<ChatHub>("/chatHub");
});
//启动MVC
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}

Authorize

代码如下:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using test.Controllers; namespace test.Hubs
{
//[Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme)] //cookie 方式
[Authorize(AuthenticationSchemes = "JwtBearer")] //自定义JwtBearer方案
public class ChatHub : Hub //引用 using Microsoft.AspNetCore.SignalR;
{ public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
} /// <summary>
/// 客户端连接的时候调用
/// </summary>
/// <returns></returns>
[Authorize]//添加Authorize标签,可以加在方法上,也可以加在类上
public override async Task OnConnectedAsync()
{
System.Diagnostics.Trace.WriteLine("客户端连接成功"); await base.OnConnectedAsync();
}//所有链接的客户端都会在这里 /// <summary>
/// 连接终止时调用。
/// </summary>
/// <returns></returns>
public override Task OnDisconnectedAsync(Exception exception)
{
System.Diagnostics.Trace.WriteLine("连接终止");
return base.OnDisconnectedAsync(exception);
} }
}

为了前端判断授权失败跳转相应处理

Startup.cs文件更改:

                    //此处为权限验证失败后触发的事件
OnChallenge = context =>
{
//此处代码为终止.Net Core默认的返回类型和数据结果,这个很重要哦,必须
context.HandleResponse();
//自定义自己想要返回的数据结果,我这里要返回的是Json对象,通过引用Newtonsoft.Json库进行转换
var payload = JsonConvert.SerializeObject(new { Code = "", Message = "身份认证失败!" });
//自定义返回的数据类型
context.Response.ContentType = "application/json";
//自定义返回状态码,默认为401 我这里改成 200
//context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
//输出Json数据结果
context.Response.WriteAsync(payload);
return Task.FromResult();
}

前端代码更改:

        connection.start().then(function (data) {
console.log('已成功连接到signalr服务器');
document.getElementById("sendButton").disabled = false;
}).catch(function (error) {
if (error.statusCode) {//判断undefined、null与NaN
if (error.statusCode == "401") {
alert("error.message == Unauthorized");
}
} else {
console.error(error.toString());
}
});

如图:

注意:

1、jwt自己会验证。

2、jwt的token而不是自己随便写(token)

以及

以及

代码如下:

//Token颁发机构
ValidIssuer = jwtSettings.Issuer,
//颁发给谁
ValidAudience = jwtSettings.Audience,
//这里的key要进行加密
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey)),

3、控制台查看日志(日志级别调低)

参考 https://www.cnblogs.com/pingming/p/11169799.html

四、Signalr手持令牌验证的更多相关文章

  1. .NET WebAPI 用ActionFilterAttribute实现token令牌验证与对Action的权限控制

    项目背景是一个社区类的APP(求轻吐...),博主主要负责后台业务及接口.以前没玩过webAPI,但是领导要求必须用这个(具体原因鬼知道),只好硬着头皮上了. 最近刚做完权限这一块,分享出来给大家.欢 ...

  2. ThinkPHP表单令牌验证功能详细介绍

    注:TP版本为3.1.3 在ThinkPHP框架下,两次提交同一个表单,比如提交信息后在浏览器点击后退退回上次的页面,重新点击提交按钮,就会提示“表单令牌错误”的信息. ThinkPHP新版内置了表单 ...

  3. WebAPI 用ActionFilterAttribute实现token令牌验证与对Action的权限控制

    .NET WebAPI 用ActionFilterAttribute实现token令牌验证与对Action的权限控制 项目背景是一个社区类的APP(求轻吐...),博主主要负责后台业务及接口.以前没玩 ...

  4. Nginx集群之SSL证书的WebApi令牌验证

    目录 1       大概思路... 1 2       Nginx集群之SSL证书的WebApi令牌验证... 1 3       Openssl生成SSL证书... 2 4       编写.NE ...

  5. ASP.NET Core Razor页面禁用防伪令牌验证

    在这篇短文中,我将向您介绍如何ASP.NET Core Razor页面中禁用防伪令牌验证. Razor页面是ASP.NET Core 2.0中增加的一个页面控制器框架,用于构建动态的.数据驱动的网站: ...

  6. 动态令牌验证遇到的问题(判断用户长按backspace键)

    因为最近负责泰康项目的前端工作,他们的登录需要进行安全验证,也就是所谓的双因素验证,即在OA平台登录过后,还需要安全部门发送安全令牌进行验证.令牌验证效果如下: 主要功能有:1.默认第一项focus. ...

  7. ThinkPHP自动令牌验证(附实例)

    一.数据表结构 user表结构如下: id username password 二.view模板部分 /view/index.html页面如下:   1 2 3 4 5 6 <form acti ...

  8. dubbo之令牌验证

    防止消费者绕过注册中心访问提供者 在注册中心控制权限,以决定要不要下发令牌给消费者 注册中心可灵活改变授权方式,而不需修改或升级提供者 可以全局设置开启令牌验证 <!--随机token令牌,使用 ...

  9. token方法可用于临时关闭令牌验证,

    token方法可用于临时关闭令牌验证,例如: $model->token(false)->create(); 即可在提交表单的时候临时关闭令牌验证(即使开启了TOKEN_ON参数). 大理 ...

随机推荐

  1. Is JavaScript a pass-by-reference or pass-by-value language?

    Is JavaScript a pass-by-reference or pass-by-value language? A very detailed explanation about copyi ...

  2. 如何消除 com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server

    在application.properties中添加以下两句话: eureka.client.register-with-eureka=falseeureka.client.fetch-registr ...

  3. Use an Excel RTD Server with DCOM

    费好大劲找到的文章,留存. Use an Excel RTD Server with DCOM 如何使用DCOM的Excel RTD服务器 Microsoft Office Excel 2007,Mi ...

  4. 转 Cookie、Session

    https://www.cnblogs.com/liwenzhou/p/8343243.html Cookie的由来 大家都知道HTTP协议是无状态的. 无状态的意思是每次请求都是独立的,它的执行情况 ...

  5. 代码实现:一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如6=1+2+3.第二个完全数是28, //它有约数1、2、4、7、14、28,除去它本身28外,其余5个数相加, //编程找出1000以内的所有完数。

    import java.util.ArrayList; import java.util.List; //一个数如果恰好等于它的因子之和,这个数就称为"完数".例如6=1+2+3. ...

  6. 监控部署nagios+snmp

    参看是否有安装:rpm -q gcc glibc glibc-common gd gd-devel xinetd openssl-devel 未安装基础支持套件的先安装: yum install -y ...

  7. RedHat 5下安装gcc编译环境的具体步骤

    RedHat 5下安装gcc编译环境的具体步骤 在RHEL5系统中默认不安装linux系统中的开发编译环境(gcc),此软件包安装时依赖其他包较多 在以前使用RHEL4时可以通过如下命令安装: rpm ...

  8. 浅谈Excel开发:二 Excel 菜单系统(转)

    编辑器加载中...http://www.cnblogs.com/yangecnu/p/Excel-Menu-System-Introduction.html 在开始Excel开发之前,需要把架子搭起来 ...

  9. Kubernetes Controller执行框架解析

    毫无疑问,声明式API以及Controller机制是Kubernetes设计理念的基础.Controller不断从API Server同步资源对象的期望状态并且在资源对象的期望状态和实际运行状态之间进 ...

  10. SpringBoot常用注解有哪些?

    @Service: 注解在类上,表示这是一个业务层bean@Controller: 注解在类上,表示这是一个控制层bean@Repository: 注解在类上,表示这是一个数据访问层bean@Comp ...