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한다.
'C#' 카테고리의 다른 글
C#] 메일발송 클래스 (0) | 2021.09.09 |
---|---|
네이버 스마트에디터] 이미지파일 업로드시 Base64인코딩 태그로 수정 (0) | 2021.09.06 |
C#] AES128, AES256 암호화 복호화 코드 (0) | 2020.10.16 |
C#] 파일 업로드 시, MIME TYPE 체크, 확장자 필터링 (0) | 2020.10.15 |
C#] XSS(Cross-site Scripting) - 스크립트 필터링 (0) | 2020.10.06 |
댓글