인증(방식)과 기능 구현

YEON·2022년 7월 13일
1

인증 & 인가

  • 인증 (Authentication)
    사용자가 누구인지 확인하는 절차 (ex.회원가입, 로그인)
  • 인가 (Authorization)
    사용자가 요청(Request)하는 동작을 할 수 있는 권한이 있는지 확인하는 절차 (ex.글 삭제,즐겨찾기 접근)


인증 방식

세션(서버) 기반 인증

서버 측에서 사용자들의 정보를 기억
서버에서 사용자들의 정보를 기억하기 위해 데이터베이스 등을 통해 (세션)정보를 저장하는데,
클라이언트로부터 요청을 받으면(ex. 로그인) 세션에 사용자 정보를 저장해두고 클라이언트의 상태를 계속해서 유지해서(statefull) 이 정보를 서비스에 이용한다.
세션&토큰에 더 자세한 설명은 이전에 포스팅을 정리해두었다.


세션의 동작 방식은 다음과 같다.

  1. 클라이언트는 서버 접속 시 세션ID를 발급받는다.
    (서버는 세션 ID를 하나 생성하여 쿠키 값으로 만들고 Set Cookie 라는 헤더를 통해서 클라이언트에 전송)
  2. 클라이언트는 발급받은 세션ID를 쿠키로 저장하고,
  3. 이후 서버에 요청할 때, 쿠키에 저장된 세션ID를 전달한다.
  4. 서버에서는 전달받은 세션ID로 DB에서 클라이언트 정보를 가져와서 처리 후 응답한다.


토큰 기반 인증

사용자에게 토큰을 발급하면 클라이언트가 발급받은 토큰을 기억
인증받은 사용자들에게 토큰을 발급하면 클라이언트가 서버에 요청을 할 때 헤더에 토큰을 함께 보내도록 하여 유효성 검사를 한다.

  • 확장성 측면
    토큰은 클라이언트 측에 저장된다. 때문에 클라이언트와 서버의 연결고리가 없기 때문에, 서버는 Stateless 하여 확장하기에 매우 적합하다. 또한 토큰을 사용하면, 세션 정보가 있는 서버를 찾을 필요 없으므로 어떠한 서버로 요청이 와도 상관이 없다.
    OAuth의 경우 소셜 계정(ex.Naver)을 이용하여 다른 웹서비스에서도 로그인이 가능하다.

  • 보안성 측면
    클라이언트가 서버로 요청을 보낼 때 더 이상 쿠키를 전달하지 않으므로, 쿠키 사용에 의한 취약점이 사라지게 된다.
    하지만 토큰 환경의 취약점이 존재할 수 있고 만일 토큰이 공격자(해커)에게 탈취되었다면, 한번 발급된 토큰은 임의로 만료시킬 수 없으므로 공격자는 토큰이 만료될 때까지 계속 공격 할 수 있다.


토큰 인증 동작 방식은 다음과 같다.

  1. 서버는 아이디,비밀번호 등으로 검증을 완료한 사용자에게 토큰을 발급한다.
  2. 클라이언트는 토큰을 저장한 후, 서버에 요청(Request)할 때 헤더에 토큰을 함께 보낸다.
  3. 서버는 해당 JWT의 유효성을 검사하고 인가한다.



요약

세션 기반 인증

  • 장점) 정보가 서버에 저장되기 때문에 토큰 기반 인증에 비해 위변조 혹은 손상 우려가 없다.
  • 단점) 사용자가 늘어나면 서버를 확장해야하거나 데이터베이스에 무리가 갈 수 있다.

토큰 기반 인증

  • 장점) 토큰은 클라이언트 측에서 저장되어 서버는 Stateless 되고 확장에 적합해진다.
  • 단점) 한번 발급된 토큰은 임의로 만료시킬 수 없다. 따라서 토큰이 공격자(해커)에게 탈취되었다면, 공격자는 토큰이 만료될 때까지 계속 공격 할 수 있다.

두 방식의 차이
'인증 확인 증거를 어디에 저장하는가' 이다.
세션 기반 인증은 서버(DB 서버)에, 토큰 기반 인증은 클라이언트 측에 저장한다.






인증 정보 전달 방법

데이터 요청 시 인증 정보 전달 방법은 HTTP 인증 프레임워크를 통해서 전달된다.
웹 애플리케이션이 HTTP 요청 메시지를 받으면, 서버는 요청을 처리하는 대신에 현재 사용자를 식별할 수있는 '인증 요구'로 응답할 수 있다.

HTTP는 사용자 인증을 하는 데 사용하는 자체 인증요구/응답 프레임워크를 제공한다.
추가로, 필요에 따라 고쳐 쓸 수 있는 제어 헤더를 통해, 다른 인증 프로토콜에 맞추어 확장할 수 있는 프레임워크를 제공한다.


일반적인 HTTP 인증 프레임 워크 응답 흐름은 다음과 같다.

  1. 데이터 요청
    클라이언트가 서버에 정보를 요청한다. 이 때에는 아무런 헤더도 필요없고, 그저 GET 메서드 정보만 전해진다
GET /favorites HTTP/1.1
accept: application/json
  1. 인증 요구
    서버가 사용자에게 401 Unauthorized 상태 정보와 함께 비밀번호 등 요청을 반려한다.
    WWW-Authenticate 헤더에 어떻게 인증해야하는지 각 영역에 대한 설명을 덧붙인다.
HTTP/1.1 401 
...
WWW-Authenticate: Bearer realm="Nextstep"
...
  1. 인증
    클라이언트가 다시 요청을 보내는 것이다.
    인증 알고리즘과 비밀번호 등을 기술한 Authorization 헤더를 함께 보낸다.
GET /favorites HTTP/1.1
authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJpZFwiOjEsXCJlbWFpbFwiOlwiZW1haWxAZW1haWwuY29tXCIsXCJwYXNzd29yZFwiOlwicGFzc3dvcmRcIixcImFnZVwiOjIwLFwicHJpbmNpcGFsXCI6XCJlbWFpbEBlbWFpbC5jb21cIixcImNyZWRlbnRpYWxzXCI6XCJwYXNzd29yZFwifSIsImlhdCI6MTU5NDkwNjAwMCwiZXhwIjoxNTk0OTA5NjAwfQ.wA44l_2vBJAS_oZDSW4mQ4r-Wwgao2tMCYGjW0f_nEs
accept: application/json
...
  1. 데이터 응답
    인증 정보가 옳다면 200 상태정보(성공 응답)과 함께 요청한 데이터를 응답한다.
    (선택적으로, 서버가 Authentication-Info 헤더를 함께 보낸다.)
HTTP/1.1 200 
...
[
  {
      "id": 1,
      "source": {
         "id": 1,
         "name": "교대역",
         "createdDate": "2020-07-16T22:26:39.604",
         "modifiedDate": "2020-07-16T22:26:39.604"
       }
   }
]






인증을 통한 기능/인수 테스트 구현

  • 토큰 발급 로그인 기능 인수 테스트 구현
  • 토큰 인증을 통한 내 정보 조회 기능 구현
  • 토큰 인증을 통한 즐겨 찾기 기능 구현

인수 테스트의 인증 프로세스

세션 인증 기반 인수 테스트

  • 브라우저 동작 방식과 같은 환경을 구축한다.
  • 로그인 응답의 Cookie 에 담겨 있는 Session ID 를 꺼내 요청에 전달한다.
  • 로그인 상태의 기능 테스트를 하기 위해서는 두 번의 요청이 필요하다.
    1) Session ID를 발급받기 위한 테스트를 위한 요청
    2) 테스트할 기능에 대한 요청

토큰 인증 기반 인수 테스트

  • 로그인을 통해 토큰을 발급 받고, 매 요청 시 토큰을 포함하여 요청한다.

토큰 인증 인수 테스트 구현

RequestSpecification.auth()

  • form("email@eamil.com", "password")
  • basic("email@eamil.com", "password")
  • oauth2(accessToken)

HTML form 로그인

RestAssured
	.given().log().all()
    .auth().form(email, password, new FormAuthConfig("/login/session", NAME, PASSWORD))
    .accept(MediaType.APPLICATION_JSON_VALUE).
    .when().get("/members/me")
    .then().log().all().
    .statusCode(HttpStatus.OK.value())
    .extract();

코드 구현 예제

@Test
void 내_정보_조회() {
	//given
	회원_생성_요청(EMAIL, PASSWORD, AGE);
	String token = 로그인_되어_있음(EMAIL, PASSWORD);
	//when
	ExtractableResponse<Response> findResponse = 내_정보_조회_요청(token);
	//then
	회원_정보_조회됨(findResponse, EMAIL, AGE);
  }
public static ExtractableResponse<Response> 내_정보_조회_요청(String token) {
	return RestAssured
			.given().log().all()
            .auth().oauth2(token)
            .accept(MediaType.APPLICATION_JSON_VALUE)
            .when().get("/members/me")
            .then().log().all()
            .extract();
  }






[참조]
https://mangkyu.tistory.com/55
https://velog.io/@arthur/%EC%84%B8%EC%85%98-%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D-vs-%ED%86%A0%ED%81%B0-%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D#%EC%84%B8%EC%85%98-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D-vs-%ED%86%A0%ED%81%B0-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D / 세션,토큰 기반 인증
https://runebook.dev/ko/docs/http/authentication
https://kscodebase.tistory.com/316 / HTTP authentication
프로젝트 공방 1기 - 인증과 인증 도구

profile
- 👩🏻‍💻

0개의 댓글