欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.net core webapi jwt 更为清爽的认证详解

程序员文章站 2023-12-04 23:22:28
我的方式非主流,控制却可以更加灵活,喜欢的朋友,不妨花一点时间学习一下 jwt认证分为两部分,第一部分是加密解密,第二部分是灵活的应用于中间件,我的处理方式是将获取tok...

我的方式非主流,控制却可以更加灵活,喜欢的朋友,不妨花一点时间学习一下

jwt认证分为两部分,第一部分是加密解密,第二部分是灵活的应用于中间件,我的处理方式是将获取token放到api的一个具体的controller中,将发放token与验证分离,token的失效时间,发证者,使用者等信息存放到config中。

1.配置:

在appsettings.json中增加配置

"jwt": {
"issuer": "issuer",//随意定义
"audience": "audience",//随意定义
"secretkey": "abc",//随意定义
"lifetime": 20, //单位分钟
"validatelifetime": true,//验证过期时间
"headfield": "useless", //头字段
"prefix": "prefix", //前缀
"ignoreurls": [ "/auth/gettoken" ]//忽略验证的url
}

2:定义配置类:

internal class jwtconfig
  {
    public string issuer { get; set; }
    public string audience { get; set; }

    /// <summary>
    /// 加密key
    /// </summary>
    public string secretkey { get; set; }
    /// <summary>
    /// 生命周期
    /// </summary>
    public int lifetime { get; set; }
    /// <summary>
    /// 是否验证生命周期
    /// </summary>
    public bool validatelifetime { get; set; }
    /// <summary>
    /// 验证头字段
    /// </summary>
    public string headfield { get; set; }
    /// <summary>
    /// jwt验证前缀
    /// </summary>
    public string prefix { get; set; }
    /// <summary>
    /// 忽略验证的url
    /// </summary>
    public list<string> ignoreurls { get; set; }
  }

3.加密解密接口:

 public interface ijwt
  {
    string gettoken(dictionary<string, string> clims);
    bool validatetoken(string token,out dictionary<string ,string> clims);
  }

4.加密解密的实现类:

install -package system.identitymodel.tokens.jwt

 public class jwt : ijwt
  {
    private iconfiguration _configuration;
    private string _base64secret;
    private jwtconfig _jwtconfig = new jwtconfig();
    public jwt(iconfiguration configration)
    {
      this._configuration = configration;
      configration.getsection("jwt").bind(_jwtconfig);
      getsecret();
    }
    /// <summary>
    /// 获取到加密串
    /// </summary>
    private void getsecret()
    {
      var encoding = new system.text.asciiencoding();
      byte[] keybyte = encoding.getbytes("salt");
      byte[] messagebytes = encoding.getbytes(this._jwtconfig.secretkey);
      using (var hmacsha256 = new hmacsha256(keybyte))
      {
        byte[] hashmessage = hmacsha256.computehash(messagebytes);
        this._base64secret= convert.tobase64string(hashmessage);
      }
    }
    /// <summary>
    /// 生成token
    /// </summary>
    /// <param name="claims"></param>
    /// <returns></returns>
    public string gettoken(dictionary<string, string> claims)
    {
      list<claim> claimsall = new list<claim>();
      foreach (var item in claims)
      {
        claimsall.add(new claim(item.key, item.value));
      }
      var symmetrickey = convert.frombase64string(this._base64secret);
      var tokenhandler = new jwtsecuritytokenhandler();
      var tokendescriptor = new securitytokendescriptor
      {
        issuer = _jwtconfig.issuer,
        audience = _jwtconfig.audience,
        subject = new claimsidentity(claimsall),
        notbefore = datetime.now,
        expires = datetime.now.addminutes(this._jwtconfig.lifetime),
        signingcredentials =new signingcredentials(new symmetricsecuritykey(symmetrickey),
                      securityalgorithms.hmacsha256signature)
      };
      var securitytoken = tokenhandler.createtoken(tokendescriptor);
      return tokenhandler.writetoken(securitytoken);
    }
    public bool validatetoken(string token, out dictionary<string, string> clims)
    {
      clims = new dictionary<string, string>();
      claimsprincipal principal = null;
      if (string.isnullorwhitespace(token))
      {
        return false;
      }
      var handler = new jwtsecuritytokenhandler();
      try
      {
        var jwt = handler.readjwttoken(token);

        if (jwt == null)
        {
          return false;
        }
        var secretbytes = convert.frombase64string(this._base64secret);
        var validationparameters = new tokenvalidationparameters
        {
          requireexpirationtime = true,
          issuersigningkey = new symmetricsecuritykey(secretbytes),
          clockskew = timespan.zero,
          validateissuer = true,//是否验证issuer
          validateaudience = true,//是否验证audience
          validatelifetime = this._jwtconfig.validatelifetime,//是否验证失效时间
          validateissuersigningkey = true,//是否验证securitykey
          validaudience = this._jwtconfig.audience,
          validissuer = this._jwtconfig.issuer
        };
        securitytoken securitytoken;
        principal = handler.validatetoken(token, validationparameters, out securitytoken);
        foreach (var item in principal.claims)
        {
          clims.add(item.type, item.value);
        }
        return true;
      }
      catch (exception ex)
      {
        return false;
      }
    }
  }

5.定义获取token的controller:

在startup.configureservices中注入 ijwt

services.addtransient<ijwt, jwt>(); // jwt注入

[route("[controller]/[action]")]
  [apicontroller]
  public class authcontroller : controllerbase
  {
    private ijwt _jwt;
    public authcontroller(ijwt jwt)
    {
      this._jwt = jwt;
    }
    /// <summary>
    /// gettoken
    /// </summary>
    /// <returns></returns>
    [httppost]
    public iactionresult gettoken()
    {
      if (true)
      {
        dictionary<string, string> clims = new dictionary<string, string>();
        clims.add("username", username);
        return new jsonresult(this._jwt.gettoken(clims));
      }
    }
  }

6.创建中间件:

 public class usejwtmiddleware
  {
    private readonly requestdelegate _next;
    private jwtconfig _jwtconfig =new jwtconfig();
    private ijwt _jwt;
    public usejwtmiddleware(requestdelegate next, iconfiguration configration,ijwt jwt)
    {
      _next = next;
      this._jwt = jwt;
      configration.getsection("jwt").bind(_jwtconfig);
    }
    public task invokeasync(httpcontext context)
    {
      if (_jwtconfig.ignoreurls.contains(context.request.path))
      {
        return this._next(context);
      }
      else
      {
        if (context.request.headers.trygetvalue(this._jwtconfig.headfield, out microsoft.extensions.primitives.stringvalues authvalue))
        {
          var authstr = authvalue.tostring();
          if (this._jwtconfig.prefix.length > 0)
          {
            authstr = authvalue.tostring().substring(this._jwtconfig.prefix.length+1, authvalue.tostring().length -(this._jwtconfig.prefix.length+1));
          }
          if (this._jwt.validatetoken(authstr, out dictionary<string, string> clims))
          {
            foreach (var item in clims)
            {
              context.items.add(item.key, item.value);
            }
            return this._next(context);
          }
          else
          {
            context.response.statuscode = 401;
            context.response.contenttype = "application/json";
            return context.response.writeasync("{\"status\":401,\"statusmsg\":\"auth vaild fail\"}");
          }
        }
        else
        {
          context.response.statuscode = 401;
          context.response.contenttype = "application/json";
          return context.response.writeasync("{\"status\":401,\"statusmsg\":\"auth vaild fail\"}");
        }
      }
    }
  }

7.中间件暴露出去

public static class useusejwtmiddlewareextensions
  {
    /// <summary>
    /// 权限检查
    /// </summary>
    /// <param name="builder"></param>
    /// <returns></returns>
    public static iapplicationbuilder usejwt(this iapplicationbuilder builder)
    {
      return builder.usemiddleware<usejwtmiddleware>();
    }
  }

8.在startup.configure中使用中间件:

app.usejwt();

以1的配置为例:

除了请求 /auth/gettoken 不需要加头信息外,其他的请求一律要求头信息中必须带着

userless:prefix (从auth/gettoken中获取到的token)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。