.NET 6 Web API Authentication | Minimal API & Swagger (CRUD)
유튜브의 Code with Julian 님의 영상 보고 작성하였습니다. 이 문서는 해당 영상의 반 이후 (swagger 추가 이후)를 문서로 남긴 것입니다.
세션 VS. 토큰! JWT가 뭔가요? | 얄팍한 코딩사전
AllowedHosts 아래에 추가
"AllowedHosts": "*",
"Jwt": {
"Key": "DhftOS5uphK3vmCJQrexST1RsyjZBjXWRgJMFPU4",
"Issuer": "https://localhost:포트번호/",
"Audience": "https://localhost:포트번호/"
}
이 키는 서버의 public key에 해당한다.
포트번호 같은 경우에는 VS에서 F5 눌러서 실행했을 때 서버가 열리는 포트번호를 적으면 된다.
builder 생성 코드 아래에 다음과 같이 추가한다.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateActor = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});
builder.Services.AddAuthorization();
appsetting.json
을 참고하는 것이다.app 인스턴스 생성 아래에 다음과 같이 추가한다.
app.UseAuthorization();
app.UseAuthentication();
유저가 로그인을 하면 서버에서 jwt 토큰을 만들어서 주어야 하고, 해당 토큰은 웹 브라우저의 쿠키에 저장된다.
유저가 인증이 필요한 시도를 할 때마다 request와 함께 jwt 토큰이 생성되어 전송된다
Program.cs의 app.UseAuthetication(); 아래에 메서드들 정의된 곳에 추가한다. 위치는 app생성~app.run() 사이라면 상관 없다.
app.MapPost("/login",
(UserLogin user, IUserService service) => Login(user, service));
위에서 정의한 함수를 구현. 위 정의부와 마찬가지로 구현 암데나 하면 됨.
IResult Login(UserLogin user, IUserService service)
{
if (!string.IsNullOrEmpty(user.Username) &&
!string.IsNullOrEmpty(user.Password))
{
var loggedInUser = service.Get(user);
if (loggedInUser is null) return Results.NotFound("User not found");
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, loggedInUser.Username),
new Claim(ClaimTypes.Email, loggedInUser.EmailAddress),
new Claim(ClaimTypes.GivenName, loggedInUser.GivenName),
new Claim(ClaimTypes.Surname, loggedInUser.Surname),
new Claim(ClaimTypes.Role, loggedInUser.Role)
};
var token = new JwtSecurityToken
(
issuer: builder.Configuration["Jwt:Issuer"],
audience: builder.Configuration["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(60),
notBefore: DateTime.UtcNow,
signingCredentials: new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
SecurityAlgorithms.HmacSha256)
);
var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
return Results.Ok(tokenString);
}
return Results.BadRequest("Invalid user credentials");
}
길다 길어
하나씩 살펴보자.
if (!string.IsNullOrEmpty(user.Username) && !string.IsNullOrEmpty(user.Password))
return Results.BadRequest("Invalid user credentials");
var loggedInUser = service.Get(user); if (loggedInUser is null) return Results.NotFound("User not found");
expires: DateTime.UtcNow.AddDays(60),
이 튜토리얼에서 유저는 두가지 타입이 있음. admin과 standard(그냥)유저임.
몇몇 ENDPOINT에 대해 admin만 접근할 수 있도록 처리해보자.
app.MapPost("/create",
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Administrator")]
(Movie movie, IMovieService service) => Create(movie, service));
create 에 대해 두번째 줄이 추가되었다.
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Administrator")]
app.MapGet("/get",
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Standard, Administrator")]
(int id, IMovieService service) => Get(id, service));
이번에는 Standard, Admin 모두 가능. 일단 login으로 authentication 받아야하는 것은 동일하다. 일단 authentication 받아놓고, 이렇게 메서드에 대해 너가 스탠다드, 어드민 이렇게 가리는 것이 authorize이다.
update, delete에 대해서도 위와 같이 Admin만 허용하는 코드를 추가한다.
(튜토리얼의 앞선 부분에서 swagger를 추가했었음)
이거까지만 하고 Run 해서 swagger 돌리면, login이 되긴 되는데 막상 그 상태가 유지가 안 된다. 다음과 같은 코드를 program.cs에 추가.
builder.Services.AddSwaggerGen(options =>
{
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Name = "Authorization",
Description = "Bearer Authentication with JWT Token"
});
});
다시 swagger 켜면 Authorize 버튼이 생긴다. Value 항목에 아래 api 테스트해서 로그인 시도한 후 Response body에 나오는 값을 복붙 한다.
이제 Authorization 접근 테스트가 가능하다.
끝!