출처: https://youtu.be/mgeuh8k3I4g
그동안 TIM Corey 선생님의 강좌를 clone 하다가 다른분도 해봅니다.
Nick Chapsas 의 인증 ,허가의 관한 강좌입니다.
제가 앞으로 권한 ,인증의 관련 업무를 할것 같아 공부중이기도 하고요
jwt 설명
출처: https://youtu.be/qDJYgGzmalQ
보통 사용자가 웹사이트의 접근할때 사용허가를 받아야 합니다.
과거 모노롤틱 개발에서 인증을 통과한 사용자는 식별값은 보통 서바가 세션에 저장하는것이 보통이었습니다.
이때 사용자는 서버에게 요청을 보낼때 자식을 구분하는 Key가 있느데 보통
cookie 값을 사용합니다.
하지만 이 웹사이트가 확장을 한다고 했을때 이 세션은 공유되기 어렵습니다.
이문제를해결하기 위해 stateless 로 서버에서 사용자 상태값을 따로 저장하지 않습니다.
보다 발전 된 형태의 시스템 구성 흔히들 말하는 API로 묶인 MSA입니다.
사용자는 하나같이 보이지만 실제로는 물리적으로 트래픽을 분산시킵니다.
이때 API는 사용자를 식별하기 위해 요청 Header 에 jwt token을 보내줍니다.
Header
typ: 토큰의 타입을 지정합니다. 바로 JWT 이죠.
alg: 해싱 알고리즘을 지정합니다. 해싱 알고리즘으로는 보통 HMAC SHA256 혹은 RSA 가 사용되며, 이 알고리즘은, 토큰을 검증 할 때 사용되는 signature 부분에서 사용됩니다.
정보(payload)
token에 담을 정보
{ "iss": "velopert.com", "exp": "1485270000000", "https://velopert.com/jwt_claims/is_admin": true, "userId": "11028373727102", "username": "velopert" }
서명
JSON Web Token 의 마지막 부분은 바로 서명(signature) 입니다. 이 서명은 헤더의 인코딩값과, 정보의 인코딩값을 합친후 주어진 비밀키로 해쉬를 하여 생성합니다
API 프로젝트를 생성합니다.
귀찮으니 기본 와꾸는 만들자고요 ^^
그다음 nuget package
Microsoft.AspNetCore.Authentication.JwtBearer
를 설치합니다.
그리고 program.cs 에서
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
});
서비스를 추가합니다.
그리고 jwt 기본 옵션 설정값을 appsettings.json을 저장합니다.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"JwtSettings": {
"Issuer": "https://dotnetdeve.com",
"Audience": "https://dotnetdeve.com/bluebird",
"Key": "dotnetdevforeverdotnetdevforeverdotnetdevforeverdotnetdevforeverdotnetdevforever"
}
}
추가로 아까 등록한 jwt 인증에
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x=> {
x.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = config["JwtSettings:Issuer"],
ValidAudience = config["JwtSettings:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["JwtSettings:Key"]!)),
ValidateIssuer = true,
ValidateAudience= true,
ValidateLifetime = true,
ValidateIssuerSigningKey =true
};
});
Token 에 대한 인증 옵션을 삽입합니다.
ValidIssuer,ValidAudience,IssuerSigningKey 는 이값이 여기서 사용되는 값이라는 의미이고 ValidateIssuer,ValidateAudience,ValidateLifetime,ValidateIssuerSigningKey
필수 여부입니다.
여담으로 이 아저씨 말 진짜 빠르네요 10분짜리 강의인데 분량은 한 30분 되는것 같네요
그리고 builder.Services.AddAuthentication(); 인증서비스를 추가합니다.
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
미드웨어도 추가합니다. (순서 중요!)
그리고 controller 에 권한 속성을 추가합니다.
[ApiController]
[Route("[controller]")]
[Authorize]
public class WeatherForecastController : ControllerBase
이번에는 token 을 생성하는 conroller 를 추가하겠습니다.
[HttpGet]
[Route("Token")]
public IActionResult GenerateToken()
{
var tokenhandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(TokenSecret);
var timespan = TimeSpan.FromHours(8);
var claims = new List<Claim>
{
new (JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()),
new (JwtRegisteredClaimNames.Sub,"dotnetdev"),
new (JwtRegisteredClaimNames.Email,"dot@korea.com"),
new ("Userid","blueBird"),
};
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.Add(timespan),
Issuer = "https://dotnetdeve.com",
Audience = "https://dotnetdeve.com/bluebird",
SigningCredentials =new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256)
};
var token = tokenhandler.CreateToken(tokenDescriptor);
var jwt = tokenhandler.WriteToken(token);
return Ok(jwt);
}
원래 이렇게 간단하게 하면 안되지만 구현에만 초점을 맞춰서 심플하게 했습니다.
swagger로 확인해보니 잘나오는군요 나온 토큰을 https://jwt.ms/
여기에서 확인해봅니다.
이 token 이 이 API 에서도 사용이 가능합니다. Insomnia(https://insomnia.rest/) 로 확인해봅니다.
header 에 Authorization를 추가하고 Bearer [token] 포함합니다.
역시 잘나오네요
roll 명을 저장할 IdentityData.cs 생성한후
public class IdentityData
{
public const string AdminUserClaimName = "admin";
public const string AdminUserPolicyName = "Admin";
}
Program.cs에서 정책을 등록합니다.
builder.Services.AddAuthorization(option =>
{
option.AddPolicy(IdentityData.AdminUserClaimName, p => p.RequireClaim(IdentityData.AdminUserClaimName,"true"));
});
token 에 IdentityData.AdminUserClaimName 이 claim value가 true 여야 통과한다는 의미입니다.
이제 이 정책을 쓰는 API를 만들어보겠습니다.
[HttpGet("Test1")]
[Authorize(Policy =IdentityData.AdminUserPolicyName)]
public string GetAutor()
{
return "안녕";
}
[Authorize(Policy =IdentityData.AdminUserPolicyName)]
이 Claim이 포함되야 통과 된다 의미입니다.
그리고 token 생성에서
var claims = new List<Claim>
{
new (JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()),
new (JwtRegisteredClaimNames.Sub,"dotnetdev"),
new (JwtRegisteredClaimNames.Email,"dot@korea.com"),
new ("Userid","blueBird"),
new ("admin","true"),
};
이렇게 new ("admin","true") claim을 추가합니다.
이렇게 잘 작동되는 모습을 볼수 있습니다.
Git Source : https://github.com/BlueBirdJun/JwtAuth_Tutorial.git
이렇게 API 의 인증 권한을 강좌를 최대한 Simple 하게 작성해보았습니다.
그동안 정리가 안되서 힘들었는데 저도 참 뜻깊은 시간이었습니다.
앞으로는 nick 아저씨 강좌를 위주로 볼생각입니다.