LG 유레카 2기_프론트엔드

[ Web-security ] REST & CORS & JWT

ChatjihoiPT 2025. 4. 28. 16:28
 

1. Rest(Representational State Transfer)

 

자원(서비스할 대상)을 화면을 통해서 표시하던 것에서 자원에 대한 정보를 xml 또는 JSON으로 표시

  • 즉, 약속된 주소와 요청 방법만으로 데이터를 주고 받는 것
  • URL은 하나의 고유한 리소스를 대표하도록 설계

1.1 Web만 서비스

웹 브라우저가 요청과 응답을 보내고 받음

  • 웹 전용 시스템
  • 다중 플랫폼 시대
  • 모델 재사용

1.2 View가 다변화된 시대 (Android, Web, IOS)

Model은 재사용되고, 각 view를 위한 controller가 필요


1.3 Rest-ful Service

자원에 대한 요청을 url로 구별하고, 상세는 method 방식으로 구분


2. CORS (Cross-Origin Resource Sharing)

동일 출처 정책과 CORS

다른 사이트로 요청할 때 브라우저가 맞는 지 확인하는 보안장치


2.1 SOP (Same-Origin Policy)

브라우저는 보안상 SOP를 따름

Ajax 통신은 동일한 출처의 리소스만 요청할 수 있다.


2.2 CORS

최초에 리소스를 제공한 출처와 다른 곳에서 리소스를 요청하는 경우에는 특정 Http header를 사용하여 웹 어플리케이션의 cross-origin 요청을 브라우저가 제한적으로 허용하는 정책

  • Access-Control-Request-Method: 본 요청에서 사용하는 메서드
  • Access-Control-Request-Headers: 본 요청에 포함될 헤더
  • Origin: 요청을 보낼 출처(위치)로 URL 중 scheme(protocal)과 host(IP & domain), port로 이루어진 값(객체)를 말함

CORS 요청의 종류 요청 전송 → 서버 처리 → 응답 변환 → 브라우저 처리

 

1. Preflight 요청
본 요청 전에 options 메소드로 서버에 권환 확인을 먼저 수행

- 메소드
GET, POST, HEAD, PUT, DELETE, PATCH
- 커스텀 헤더 사용
Authorization, X-Token
- MIME 타입 ( application/xml )
파일의 형식과 콘텐츠 종류를 알려주는 문자열 형식
(HTTP 요청/응답. 브라우저 통신 전반에서 콘텐츠의 타입(Content-Type)을 표현)

 

2. Simple 요청

특정 조건을 만족하려면 예비 요청없이 바로 서버에 요청

- 허용된 메소드
GET, POST, HEAD 만 사용
- 안전한 헤더 사용
CORS safe-listed 헤더만 사용
- 제한된 Content-Type
application/x-www-form-urlencoded, multipart/form-data, text/plain

3. JWT (JsonWebToken)

로그인을 증명하는 토큰



1. 기존 cookie랑 session을 통한 인증 처리 문제

  • 쿠키는 노출되기 때문에 민감 정보까지 노출됨
  • 세션은 저장소 문제가 발생하면 인증 체제가 무너져서 이전에 다른 인증된 유저 또한 인증을 사용할 수 없음

2. JWT

인증에 필요한 정보를 암호화시킨 JSON 토큰으로 인터넷 표준 인증 방식

  • 공개/ 개인 키를 쌍으로 사용

3. JWT 구조

  • Header, Payload, Signature
  • 개인 보안성이 뛰어나다
  • (허나 키 자체의 중요한 개인 정보를 넣으면 안됨)
<장점>
이미 토큰 자체가 인증된 정보이기 때문에 세션 저장소가 필수적으로 필요하진 않음

<단점>
JWT는 기본적으로 Claim에 대한 정보를 암호화하지 않음

 

  • salt

비밀번호를 암호화 시켜줄 때 → 개인정보가 털릴 위험성이 있음

따라서 salt key를 사용해서 개인정보의 보완성을 높임

개인별로 salt키를 따로 보관


4. 실습

SWAGGER > SRC > UTIL > JWTUtil

///TODO 4. Token 발급하는 함수 작성하는 법

//// JWT 구성 : Header + Payload(Claim) + Signature

return Jwts.builder().header().add(headers).and().claims(claims).subject(subject)
				.issuedAt(new Date(System.currentTimeMillis()))
				.expiration(new Date(System.currentTimeMillis() + expireTime))
				.signWith(getSigningKey()).compact(); 

header >

return Jwts.builder().header().add(headers).and()

payload >

.claims(claims).subject(subject)
				.issuedAt(new Date(System.currentTimeMillis()))
				.expiration(new Date(System.currentTimeMillis() + expireTime))

signature >

	.signWith(getSigningKey()).compact(); 

////TODO 6. id 정보를 이용한 AccessToken을 생성하는 함수 작성하기

public String createAccessToken(String userId) {

Map<String, Object> claims = new HashMap<>();

claims.put("userId", userId);

claims.put("tokenType", "ACCESS");

return generateToken(claims, "access-token", accessTokenExpireTime);

}

////TODO 7. id 정보를 이용한 RefreshToken을 생성하는 함수 작성하기

public String createRefreshToken(String userId) {

Map<String, Object> claims = new HashMap<>();

claims.put("userId", userId);

claims.put("tokenType", "REFRESH");

return generateToken(claims, "refresh-token", refreshTokenExpireTime);

}

////TODO 9. Token에서 id를 추출하는 함수 작성하기

  	public String getUserId(String authorization ) {
		Jws<Claims> claims = null;
		try {
			claims = Jwts.parser().verifyWith(getSigningKey()).build().parseSignedClaims(authorization);
		} catch (Exception e) {
			log.error(e.getMessage());
			throw new UnAuthorizedException();
		}
		Map<String, Object> value = claims.getPayload();
		log.info("value : {}", value);
		return (String) value.get("userId");
	}

 

*어떤 상황에 동작하는지 설정

config > WebMvcConfiguration
  • addIntercepter이 작동함

Spring에서 요청(Request)이 컨트롤러에 도달하기 전에 가로채서 처리할 수 있게 해주는 기능


*Intercepter

해당하는 컨트롤러에 응답하기 전에 정보 가져와 토큰 검사

→ 토큰 검사하기 전에 로그인 검사
→ 토큰 검사하기
→ 그 토큰을 Header에 보냄
→ 공식 헤더: HeaderAuth = “Authorization”/ 비공식 헤더: RereshAuth

 

  • 인증 체크: Prehandler
  • 응답하기 전: Posthandler </aside>