.NET Core 3.X 使用 JWT 案例
在前后端分离的架构中,前端需要通过 API 接口的方式获取数据,但 API 是无状态的,没有办法知道每次请求的身份,也就没有办法做权限的控制。
如果不做控制,API 就对任何人敞开了大门,只要拿到了接口地址就可以进行调用,这是非常危险的。
本文主要介绍下在 dotNET Core Web API 中使用 Jwt 来实现接口的认证。
Jwt 简介
Jwt 的全称是 JSON Web Token,是目前比较流行的接口认证解决方案。有了 Jwt 后,从客户端请求接口的流程如下图:
- 客户端发送用户名密码信息到认证服务器获取 token;
- 客户端请求 API 获取数据时带上 token;
- 服务器端验证 token,合法则返回正确的数据。
有一个网站叫:https://jwt.io/ ,我们在这个站点上对 Jwt 产生的 token 做验证:
从上图可以看出 Jwt 生产的 token 由三个部分组成:
- Header(红色):头
- Playload(紫色):负载
- Verify Sigantuer(蓝色):签名
这三个部分由英文的点进行分隔开
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoib2VjMjAwMyIsInNpdGUiOiJodHRwOi8vZndoeXkuY29tIiwiaWF0IjoxNTE2MjM5MDIyfQ.DYgo4eEUwlYJqQoLvAuFPxFRVcCow77Zyl2byaK6Uxk
|
Header
头信息是一个 Json 格式的数据
{
|
- alg:表示加密的算法
- typ:表示 token 的类型
Playload
Playload 是 token 的主体内容部分,我们可以用来传递一些信息给客户端,比如过期时间就是通过 Playload 来进行传递的。 但因为默认情况下 Playload 的数据是明文的,所以敏感信息不要放在这里。
Verify Sigantuer
Verify Sigantuer 是对前面两个部分的签名,防止数据篡改。
使用 Jwt
下面一步步介绍在 dotNET Core Web API 项目中使用 Jwt:
1、添加 Jwt 的包引用
在 Web API 项目中添加对 Microsoft.AspNetCore.Authentication.JwtBearer
包的引用
2、修改 Starup
在Starup 类中的ConfigureServices 方法添加如下代码:
//解决跨域问题 services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader()); }); // jwt 认证 JwtSettings jwtSettings = new JwtSettings(); services.Configure<JwtSettings>(Configuration.GetSection("JwtSettings")); Configuration.GetSection("JwtSettings").Bind(jwtSettings); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(o => { o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters() { ValidateIssuerSigningKey = true, ValidIssuer = jwtSettings.Issuer, ValidAudience = jwtSettings.Audience, //用于签名验证 IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtSettings.SecretKey)), ValidateIssuer = false, ValidateAudience = false }; });
3、添加 JwtSettings 对象
public class JwtSettings { public string Issuer { get; set; } public string Audience { get; set; } public string SecretKey { get; set; } }
4、appsettings.json 文件中添加 JwtSettings 配置:
SecretKey 字段必须是16的倍数。
"JwtSettings": { "Audience": "123456", "Issuer": "123456",
"SecretKey": "ihGpquvKl4POannM"
}
5、Startup 类中的 Configure 方法添加如下代码:
//解决跨域问题 app.UseCors("CorsPolicy"); IdentityModelEventSource.ShowPII = true;
app.UseRouting();
//顺序不能颠倒
app.UseAuthentication();
app.UseAuthorization();
6、添加AuthorizeController 控制器
using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using coreTest.Common.Model; using coreTest.Model; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; namespace coreTest.Controllers { [Route("api/[controller]")] [ApiController] public class AuthorizeController : ControllerBase { private readonly JwtSettings _jwtSettings; public AuthorizeController(IOptions<JwtSettings> options) { _jwtSettings = options.Value; } [HttpPost] [Route("Token")] public Result<string> Token(User user) { Result<string> result = new Result<string>(); if (user == null) { result.Data = string.Empty; return result; } if (user.UserName == "admin" && user.Pwd == "123456") { var claims = new Claim[] { new Claim(ClaimTypes.Name,user.UserName) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken(_jwtSettings.Issuer, _jwtSettings.Audience, claims, DateTime.Now, DateTime.Now.AddMinutes(10), creds); string res = new JwtSecurityTokenHandler().WriteToken(token); result.Data = res; return result; } return result; } } }
Result 类:
public class Result<T> { public Result() { ReturnCode = ReturnCode.Fail; ReturnMsg = "未知错误"; } public ReturnCode ReturnCode { get; set; } public string ReturnMsg { get; set; } public T Data { get; set; } }
7、在需要进行认证的控制器或接口方法上添加 [Authorize]
标记。 添加 SysUserController 控制器 ,如下:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using coreTest.IService; using coreTest.Model; using Dapper.Extension.AspNetCore; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace coreTest.Controllers { [Authorize] [Route("api/[controller]")] [ApiController] public class SysUserController : ControllerBase { private readonly ILogger<SysUserController> logger; private readonly IDapper dapper; private readonly ISysUserService sysUserService; public SysUserController(ILogger<SysUserController> _logger, IDapper _dapper, ISysUserService _sysUserService) { logger = _logger; dapper = _dapper; sysUserService = _sysUserService; } [HttpGet] [Route("Index")] public JsonResult Index(int pageIndex = 1, int pageSize = 10) { var result = sysUserService.GetList(pageIndex, pageSize); return new JsonResult(result); } } }
8、使用 Postman 测试
1)、调用接口 http://localhost:5000/api/Authorize/token
获取 token。
2)、在请求接口时使用 Authorization 的方式使用 token,token 的类型为 Bearer Token ,可以看到带上 token 后,数据正常返回。
9、前端ajax调用案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="js/jquery-3.5.1.js"></script> </head> <body> <input type="button" value="登录" onclick="submit()" /> <input type="button" value="获取数据" onclick="getData()" /> <script> let Authorization = ''; function submit(){ console.log(111); let url = "http://localhost:4213/api/Authorize/Token"; let p = { "userName": "admin", "pwd": "123456" }; $.ajax({ type: 'post', url: url, dataType: 'json', cache: false, headers : { 'Content-Type' : 'application/json;charset=utf-8', }, data: JSON.stringify(p), success: function (data) { console.log('success'); console.log(data); console.log(data.data); Authorization = 'Bearer '+data.data; }, error: function(er){ console.log('error'); console.log(er); console.log(er.responseText); } }); } function getData(){ let getUrl = "http://localhost:4213/api/SysUser/Index?pageIndex=1&pageSize=10"; $.ajax({ type: 'get', url: getUrl, dataType: 'json', cache: false, headers : { 'Content-Type' : 'application/json;charset=utf-8', 'Authorization':Authorization }, // data: JSON.stringify(paramData), success: function (data) { console.log(data); }, error: function(er){ } }); } </script> </body> </html>