[WebApi]ASP.Net Core 中使用JWT认证(3.1版本,5.0也可以使用)

Posted 厦门德仔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[WebApi]ASP.Net Core 中使用JWT认证(3.1版本,5.0也可以使用)相关的知识,希望对你有一定的参考价值。

ASP.Net Core 中使用JWT认证

JWT简单介绍

关于JWT网上有很多介绍,这里就不介绍了,本文主要以实现为主。
JWT由3部分构成:
HEADER 、PAYLOAD 、SIGNATURE
HEADER :包含token的元数据,主要是加密算法,和签名的类型,如下面的信息,说明了加密的对象类型是JWT,加密算法是HMAC SHA-256

"alg":"HS256","typ":"JWT"

PAYLOAD :主要包含一些声明信息(claim),这些声明是key-value对的数据结构。通常如用户名,角色等信息,过期日期等,因为是未加密的,所以不建议存放敏感信息。

SIGNATURE:jwt要符合jws(Json Web Signature)的标准生成一个最终的签名。把编码后的Header和Payload信息加在一起,然后使用一个强加密算法进行加密。HS256(BASE64(Header).Base64(Payload),secret)

开始编码

首先我们先创建一个ASP.NET Core Web API项目 版本随意。
创建好项目后我们先创建几个文件夹,分别是Model、IdentityService。
然后我们在Model文件夹中创建一个TokenModel的类用来存储签发或者验证时的信息。如下:

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ZettlercnMIS.Api.Models

    public class tokenModel
    
        /// <summary>
        /// 密钥
        /// </summary>
        public string Secret  get; set; 
        /// <summary>
        /// 发布者
        /// </summary>
        public string Issuer  get; set; 
        /// <summary>
        /// 接收者
        /// </summary>
        public string Audience  get; set; 
        /// <summary>
        /// 过期时间
        /// </summary>
        public int AccessExpiration  get; set; 
        /// <summary>
        /// 刷新时间
        /// </summary>
        public int RefreshExpiration  get; set; 
    


然后在 appsettings.Development.json 增加jwt使用到的配置信息(如果是生成环境在appsettings.json添加即可)

  "JWTTokenconfig": 
    "Secret": "fsdjfljoiuweihjfskghrfygierwuer9uwekhdkcnh940233+f+s",
    "Issuer": "dezai.cn", //wkea.cn
    "Audience": "user",//user
    "AccessExpiration": 30,
    "RefreshExpiration": 60
  ,

然后在startup.cs类中的ConfigureServices方法中读取配置

 public void ConfigureServices(IServiceCollection services)
        

            services.AddControllers();
            services.AddSwaggerGen(c =>
            
                c.SwaggerDoc("v1", new OpenApiInfo  Title = "testjwt", Version = "v1" );
            );
            services.Configure<tokenModel>(Configuration.GetSection("JWTTokenconfig"));
            var token = Configuration.GetSection("JWTTokenconfig").Get<tokenModel>();
        

到目前为止,我们完成了一些基础工作,下面再webapi中注入jwt的验证服务,并在中间件管道中启用authentication中间件。startup类中要引用jwt验证服务的命名空间。注:这两个包要自己NuGet下载下来。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;

然后在ConfigureServices方法中添加如下配置

  services.AddAuthentication(option =>
            
                option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            ).AddJwtBearer(option =>
            
                option.RequireHttpsMetadata = false;
                option.SaveToken = true;
                option.TokenValidationParameters = new TokenValidationParameters
                
                    ValidateIssuer = false,//是否验证Issuer
                    ValidateAudience = false,//是否验证Audience
                    ValidateIssuerSigningKey = true,//是否验证SigningKey
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(token.Secret)),//拿到SigningKey
                    ValidIssuer = token.Issuer,
                    ValidAudience = token.Audience,
                    ClockSkew = TimeSpan.FromMinutes(0)//设置缓冲时间,token的总有有效时间等于这个时间加上jwt的过期时间,如果不配置默认是5分钟
                ;
            );

再在Configure方法中启用验证

   public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "testjwt v1"));
            

            app.UseHttpsRedirection();

            app.UseRouting();

           //开启下面两个配置
            app.UseAuthentication();//认证
            app.UseAuthorization();//授权

            app.UseEndpoints(endpoints =>
            
                endpoints.MapControllers();
            );
        

注意,这里必须要把认证和授权都开启,如果只开一个在后面验证时一直都会报一个401错误,而且二者顺序不能倒置。
上面完成了JWT验证的功能。接下来就是实现jwttoken的签发流程了。一般我们的流程是用户输入账号密码我们校验是否正确,再确定是否签发token.

下面我们创建一个用户类Users用来模拟正常的模拟登录时存储用户信息,如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ZettlercnMIS.Api.Models

    public class User
    
        public string UserName  get; set; 

        public string Password  get; set; 
    


然后创建一个控制器来让用户请求token

namespace ZettlercnMIS.Api.Controllers

    [Route("api/[controller]/[action]")]
    public class HomeController : Controller
    
      
        [HttpGet]
        public string GetToken(User user) 
            return "";
        
    


目前上面的控制器只实现了基本的逻辑,下面我们要创建签发token的服务,去完成具体的业务。第一步我们先在IdentityServer文件夹下创建对应的服务接口,命名为IAuthenticateService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using testjwt.Models;

namespace ZettlercnMIS.Api.Controllers

    public interface IAuthenticateService
    
        bool IsAuthenticated(User request, out string token);
    


然后实现接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using testjwt.Models;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.IdentityModel.Tokens.Jwt;

namespace ZettlercnMIS.Api.Controllers

    public class AuthenticateService : IAuthenticateService
    
        public bool IsAuthenticated(User request, out string token)
        
            throw new NotImplementedException();
        
    


在Startup的ConfigureServices方法中注册服务

services.AddScoped<IAuthenticateService, AuthenticateService>();
1

然后在HomeController中注入IAuthenticateService服务,具体如下

using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ZettlercnMIS.Api.IdentityServer;
using ZettlercnMIS.Api.Models;
using ZettlercnMIS.Utility;

namespace ZettlercnMIS.Api.Controllers

    [Route("api/[controller]/[action]")]
    public class HomeController : Controller
    
        private readonly IAuthenticateService _authenservice;
        public HomeController(IAuthenticateService service)
        
            _authenservice = service;
        

        /// <summary>
        /// 通过身份验证获取token令牌
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        [HttpPost]
        public LoginResult GetToken([FromBody] User user)
        
            LoginResult rs = new LoginResult();
            ///正常来说这里应该是链接数据库查询用户名密码是否正确,但是这里我们直接模拟一下测试就可以了
            if (user.UserName != "abc" || user.Password != "123")
            
                rs.Message = "fail";
                rs.Success = false;
            
            //签发token
            string token;
            if (_authenservice.IsAuthenticated(user, out token))
            
                rs.Message = "已获取token";
                rs.Token = token;
                rs.Success = true;
            
            return rs;
        
        /// <summary>
        /// 测试一个带身份验证的API接口
        /// </summary>
        /// <returns></returns>
        [Authorize]
        [HttpGet]
        public string GetAPI()
        
            return "";
        
    


将User注入到容器中

services.AddScoped<User>();

接下来我们完善一下继承了IAuthenticateService接口的AuthenticateService类,首先先注入tokenmodel,然后实现一下生成token的逻辑

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using ZettlercnMIS.Api.Models;

namespace ZettlercnMIS.Api.IdentityServer


    public class AuthenticateService : IAuthenticateService
    
        public readonly tokenModel _jwtModel;
        public AuthenticateService(IOptions<tokenModel> jwtModel) 
            _jwtModel = jwtModel.Value;
        
        public bool IsAuthenticated(User user, out string token)
        
            
            token = string.Empty;
           
            var claims = new[] 
                new Claim(ClaimTypes.Name,user.UserName),
                new Claim(ClaimTypes.Role, "admin, Manage")
            ;
            //密钥
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtModel.Secret));
            //凭证
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            //生成Token
            var jwtToken = new JwtSecurityToken(
                _jwtModel.Issuer,
                _jwtModel.Audience, 
                claims,
                expires: DateTime.Now.AddMinutes(_jwtModel.AccessExpiration),
                signingCredentials: 
                credentials);

            token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
            return true;

        
    

  

特别注意下tokenmodel的注入是以IOptions的接口类型注入的,在Startpup中我们是通过配置项的方式注册tokenmodel类型的。
以上就已经实现jwttoken验证的基本方法了,HOME控制器来测试一下

using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ZettlercnMIS.Api.IdentityServer;
using ZettlercnMIS.Api.Models;
using ZettlercnMIS.Utility;

namespace ZettlercnMIS.Api.Controllers

    [Route("api/[controller]/[action]")]
    public class HomeController : Controller
    
        private readonly IAuthenticateService _authenservice;
        public HomeController(IAuthenticateService service)
        
            _authenservice = service;
        

        /// <summary>
        /// 通过身份验证获取token令牌
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        [HttpPost]
        public LoginResult GetToken([FromBody] User user)
        
            LoginResult rs = new LoginResult();
            ///正常来说这里应该是链接数据库查询用户名密码是否正确,但是这里我们直接模拟一下测试就可以了
            if (user.UserName != "abc" || user.Password != "123")
            
                rs.Message = "fail";
                rs.Success = false;
            
            //签发token
            string token;
            if (_authenservice.IsAuthenticated(user, out token))
            
                rs.Message = "已获取token";
                rs.Token = token;
                rs.Success = true;
            
            return rs;
        
        /// <summary>
        /// 测试一个带身份验证的API接口
        /// </summary>
        /// <returns></returns>
        [Authorize]
        [HttpGet]
        public string GetAPI()
        
            return "恭喜你又学会一个新技能!";
        
    


这里加上了[Authorize]特性就说明要校验token
运行项目

我们先不获取token然后直接去请求test

发生了401错误,就是token验证失败
接下来我们测试获取token,我们写死的账号密码,张三、123

获取到了token,接下来我们用接口测试工具带上token来测试一下test(别问为什么要用接口测试工具,但凡swagger要是支持在请求头里面写数据我也不会专门下个测试工具。),这里我用的是apipost怎么使用这里就不阐述了。

记得这里选Bearer auth认证,因为我们在配置的时候使用的时这个。然后将生成的token复制过去然后点击发送,这个时候就请求成功了。

为方面演示:利用Jquery 前端ajax提交请求

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <meta charset="utf-8" />
    @*<script src="~/lib/jquery/dist/jquery.min.js"></script>*@
    <script src="~/lib/jquery-3.3.1/jquery-3.1.1.js"></script>
    <script src="~/lib/jquery-3.3.1/jquery-3.1.1.min.js"></script>
</head>
<body>
    <fieldset>
        <legend>身份验证</legend>
        <form id="login_form">
            <label for="UserName">用户名:</label><input type="text" name="userName" id="userName" value="admin" />
            获取 ASP .Net Core WebAPI 中的所有身份角色

ASP.NET Core 2.2 WebAPI 405 方法不允许

ASP.NET Core 5.0 WebAPI 中的多种身份验证方案

在 ASP.NET Core WebAPI 中获取 OData 计数

使用 ASP.NET Core MVC 创建 Web API

[Asp.Net Core]ASP.NET Core WebApi使用Swagger生成api说明文档看这篇就够了