하효닝
log(hahyun ^ B)
하효닝
전체 방문자
오늘
어제
  • 분류 전체보기 (140)
    • Diary (0)
    • Web (7)
    • Frontend (8)
    • Python (44)
      • Python (1)
      • Algorithm (13)
      • Coding Test (30)
    • Django (3)
      • Django (2)
      • Django Rest (1)
    • Java (14)
      • Java (10)
      • Java Tuning (4)
    • Spring (34)
      • Spring (7)
      • Spring MVC (5)
      • DB 접근기술 (1)
      • JPA (10)
      • Spring Security (3)
      • Rest API (8)
    • Computer Science (26)
      • Operating System (8)
      • Linux (2)
      • Network (2)
      • Database (9)
      • SQL Tuning (5)
    • AWS (2)
    • Git (0)
    • etc (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
하효닝

log(hahyun ^ B)

Spring/Rest API

REST API 인증 기법

2022. 1. 20. 04:16

※ 인증은 사용자가 누구냐에 관한 것이고, 인가는 사용자가 사용할 수 있는 자원을 말한다.

 

Basic 인증

  • 상태가 없는 웹 애플리케이션에서 인증을 구현하는 가장 간단한 방법으로, 모든 HTTP 요청에 아이디와 비밀번호를 같이 보낸다.
  • 최초 로그인한 후 HTTP 요청 헤더의 Authorization: 부분에 Basic <ID>:<Password> 처럼 아이디와 비밀번호를 콜론으로 이어붙인 Base64로 인코딩한 문자열을 함께 보낸다.
  • HTTP 요청을 수신한 서버는 인코딩된 문자열을 디코딩해 아이디와 비밀번호를 찾아낸 후 사용자 정보가 저장된 데이터베이스 또는 인증 서버의 레코드와 비교한다.
  • 만약 데이터베이스의 레코드와 아이디와 비밀번호가 일치하면 요청받은 일을 수행하고, 아니면 거부한다.

 

문제점

  1. Basic 인증 방법은 아이디와 비밀번호를 노출하기 때문에 HTTP와 사용하기에 보안에 취약하다.
    • 따라서 중간에 누군가 HTTP 요청을 가로채 문자열을 디코딩하면 아아이디와 비밀번호를 알아낼 수 있기 때문에, 반드시 HTTPS와 사용해야 한다.
  2. 모든 요청이 일종의 로그인 요청이기 때문에 사용자를 로그아웃 시킬 수 없다.
  3. 사용자의 계정 정보가 있는 저장장소의 경우, 인증 서버와 인증 DB에 과부하가 걸릴 확률이 높다.
  4. 인증 서버가 단일 장애점이 된다. 즉, 인증 서버에 오류가 나는 경우 전체 시스템이 가동 불가

 

토큰 기반 인증

  • 토큰은 사용자를 구별할 수 있는 문자열로, 최초 로그인 시 서버가 만들어 준다.
  • 서버가 토큰을 만들어 반환하면 클라이언트는 이후 요청에 아이디와 비밀번호 대신 토큰을 계속 넘겨 자신이 인증된 사용자임을 알린다.
  • 토큰을 기반으로 한 요청은 헤더에 Authorization: Bearer <TOKEN> 을 명시해야 하고, 서버는 이 토큰을 받아 인증한다.

  • 아이디와 비밀번호를 매번 네트워크를 통해 전송해야 할 필요가 없으므로 보안 측면에서 좀 더 안전하다.
  • 서버가 토큰을 마음대로 생성할 수 있으므로, 사용자의 인가 정보 또는 유효시간을 정해 관리할 수 있다.
  • But, 스케일 문제는 해결하지 못한다.

 

JSON 웹 토큰 (JWT)

  • 서버에서 전자 서명된 토큰을 이용하면 인증에 따른 스케일 문제 해결 가능하며, 전자 서명된 토큰 중 하나가 JSON WEB TOKEN
  • JWT는 JSON 형태로 된 토큰으로, {header}.{payload}.{signature}로 구성된다.

Header typ 토큰의 타입
alg 토큰의 서명을 발행하는 데 사용된 해시 알고리즘 종류
Payload sub 토큰의 주인으로, ID처럼 유일한 식별자
iss 토큰을 발행한 주체
iat 토큰이 발행된 날짜와 시간
exp 토큰이 만료되는 시간
Signature 토큰을 발행한 주체 Issuer가 발행한 서명으로, 토큰의 유효성 검사에 사용

 

※ JWT도 토큰 기반 인증이므로 서버가 생성하지만, 토큰 기반 인증과의 차이점은 서버가 헤더와 페이로드를 생성한 후 전자 서명을 한다는 점

 

 

JWT 토큰 생성과 인증

  1. 브라우저에서 최초 로그인 시, 서버는 사용자의 아이디와 비밀번호를 서버에 저장된 아이디와 비밀번호와 비교해 인증한다.
  2. 사용자 정보를 바탕으로 {헤더}.{페이로드} 부분을 작성하고,자신의  secret 키로 {헤더}.{페이로드} 전자 서명한다.
    • 전자 서명이란 {헤더}.{페이로드}와 시크릿키를 이용해 해시 함수에 돌려 암호화한 결과값
    • 시크릿키란 나만 알고있는 문자열, 비밀번호 같은 것
  3. 전자 서명의 결과로 나온 값을 {헤더}.{페이로드}.{서명}으로 이어 붙이고 Base64로 인코딩 후 반환한다.
  4. 누군가 이 토큰으로 리소스 접근을 요청하면, 서버는 일단 이 토큰을 Base64로 디코딩한다.
  5. 디코딩해서 얻은 JSON을 {헤더}.{페이로드}와 {서명} 부분으로 나눈다.
  6. 서버는 {헤더}.{페이로드}와 자신이 갖고 있는 secret으로 전자 서명을 만든 후 방금 만든 전자 서명을 HTTP 요청이 갖고 온 {서명} 부분과 비교해 토큰의 유효성을 검사한다.

 

특징

  • 인증 서버에 토큰의 유효성에 대해 물어볼 필요가 없고, 이는 인증 서버에 부하를 일이키지 않는다는 뜻으로 더 이상 인증 서버가 단일 장애점이 아니다.
  • 누군가 토큰을 훔쳐가면 해당 계정의 리소스에 접근할 수 있기 때문에 반드시 HTTPS를 통해 통신해야 한다.
  • 스프링 시큐리티를 이용하면 요청을 인증하는 코드를 한 번만 짜고, 이 코드가 모든 API를 수행하기 바로 전에 실행되도록 설정할 수 있다.

 

토큰 생성 코드

@Slf4j
@Service
public class TokenProvider {

    private static final String SECRET_KEY = "NMA8JPctFuna59f5";

    public String create(UserEntity userEntity) {
        // 만료기한 1일
        Date expiryDate = Date.from(Instant.now()
                        .plus(1, ChronoUnit.DAYS));

        // JWT Token 생성
        return Jwts.builder()
                // header, secret_key
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
                // payload
                .setSubject(userEntity.getId())
                .setIssuer("My app")
                .setIssuedAt(new Date())
                .setExpiration(expiryDate)
                .compact();
    }

    public String validateAndGetUserId(String token) {
        // parseClaimsJws: Base64로 디코딩 및 파싱
        // 헤더와 페이로드를 setSigningKey로 넘어온 시크릿을 이용해 서명한 후 token의 서명과 비교
        // 위조되지 않았다면 페이로드 리턴, 위조라면 예외 날림
        // userId가 필요하므로 getBody 호출
        Claims claims = Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();

        return claims.getSubject();
    }
}
  • create()는 JWT 라이브러리를 이용해 JWT 토큰을 생성한다.
  • validateAndGetUserId()는 토큰을 디코딩 및 파싱하고 토큰의 위조 여부를 확인하고, 사용자의 아이디를 리턴한다.
  • 라이브러리를 이용하면 JSON을 생성, 서명, 인코딩, 디코딩, 파싱하는 작업을 하지 않아도 된다.

'Spring > Rest API' 카테고리의 다른 글

REST 서비스 사용  (0) 2022.01.30
스프링 데이터 REST  (0) 2022.01.30
REST 엔드포인트 정의  (0) 2022.01.30
RestContoller 요청과 응답 방법  (0) 2022.01.20
REST 아키텍처 패턴  (0) 2022.01.20
    'Spring/Rest API' 카테고리의 다른 글
    • 스프링 데이터 REST
    • REST 엔드포인트 정의
    • RestContoller 요청과 응답 방법
    • REST 아키텍처 패턴
    하효닝
    하효닝

    티스토리툴바