.Net Core WebApi集成JWT实现身份认证

前言

最近有朋友问我jwt的使用,有感而发写篇文章,由于一直使用框架开发,这些技术还真有些手生了,特此写篇文章做下笔记。

随着技术的发展,分布式web应用的普及,通过session管理用户登录状态成本越来越高,因此慢慢发展成为token的方式做登录身份校验,然后通过token去取redis中的缓存的用户信息,随着之后jwt的出现,校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录更为简单。JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。适用于多客户端的前后端解决方案,JWT 是无状态化的,更适用于 RESTful 风格的接口验证。本文主要介绍使用JWT进行接口身份认证。

jwt介绍

JWT的结构体是什么样的?
JWT由三部分组成,分别是头信息、有效载荷、签名,中间以(.)分隔
(1)header(头信息) 描述 JWT 的元数据的JSON对象
由两部分组成,令牌类型(即:JWT)、散列算法(HMAC、RSASSA、RSASSA-PSS等)

{"alg":"HS256","typ":"JWT"}

(2)Payload(有效载荷)
JWT的第二部分是payload,其中包含claims。claims是关于实体(常用的是用户信息)和其他数据的声明,claims有三种类型: registered, public, and private claims。
一个用来存放实际需要传递的数据的JSON 对象。如:

"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "admin",
    "exp": 1610877510,
    "iss": "cba",
    "aud": "cba"
}

(3)Signature
要创建签名部分,必须采用编码的Header,编码的Payload,秘钥,Header中指定的算法,并对其进行签名。 对前两部分(Header、Payload)的签名,防止数据篡改。

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

JWT示例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJleHAiOjE2MTA4Nzc1MTAsImlzcyI6ImNiYSIsImF1ZCI6ImNiYSJ9.O9lbZwfqRuA6vKcRCfYieA1zLkTPppdSvTc8UzwCkNw 

一.准备工作

Demo可以在我的资源中下载 Demo
https://download.csdn.net/download/qq_39569480/24392815
(1).添加NuGet程序包
Microsoft.AspNetCore.Authentication.JwtBearer
在这里插入图片描述
(2).appsettings.json

"JwtConfig": {
    "SecretKey": "123123123123", // 密钥   可以是guid 也可以是随便一个字符串
    "Issuer": "zhangsan", // 颁发者
    "Audience": "zhangsan", // 接收者
    "Expired": 30 // 过期时间(30min)
  }, 

二.创建JWT配置类

using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 /// <summary>
    /// jwt配置
    /// </summary>
    public class JwtConfig : IOptions<JwtConfig>
    {
        public JwtConfig Value => this;
        public string SecretKey { get; set; }
        public string Issuer { get; set; }
        public string Audience { get; set; }
        public int Expired { get; set; }
        public DateTime NotBefore => DateTime.UtcNow;
        public DateTime IssuedAt => DateTime.UtcNow;
        public DateTime Expiration => IssuedAt.AddMinutes(Expired);
        private SecurityKey SigningKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));
        public SigningCredentials SigningCredentials =>
            new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);
    }

三.创建JWT工具类

 public class GenerateJwt
    {
        private readonly JwtConfig _jwtConfig;
        public GenerateJwt(IOptions<JwtConfig> jwtConfig)
        {
            _jwtConfig = jwtConfig.Value;
        }
        /// <summary>
        /// 生成token
        /// </summary>
        /// <param name="sub"></param>
        /// <param name="customClaims">携带的用户信息</param>
        /// <returns></returns>
        public JwtTokenResult GenerateEncodedTokenAsync(string sub, LoginUserModel customClaims)
        {
            //创建用户身份标识,可按需要添加更多信息
            var claims = new List<Claim>
            {
                new Claim("userid", customClaims.userid),
                new Claim("username", customClaims.username),
                //new Claim("realname",customClaims.realname),
                //new Claim("roles", string.Join(";",customClaims.roles)),
                //new Claim("permissions", string.Join(";",customClaims.permissions)),
                //new Claim("normalPermissions", string.Join(";",customClaims.normalPermissions)),
                new Claim(JwtRegisteredClaimNames.Sub, sub),
            };
            //创建令牌
            var jwt = new JwtSecurityToken(
                issuer: _jwtConfig.Issuer,
                audience: _jwtConfig.Audience,
                claims: claims,
                notBefore: _jwtConfig.NotBefore,
                expires: _jwtConfig.Expiration,
                signingCredentials: _jwtConfig.SigningCredentials);
            string access_token = new JwtSecurityTokenHandler().WriteToken(jwt);
            return new JwtTokenResult()
            {
                access_token = access_token,
                expires_in = _jwtConfig.Expired * 60,
                token_type = JwtBearerDefaults.AuthenticationScheme,
                user = customClaims
            };
        }
    }

四.JwtTokenResult

登录成功生成jwt返回给前端的model,LoginUserModel是用户信息model,我这里携带是为了避免前端解析token。

/// <summary>
    /// 登录成功返回model
    /// </summary>
    public class JwtTokenResult
    {
        public string access_token { get; set; }
        public string refresh_token { get; set; }
        /// <summary>
        /// 过期时间(单位秒)
        /// </summary>
        public int expires_in { get; set; }
        public string token_type { get; set; }
        public LoginUserModel user { get; set; }
    }

    public class LoginUserModel
    {
        public string userid { get; set; }
        public string username { get; set; }
        public string realname { get; set; }
        public string roles { get; set; }
        public string permissions { get; set; }
        public string normalPermissions { get; set; }
    }

五.Startup配置

(1) .ConfigureServices注入jwt

             //注入jwt
            services.AddScoped<GenerateJwt>();
            services.Configure<JwtConfig>(Configuration.GetSection("JwtConfig"));

            #region jwt验证
            var jwtConfig = new JwtConfig();
            Configuration.Bind("JwtConfig", jwtConfig);
            services
                .AddAuthentication(option =>
                {
                    //认证middleware配置
                    option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        //Token颁发机构
                        ValidIssuer = jwtConfig.Issuer,
                        //颁发给谁
                        ValidAudience = jwtConfig.Audience,
                        //这里的key要进行加密
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecretKey)),
                        //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
                        ValidateLifetime = true,
                    };
                });
            #endregion

(2).Configure

 app.UseAuthentication();//要在授权之前认证

在这里插入图片描述

六.调用获取token

using JwtDemo.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace JwtDemo.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : Controller
    {
        private readonly GenerateJwt _generateJwt;
        public TestController(GenerateJwt generate) {
            this._generateJwt = generate;
        }
        [HttpGet]
        public ActionResult GetLogin(string name,string userid) {

            var claims = new LoginUserModel()
            {
                userid = userid,
                username = name,
                realname = name,
                //roles = string.Join(";", user.roles),
                //permissions = string.Join(";", user.permissions), 
            };
            var refreshToken = Guid.NewGuid().ToString(); 
			//当然  你要在生成token之前要验证一下账户是否在数据库存在  存在则生成
			/*
			数据库查询
			*/
            var jwtTokenResult = _generateJwt.GenerateEncodedTokenAsync(userid, claims);
            jwtTokenResult.refresh_token = refreshToken;
            return Json(jwtTokenResult);//这里可按需返回   如果不想返回用户信息  比如密码  可以在_generateJwt.GenerateEncodedTokenAsync去掉哦
        }
    }
}

swagger调试
在这里插入图片描述

postman请求
在这里插入图片描述

七.在后台中有时候我们需要获取当前用户的一些属性

JwtUser,用户解析jwt 获取当前用户信息

public static class JwtUser
    {
        /// <summary>
        /// 解析jwt 获取当前用户信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns>用户信息</returns>
        public static LoginUserModel GetRequestUser(this HttpRequest request)
        {
            var authorization = request.Headers["Authorization"].ToString();
            var auth = authorization.Split(" ")[1];
            var jwtArr = auth.Split('.');
            var dic = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));
            //解析Claims
            var reqUser = new LoginUserModel
            {
                userid = dic["userid"],
                username = dic["username"],
                realname = dic["realname"],
                roles = dic["roles"],
                permissions = dic["permissions"],
                normalPermissions = dic["normalPermissions"],
            };
            return reqUser;
        } 
    }

控制器中这样获取

[HttpGet]
        public ActionResult GetUser() {

             var aa= JwtUser.GetRequestUser(this.Request);
            return Json(aa.userid+"===="+ aa.username); //获取到当前用户的姓名  角色等等就可以做一些业务处理
        }

八.使用

在接口上标记身份验证
[AllowAnonymous] 标记此标识的接口是不需要验证的
[Authorize] 标记此标识的接口需要验证

		[Authorize]
		[HttpGet] 
        public ActionResult GetDataList(string 参数)
        {
            /*
             业务代码
             */
            return Json("");
        }

也可以直接标记在控制器上
获取token的接口最好单独写在一个控制器
在这里插入图片描述

九.Swagger UI添加认证

在项目中通常都添加了Swagger UI来展示接口及基础测试,那么如果添加了认证后,如何在调用接口前添加认证信息呢?
  
   在Startup中:ConfigureServices中添加Swagger设置时,添加认证设置

#region 启用swagger验证功能
                //添加一个必须的全局安全信息,和AddSecurityDefinition方法指定的方案名称一致即可。
                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference {
                            Type = ReferenceType.SecurityScheme,
                            Id = "Bearer"
                            }
                        },
                    new string[] { }
                    }
                });
                c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
                    Name = "Authorization",//jwt默认的参数名称
                    In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                    Type = SecuritySchemeType.ApiKey,
                    BearerFormat = "JWT",
                    Scheme = "Bearer",

                });
                #endregion

添加token即可
输入时 要输入Bearer +token 注意Bearer后有空格
在这里插入图片描述
在这里插入图片描述

ok感谢观看

  • 9
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香煎三文鱼

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值