asp net core 2.1 에서 jwt 를 어떻게 사용 하 는 지(원리 부터 정통 까지)

16851 단어 aspnetcore2.1jwt
왜 Jwt 를 사용 합 니까?
최근 모 바 일 개발 의 열기 가 점점 커지 고 있 습 니 다.학교 에서 개최 하 는 각종 경 기 는 모 바 일 앱 으로 장면 을 지탱 해 야 하기 때문에 백 엔 드 를 쓰 는 사람 으로서 예전 의 세 션 기반 인증 방식 을 개선 할 필요 가 있 습 니 다.이 유 는 다음 과 같 습 니 다.
  • 모 바 일 엔 드 는 항상 장시간(1~2 주)온라인 을 유지 해 야 하지만 Session 은 서버 에 이렇게 오래 보관 하기 가 쉽 지 않 습 니 다.데이터 베 이 스 를 지속 적 으로 유지 할 수 있 지만 자원 이 많이 듭 니 다
  • 모 바 일 은 흔히 사용 하 는 웹 기술 이 아니 기 때문에 Cookie 에 숨겨 진 SessionId 는 서버 에 쉽게 전달 되 지 않 습 니 다
  • 서버 가 클 라 이언 트 에 노출 된 인 터 페 이 스 는 흔히 RESTful 스타일 인 데 이것 은 상태 가 없 는 API 스타일 이기 때문에 신분 인증 방식 이 가장 좋 고 상태 가 없 는 것 이 좋다
  • 그래서 저 는 Jwt(JSon Web Token)라 는 기술 을 선 택 했 습 니 다.Jwt 는 세 션 과 반대로 Jwt 는 사용자 정 보 를 Token 의 payload 필드 에 저장 해 클 라 이언 트 에 저장 하고 RSA 암호 화 방식 을 통 해 데이터 가 변경 되 지 않도록 데이터 의 유효성 을 검증 하 는 무상 태 분포 식 인증 방식 이다.
    다음은 Jwt 를 사용 하 는 시스템 의 인증 절차 입 니 다.

    이 를 통 해 알 수 있 듯 이 사용자 의 정 보 는 Token 에 저장 되 고 Token 은 사용자 의 장치 에 분포 되 어 있 기 때문에 서버 는 더 이상 메모리 에 사용자 정 보 를 저장 할 필요 가 없습니다.
    인증 한 Token 이 전달 할 때 상당히 간단 한 형식 으로 header 에 저장 되 어 클 라 이언 트 가 이 를 조작 하 는 데 편리 합 니 다.
    의 원리
    jwt 는 모든 언어 에 통용 되 며,비밀 키 만 알 면 다른 언어 는 jwt 의 유효성 을 판단 할 수 있 습 니 다.
    jwt 의 구성;Header 부분 Base 64 전환.Payload 부분 Base 64 전환.HS 256 방식 으로 비밀 키 에 따라 앞의 두 부분 을 암호 화한 다음 Base 64 로 전환 합 니 다.그 중에서 사용 하 는 hs 256 암호 화 는 header 부분 에서 지정 한 것 이 고 홈 페이지 를 통 해서 도 볼 수 있 습 니 다.다음 그림:

    원 리 는 이렇게 간단 하 다.그렇다면 도대체 C\#를 어떻게 사용 하여 실현 할 것 인가?그 정확성 을 어떻게 확정 할 것 인가?계속 하 세 요.
    C\#구현 사용 하기
    Microsoft.Identity Model.Tokens.dll,asp.net core 2.1 에 사용 할 오늘 의 방법 을 정의 합 니 다.다른 버 전이 없 으 면 nuget 라 이브 러 리 가 필요 합 니 다.
    
    /// <summary>
     ///   jwttoken,     
     /// </summary>
     /// <param name="payLoad"></param>
     /// <param name="header"></param>
     /// <returns></returns>
     public static string CreateToken(Dictionary<string, object> payLoad,int expiresMinute, Dictionary<string, object> header = null)
     {
      if (header == null)
      {
      header = new Dictionary<string, object>(new List<KeyValuePair<string, object>>() {
       new KeyValuePair<string, object>("alg", "HS256"),
       new KeyValuePair<string, object>("typ", "JWT")
      });
      }
      //  jwt    (      )
      var now = DateTime.UtcNow;
      payLoad["nbf"] = ToUnixEpochDate( now);//      
      payLoad["exp"] = ToUnixEpochDate(now.Add(TimeSpan.FromMinutes(expiresMinute)));//      
    
      var encodedHeader = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(header));
      var encodedPayload = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(payLoad));
    
      var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(securityKey));
      var encodedSignature = Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(encodedHeader, ".", encodedPayload))));
    
      var encodedJwt = string.Concat(encodedHeader, ".", encodedPayload, ".", encodedSignature);
      return encodedJwt;
     }
     public static long ToUnixEpochDate(DateTime date) =>  (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
    이 방법 은 매우 간단 합 니 다.header 키 쌍 과 pay Load 키 쌍 을 입력 한 다음 원리 에 따라 Base 64 변환 과 hs 256 암호 화 를 진행 합 니 다.그 다음 에 테스트 클래스 를 사용 하여 테스트 합 니 다.코드 는 다음 과 같 습 니 다.
    
    [TestMethod]
     public void TokenValidateTest()
     {
      Dictionary<string, object> payLoad = new Dictionary<string, object>();
      payLoad.Add("sub", "rober");
      payLoad.Add("jti", "09e572c7-62d0-4198-9cce-0915d7493806");
      payLoad.Add("nbf", null);
      payLoad.Add("exp", null);
      payLoad.Add("iss", "roberIssuer");
      payLoad.Add("aud", "roberAudience");
      payLoad.Add("age", 30);
    
      var encodeJwt = TokenContext.CreateToken(payLoad, 30);
    
      var result = TokenContext.Validate(encodeJwt, (load) => { return true; });
      Assert.IsTrue(result);
     }
    우선 뒤의 검증 을 상관 하지 않 고,이 가운데 생 성 된 encodeJwt 의 값 을 살 펴 보 자.3VLciIsImF1ZCI6InJVYmVyQXVkaWVuY2UiLCJhZ2UiOjMwQ.7Is2KYHAtSr5fW2gPU1jGeH Pzz2ULCZJGCWb40LSYyw

    첫 번 째 부분 과 두 번 째 부분 은 암호 화 된 것 이 아니 라 Base 64 변환 일 뿐 입 니 다.우 리 는 다른 언어 를 통 해 쉽게 변환 할 수 있 습 니 다.다음 과 같이 javascript 을 사용 하여 전환 할 수 있 습 니 다.window.atob(base 64 암호 화)window.btoa(base 64 복호화)
    
    var header=JSON.parse(window.atob('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'))
    다음 그림:

    나 는 다시 payLoa 를 바 꾸 었 다var payLoad=JSON.parse(window.atob('eyJzdWIiOiJyb2JlciIsImp0aSI6IjY0OWMyYjUxLTE4ZGQtNDEzYy05Yzc5LTI4NWNhMDAxODU2NSIsIm5iZiI6MTU0MDYxMDY2NSwiZXhwIjoxNTQwNjEyNDY1LCJpc3MiOiJyb2Jlcklzc3VlciIsImF1ZCI6InJvYmVyQXVkaWVuY2UiLCJhZ2UiOjMwfQ')) 다음 그림:

    따라서 여기 서 알 수 있 듯 이 Base 64 는 암호 화 에 속 하 는 것 이 아니 라 간단하게 전환 되 기 때문에 payLoad 에 중요 한 내용 을 저장 할 수 없습니다.예 를 들 어 비밀번호 등 입 니 다.
    aspnetcore 에서 자체 가지 고 있 는 클래스 를 사용 하여 jwt 생 성
    aspnet core 에서 jwt 도움말 류 를 가 져 왔 습 니 다.사실은 원리 와 같이 위 에 포장 을 하고 내용 을 풍부 화 시 켰 습 니 다.우 리 는 정태 적 인 방법 을 계속 사용 합 니 다.다음 과 같 습 니 다.
    
    /// <summary>
     ///   jwtToken,        ,    HS256  ,          ,     
     ///       CreateToken  
     /// </summary>
     /// <param name="payLoad"></param>
     /// <param name="expiresMinute">    </param>
     /// <returns></returns>
     public static string CreateTokenByHandler(Dictionary<string, object> payLoad, int expiresMinute)
     {
      
      var now = DateTime.UtcNow;
    
      // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
      // You can add other claims here, if you want:
      var claims = new List<Claim>();
      foreach (var key in payLoad.Keys)
      {
      var tempClaim = new Claim(key, payLoad[key]?.ToString());
      claims.Add(tempClaim);
      }
      
    
      // Create the JWT and write it to a string
      var jwt = new JwtSecurityToken(
      issuer: null,
      audience: null,
      claims: claims,
      notBefore: now,
      expires: now.Add(TimeSpan.FromMinutes(expiresMinute)),
      signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(securityKey)), SecurityAlgorithms.HmacSha256));
      var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
      return encodedJwt;
     }
    이 효 과 는 위 와 똑 같 습 니 다.같은 header,payload,비밀 키 를 사용 하면 jwt 가 생 성 된 것 과 같 습 니 다.여기 서 보 여 주지 않 고 관심 있 는 것 은 스스로 시도 할 수 있 습 니 다.
    aspntcore 에서 사용자 정의 jwt 인증 을 어떻게 사용 합 니까?
    위 에서 그렇게 많은 이 야 기 를 했 습 니 다.다만 jwt 를 어떻게 사용 하여 검증 하 는 지 더 잘 이해 하기 위해 서 입 니 다.그것 은 jwt 가 어떻게 검증 하 는 것 입 니까?http 요청 이 오 면 http 요청 머리의 Authorization 에 jwt 가 지 니 고 있 습 니 다.먼저 어떻게 얻 는 지 보지 않 고 그 가 어떻게 검증 하 는 지 먼저 보 자.우 리 는 정적 방법 을 정의 한다.다음 과 같다.
    
    /// <summary>
     ///              ,
     /// </summary>
     /// <param name="encodeJwt"></param>
     /// <param name="validatePayLoad">       ;         ,      , </param>
     ///   :payLoad["aud"]?.ToString() == "roberAuddience";
     ///   :        
     /// <returns></returns>
     public static bool Validate(string encodeJwt,Func<Dictionary<string,object>,bool> validatePayLoad)
     {
      var success = true;
      var jwtArr = encodeJwt.Split('.');
      var header = JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlEncoder.Decode(jwtArr[0]));
      var payLoad = JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlEncoder.Decode(jwtArr[1]));
    
      var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(securityKey));
      //          (   )
      success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
      if (!success)
      {
      return success;//         
      }
      //           (     )
      var now = ToUnixEpochDate(DateTime.UtcNow);
      success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));
    
      //            
      success = success && validatePayLoad(payLoad);
    
      return success;
     }
    그 중에서 vaidatePayLoad 인 자 는 사용자 정의 검증 Fun 입 니 다.이 Fun 방법 을 실행 할 때 복호화 한 payload 를 매개 변수 로 입력 합 니 다.
    우 리 는 검증 을 통 해 두 부분 으로 나 누 었 다.
    첫째,필요 하 다.
    jwt 서명 이 정확 한 지 여 부 는 상기 코드 의 실현 을 보십시오jwt 가 가능 한 시간 내 에 상기 코드 의 실현 여 부 를 보십시오둘째,사용자 정의(각 복잡 한 원 리 는 pay Load 의 특정한 값 을 얻 은 다음 에 이 값 에 대해 각종 판독 을 하 는 것 입 니 다.
  • 이 jwt 가 블랙리스트 에 들 었 는 지
  • aud==‘roberAudience'
  • 테스트 클래스 검증 을 통과 하 겠 습 니 다.
    
    [TestMethod]
      public void TokenCustomerValidateTest()
      {
       Dictionary<string, object> payLoad = new Dictionary<string, object>();
       payLoad.Add("sub", "rober");
       payLoad.Add("jti", Guid.NewGuid().ToString());
       payLoad.Add("nbf", null);
       payLoad.Add("exp", null);
       payLoad.Add("iss", "roberIssuer");
       payLoad.Add("aud", "roberAudience");
       payLoad.Add("age", 30);
    
       var encodeJwt = TokenContext.CreateToken(payLoad, 30);
    
       var result = TokenContext.Validate(encodeJwt, (load) => {
        var success = true;
        //      aud     roberAudience
        success = success&& load["aud"]?.ToString() == "roberAudience";
    
        //  age>20 
        int.TryParse(load["age"].ToString(), out int age);
        Assert.IsTrue(age > 30);
        //     jwt    jti        
    
        return success;
       });
       Assert.IsTrue(result);
      }
    위 와 같이 우 리 는 jwt 중의 payload 를 분석 한 다음 에 각종 복잡 한 원 하 는 검증 을 할 수 있다.
    사실,aspnet core 의 역할,사용자,전략,사용자 정의 정책 에 대한 검증 은 이곳 의 사용자 정의 검증 에 해당 합 니 다.장 에서 상세 한 설명 과 대 비 를 할 것 입 니 다.여 기 는 잠시 설명 하지 않 습 니 다.
    위 를 보고 jwt 가 간단 하 다 고 생각 하 시 죠?주로 두 편 이에 요.
    jwt 만 들 기;jwt 검증;전체 코드 는 다음 과 같 습 니 다:
    
    /// <summary>
     /// Token   ,  token      
     /// </summary>
     public class TokenContext
     {
      /// <summary>
      ///   ,          
      /// </summary>
      public static string securityKey = "GQDstclechengroberbojPOXOYg5MbeJ1XT0uFiwDVvVBrk";
    
      /// <summary>
      ///   jwttoken,     
      /// </summary>
      /// <param name="payLoad"></param>
      /// <param name="header"></param>
      /// <returns></returns>
      public static string CreateToken(Dictionary<string, object> payLoad,int expiresMinute, Dictionary<string, object> header = null)
      {
       if (header == null)
       {
        header = new Dictionary<string, object>(new List<KeyValuePair<string, object>>() {
         new KeyValuePair<string, object>("alg", "HS256"),
         new KeyValuePair<string, object>("typ", "JWT")
        });
       }
       //  jwt    (      )
       var now = DateTime.UtcNow;
       payLoad["nbf"] = ToUnixEpochDate( now);//      
       payLoad["exp"] = ToUnixEpochDate(now.Add(TimeSpan.FromMinutes(expiresMinute)));//      
    
       var encodedHeader = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(header));
       var encodedPayload = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(payLoad));
    
       var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(securityKey));
       var encodedSignature = Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(encodedHeader, ".", encodedPayload))));
    
       var encodedJwt = string.Concat(encodedHeader, ".", encodedPayload, ".", encodedSignature);
       return encodedJwt;
      }
    
      /// <summary>
      ///   jwtToken,        ,    HS256  ,          ,     
      ///       CreateToken  
      /// </summary>
      /// <param name="payLoad"></param>
      /// <param name="expiresMinute">    </param>
      /// <returns></returns>
      public static string CreateTokenByHandler(Dictionary<string, object> payLoad, int expiresMinute)
      {
       
       var now = DateTime.UtcNow;
    
       // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
       // You can add other claims here, if you want:
       var claims = new List<Claim>();
       foreach (var key in payLoad.Keys)
       {
        var tempClaim = new Claim(key, payLoad[key]?.ToString());
        claims.Add(tempClaim);
       }
       
    
       // Create the JWT and write it to a string
       var jwt = new JwtSecurityToken(
        issuer: null,
        audience: null,
        claims: claims,
        notBefore: now,
        expires: now.Add(TimeSpan.FromMinutes(expiresMinute)),
        signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(securityKey)), SecurityAlgorithms.HmacSha256));
       var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
       return encodedJwt;
      }
    
      /// <summary>
      ///              ,
      /// </summary>
      /// <param name="encodeJwt"></param>
      /// <param name="validatePayLoad">       ;         ,      , </param>
      ///   :payLoad["aud"]?.ToString() == "roberAuddience";
      ///   :        
      /// <returns></returns>
      public static bool Validate(string encodeJwt,Func<Dictionary<string,object>,bool> validatePayLoad)
      {
       var success = true;
       var jwtArr = encodeJwt.Split('.');
       var header = JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlEncoder.Decode(jwtArr[0]));
       var payLoad = JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlEncoder.Decode(jwtArr[1]));
    
       var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(securityKey));
       //          (   )
       success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
       if (!success)
       {
        return success;//         
       }
       //           (     )
       var now = ToUnixEpochDate(DateTime.UtcNow);
       success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));
    
       //            
       success = success && validatePayLoad(payLoad);
    
       return success;
      }
      /// <summary>
      ///   jwt  payLoad
      /// </summary>
      /// <param name="encodeJwt"></param>
      /// <returns></returns>
      public static Dictionary<string ,object> GetPayLoad(string encodeJwt)
      {
       var jwtArr = encodeJwt.Split('.');
       var payLoad = JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlEncoder.Decode(jwtArr[1]));
       return payLoad;
      }
      public static long ToUnixEpochDate(DateTime date) => 
       (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
     }
    이상 은 jwt 의 기본 내용 입 니 다.그것 은 정말 간단 합 니 다.aspnet core 의 각종 쓰기 방법 에 의 해 어 지 럽 히 지 마 세 요.jwt 와 관련 된 검증 이 라면 모두 위 에 있 는 것들 을 바탕 으로 합 니 다.
    다음 장 에 서 는 다음 과 같이 설명 할 것 이다.
  • aspnet core 에서 jwt 파이프 인증 을 사용자 정의 합 니 다
  • aspnet core 에서 사용자 정의 정책 검증 Common AuthorizeHandler:AuthorizationHandler
  • jwt 논리 검증 과 원생 의 역할,사용자,전략 등 을 비교 합 니 다총결산
    이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기