본문 바로가기
C#

C#] JWT, JSON Web Token 사용법

by Fastlane 2020. 10. 23.
728x90
반응형

REST API 호출 시, client에서 header에 JWT Token을 함께 전송함으로, REST API Server에서 client를 검증할 수 있다. 

 

REST API에서 client의 요청에 따라 1. Token 생성, 2. Token검증, 3. Token 정보 조회를 할 수 있는 기능을 추가해보자. 

 

REST API Server 프레임워크 : .NET Framework 4.5

 

1. REST API 프로젝트에 System.IdentityModel.Tokens.Jwt를 추가한다. 

Install-Package System.IdentityModel.Tokens.Jwt

packages.config에서 설치 확인!

<package id="System.IdentityModel.Tokens.Jwt" version="4.0.0" targetFramework="net45" />

2. JWT 클래스 추가 

REST API 프로젝트에 JWT폴더 추가, 하위에 clsJWT.cs 파일 추가 

using System.IdentityMSystem.IdentityModel.Tokens; 를 사용한다. 

3. createToken 함수 생성

사용자의 회사명과 이름을 전달받아서, JWT에 정보를 추가한다. 

using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Security.Claims;
using System.Text;

namespace REST.API.JWT
{
    public static class clsJWT
    {
        static string plainTextSecurityKey = "123456789abcdefgblahblah";
        static InMemorySymmetricSecurityKey signingKey = new InMemorySymmetricSecurityKey(Encoding.UTF8.GetBytes(plainTextSecurityKey));
        static SigningCredentials signingCredentials = new SigningCredentials(signingKey,
            SecurityAlgorithms.HmacSha256Signature, SecurityAlgorithms.Sha256Digest);
        static int lifetimeMinutes = 43200; //30일


        public static string createToken(string company, string name)
        {
            var claimsIdentity = new ClaimsIdentity(new List<Claim>()
            {
                new Claim("Company", company),
                new Claim("Name", name)
            }, "Employee");


            var securityTokenDescriptor = new SecurityTokenDescriptor()
            {
                AppliesToAddress = "https://대리점.com",
                TokenIssuerName = "https://회사.com",
                Subject = claimsIdentity,
                SigningCredentials = signingCredentials,
            };

            var tokenHandler = new JwtSecurityTokenHandler();
            tokenHandler.TokenLifetimeInMinutes = lifetimeMinutes;
            var plainToken = tokenHandler.CreateToken(securityTokenDescriptor);
            var signedAndEncodedToken = tokenHandler.WriteToken(plainToken);

            return signedAndEncodedToken;
        }

    }
}

회사이름과, 이름을 전달해서 JWT를 만들어보자. 

string token = clsJWT.createToken("좋은회사", "홍길동");

JWT는 아래와 같이 3가지 부분으로 이루어진 문자열(plainToken)로 생성되었다. 

{{"typ":"JWT","alg":"HS256"}.{"Company":"좋은회사","Name":"홍길동","iss":"https://회사.com","aud":"https://대리점.com","exp":1606024517,"nbf":1603432517}RawData: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJDb21wYW55Ijoi7KKL7J2A7ZqM7IKsIiwiTmFtZSI6Iu2Zjeq4uOuPmSIsImlzcyI6Imh0dHBzOi8v7ZqM7IKsLmNvbSIsImF1ZCI6Imh0dHBzOi8v64yA66as7KCQLmNvbSIsImV4cCI6MTYwNjAyNDUxNywibmJmIjoxNjAzNDMyNTE3fQ.9Fz3mAtdt5FHdvRLbUAHqxzDS1G62j4G4ukEcnUatSY}

1. header : {"typ":"JWT","alg":"HS256"} 

2. payload : {"Company":"좋은회사","Name":"홍길동","iss":"https://회사.com","aud":"https://대리점.com","exp":1606024517,"nbf":1603432517}

여기서 Company, Name은 client와 server간 협의하에 추가된 클레임이다. 

나머지 클레임들은 token에 대한 정보들이 담긴 클레임이다. 

iss : 토큰 발급자

aud : 토큰 대상자

exp : 토큰의 만료시간

nbf : 토큰 활성날짜

3. token

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.

eyJDb21wYW55Ijoi7KKL7J2A7ZqM7IKsIiwiTmFtZSI6Iu2Zjeq4uOuPmSIsImlzcyI6Imh0dHBzOi8v7ZqM7IKsLmNvbSIsImF1Z

CI6Imh0dHBzOi8v64yA66as7KCQLmNvbSIsImV4cCI6MTYwNjAyNDUxNywibmJmIjoxNjAzNDMyNTE3fQ.

9Fz3mAtdt5FHdvRLbUAHqxzDS1G62j4G4ukEcnUatSY

토큰은 아래와 같이 구성되어 있다. 

header인코딩.play인코딩.header인코딩.play인코딩을 key값으로 해싱하고 base64로 인코딩한 값

 

WriteToken함수로 token만 받을 수 있다.  

4. isValidToken 함수 생성

	public static JwtSecurityToken isValidToken(string signedAndEncodedToken)
        {

            var tokenHandler = new JwtSecurityTokenHandler();
            var tokenValidationParameters = new TokenValidationParameters()
            {
                ValidAudiences = new string[]
                {
                "https://대리점.com"
                },
                ValidIssuers = new string[]
                {
                "https://회사.com"
                },
                IssuerSigningKey = signingKey
            };

            SecurityToken validatedToken;

            tokenHandler.ValidateToken(signedAndEncodedToken,
                tokenValidationParameters, out validatedToken);

            return validatedToken as JwtSecurityToken;

        }

생성된 Token을 전달하여, validatedToken을 return 받자. 

JwtSecurityToken objToken = clsJWT.isValidToken("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJDb21wYW55Ijoi7KKL7J2A7ZqM7IKsIiwiTmFtZSI6Iu2Zjeq4uOuPmSIsImlzcyI6Imh0dHBzOi8v7ZqM7IKsLmNvbSIsImF1ZCI6Imh0dHBzOi8v64yA66as7KCQLmNvbSIsImV4cCI6MTYwNjAyNDUxNywibmJmIjoxNjAzNDMyNTE3fQ.9Fz3mAtdt5FHdvRLbUAHqxzDS1G62j4G4ukEcnUatSY");

Token내용을 확인할 수 있다. 

 

잘못된 Token을 전달해보자! 

token의 맨 앞글자 e -> E 로 변경해서 isValidToken함수를 실행하니, 에러가 반환된다. 

 

에러메시지는 다음과 같다. header부분을 디코딩할 수 없다는 내용이다. 

    "Message": "오류가 발생했습니다.",

    "ExceptionMessage": "IDX10703: Unable to decode the 'header': 'EyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9' as Base64url encoded string. jwtEncodedString: 'EyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJDb21wYW55Ijoi7KKL7J2A7ZqM7IKsIiwiTmFtZSI6Iu2Zjeq4uOuPmSIsImlzcyI6Imh0dHBzOi8v7ZqM7IKsLmNvbSIsImF1ZCI6Imh0dHBzOi8v64yA66as7KCQLmNvbSIsImV4cCI6MTYwNjAyNDUxNywibmJmIjoxNjAzNDMyNTE3fQ.9Fz3mAtdt5FHdvRLbUAHqxzDS1G62j4G4ukEcnUatSY'.",

5. readToken 함수 생성

유효성 검증 없이, JWT 내용을 확인할 때 사용할 수 있다. 

        public static JwtSecurityToken readToken(string signedAndEncodedToken)
        {

            var tokenHandler = new JwtSecurityTokenHandler();

            SecurityToken validatedToken;

            validatedToken = tokenHandler.ReadToken(signedAndEncodedToken);

            return validatedToken as JwtSecurityToken;

        }

 

6. 결론

Client에서 로그인 시, Server에서 JWT Token을 생성해서 반환해주고, client에서 로그인 후 호출하는 모든 API에 JWT Token을 Header에 포함한다. 

 

Server에서는 Request Header의 JWT Token을 검증해서 적절한 결과값을 return한다. 

 

 

728x90
반응형

댓글