.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

头信息是一个 Json 格式的数据

{
"alg": "HS256",
"typ": "JWT"
}
  • 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>
原文地址:https://www.cnblogs.com/zoro-zero/p/14235419.html