프로그래밍 월드/Javascript & jQuery

JWT(JSON Web Token)란? 서버사이드 aspx일 경우의 예제입니다.

용가리TM 2025. 2. 5. 15:48
반응형

먼저 JWT의 기본 개념에 대해 설명드리겠습니다:

JWT는 당사자간에 정보를 JSON 객체로 안전하게 전송하기 위한 간단한 방법입니다. 이 정보는 디지털 서명되어 있어 신뢰할 수 있습니다.

JWT의 구조:

  1. Header: 토큰 유형과 사용된 서명 알고리즘
  2. Payload: 전달하려는 데이터(클레임)
  3. Signature: 헤더와 페이로드를 기반으로 한 서명

주요 사용 사례:

  • 인증(Authentication)
  • 정보 교환
  • 상태 유지(Stateless) 서버 구현

 

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

public class JwtAuthenticationExample : System.Web.UI.Page
{
    private readonly string _secretKey = "your-256-bit-secret-key-here";
    
    // JWT 토큰 생성 메서드
    protected string GenerateJwtToken(string userId, string username, string role)
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secretKey));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, userId),
            new Claim(JwtRegisteredClaimNames.UniqueName, username),
            new Claim(ClaimTypes.Role, role),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString())
        };

        var token = new JwtSecurityToken(
            issuer: "your-issuer",
            audience: "your-audience",
            claims: claims,
            expires: DateTime.UtcNow.AddHours(1),
            signingCredentials: credentials
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    // JWT 토큰 검증 메서드
    protected bool ValidateToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_secretKey);

        try
        {
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = true,
                ValidIssuer = "your-issuer",
                ValidateAudience = true,
                ValidAudience = "your-audience",
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero
            }, out SecurityToken validatedToken);

            return true;
        }
        catch
        {
            return false;
        }
    }

    // 로그인 처리 예제
    protected void LoginButton_Click(object sender, EventArgs e)
    {
        // 실제 구현에서는 데이터베이스에서 사용자 검증
        string userId = "123";
        string username = "testuser";
        string role = "user";

        string token = GenerateJwtToken(userId, username, role);
        
        // 토큰을 클라이언트에 전달 (예: 쿠키로 저장)
        Response.Cookies.Add(new HttpCookie("jwt_token", token));
    }

    // 보호된 리소스 접근 예제
    protected void Page_Load(object sender, EventArgs e)
    {
        HttpCookie tokenCookie = Request.Cookies["jwt_token"];
        
        if (tokenCookie != null && ValidateToken(tokenCookie.Value))
        {
            // 인증된 사용자 처리
            // 보호된 리소스에 접근 허용
        }
        else
        {
            // 인증되지 않은 사용자 처리
            Response.Redirect("~/Login.aspx");
        }
    }
}

// Web.config 설정
/*
<configuration>
  <system.web>
    <authentication mode="None" />
  </system.web>
  <system.webServer>
    <modules>
      <remove name="FormsAuthentication" />
    </modules>
  </system.webServer>
</configuration>
*/

 

 

이 예제 코드의 주요 특징:

  1. 토큰 생성 (GenerateJwtToken):
    • 사용자 ID, 이름, 역할 등의 클레임 포함
    • 토큰 만료 시간 설정 (1시간)
    • HMAC SHA256 알고리즘으로 서명
  2. 토큰 검증 (ValidateToken):
    • 토큰의 서명 검증
    • 발급자(issuer)와 수신자(audience) 검증
    • 만료 시간 검증
  3. 실제 사용 예시:
    • 로그인 시 토큰 생성
    • 보호된 페이지 접근 시 토큰 검증

3번의 로그인 버튼 쪽이랑 페이지 로드 부분은 별로 신경 안 쓰셔도 됩니다. 

보통 클라이언트에서 위에 두개의 함수를 이용하여 토큰을 생성하고 검증하는 부분으로 사용되기 때문입니다.

 

구현 시 주의사항:

  1. 시크릿 키는 안전하게 보관해야 합니다
  2. HTTPS를 사용하여 통신해야 합니다
  3. 토큰 만료 시간을 적절히 설정해야 합니다
  4. 필요한 패키지를 설치해야 합니다:

 

 

 

JWT를 활용한 클라이언트 구현을 자세히 만들어보겠습니다.

<!-- Login.aspx -->
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="JwtExample.Login" %>

<!DOCTYPE html>
<html>
<head>
    <title>로그인</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <style>
        .login-container {
            max-width: 400px;
            margin: 50px auto;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        .form-group {
            margin-bottom: 15px;
        }
        .form-group label {
            display: block;
            margin-bottom: 5px;
        }
        .form-group input {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        .error-message {
            color: red;
            margin-top: 10px;
            display: none;
        }
    </style>
</head>
<body>
    <div class="login-container">
        <h2>로그인</h2>
        <div class="form-group">
            <label for="username">사용자명:</label>
            <input type="text" id="username" name="username" />
        </div>
        <div class="form-group">
            <label for="password">비밀번호:</label>
            <input type="password" id="password" name="password" />
        </div>
        <button onclick="login()">로그인</button>
        <div id="errorMessage" class="error-message"></div>
    </div>

    <script>
        function login() {
            const username = $('#username').val();
            const password = $('#password').val();

            // 로그인 요청
            $.ajax({
                url: 'Login.aspx/LoginUser',
                type: 'POST',
                contentType: 'application/json',
                data: JSON.stringify({
                    username: username,
                    password: password
                }),
                success: function(response) {
                    if (response.d.success) {
                        // JWT 토큰 저장
                        localStorage.setItem('jwt_token', response.d.token);
                        // 사용자 정보 저장
                        localStorage.setItem('user_info', JSON.stringify(response.d.userInfo));
                        // 메인 페이지로 리다이렉트
                        window.location.href = 'Dashboard.aspx';
                    } else {
                        $('#errorMessage').text(response.d.message).show();
                    }
                },
                error: function() {
                    $('#errorMessage').text('로그인 중 오류가 발생했습니다.').show();
                }
            });
        }
    </script>
</body>
</html>

<!-- Dashboard.aspx -->
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Dashboard.aspx.cs" Inherits="JwtExample.Dashboard" %>

<!DOCTYPE html>
<html>
<head>
   <title>대시보드</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <style>
        .dashboard-container {
            max-width: 800px;
            margin: 20px auto;
            padding: 20px;
        }
        .header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        .user-info {
            margin-bottom: 20px;
        }
        .protected-content {
            border: 1px solid #ddd;
            padding: 20px;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <div class="dashboard-container">
        <div class="header">
            <h2>대시보드</h2>
            <button onclick="logout()">로그아웃</button>
        </div>
        <div id="userInfo" class="user-info"></div>
        <div class="protected-content">
            <h3>보호된 컨텐츠</h3>
            <div id="protectedData"></div>
        </div>
    </div>

    <script>
        // 페이지 로드 시 인증 확인
        $(document).ready(function() {
            checkAuth();
            loadUserInfo();
            loadProtectedData();
        });

        // 인증 확인
        function checkAuth() {
            const token = localStorage.getItem('jwt_token');
            if (!token) {
                window.location.href = 'Login.aspx';
                return;
            }
        }

        // 사용자 정보 로드
        function loadUserInfo() {
            const userInfo = JSON.parse(localStorage.getItem('user_info'));
            if (userInfo) {
                $('#userInfo').html(`
                    <p>사용자명: ${userInfo.username}</p>
                    <p>역할: ${userInfo.role}</p>
                `);
            }
        }

        // 보호된 데이터 로드
        function loadProtectedData() {
            $.ajax({
                url: 'Dashboard.aspx/GetProtectedData',
                type: 'POST',
                contentType: 'application/json',
                headers: {
                    'Authorization': 'Bearer ' + localStorage.getItem('jwt_token')
                },
                success: function(response) {
                    $('#protectedData').html(response.d.data);
                },
                error: function(xhr) {
                    if (xhr.status === 401) {
                        // 인증 실패 시 로그인 페이지로 리다이렉트
                        logout();
                    } else {
                        $('#protectedData').html('데이터 로드 중 오류가 발생했습니다.');
                    }
                }
            });
        }

        // 로그아웃
        function logout() {
            localStorage.removeItem('jwt_token');
            localStorage.removeItem('user_info');
            window.location.href = 'Login.aspx';
        }

        // API 요청을 위한 공통 함수
        function apiRequest(url, method, data) {
            return $.ajax({
                url: url,
                type: method,
                contentType: 'application/json',
                headers: {
                    'Authorization': 'Bearer ' + localStorage.getItem('jwt_token')
                },
                data: JSON.stringify(data)
            });
        }
    </script>
</body>
</html>
 

이 클라이언트 구현의 주요 특징과 기능을 설명드리겠습니다:

  1. 로그인 페이지 (Login.aspx):
    • 사용자명과 비밀번호 입력 폼 제공
    • AJAX를 사용한 비동기 로그인 처리
    • 로그인 성공 시 JWT 토큰을 localStorage에 저장
    • 에러 메시지 표시 기능
  2. 대시보드 페이지 (Dashboard.aspx):
    • 인증된 사용자만 접근 가능
    • 사용자 정보 표시
    • 보호된 데이터 로드 기능
    • 로그아웃 기능
  3. 주요 보안 기능:
    • 모든 API 요청에 JWT 토큰 포함
    • 인증되지 않은 접근 시 자동 리다이렉트
    • 토큰 만료 시 자동 로그아웃

구현 시 주의사항:

  1. localStorage 대신 보안이 더 필요한 경우 httpOnly 쿠키 사용을 고려해 볼 수 있습니다.
  2. 실제 운영 환경에서는 HTTPS 사용 필수입니다.
  3. CSRF 공격 방지를 위한 대책을 추가해 주는 게 좋습니다.

이상 JWT에 대하여 간단하게 알아보았습니다.

반응형