ASP.Net Core 3.1 中使用JWT認證

来源:https://www.cnblogs.com/liuww/archive/2020/01/10/12177272.html
-Advertisement-
Play Games

JWT認證簡單介紹 關於Jwt的介紹網上很多,此處不在贅述,我們主要看看jwt的結構。 JWT主要由三部分組成,如下: 包含token的元數據,主要是加密演算法,和簽名的類型,如下麵的信息,說明瞭 加密的對象類型是JWT,加密演算法是HMAC SHA 256 然後需要通過BASE64編碼後存入token ...


JWT認證簡單介紹

    關於Jwt的介紹網上很多,此處不在贅述,我們主要看看jwt的結構。

    JWT主要由三部分組成,如下:

HEADER.PAYLOAD.SIGNATURE

    HEADER包含token的元數據,主要是加密演算法,和簽名的類型,如下麵的信息,說明瞭

加密的對象類型是JWT,加密演算法是HMAC SHA-256

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

    然後需要通過BASE64編碼後存入token中

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9    

    Payload主要包含一些聲明信息(claim),這些聲明是key-value對的數據結構。

通常如用戶名,角色等信息,過期日期等,因為是未加密的,所以不建議存放敏感信息。

{"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name":"admin","exp":1578645536,"iss":"webapi.cn","aud":"WebApi"}

也需要通過BASE64編碼後存入token中

eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJleHAiOjE1Nzg2NDU1MzYsImlzcyI6IndlYmFwaS5jbiIsImF1ZCI6IldlYkFwaSJ9 

     Signaturejwt要符合jws(Json Web Signature)的標準生成一個最終的簽名。把編碼後的Header和Payload信息加在一起,然後使用一個強加密演算法,如 HmacSHA256,進行加密。HS256(BASE64(Header).Base64(Payload),secret)

2_akEH40LR2QWekgjm8Tt3lesSbKtDethmJMo_3jpF4

    最後生成的token如下

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJleHAiOjE1Nzg2NDU1MzYsImlzcyI6IndlYmFwaS5jbiIsImF1ZCI6IldlYkFwaSJ9.2_akEH40LR2QWekgjm8Tt3lesSbKtDethmJMo_3jpF4

   

開發環境

框架:asp.net 3.1

IDE:VS2019

ASP.NET 3.1 Webapi中使用JWT認證

    命令行中執行執行以下命令,創建webapix項目:

dotnet new webapi -n Webapi -o WebApi

    特別註意的時,3.x預設是沒有jwt的Microsoft.AspNetCore.Authentication.JwtBearer庫的,所以需要手動添加NuGet Package,切換到項目所在目錄,執行 .net cli命令

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 3.1.0

    創建一個簡單的POCO類,用來存儲簽發或者驗證jwt時用到的信息

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

namespace Webapi.Models

{
    public class TokenManagement
    {
        [JsonProperty("secret")]
        public string Secret { get; set; }

        [JsonProperty("issuer")]
        public string Issuer { get; set; }

        [JsonProperty("audience")]
        public string Audience { get; set; }

        [JsonProperty("accessExpiration")]
        public int AccessExpiration { get; set; }

        [JsonProperty("refreshExpiration")]
        public int RefreshExpiration { get; set; }
    }
}

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

"tokenManagement": {
        "secret": "123456",
        "issuer": "webapi.cn",
        "audience": "WebApi",
        "accessExpiration": 30,
        "refreshExpiration": 60
    }

    然後再startup類的ConfigureServices方法中增加讀取配置信息

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.Configure<TokenManagement>(Configuration.GetSection("tokenManagement"));
            var token = Configuration.GetSection("tokenManagement").Get<TokenManagement>();

        }

    到目前為止,我們完成了一些基礎工作,下麵再webapi中註入jwt的驗證服務,併在中間件管道中啟用authentication中間件。

    startup類中要引用jwt驗證服務的命名空間

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

    然後在ConfigureServices方法中添加如下邏輯

services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.Secret)),
                    ValidIssuer = token.Issuer,
                    ValidAudience = token.Audience,
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            });

    再Configure方法中啟用驗證

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseAuthentication();
            app.UseRouting();

            app.UseAuthorization();

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

    上面完成了JWT驗證的功能,下麵就需要增加簽發token的邏輯。我們需要增加一個專門用來用戶認證和簽發token的控制器,命名成AuthenticationController,同時增加一個請求的DTO類

public class LoginRequestDTO
    {
        [Required]
        [JsonProperty("username")]
        public string Username { get; set; }


        [Required]
        [JsonProperty("password")]
        public string Password { get; set; }
    }
[Route("api/[controller]")]
    [ApiController]
    public class AuthenticationController : ControllerBase
    {
        [AllowAnonymous]
         [HttpPost, Route("requestToken")]
        public ActionResult RequestToken([FromBody] LoginRequestDTO request)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest("Invalid Request");
            }

            return Ok();

        }
    }

    目前上面的控制器只實現了基本的邏輯,下麵我們要創建簽發token的服務,去完成具體的業務。第一步我們先創建對應的服務介面,命名為IAuthenticateService

public interface IAuthenticateService
    {
        bool IsAuthenticated(LoginRequestDTO request, out string token);
    }

    接下來,實現介面

public class TokenAuthenticationService : IAuthenticateService
    {
        public bool IsAuthenticated(LoginRequestDTO request, out string token)
        {
            throw new NotImplementedException();
        }
    }

    在StartupConfigureServices方法中註冊服務

services.AddScoped<IAuthenticateService, TokenAuthenticationService>();

    在Controller中註入IAuthenticateService服務,並完善action

public class AuthenticationController : ControllerBase
    {
        private readonly IAuthenticateService _authService;
        public AuthenticationController(IAuthenticateService authService)
        {
            this._authService = authService;
        }
        [AllowAnonymous]
         [HttpPost, Route("requestToken")]
        public ActionResult RequestToken([FromBody] LoginRequestDTO request)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest("Invalid Request");
            }

            string token;
            if (_authService.IsAuthenticated(request, out token))
            {
                return Ok(token);
            }

            return BadRequest("Invalid Request");

        }
    }

    正常情況,我們都會根據請求的用戶和密碼去驗證用戶是否合法,需要連接到資料庫獲取數據進行校驗,我們這裡為了方便,假設任何請求的用戶都是合法的。

    這裡單獨加個用戶管理的服務,不在IAuthenticateService這個服務裡面添加相應邏輯,主要遵循了職責單一原則。首先和上面一樣,創建一個服務介面IUserService

public interface IUserService
    {
        bool IsValid(LoginRequestDTO req);
    }

    實現IUserService介面

public class UserService : IUserService
    {
        //模擬測試,預設都是人為驗證有效
        public bool IsValid(LoginRequestDTO req)
        {
            return true;
        }
    }

    同樣註冊到容器中

services.AddScoped<IUserService, UserService>();

    接下來,就要完善TokenAuthenticationService簽發token的邏輯,首先要註入IUserService 和 TokenManagement,然後實現具體的業務邏輯,這個token的生成還是使用的Jwt的類庫提供的api,具體不詳細描述。

特別註意下TokenManagement的註入是已IOptions的介面類型註入的,還記得在Startpup中嗎?我們是通過配置項的方式註冊TokenManagement類型的。

 public class TokenAuthenticationService : IAuthenticateService
    {
        private readonly IUserService _userService;
        private readonly TokenManagement _tokenManagement;
        public TokenAuthenticationService(IUserService userService, IOptions<TokenManagement> tokenManagement)
        {
            _userService = userService;
            _tokenManagement = tokenManagement.Value;
        }
        public bool IsAuthenticated(LoginRequestDTO request, out string token)
        {
            token = string.Empty;
            if (!_userService.IsValid(request))
                return false;
            var claims = new[]
            {
                new Claim(ClaimTypes.Name,request.Username)
            };
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenManagement.Secret));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var jwtToken = new JwtSecurityToken(_tokenManagement.Issuer, _tokenManagement.Audience, claims, expires: DateTime.Now.AddMinutes(_tokenManagement.AccessExpiration), signingCredentials: credentials);

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

            return true;

        }
    }

  準備好測試試用的APi,打上Authorize特性,表明需要授權!

[ApiController]
    [Route("[controller]")]
    [Authorize]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }

    支持我們可以測試驗證了,我們可以使用postman來進行http請求,先啟動http服務,獲取url,先測試一個訪問需要授權的介面,但沒有攜帶token信息,返回是401,表示未授權

    下麵我們先通過認證介面,獲取token,居然報錯,查詢了下,發現HS256演算法的秘鑰長度最新為128位,轉換成字元至少16字元,之前設置的秘鑰是123456,所以導致異常。

System.ArgumentOutOfRangeException: IDX10603: Decryption failed. Keys tried: 'HS256'. Exceptions caught: '128'. token: '48' (Parameter 'KeySize') at

    更新秘鑰

 "tokenManagement": {
        "secret": "123456123456123456",
        "issuer": "webapi.cn",
        "audience": "WebApi",
        "accessExpiration": 30,
        "refreshExpiration": 60
    }

    重新發起請求,成功獲取token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJleHAiOjE1Nzg2NDUyMDMsImlzcyI6IndlYmFwaS5jbiIsImF1ZCI6IldlYkFwaSJ9.AehD8WTAnEtklof2OJsvg0U4_o8_SjdxmwUjzAiuI-o

    把token帶到之前請求的api中,重新測試,成功獲取數據

總結

    基於token的認證方式,讓我們構建分散式/松耦合的系統更加容易。任何地方生成的token,只有擁有相同秘鑰,就可以再任何地方進行簽名校驗。

    當然要用好jwt認證方式,還有其他安全細節需要處理,比如palyload中不能存放敏感信息,使用https的加密傳輸方式等等,可以根據業務實際需要再進一步安全加固!

   同時我們也發現使用token,就可以擺脫cookie的限制,所以JWT是移動app開發的首選!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 歡迎大家關註我的微信公眾號,不定時更新 使用方法非常簡單,只需放到你的 utils.js 工具文件中,直接 export const 加上我的封裝方法,在別的文件中使用{方法1,方法2,方法3...}引用後就可以直接使用了! 001.輸入一個值,返回其數據類型 type = para => { re ...
  • 面向過程就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用 的時候一個一個依次調用就可以了;面向對象是把構成問題事務分解成各個對象,建立對 象的目的不是為了完成一個步驟,而是為了描敘某個事物在整個解決問題的步驟中的行為。 面向過程 理解為怎麼做,更註重過程的實現 1、首先把完成某 ...
  • 轉行的同學最關心的就是Python的薪資情況了。今天我們就來具體看一下Python現在的薪資情況。 這是一張網友統計的Python工程師工資情況圖表。詳細說明瞭現在Python工程師在各個城市的薪資情況。 ! 從圖中我們可以看到: 北京Python工程師平均薪資22K,是當之無愧的程式員最高薪的地方 ...
  • 本人免費整理了Java高級資料,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高併發分散式等教程,一共30G,需要自己領取。傳送門:https://mp.weixin.qq.com/s/osB-BOl6W-ZLTSttTkqMPQ 從 ...
  • 相信對現在Java碼農來說,@Autowired跟@Resource並不陌生,二者都可以自動註入,但是兩者的區別很多時候並沒有被註意到。 一、註解的出處 @Autowired是Spring提供的註解,需要導入包org.springframework.beans.factory.annotation. ...
  • 簡介 使用spring boot可以輕鬆創建獨立的,基於Spring框架的生產級別應用程式。Spring boot應用程式只需要很少的spring配置 特點 創建獨立的Spring應用程式 直接嵌入tomcat 提供starter依賴項,簡化構建配置 儘可能自動配置Spring和三方庫 提供生產就緒 ...
  • 微信公眾號: "Dotnet9" ,網站: "Dotnet9" ,問題或建議: "請網站留言" , 如果對您有所幫助: "歡迎贊賞" 。 使用SignalR從服務端主動推送警報日誌到各種終端(桌面、移動、網頁) 閱讀導航 1. 本文背景 2. 代碼實現 3. 本文參考 1.本文背景 工作上有個業務, ...
  • IEnumerable**和**IEnumerator**,如果不仔細看,是不是都以為它們是同樣的一個單詞。特別是我們習慣了每天看大量的中文,這種只是很小區別的單詞更是容易犯錯。在.NET的世界里好像有這種類似單詞的情況還真的不少,比如**Authentication**和**Authorizati... ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...