ASP.NET Core 3.1 Ocelot 인증 의 실현

1.인증
클 라 이언 트 가 Ocelot 를 통 해 하류 서 비 스 를 방문 할 때 하류 자원 서버 를 보호 하기 위해 인증 인증 인증 을 할 것 이 며 이 럴 때 Ocelot 에 인증 서 비 스 를 추가 해 야 한다.인증 서 비 스 를 추가 하면 Ocelot 는 인증 키 를 기반 으로 모든 요청 에 접근 할 수 있 는 자원 을 권한 을 부여 합 니 다.사용 자 는 항상 처럼 Startup.cs 에 인증 서 비 스 를 등록 해 야 합 니 다.그러나 가입 할 때마다 하나의 방안(인증 공급 자 키)을 제공 합 니 다.예 를 들 어:

public void ConfigureServices(IServiceCollection services)
{
  var authenticationProviderKey = "TestKey";
  services.AddAuthentication()
    .AddJwtBearer(authenticationProviderKey, x =>
    {
    });
}
Ocelot 인증 프로젝트 예제 에서 TestKey 는 이 프로그램 을 등록 한 다음 에 게 이 트 웨 이 프로젝트 Routes 경로 에 표시 합 니 다.

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/customers",
      "DownstreamScheme": "http",
      "DownstreamHost": "localhost",
      "DownstreamPort": 9001,
      "UpstreamPathTemplate": "/customers",
      "UpstreamHttpMethod": [ "Get" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "TestKey",
        "AllowedScopes": []
      }
    }
  ]
}
Ocelot 가 실 행 될 때 Routes.AuthenticationOptions.AuthenticationProvider Key 를 보고 주어진 키 로 등 록 된 인증 공급 자가 있 는 지 확인 합 니 다.존재 하지 않 으 면 Ocelot 가 시작 되 지 않 고 존재 하면 Routes 는 실행 할 때 이 프로그램 을 사용 합 니 다.경로 에 대한 인증 이 진행 되면 Ocelot 는 인증 미들웨어 를 실행 할 때 이와 관련 된 모든 방안 을 호출 합 니 다.인증 요청 에 실패 하면 Ocelot 는 http 상태 코드 401 을 되 돌려 줍 니 다.
2.JWT Tokens Bearer 인증
JSon Web Token(JWT)은 인터넷 애플 리 케 이 션 환경 간 성명 을 전달 하기 위 한 JSON 기반 개방 기준(RFC 7519)이다.이 token 은 치밀 하고 안전 하 며 분포 식 사이트 의 단일 로그 인(SSO)장면 에 특히 적합 하도록 설계 되 었 다.JWT 의 성명 은 일반적으로 신분 제공 자 와 서비스 제공 자 간 에 인 증 된 사용자 신분 정 보 를 전달 하여 자원 서버 에서 자원 을 얻 을 수 있 도록 하고 다른 업무 논리 에 필요 한 성명 정 보 를 추가 할 수 있 으 며 이 token 은 인증 에 직접 사용 할 수도 있 고 암호 화 될 수도 있다.

2.1JWT 영패 구조
치밀 한 형식 에서 JSON Web Tokens 는 dot(.)로 구 분 된 세 부분 으로 구성 되 는데 이들 은 Header 헤드,Payload 유효 부하,Signature 서명 이다.따라서 JWT 는 보통 다음 과 같다.xxxxx.yyyy.zzzzz(Header.Payload.Signature).
2.1.1 헤더
레이 블 은 보통 두 부분 으로 구성 된다.토 큰 의 유형,즉 JWT,그리고 사용 하고 있 는 서명 알고리즘,예 를 들 어 HMAC SHA 256 또는 RSA 등 이다.예 를 들 면:

{
 "alg": "HS256",
 "typ": "JWT"
}
그리고 이 JSON 은 Base64Url 로 인 코딩 돼 JWT 의 첫 부분 을 형성한다.
2.1.2 Payload 유효 부하
Payload 부분도 실제 전달 해 야 할 데 이 터 를 저장 하 는 JSON 대상 이다.JWT 는 선택 할 수 있 도록 7 개의 공식 필드 를 규정 했다.
  • iss(issuer):발급 자
  • exp(expiration time):만 료 시간
  • sub(sub):주제
  • aud(audience):시청자
  • nbf(Not Before):발효 시간
  • iat(Issued At):발급 시간
  • jti(JWT ID):번호
  • 공식 필드 를 제외 하고 이 부분 에서 개인 필드 를 정의 할 수 있 습 니 다.다음은 하나의 예 입 니 다.예 를 들 면:
    
    {
     "sub": "1234567890",
     "name": "John Doe",
     "admin": true
    }
    JWT 는 기본적으로 암호 화 되 지 않 고 누구나 읽 을 수 있 으 므 로 비밀 정 보 를 이 부분 에 두 지 마 십시오.이 JSON 대상 도 Base64URL 알고리즘 을 사용 하여 문자열 로 변환 해 야 합 니 다.
    2.1.3 서명
    Signature 부분 은 앞 두 부분의 서명 으로 데이터 변경 을 방지 합 니 다.우선 키(secret)를 지정 해 야 합 니 다.이 키 는 서버 에서 만 알 수 있 으 며 사용자 에 게 누설 할 수 없습니다.그리고 Header 에서 지정 한 서명 알고리즘(기본 값 HMAC SHA 256)을 사용 하여 다음 공식 에 따라 서명 을 생 성 합 니 다.
    
    HMACSHA256(
     base64UrlEncode(header) + "." +
     base64UrlEncode(payload),
     secret)
    서명 은 메시지 가 변경 되 지 않 았 음 을 검증 하 는 데 사 용 됩 니 다.또한 비밀 키 로 서명 한 토 큰 을 사용 한 경우 JWT 의 보 낸 사람 이 자신 이 주장 하 는 사람 인지 검증 할 수 있 습 니 다.세 가 지 를 모두 함께 놓 으 면 출력 은 점 으로 구 분 된 Base 64-URL 문자열 세 개 로 HTML 과 HTTP 환경 에서 쉽게 전달 할 수 있 으 며 XML 기반 표준(예 를 들 어 SAML)보다 더욱 치밀 하 다.이전 헤드 와 유효 부하 인 코딩 을 가지 고 있 으 며 기밀 서명 을 사용 하 는 JWT 를 보 여 줍 니 다.
    
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
    .eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoid3prNzAzIiwibmJmIjoiMTU5MjE0MzkzNyIsImV4cCI6MTU5MjE0Mzk5OCwiaXNzIjoiYXV0aC5qd3QuY2MiLCJhdWQiOiJkZW5nd3V8MjAyMC82LzE0IDIyOjEyOjE5In0
    .4RiwhRy0rQkZjclOFWyTpmW7v0AMaL3aeve1L-eWIz0
    사실 일반적으로 사용자 이름과 비밀 번 호 를 보 내 서 token 을 가 져 오 는 것 은 Identity 4 에서 이 루어 집 니 다.인증 사용 자 를 포함 하여 JwtToken 을 생 성 합 니 다.하지만 프로젝트 는 System.Identity Model.Tokens 라 이브 러 리 로 JwtToken 을 생 성 합 니 다.마지막 으로 jwt 토 큰 을 사용자 에 게 되 돌려 줍 니 다.JwtToken 디 코딩 통과 가능https://jwt.io/조회 중 입 니 다.
    3.프로젝트 프레젠테이션
    3.1 PIGateway 프로젝트
    이 항목 에 서 는 하위 api 서 비 스 를 보호 하기 위해 인증 을 사용 하고 JwtBearer 인증 을 사용 하여 기본 인증 방안 을 TestKey 로 설정 합 니 다.apptsettings.json 파일 에 인증 키(Secret)와 시청자(Aud)정 보 를 설정 합 니 다.
    
    {
      "Audience": {
        "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
        "Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
        "Aud": "Catcher Wong"
      }
    }
    Startup 인증 코드 는 다음 과 같 습 니 다.
    
    public void ConfigureServices(IServiceCollection services)
    {
      //  appsettings.json          (Secret)   (Aud)  
      var audienceConfig = Configuration.GetSection("Audience");
      //      
      var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"]));
      //token        
      var tokenValidationParameters = new TokenValidationParameters
      {
        //        
        ValidateIssuerSigningKey = true,
        //      
        IssuerSigningKey = signingKey,
        //       
        ValidateIssuer = true,
        //     
        ValidIssuer = audienceConfig["Iss"],
        //      
        ValidateAudience = true,
        //    
        ValidAudience = audienceConfig["Aud"],
        //    Token   ,       Token Claims  NotBefore Expires  
        ValidateLifetime = true,
        //           
        ClockSkew = TimeSpan.Zero,
        //    Token Claims     Expires
        RequireExpirationTime = true,
      };
      //      ,   TestKey
      services.AddAuthentication(o =>
      {
        o.DefaultAuthenticateScheme = "TestKey";
      })
      .AddJwtBearer("TestKey", x =>
        {
          x.RequireHttpsMetadata = false;
          // JwtBearerOptions   ,IssuerSigningKey(    )、ValidIssuer(Token    )、ValidAudience(    )        。
          x.TokenValidationParameters = tokenValidationParameters;
        });
      //  Ocelot     ,  Secret  、Iss   、Aud  
      services.AddOcelot(Configuration);
    }
    public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
      //      
      app.UseAuthentication();
      //  Ocelot   
      await app.UseOcelot();
    }
    3.1.1 Identity 서버 탑재 JWT 토 큰
    두 번 째 소절 에서 JWT Token 인증 을 소개 할 때 저 희 는 보통 사용자 이름과 비밀 번 호 를 보 내 Token 을 가 져 오 는 것 이 Identity 4 로 이 루어 진 것 임 을 알 고 있 습 니 다.사용 자 를 검증 하고 JWT Token 을 생 성 하 는 것 을 포함 합 니 다.아 이 덴 티 티 티 서버 가 JWT Token 인증 기능 을 탑재 한 셈 이다.Identity Server 를 사용 하여 Token 을 불 러 오기 위해 서 는 Configure Services 에서 프로젝트(키)를 사용 하여 Identity Server 서 비 스 를 등록 하 십시오.이 동작 을 어떻게 수행 해 야 할 지 모 르 면 Identity Server 문 서 를 찾 아 보 세 요.
    
    public void ConfigureServices(IServiceCollection services)
    {
      var authenticationProviderKey = "TestKey";
      Action<IdentityServerAuthenticationOptions> options = o =>
        {
          o.Authority = "https://whereyouridentityserverlives.com";
          o.ApiName = "api";
          o.SupportedTokens = SupportedTokens.Both;
          o.ApiSecret = "secret";
        };
      services.AddAuthentication()
        .AddIdentityServerAuthentication(authenticationProviderKey, options);
      services.AddOcelot();
    }
    Identity 4 에 서 는 Authority 파라미터 가 OIDC 서비스 주 소 를 지정 합 니 다.OIDC 는 Issuer,Issuer Signingkey 등 설정 을 자동 으로 발견 할 수 있 습 니 다.o.Audience 와 x.TokenValidationParameters=new TokenValidationParameters{ValidAudience="api"}는 같은 효 과 를 가 집 니 다.
    3.2 AuthServer 프로젝트
    이 서 비 스 는 주로 클 라 이언 트 가 보 호 받 는 자원 서버 를 요청 할 때 인증 후 클 라 이언 트 가 필요 로 하 는 JWT Token 이 생 성 되 고 JWT Token 키 코드 는 다음 과 같 습 니 다.
    
    [Route("api/[controller]")]
    public class AuthController : Controller
    {
      private IOptions<Audience> _settings;
      public AuthController(IOptions<Audience> settings)
      {
        this._settings = settings;
      }
      /// <summary>
      ///                 
      ///            
      ///              token
      ///     token,            token , headers: {'Authorization': 'Bearer ' + token}
      ///     token ,     
      /// </summary>
      /// <param name="name"></param>
      /// <param name="pwd"></param>
      /// <returns></returns>
      [HttpGet]
      public IActionResult Get(string name, string pwd)
      {
        //          
        if (name == "catcher" && pwd == "123")
        {
          var now = DateTime.UtcNow;
          //       ,      ,             
          var claims = new Claim[]
          {
            //    
            new Claim(JwtRegisteredClaimNames.Sub, name),
              //JWT ID      
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
              //      issued timestamp
            new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64)
          };
          //     Microsoft.IdentityModel.Tokens         JwtToken
    
          //    
          var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_settings.Value.Secret));
    
          //  jwt    
          var tokenValidationParameters = new TokenValidationParameters
          {
            //        
            ValidateIssuerSigningKey = true,
            //      
            IssuerSigningKey = signingKey,
            //       
            ValidateIssuer = true,
            //     
            ValidIssuer = _settings.Value.Iss,
            //      
            ValidateAudience = true,
            //    
            ValidAudience = _settings.Value.Aud,
            //    Token   ,       Token Claims  NotBefore Expires  
            ValidateLifetime = true,
            //           
            ClockSkew = TimeSpan.Zero,
            //    Token Claims     Expires
            RequireExpirationTime = true,
          };
          var jwt = new JwtSecurityToken(
            //jwt   
            issuer: _settings.Value.Iss,
            //jwt  
            audience: _settings.Value.Aud,
            //jwt    
            claims: claims,
            notBefore: now,
            //jwt      
            expires: now.Add(TimeSpan.FromMinutes(2)),
            //    :     、    
            signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
          );
          //  jwt  (json web token)
          var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
          var responseJson = new
          {
            access_token = encodedJwt,
            expires_in = (int)TimeSpan.FromMinutes(2).TotalSeconds
          };
          return Json(responseJson);
        }
        else
        {
          return Json("");
        }
      }
    }
    public class Audience
    {
      public string Secret { get; set; }
      public string Iss { get; set; }
      public string Aud { get; set; }
    }
    apptsettings.json 파일 에 인증 키(Secret)와 시청자(Aud)정 보 를 설정 합 니 다.
    
    {
      "Audience": {
        "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
        "Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
        "Aud": "Catcher Wong"
      }
    }
    3.3 CustomerAPIServices 프로젝트
    이 프로젝트 는 APIGateway 프로젝트 와 마찬가지 로 하위 api 서 비 스 를 보호 하기 위해 JwtBearer 인증 을 사용 하여 기본 인증 방안 을 TestKey 로 설정 합 니 다.apptsettings.json 파일 에 인증 키(Secret)와 시청자(Aud)정 보 를 설정 합 니 다.
    
    {
      "Audience": {
        "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
        "Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
        "Aud": "Catcher Wong"
      }
    }
    Startup 인증 코드 는 다음 과 같 습 니 다.
    
    public void ConfigureServices(IServiceCollection services)
    {
      //  appsettings.json          (Secret)   (Aud)  
      var audienceConfig = Configuration.GetSection("Audience");
      //      
      var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"]));
      //token        
      var tokenValidationParameters = new TokenValidationParameters
      {
        //        
        ValidateIssuerSigningKey = true,
        //      
        IssuerSigningKey = signingKey,
        //       
        ValidateIssuer = true,
        //     
        ValidIssuer = audienceConfig["Iss"],
        //      
        ValidateAudience = true,
        //    
        ValidAudience = audienceConfig["Aud"],
        //    Token   ,       Token Claims  NotBefore Expires  
        ValidateLifetime = true,
        //           
        ClockSkew = TimeSpan.Zero,
        //    Token Claims     Expires
        RequireExpirationTime = true,
      };
      //      ,   TestKey
      services.AddAuthentication(o =>
      {
        o.DefaultAuthenticateScheme = "TestKey";
      })
      .AddJwtBearer("TestKey", x =>
        {
          x.RequireHttpsMetadata = false;
          // JwtBearerOptions   ,IssuerSigningKey(    )、ValidIssuer(Token    )、ValidAudience(    )        。
          x.TokenValidationParameters = tokenValidationParameters;
        });
    
      services.AddMvc();
    }
    public void Configure(IApplicationBuilder app)
    {
      //      
      app.UseAuthentication();
      app.UseMvc();
    }
    customersController 에 인증 이 필요 한 방법 을 추가 합 니 다.인증 이 필요 없 는 방법:
    
    [Route("api/[controller]")]
    public class CustomersController : Controller
    {
      //      
      [Authorize]
      [HttpGet]
      public IEnumerable<string> Get()
      {
        return new string[] { "Catcher Wong", "James Li" };
      }
      [HttpGet("{id}")]
      public string Get(int id)
      {
        return $"Catcher Wong - {id}";
      }
    }
    3.4 Client App 프로젝트
    이 항목 은 클 라 이언 트 가 자원 서버 에 접근 하 는 전체 인증 프로 세 스 테스트 항목 을 모 의 하 는 것 으로 프로그램 메 인 프로그램 에서 다음 과 같은 코드 를 볼 수 있 습 니 다.
    
    class Program
    {
      static void Main(string[] args)
      {
        HttpClient client = new HttpClient();
    
        client.DefaultRequestHeaders.Clear();
        client.BaseAddress = new Uri("http://localhost:9000");
    
        // 1. without access_token will not access the service
        //  and return 401 .
        var resWithoutToken = client.GetAsync("/customers").Result;
    
        Console.WriteLine($"Sending Request to /customers , without token.");
        Console.WriteLine($"Result : {resWithoutToken.StatusCode}");
    
        //2. with access_token will access the service
        //  and return result.
        client.DefaultRequestHeaders.Clear();
        Console.WriteLine("
    Begin Auth...."); var jwt = GetJwt(); Console.WriteLine("End Auth...."); Console.WriteLine($"
    Token={jwt}"); client.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}"); var resWithToken = client.GetAsync("/customers").Result; Console.WriteLine($"
    Send Request to /customers , with token."); Console.WriteLine($"Result : {resWithToken.StatusCode}"); Console.WriteLine(resWithToken.Content.ReadAsStringAsync().Result); //3. visit no auth service Console.WriteLine("
    No Auth Service Here "); client.DefaultRequestHeaders.Clear(); var res = client.GetAsync("/customers/1").Result; Console.WriteLine($"Send Request to /customers/1"); Console.WriteLine($"Result : {res.StatusCode}"); Console.WriteLine(res.Content.ReadAsStringAsync().Result); Console.Read(); } private static string GetJwt() { HttpClient client = new HttpClient(); client.BaseAddress = new Uri( "http://localhost:9000"); client.DefaultRequestHeaders.Clear(); var res2 = client.GetAsync("/api/auth?name=catcher&pwd=123").Result; dynamic jwt = JsonConvert.DeserializeObject(res2.Content.ReadAsStringAsync().Result); return jwt.access_token; } }
    프로젝트 실행 테스트 결과 보기:

    코드 를 결합 하면 클 라 이언 트 가 Ocelot 게 이 트 웨 이 를 통 해 하류 서 비 스 를 방문 하 는 것 을 볼 수 있 습 니 다.http://localhost:9000/api/Customers/Get방법 은 이 방법 이 인증 을 거 쳐 야 처리 결 과 를 되 돌려 주기 때문에 JWT Token 인증 을 하고,Token 이 없 는 것 을 발견 하면 Ocelot 는 http 상태 코드 401 을 되 돌려 접근 을 거부 합 니 다.만약 에 저희 가 GetJwt 방법 을 통 해 AuthServer 서비스 에 로그 인 인증 을 통 해 권한 수여 Token 을 받 은 다음 에 이 자원 서버 인 터 페 이 스 를 방문 하면 처리 결 과 를 되 돌려 주 고 인증 속성 을 추가 하지 않 은http://localhost:9000/api/Customers/Get/{id}방법 을 비교 해 보면 Ocelot 인증 이 성공 했다 는 것 을 알 수 있 습 니 다!
    4.총화
    이 장 은 데모 프로젝트 와 결합 해 Ocelot 에서 JWT Token 인증 을 어떻게 사용 하 는 지 간단히 소개 하 는 것 이다.사실 정식 환경 에서 Ocelot 는 Identity Server 인증 권한 을 통합 해 야 합 니 다.마찬가지 로 Ocelot 미들웨어 를 다시 쓰 면 configuration.json 의 설정 정 보 를 데이터베이스 나 Redis 에 저장 할 수 있 습 니 다.
    참고 문헌:
    Ocelot 홈 페이지
    ASP.NET Core 3.1 Ocelot 인증 의 실현 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 관련 ASP.NET Core 3.1 Ocelot 인증 내용 은 저희 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 읽 어 주시 기 바 랍 니 다.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

    좋은 웹페이지 즐겨찾기