首先我们要弄清楚认证(Authentication)和授权(Authorization)的区别,以免混淆了。认证是确认的过程中你是谁,而授权围绕是你被允许做什么,即权限。显然,在确认允许用户做什么之前,你需要知道他们是谁,因此,在需要授权时,还必须以某种方式对用户进行身份验证。
什么是Jwt根据维基百科的定义,JSON WEB Token(JWT),是一种基于JSON的、用于在网络上声明某种主张的令牌(token)。JWT通常由三部分组成:头信息(header),消息体(payload)和签名(signature)。
为什么要使用Jwt在.NET Core之前对于Web应用程序跟踪用户登录状态最普通的方式则是使用Cookie,当用户点击登录后将对其信息进行加密并响应写入到用户浏览器的Cookie里,当用户进行请求时,服务端将对Cookie进行解密,然后创建用户身份,整个过程都是那么顺其自然,但是这是客户端是基于浏览器的情况,如果是客户端是移动app或者桌面应用程序呢?
如果我们使用Json Web Token简称为JWT而不是使用Cookie,此时Token将代表用户,同时我们不再依赖浏览器的内置机制来处理Cookie,我们仅仅只需要请求一个Token就好。这个时候就涉及到Token认证,那么什么是Token认证呢?一言以蔽之:将令牌(我们有时称为AccessToken或者是Bearer Token)附加到HTTP请求中并对其进行身份认证的过程。Token认证被广泛应用于移动端或SPA。
Json Web Token 基础JWT由三部分构成,Base64编码的Header,Base64编码的Payload,签名,三部分通过点隔开。
第一部分以Base64编码的Header主要包括Token的类型和所使用的算法,例如:
{ "alg": "HS265", "typ": "JWT"}
第二部分以Base64编码的Payload主要包含的是声明(Claims),例如,如下:
{ "sub": "765032130654732", "name": "jeffcky"}
第三部分则是将Key通过对应的加密算法生成签名,最终三部分以点隔开,比如如下形式:
key = 'secretkey' unsignedToken = encodeBase64(header) + '.' + encodeBase64(payload) signature = HMAC-SHA256(key, unsignedToken)
token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiSmVmZmNreSIsImVtYWlsIjoiMjc1MjE1NDg0NEBxcS5jb20iLCJleHAiOjE1NjU2MTUzOTgsIm5iZiI6MTU2MzE5NjE5OCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAxIn0.OJjlGJOnCCbpok05gOIgu5bwY8QYKfE2pOArtaZJbyI
到这里此时我们应该知道:JWT包含的信息并没有加密,比如为了获取Payload,我们大可通过比如谷歌控制台中的APi(atob)对其进行解码。
那如我所说既然JWT包含的信息并没有加密,只是进行了Base64编码,岂不是非常不安全呢?当然不是这样,还没说完,第三部分就是签名,虽然我们对Payload(姑且翻译为有效负载),未进行加密,但是若有蓄意更换Payload,此时签名将能充分保证Token无效,除非将签名的Key不小心暴露在光天化日之下,否则必须是安全的。好了,到了这里,我们稍稍讲解了下JWT构成,接下来我们进入如何在.NET Core中使用JWT。
.Net Core 中使用 Jwt第一步:安装NuGet包。
Microsoft.AspNetCore.Authentication.JwtBearer
Startup类
ConfigureServices 添加认证服务
#region Jwtservices.AddAuthentication(options =>{ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(options =>{ options.SaveToken = true; options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = true, ValidateAudience = true, ValidAudience = "https://www.cnblogs.com/chengtian", ValidIssuer = "https://www.cnblogs.com/chengtian", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SecureKeySecureKeySecureKeySecureKeySecureKeySecureKey")) };});#endregion
Configure 配置认证中间件
app.UseAuthentication();//认证中间件,放在其他中间件的前面
创建一个token
添加一个Token的Model命名为TokenDto
namespace MyWeb.Model{ /// /// Token /// public class TokenDto { /// /// Token /// public string Token { get; set; } /// /// 过期时间 /// public string Expiration { get; set; } }}
添加一个生成Token的帮助类
using Microsoft.IdentityModel.Logging;using Microsoft.IdentityModel.Tokens;using MyWeb.Model;using System;using System.IdentityModel.Tokens.Jwt;using System.Security.Claims;using System.Text;namespace MyWeb.Api.Auth{ /// /// 获取Token /// public static class TokenHelper { /// /// 获取Token /// /// /// /// public static TokenDto CreateToken(string userName, string password) { //从数据库验证用户名,密码 //验证通过 否则 返回Unauthorized if (!(userName == "admin" && password == "123456")) { return null; } var claims = new[] { new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") ,//当前时间 new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddMinutes(30)).ToUnixTimeSeconds()}"),//过期时间 new Claim(ClaimTypes.NameIdentifier, "1"),//用户Id, new Claim(ClaimTypes.Name, userName),//用户名 new Claim("Role", "admin")//用户角色 }; IdentityModelEventSource.ShowPII = true; //签名秘钥 可以放到json文件中 var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SecureKeySecureKeySecureKeySecureKeySecureKeySecureKey")); var token = new JwtSecurityToken( issuer: "https://www.cnblogs.com/chengtian", audience: "https://www.cnblogs.com/chengtian", expires: DateTime.Now.AddHours(2), claims: claims, signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256) ); return new TokenDto() { Token = new JwtSecurityTokenHandler().WriteToken(token), Expiration = token.ValidTo.ToLongDateString() }; } }}
创建一个控制器 AuthenticateController
[HttpGet]public ActionResultGetToken(string userName, string password){ var token = TokenHelper.CreateToken(userName, password); return Ok(token);}
测试一下,给之前的CompaniesController 控制器加上认证。你可以直接加在控制器上,也可以加在具体的某个方法上。
最后我们来测试一下。
此时我们的Api已经收到了保护,我们获取一下Token。
获取成功后,给接口请求加上 Token 。请求成功。
我们在登录完成后要获取登录的用户名或者Id,我们可以通过下面的这种方式获取。
User.FindFirst(ClaimTypes.NameIdentifier)?.Value
调试可以看到我们获取到的用户Id是1
因为我们存进去的是1
同样的方法获取一下用户名,这样就可以在授权成功后拿到用户Id进行操作了
感谢您的关注,您分享和点赞,是我分享路上最大的动力
留言区