본문 바로가기

JWT토큰

Spring security 이용하여 로그인 및 JWT 토큰생성

728x90

 

 

 

서버 기반 인증 시스템 문제점

 

세션

-유저가 인증할 때 이 기록을 서버에 저장

-메모리에 저장 혹은 데이터베이스 시스템에 저장

-유저의 수(동시접속)가 많은면 서버나 DB에 부하를준다.

 

확장성

-클러스터링 구성 시 세션 정보도 같이 공유해야함

-서버 구성이 복잡해짐

 

cors

-쿠키를 여러 도메인에서 관리하는 것이 번거롭다.

 

 

토큰기반 인증시스템 작동원리

 

작동원리

-유저가 아이디와 비밀번호로 로그인 수행

-서버 측에서 해당 정보 검증

-계정 정보가 정확하다면 서버측에서 유저에게 signed 토큰을 발급

-클라이언트에서는 토큰을 저장해 두고 요청마다 토큰을 서버에 함께 전달

-서버에서 토큰을 검증하고 요청에 응답

 

헤더(Header) - 두 가지 정보를 포함한다.

 

- typ : 토큰의 타입을 지정 (JWT)

- alg : 해싱 알고리즘 지정 (HMAC SHA256, RSA ..)

 

정보(payload)

 

- 토큰에 담을 정보가 포함 (클레임이라고 함, name/value 쌍(key/value)으로 구성)

- 클레임(claim)은 다음 세 분류로 나뉨

 * 등록된 (registered) 클레임 - iss, sub, aud, exp, nbf, sat, jti

 * 공개 (public) 클레임 - 충돌 방지 이름이 필요 (주로 URI 형식으로 네이밍)

 * 비공개 (private) 클레임 - 클라이언트와 서버 간의 협의 하에 사용되는 클레임 이름들

 

서명 (signature)

 

- 헤더의 인코딩 값과 정보의 인코딩 값을 합친 후 주어진 비밀키로 해쉬 하여 생성

 

 

. 이 두 개 들어가 있는데 header, payload, signature로 구분되어 있는 것으로 보면 된다.

점(.) 제일 앞에 것은 header, 중간 것은 payload, 두 번째 점(.) 뒤에 것은 signature로 구분

 

 

토큰을 사용하는구조는 다음과 같습니다.

 

 

 

실습 예제 

 

JAVA

 

SecurityService.java(interface)

 

package com.jang.mtg.service;

public interface SecurityService {

	String createToken(String subject,long ttlMillis);
	String getSubject(String token);
	
}

 

SecurityServiceImpl(class)

 

package com.jang.mtg.service;



import java.security.Key;
import java.util.Date;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import org.springframework.stereotype.Service;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Service
public class SecurityServiceImpl implements SecurityService {

	
	private static String secretKey="4C8kum4LxyKWYLM78sKdXrzb8jDCFyfX";
	
	@Override
	public String createToken(String subject, long ttlMillis) {
		// TODO Auto-generated method stub
		
		System.out.println("들어왔당");
		
		if(ttlMillis ==0) {
			throw new RuntimeException("토큰 만료기간은 0이상 이어야합니다.");
		}
		
		//HS256방식으로 암호화 방식 설정
		System.out.println("여기");
		SignatureAlgorithm signatureAlgorithm=SignatureAlgorithm.HS256;
		byte[]apiKeySeBytes=DatatypeConverter.parseBase64Binary(secretKey);
		Key signingKey=new SecretKeySpec(apiKeySeBytes, signatureAlgorithm.getJcaName());
		
		JwtBuilder builder =Jwts.builder()
				.setSubject(subject)
				.signWith(signatureAlgorithm, signingKey);
		
		long nowMillis=System.currentTimeMillis();
		builder.setExpiration(new Date(nowMillis+ttlMillis));
		return builder.compact();
	}
	
	//token을 식별

	@Override
	public String getSubject(String token) {
		// TODO Auto-generated method stub
		
		Claims claims=Jwts.parser()
				.setSigningKey(DatatypeConverter.parseBase64Binary(secretKey))
				.parseClaimsJws(token).getBody();
		
		
		return claims.getSubject();
	}
	
	

}

 

createtoken 은  HS256방식으로 암호화하여 토큰을 생성하는코드이다.

 

getSubject는 들어온 토큰값을 이용해서 다시 subject 값을 추출해내는 코드이다.

 

 

해당하는 바와 같이 테스트를 해보았다.

 

Contorller

//토큰 발행
	@GetMapping("/token")
	public String generateToken(@RequestParam String subject,Model model){
		System.out.println(subject);
		String token=securityService.createToken(subject, 1000*60*60);
		Map<String,Object> map =new HashMap<>();
		map.put("userid", subject);
		map.put("token", token);
		model.addAttribute("user", new User());
		System.out.println(map);
		return "login";
		
	}
	//token 요청(식별)
	@GetMapping("security/get/subject")
	public String getSubject(@RequestParam String token,Model model) {
		String subject =securityService.getSubject(token);
		System.out.println(subject);
		model.addAttribute("user", new User());
		return "login";
	
	}

 

URL에 http://localhost:8080/token?subject=kim 하는값을 입력을 해보면

 

{userid=kim, token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJraW0iLCJleHAiOjE2Njg5NDYwNzF9.MGbhb7HFGlzPiOZFNtswp9OZesY1P4AtEsYbN26Tp6s}

 

userid와 token 값이 나타나는것을 알수있다.

 

URL에 http://localhost:8080/security/get/subject?token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJraW0iLCJleHAiOjE2Njg5NDYwNzF9.MGbhb7HFGlzPiOZFNtswp9OZesY1P4AtEsYbN26Tp6s 해당하는 토큰값을 다시 입력을 해주면 ?

 

KIM이라는 subject값이 나오는것을 알수있다.

 

다음과 같은방법으로 다음에는 로그인 및 다른 api를 사용할 때 토큰값을 같이 넘겨 해당하는 사용자가 맞는지

일치시켜보겠다.

 

728x90