본문 바로가기
Programming/Spring Boot

[Spring Boot] JWT HMAC 암호화

by 공부합시다홍아 2024. 5. 30.
 

[Spring Boot] JWT

[Spring Boot] Spring Security스프링 시큐리티스프링 시큐리티는 스프링에 login을 처리하는 모듈이다.같은 시큐리티 기반이라도 코딩이 버전별로, 로그인방식에 따라 모두 다르다.시큐리티는 방대한

hong-study.tistory.com

이전 글에서 JWT는 무엇이고 왜 사용하는지에 대해 설명하였다.

이번에는 JWT를 직접 구현해봄으로써, 어떤 동작원리를 가지고 있는지 이해해보려고 한다.


JAVA -JWT 라이브러리 추가

build.gradle

implementation 'com.auth0:java-jwt:4.4.0'

build.gradle에 아래와 같이 라이브러리를 추가한다.
다른 버전이나 다른 개발 환경에서 사용하고 싶다면 아래에서 jwt를 검색해서 사용하면 된다.

  https://mvnrepository.com/


이번 연습 때는  jwt Service 영역을 만들고, Controller에 Rest 방식을 이용해 간단하게 데이터를 주고 받을 것이다. 

테스트 환경으로는 Boomerang 크롬 확장자를 이용하여 데이터를 주고 받는다.

JWTService.java

package com.project.jwt.util;

import java.util.Date;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.JWTVerifier;

public class JWTService {

	private static String secretKey = "test1010"; // 시그니처를 만들기 위한 비밀키

    // JWT 토큰 생성
    public static String createToken(String username) {
    	//암호화알고리즘
        Algorithm algorithm = Algorithm.HMAC256(secretKey);
        
        //만료시간
        long expire = System.currentTimeMillis() + 3600000; //1시간뒤
        
        // 페이로드 생성
        Builder builder = JWT.create()
                .withSubject(username) // 토큰의 주제(subject) 설정
                .withIssuedAt(new Date()) // 토큰 발급 시간 설정 (현재 시간)
                .withExpiresAt(new Date(expire)) // 토큰 만료 시간 설정
                .withIssuer("coding404서버") //토큰발행자
        		.withClaim("admin", "나야나~"); //공개클래임

        return builder.sign(algorithm); // 비밀 키로 서명하여 토큰 생성
    }
	
    //토큰검증
    public static boolean validateToken(String token) throws JWTVerificationException { //확인단계중하나라도 실패
        Algorithm algorithm = Algorithm.HMAC256(secretKey); //검증 알고리즘
        JWTVerifier verifier = JWT.require(algorithm).build(); //token을 검증할 객체생성
         
        verifier.verify(token); // 토큰 검증을 수행하며, 만료 시간도 자동으로 검사됨

        return true; // 검증 성공 시 true 반환
    }
    
	
}

Signature를 위한 비밀키 생성

암호화 이전에 누가 사용하는지를 명시해줄 secretKey를 생성한다.

JWT 토큰 생성에 필요한 알고리즘 생성

Algorithm algorithm = Algorithm.HMAC256(secretKey);

만료 시간 생성

long expire = System.currentTimeMillis() + 3600000; //1시간뒤

토큰 생성

Builder builder = JWT.create()
                .withSubject(username) // 토큰의 주제(subject) 설정
                .withIssuedAt(new Date()) // 토큰 발급 시간 설정 (현재 시간)
                .withExpiresAt(new Date(expire)) // 토큰 만료 시간 설정
                .withIssuer("coding404서버") //토큰발행자
                .withClaim("admin", "나야나~"); //공개클래임

토큰을 생성할 때 서명에 생성한 알고리즘을 이용
토큰 생성에 권장되는 미리 정의된 몇몇의 정보나, 넣고 싶은 정보를 정의해서 넣을 수도 있다.

위 순서는 이전 포스터에서 설명한 페이로드에 대한 내용에서 알 수 있고, 그 순서에 따라 값을 설명할 수 있다.

비밀 키로 서명하여 토큰 생성

return builder.sign(algorithm); // 비밀 키로 서명하여 토큰 생성

위 처럼 JWTService에서 토큰 생성에 대한 비즈니스로직이 완성되면 Controller를 통해서 사용자 요청에 따라 토큰을 발행할 수 있다.

APIController

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.project.jwt.command.MemberVO;
import com.project.jwt.util.JWTService;

@RestController
public class APIController {

	// JWT - API 기반의 인증 정보를 처리할 때, 토큰 발급 ( 발행자 )
	
	@PostMapping("/login")
	public ResponseEntity<Object> login(@RequestBody MemberVO vo){
		
		// 사용자 정보를 받아서 로그인 처리
		// ok - 로그인 성공
		// 토큰 발행
		String token = JWTService.createToken(vo.getUsername());
		
		return new ResponseEntity<>(token, HttpStatus.OK);
	}
	
	@PostMapping("/api/v1/getInfo")
	public ResponseEntity<Object> getInfo(HttpServletRequest request){
		
		// 클라이언트에서 토큰을 헤더라는 곳에 담는다.
		// 토큰을 전달받아 유효성을 확인한 후 만료인지, 통과인지 확인
		String token = request.getHeader("Authorization");
		System.out.println(token);
		
		// 토큰이 유효한지 확인, 
		try {
			boolean result = JWTService.validateToken(token);
			System.out.println("토큰의 무결성 여부 : " + result);
		} catch (Exception e) {
			e.printStackTrace(); //토큰이 위조 됐을 경우
			return new ResponseEntity<>("토큰 위조", HttpStatus.UNAUTHORIZED); //토큰 위조
		}
		
		return new ResponseEntity<>("통과된 사람이면 여기에 회원정보 발급", HttpStatus.OK);
	}
}

위 사진과 같이 부메랑을 이용해 사용자 요청 URL을 통해 json 형식의 파일로 요청을 하면
서버로 부터 토큰이 발행되는 것을 확인할 수 있다.

이 토큰을 사용하여, 복호화를 진행하고 싶으면 jwt 공홈을 이용해 확인할 수 있다.

 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io


토큰의 유효성 검사

JWTService

	
    //토큰검증
    public static boolean validateToken(String token) throws JWTVerificationException { //확인단계중하나라도 실패
        Algorithm algorithm = Algorithm.HMAC256(secretKey); //검증 알고리즘
        JWTVerifier verifier = JWT.require(algorithm).build(); //token을 검증할 객체생성
         
        verifier.verify(token); // 토큰 검증을 수행하며, 만료 시간도 자동으로 검사됨

        return true; // 검증 성공 시 true 반환
    }
    
	
}

요청된 토큰에 대해 검증을 하는 코드이다.

코드는 Boolean 타입으로 위조 여부에 대한 값이 true / false만 반환한다.
검증 알고리즘을 통해, token을 검증할 객체를 생성한다.

APIController

@PostMapping("/api/v1/getInfo")
public ResponseEntity<Object> getInfo(HttpServletRequest request){

    // 클라이언트에서 토큰을 헤더라는 곳에 담는다.
    // 토큰을 전달받아 유효성을 확인한 후 만료인지, 통과인지 확인
    String token = request.getHeader("Authorization");
    System.out.println(token);

    // 토큰이 유효한지 확인, 
    try {
        boolean result = JWTService.validateToken(token);
        System.out.println("토큰의 무결성 여부 : " + result);
    } catch (Exception e) {
        e.printStackTrace(); //토큰이 위조 됐을 경우
        return new ResponseEntity<>("토큰 위조", HttpStatus.UNAUTHORIZED); //토큰 위조
    }




    return new ResponseEntity<>("통과된 사람이면 여기에 회원정보 발급", HttpStatus.OK);
}

위 처럼 "/api/v1/getInfo" 라는 요청 url이 Post방식으로 들어오게 되면 토큰 유효성 검증을 실시하게 된다.
Header에는 여러 가지 값들이 담기긴 하지만 대표적으로 "Authorization"에 이전에 발급된 토큰의 값을 전달하게 된다.

728x90