(ASP.NET Core) JWT인증 처리

 

ASP.NET Core에서 JWT + 쿠키 클레임을 통한 인증 처리를 사용할 수 있습니다.

우선 ASP.NET Core에서 JWT를 사용하기 위해 누겟에서 관련 패키지를 받아 설치 합니다.


System.IdentityModel.Tokens.Jwt

Microsoft.AspNetCore.Authentication.JwtBeare

Microsoft.AspNetCore.Identity

Microsoft.AspNetCore.Authentication


다음 코드는 ASP.NET Core에서 제공되는 Claim 쿠키 인증 + JWT를 사용한 인증 처리 방식 입니다.

ID, Password체크 후 사용자 인증 처리

var authClaims = new List<Claim>
{
    new Claim(ClaimTypes.NameIdentifier, 사용자인증ID),
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
    authClaims.Add(new Claim(ClaimTypes.Role, "일반사용자"));
    // 추가적으로 클라이언트 인증 정보를 Claim으로 추가 할 수 있음
};
 
// JWT 인증 페이로드 사인 비밀키
var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
 
// JWT 토큰 생성
var token = new JwtSecurityToken(
    issuer: _configuration["JWT:ValidIssuer"],
    audience: _configuration["JWT:ValidAudience"],
    expires: DateTime.Now.AddMonths(3),
    claims: authClaims,
    signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
);
 
// 엑세스키 발급
string accessToken = new JwtSecurityTokenHandler().WriteToken(token);

ConfigureServices(IServiceCollection services)에 Authentication정보 서비스 주입을 등록해서 jwt스키마를 사용하도록 설정하고,

Configure(IApplicationBuilder app, IWebHostEnvironment env)에 클래임 쿠키 인증을 사용 하도록 추가 합니다.

클라이언트로 부터 헤더정보에 넘어 오는 JWT 엑세스 토큰의 유효성 검사는 필터처리나 미들웨어등록으로 검사할 수 있는데

여기서는 미들웨어를 등록해서 인증이 필요한 컨트롤러에 요청이 될때 토큰값 유효성 검사가 이루어 지도록 처리 했습니다.

[Startup.cs]

public void ConfigureServices(IServiceCollection services) {
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    })
 
    // Adding Jwt Bearer
    .AddJwtBearer(options =>
    {
        options.SaveToken = true;
        options.RequireHttpsMetadata = false;
        options.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidAudience = Configuration["JWT:ValidAudience"],
            ValidIssuer = Configuration["JWT:ValidIssuer"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Secret"]))
        };
    });
}
 
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    app.UseAuthentication();
    app.UseAuthorization();
 
    // jwt인증 체크 Middleware 추가, [api] 컨트롤러만 적용
    app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), appBuilder =>
    {
        appBuilder.UseMiddleware<JwtMiddleware>();
    });
}

사용된 미들웨어 코드는 다음과 같습니다.

[JwtMiddleware.cs]

public class JwtMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IConfiguration _configuration;
    private const string _ID_ = "xxxService";
    private const string APIKEYNAME = "Authorization";
 
    public JwtMiddleware(RequestDelegate next, IConfiguration configuration)
    {
        _next = next;
        _configuration = configuration;
    }
 
    public async Task InvokeAsync(HttpContext context)
    {
        if (!context.Request.Headers.TryGetValue(APIKEYNAME, out var extractedApiKey))
        {
            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            await context.Response.WriteAsync("Api Key was not provided. (Using ApiKeyMiddleware)");
            return;
        }
        
        string accessToken = context.Request.Headers[APIKEYNAME].FirstOrDefault()?.Split(" ").Last();  // authorization bearer 형식의 헤더 키 값으로 넘어옴
        if (this.ValidateToken(accessToken) == false)
        {
            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            await context.Response.WriteAsync("jwt token validation failed");
            return;
        }
        await _next(context);
    }
 
    private bool ValidateToken(string token)
    {
        if (string.IsNullOrWhiteSpace(token))
            return false;
 
        try
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var authSigningKey = Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]);
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(authSigningKey),
                ValidateIssuer = false,
                ValidateAudience = false,
                // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
                ClockSkew = TimeSpan.Zero
            }, out SecurityToken validatedToken);
            var jwtToken = (JwtSecurityToken)validatedToken;
            var userId = jwtToken.Claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value;
 
            return (userId == _ID_);
        }
        catch (Exception ex)
        {
            return false;
        }
    }
}

위 처럼 미들웨어를 등록하면 인증이 필요한(위 코드 예시 에서는 라우터 경로 ‘/api/’로 시작되는 요청인 경우) 컨트롤러 요청시 JWT 유효성 체크가 이루어 집니다.

실제 인증이 필요한 컨트롤러에서 Authorize어트리뷰트를 사용해서 인증 처리를 사용할 수 있습니다.

Authorize(Roles = "일반사용자")]
public async Task<IActionResult> Something()
{
    ...
}