TIL-인증/인가(Spring 숙련 2주차)_25.04.01

kb·2025년 4월 1일

Spring

목록 보기
12/21

Spring 숙련 2주차 (인증/인가)

Keyword

  • Cookie
  • Session
  • Token
  • JWT
  • Filter
  • Servlet Filter
  • Cookie
- 사용자의 웹 브라우저에 저장되는 정보. 사용자의 상태 혹은 세션을 유지하거나 사용자 경험을 개선하기 위해 사용
- HTTP는 Stateless, Connectionless 특성을 가짐 > Client가 재요청 시 이전 요청 정보 기억X
- 로그인과 같이 상태를 유지해야 하는 경우가 발생 > 브라우저를 완전히 종료한 뒤 다시 열어도 사용자 정보가 유지되어야 함
- 로그인 시 전달된 ID, PW로 USER 테이블 조회하여 일치여부 확인
- 일치한다면 Set-Cookie를 활용해 Cookie에 사용할 값 저장
- 로그인 이후 모든 요청마다 Request Header에 항상 Cookie 값을 담아 요청 (네트워크 트래픽 추가 발생 / 최소한의 정보만 사용)
- Cookie에 담겨 있는 값으로 인증/인가 진행
  • Cookie Header
- 서버에서는 HTTP 응답 헤더에 Set-Cookie 속성을 사용해 생성하고 설정
> 1. Set-Cookie : S -> C로 Cookie 전달(Response Header)
> 2. Cookie : C가 Cookie를 저장하고 HTTP 요청 시 S로 전달(Request Header)
- Cookie의 생명주기
> 1. 세션 Cookie
>> 만료 날짜를 생략하면 브라우저 완전 종료시까지만 유지(Default) ... expires, max-age가 생략된 경우
>> 브라우저를 완전 종료 후 다시 페이지를 방문했을 때 다시 로그인해야
> 2. 영속 Cookie
>> 만료 날짜를 입력하면 해당 날짜까지 유지
>> ex) expires=Sat, 11-Dec-2024 00:00:00 GMT;
>> 만료일 도래 시 쿠키 삭제
>> max-age = 3600 (second, 3600초는 한 시간. 60*60)
>> 0이 되거나 음수를 지정하면 쿠키가 삭제됨
- Cookie의 도메인
> 쿠키가 아무 사이트에서나 생기고 동작하면 안됨
> domain=spartacodingclub.kr
>> domain=spartacodingclub.kr를 지정해 쿠키를 저장
>> dev.spartacodingclub.kr과 같은 서브 도메인에서도 쿠키에 접근
> domain을 생략하면 현재 문서 기준 도메인만 적용
- Cookie의 경로
> 1차적으로 도메인으로 필터링한 후 Path가 적용
> 일반적으로 path=/ 루트(전체)로 지정
> 위 경로를 포함한 하위 경로 페이지만 쿠키에 접근
> ex) path=/api 지정 -> path=/api/example 가능 <> path=/example 불가능
- Cookie 보안
> 1. Secure
>> 기본적으로 Cookie는 http, https 구분하지 않고 전송
>> Secure를 적용하면 https인 경우에만 전송 (s=Secure)
> 2. HttpOnly
>> XSS(Cross-site-Scripting) 공격 방지
>> 악성 스크립트를 웹 페이지에 삽입하여 다른 사용자의 브라우저에서 실행되도록 하는 공격
>> 자바스크립트에서 Cookie 접근을 못하도록 막음
>> HTTP 요청시 사용
> 3. SameStie
>> 비교적 최신 기능. 브라우저 지원 여부 확인 필요
>> CSRF(Cross-Site Request Forgery) 공격 방지
>> CSRF 공격: 사용자가 의도하지 않은 상태에서 특정 요청을 서버에 전송하게 하여 사용자 계정에서 원치 않는 행동을 하게 만듦
>> 요청 도메인과 쿠키에 설정된 도메인이 같은 경우만 쿠키 전송
  • Cookie로 로그인 상태 유지하기
- Cookie는 주로 사용자 세션 관리(로그인, 장바구니, 접속시간)나 광고 트래킹(사용자 행동) 등의 목적으로 사용
- Cookie 사용 방법
> 한번 로그인에 성공하면 HTTP Response에 쿠키를 담아 브라우저에 전달
> 브라우저는 요청마다 Cookie를 함께 전송
> 보안상의 문제로 name=원욱이 아닌 userId=1과 같은 index 정보를 저장 ... 이것 또한 보안 문제가 있음
> 요구사항에 맞추어 세션 Cookie를 사용할지 영속 Cookie를 사용할지 결정
- 예시 ... 1) 로그인 회원 home 페이지 이동 2) home 페이지를 보려면 로그인이 필수 3) 미로그인 회원 login 페이지 이동 4) 브라우저 종료 시 로그아웃 // 강의노트 코드 참고할 것. 아래의 내용을 설계해야 함.
> USER 클래스
> 로그인 요청 DTO
> 로그인 응답 DTO
> 유저 조회 응답 DTO
> HomeController
> UserController
> UserService
> UserRepository
> login.html
> home.html
  • Cookie 문제점
- Cookie는 보안에 취약하여 userId=1 형태의 방식으로 로그인 구현X
> 쿠키 값은 임의로 변경할 수 있어, Client가 임의로 쿠키의 값을 변경하면 서버는 다른 유저로 인식
- Cookie에 저장된 Data는 탈취되기 쉽다
> 네트워크 전송 구간에서 탈취될 확률이 매우 높다
- 보안 대처 방법
> 쿠키에 중요한값 저장X
> 사용자별로 일반 유저나 해커들이 알아보지 못하는 값 노출
>> 일반적으로 암호화된 Token을 쿠키에 저장
>> 서버에서 암호화된 Token과 사용자를 매핑해서 인식
>> Token은 서버에서 관리
>> 토큰은 해커가 임의의 값을 넣어도 동작하지 않도록 만들어야 함
>> 해커가 토큰을 탈취해도 사용할 수 없도록 토큰 만료시간을 짧게 설정
>> 탈취가 의심되는 경우 해당 토큰 강제 만료시키면 됨

Session 1

  • Session
- Cookie를 사용한 방식은 여러 가지 보안 문제가 있음. 결국 보안 문제를 해결하려면 중요한 정보는 모두 서버에 저장해야 함. Client와 서버는 예측 불가능한 임의의 값으로 연결해야 함
- 서버에서 중요한 정보를 보관하며 로그인 연결을 유지하는 방법
- Session 생성 순서
> 1. 로그인 성공 시 Server에서 임의로 만든 Session ID를 생성 (예측 불가하게, UUID와 같은 값 활용)
> 2. 생성된 Session ID와 조회한 User 인스턴스를 서버의 Session 저장소에 저장 (서버에 유저와 관련된 중요한 정보 저정)
- Session 동작 순서
> 1. 로그인
>> 상태유지를 위해 Cookie를 사용 - 서버는 클라이언트에 Set-Cookie: SessionId = 임의생성값을 전달
>> 클라이언트는 Cookie 저장소에 전달받은 SessionId값을 저장
>> Sessions을 활용하면 유저와 관련된 정보는 클라이언트에 없다.
> 2. 로그인 이후 요청
>> 클라이언트는 모든 요청에 Cookie의 SessionId를 전달
>> 서버에서는 Cookie를 통해 전달된 SessionId로 Session 저장소를 조회
>> 로그인 시 저장한 Session 정보를 서버에서 사용
- Session 특징
> 1. Session을 사용해 서버에서 민감한 정보들을 저장
>> 예측이 불가능한 세션 ID를 사용하여 쿠키값을 변조해도 문제X
>> 세션 ID에 중요한 정보는 들어있지 않다
>> 시간이 지나면 세션이 만료되도록 설정
>> 해킹이 의심되는경우 해당 세션을 제거 
> 2. Session은 특별한 것이 아니라 단지 Cookie를 사용해 클라이언트가 아닌 서버에서 데이터를 저장
> 3. Servlet은 Session을 자체적으로 지원함
  • Servlet의 HttpSession
- Servlet이 공식적으로 지원하는 Session인 HttpSession은 Session 구현에 필요한 다양한 기능들을 지원
- 상수를 클래스로 관리하는 방법
> 상수로 활용할 class는 인스턴스를 생성(new)하지 않음
> abstract 추상클래스 혹은 interface로 만들어 상수값만 사용
> Servlet을 통해 HttpSession을 생성하면 SessionId가 JSESSIONID로 생성 > JSESSIONID의 Value는 예측 불가능한 랜덤값으로 생성
- 코드 에시
> SessionUserController
> SessionHomeController
> session-home.html
> session-login.html

Session 2

  • Spring의 Session
- Spring에서는 Session을 쉽게 다루도록 @SessionAttribute라는 어노테이션 제공
- @SessionAttribute
> request.getSession(true);와는 다르게 Session을 새로 생성하는 기능은 없음
> 이미 로그인이 완료된 사용자를 찾는 경우 즉, Session이 있는 경우 사용
- 처음 로그인을 시도하는 경우
> 완전히 처음 로그인을 시도하는 경우 URL 뒤에 JSESSIONID=값이 함께 전달됨
> 해당 값은 굳이 필요 없음. 내부적으로 Set-Cookie로 SessionID와 값을 넣어주기 때문
> Cookie를 지원하지 않으면 URL을 통해 Session을 유지하는 방법에 사용됨
>> 하지만, 사용하려면 모든 요청 URL에 jsessionId 값이 전달되어야 함
>> Template Engine을 사용하면 jsessionId를 URL에 자동으로 포함
- URL이 아닌 Cookie를 통해서만 Session을 유지하고자 한다면?
> application.properties
> application.yml
  • Session 정보
- HttpSession은 Session을 간편하게 사용할 수 있도록 다양한 기능을 지원
> 1. session.getId();
>> jsessionId 값을 조회할 수 있음
> 2. session.getMaxInactiveInterval();
>> 세션의 유효시간
>> second 단위 default는 30분(1800초)이다.
> 3. session..getCreationTime();
>> 세션 생성 시간 (ex. Sat Dec 9 15:40:23 KST 2024)
> 4. session.getLastAccessedTime();
>> 해당 세션에 마지막으로 접근한 시간 (ex. Sat Dec 9 15:40:23 KST 2024)
> 5. session.isNew();
>> 새로 생성된 세션인지 여부

Session 3

  • Session TimeOut
- Session은 logout 기능을 사용하여 session.invalidate();가 되어야 삭제되지만, 대부분의 사용자들은 로그아웃을 굳이 하지않고, 브라우저를 종료함
- Session의 문제점
> HTTP는 Connectionless 특성 -> 서버가 브라우저 종료 여부 판별 못함
> 서버에서 Session을 언제 삭제해야 하는지 판단하기 힘듦
> JSESSIONID의 값을 탈취 당한 경우 해당 값으로 악의적인 요청 가능
> 세션은 서버 메모리에 생성되고 자원은 한정적이기 때문에 꼭 필요한 경우만 생성해야함 (ex. 로그인한 유저가 100만명이라면..?)
- Session 생명주기
> 기본적으로 30분을 기준으로 세션을 삭제
> 실제 로그인 후 30분 이상의 시간동안 사용중인 사용자의 세션 또한 삭제됨
> 다시 로그인 해야하는 경우가 발생
- HttpSession 사용
> 세션 생성시점 30분이 아닌 서버에 최근 Session을 요청한 시간을 기준으로 30분 유지
> HttpSessionㅇ느 기본적으로 해당 방식으로 세션의 생명주기를 관리
> Session 정보에서 LastAccessedTime을 기준으로 30분이 지나면 WAS가 내부적으로 세션을 삭제함
  • Session의 한계
- Session은 서버의 메모리를 사용하여 확장성이 제한됨
- 서버가 DB 혹은 메모리에 저장된 세션 정보를 매번 조회하여 오버헤드가 발생
> 오버헤드: 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간, 메모리 등을 의미
- 서버가 상태를 유지해야 하므로 사용자 수가 많아질수록 부담이 커짐
- Cookie는 웹 브라우저에만 존재하여 모바일 앱 등의 다양한 클라이언트에서 인증을 처리할 수 없음
- Scale Out(수평적 확장)에서 서버간 세션 공유가 어려움
  • 정리
- 인증과 인가
> 1. 인증(Authenticiation) : 사용자가 누구인지 확인하는 과정
>> ex) 로그인
> 2. 인가(Authorization) : 사용자가 어떤 권한을 가지고 있는지 결정하는 과정 / 반드시 인증이 선행되어야
>> ex) 회원만 조회 가능한 게시글, 본인이 작성한 게시글 수정
- 쿠키
> 웹 브라우저(Client)에 저장되는 데이터
> 사용자의 방문 기록, 로그인 상태 유지, 개인 맞춤 설정 등을 저장, HTTP 특성 극복, 광고 정보 등에 활용
> 클라이언트 측에 저장. 서버에 요청을 보낼 때마다 포함되어 전송됨
> 만료 날짜 설정 가능. 세션 쿠키(브라우저 종료 시 삭제)와 영속 쿠키(지정된 기간 동안 유지)로 구분
- 세션
> 사용자와 서버 간의 상태를 유지하기 위한 방법
> 로그인 정보, 사용자 활동 등을 서버 측에서 관리
> 서버 측에 저장, 세션 ID가 쿠키에 저장되어 클라이언트와 연결됨
> 브라우저를 닫거나 일정 시간이 지나면 만료
> 관리
>> 1. 세션은 메모리를 사용 -> 세션 많아지면 서버 장애 발생 가능(메모리 리소스 부족) -> 최소한의 데이터만 저장해야
>> 2. HttpSession은 LastAccessedTime을 기준으로 30분의 생명주기를 가지고 있음
>> 3. 세션의 시간을 너무 오래 유지하여도 메모리 리소스가 부족할 수 있음 (적당한 시간 설정이 꼭 필요(Default 30분))
- 

Token

  • Token
- Web Application이나 API에서 인증(Authentication)과 인가(Authorization) 과정에서 사용되며 사용자 또는 시스템의 신원과 권한을 증명하고 요청의 유효성을 검증하는 데 사용되는 디지털 문자열
- Token을 사용하는 이유
> 1. Token은 서버가 아닌 클라이언트에 저장되어 서버 부담을 덜어줌
> 2. Cookie는 웹 브라우저에만 존재하여 모바일 앱 등의 다양한 클라이언트에서 인증을 처리할 수 없음
> 3. Token 방식은 Stateless를 기반으로 하여 확장성이 뛰어남
> 4. 인증된 사용자임을 확인하기 위한 고유한 서명을 포함하여 위조된 요청인지 확인할 수 있음
- Token 동작 순서
> Token 생성 시 사용자의 고유한 정보를 포함
> DB에 접근하지 않고 Token의 유효성만 검증
- Token 단점
> Cookie/Session 방식보다 Token 자체의 데이터 용량이 큼 > 요청이 많아지면 그만큼 트래픽이 증가
> Payload(전송되는 데이터)는 암호화되지 않아서 중요한 데이터를 담을 수 없음
> Token을 탈취당하면 대처하기 어려워 만료 시간(30분)을 설정함
  • JWT(JSON Web Token)
- 인증에 필요한 정보들을 암호화 시킨 JSON 형태의 Token
- JSON 데이터 포맷을 사용해 정보를 효율적으로 저장하고 암호화로 서버의 안정성을 높임
- JWT 구조
> 1. Header
>> 토큰의 타입과 해싱 알고리즘을 정의 (예: {"alg":"HS256", "type": "JWT"}
> 2. Payload
>> 실제 인증과 관련된 데이터(Claims)를 담고 있음
>> Claims의 종류 - Registered Claims ... 미리 정의된 Claims
>>> iss(issuer): 발행자
>>> exp(expiration time): 만료 시간
>>> sub(subject): 제목
>>> iat(issued At): 발행 시간
>>> jti(JWT ID): 토큰의 고유 식별자
>> Public Claims: 사용자가 정의할 수 있는 클레임, 공개용 정보 전달 목적
>> Private Claims: 사용자 지정 클레임, 당사자들 간에 정보를 공유하기 위한 목적
> 3. Signature
>> Header와 Payload를 서버의 Secret Key로 서명하여 암호화
>> 암호화는 Header에서 정의한 알고리즘(alg)을 활용
>> 서명을 통해 서버는 Token이 변조되지 않았음을 확인
>> Header와 Payload는 Encoding된 값이기 때문에 복호화 혹은 값 수정이 가능하지만, Signature는 서버에서 관리하는 값이기 때문에 Secret Key가 유출되지 않는 이상 복호화할 수 없음

JWT

  • JWT 인증
- JWT는 Base64로 인코딩되어 쉽게 복호화 가능. Payload가 그대로 노출되기 때문에 비밀번호나 민감한 정보 저장 X
- JWT 인증 과정
> 1. 클라이언트의 로그인 요청
> 2. 로그인에 성공했다면 Header, Payload에 Secret Key를 사용하여 Signature를 만듦 -> 이후 Base64로 Encoding / 일반적으로 Cookie에 담아 클라이언트에게 JWT를 발급
> 3. 발급받은 JWT를 저장 후 서버에 요청할 때 Authorization Header에 JWT를 담아 보냄
> 4. 서버에서 JWT의 유효성 검사를 통해 통과한다면 인증에 성공하여 요청을 처리 -> JWT 만료, 위변조 여부를 검사
- JWT의 유효성 검사
> 1. A의 JWT를 B가 탈취
> 2. B가 탈취한 JWT를 임의로 수정
> 3. B가 수정한 JWT로 Server에 요청
> 4. 서버는 Signature를 사용하여 유효성 검사(Signature 불일치)
>> Header, Payload를 서버의 Secret Key 값을 이용해 Signature를 다시 만들어 비교
>> 임의로 조작된 데이터를 판별할 수 있음
- JWT의 목적은 정보 보호가 아닌, 위조 방지에 있음
  • JWT 장단점
- JWT 장점
> 1. Signature로 서버의 보안성이 증가
> 2. Token 자체가 필요한 정보(유저 및 검증 정보)들을 모두 가짐
> 3. 서버는 인증 정보와 관련된 별도의 저장소를 사용하지 않음
> 4. 서버의 수평 확장성(Scale Out)이 높아짐
> 5. Cookie가 없는 다른 환경에서도 인증/인가를 적용할 수 있음
> 6. DB를 조회하지 않아도 됨
(Mobile의 경우 App을 자주 닫거나 백그라운드로 전환하여 Session 방식을 사용하지 않음
- JWT 단점
> 1. Payload는 암호화 된 것이 아니라 민감한 정보 다루지 못함
> 2. Token의 길이가 길어서 트래픽이 증가하면 네트워크에 부하가 증가
> 3. 클라이언트 측에서 Token을 관리하기 때문에 탈취당하면 대처가 어려움
  • Access Token, Refresh Token
- Token은 클라이언트에서 관리하여 탈취당할 위험성이 높기 때문에 만료시간 설정이 필요 이때 발생하는 단점을 극복하기 위해 Access Token과 Refresh Token을 사용
- Token의 유형
> 1. Access Token
>> 사용자 인증 후 서버가 발급하는 유저 정보가 담긴 토큰
>> 유효 기간 동안 API나 리소스에 접근할 때 사용
> 2. Refresh Token
>> Access Token은 보안을 위해 짧은 수명을 가짐
>> Access Token이 만료된 경우 재발급 받기 위해 사용
>> 주로 데이터베이스에 유저 정보와 같이 저장
- Access Token, Refresh Token 인증
> 1. 클라이언트의 로그인 요청
> 2. 로그인에 성공했다면 Header, Payload에 Secret Key르 사용해 Signature를 만듦
> 3. 발급받은 JWT를 저장 후 서버에 요청할 때 Authorization Header에 JWT(Access Token)을 담아 보냄
> 4. 서버에서 JWT의 유효성 검사를 통해 통과한다면 인증에 성공해 요청을 처리
> 5. Access Token이 만료 되었다면 Refresh Token으로 토근 재발급을 요청
> 6. 서버로부터 Access Token을 재발급 받음
- JWT를 Access Token만을 사용하여 인증한다면 탈취되어 보안에 취약할 수 있음.
- 유효 시간을 부여하여 문제를 해결하지만 유효 시간이 짧다면 로그인을 자주 해야하기 때문에 Refresh Token을 적용

Filter

  • 공통 관심 사항(cross-cutting concerns)
- 같은 말로 횡단 관심사라고 함. 여러 위치에서 공통적으로 사용되는 부가 기능이고, Filter가 나오게된 이유는 공통 관심사(Cross Cutting Concern)의 처리 때문
- 핵심 기능 : 비즈니스 로직 (예, 댓글 CRUD, 회원가입 CRUD)
- 부가 기능 : 비즈니스 로직과는 별개로 동작하는 기능(예, 로그 출력 / 보안, 트랜잭션 등)
- 요구사항: 로그인 한 유저만 특정 API를 사용할 수 있어야 한다.
- 해결 방법 : 언제나 핵심은 수정에 있다!
> 1. 화면에서 로그인 하지 않으면 API를 사용하지 못하도록 막음
>> 유저가 HTTP 요청을 마음대로 조작할 수 있음
> 2. Controller에서 로그인 여부를 체크하는 Logic을 작성
>> 실제로는 인증이 필요한 모든 컨트롤러에 공통으로 로그인 여부를 체크해야함
>> 로그인 로직이 변경될 때 마다 로그인 여부를 체크하는 Logic 또한 변경될 가능성이 높음
>>> 예시: create / update / delete 로직이 있다고 할 때, 모든 메서드에 로그인 여부 확인 로직이 필요함.
>>> 로그인 여부 확인 로직이 공통 관심사이고, 이는 인증: 로그인이라 생각할 수 있음
>>> 공통 관심사는 로그인뿐만 아니라 더 큰 범위를 의미함
> 3. Spring AOP를 활용할 수 있음
> 4. Web과 관련된 공통 관심사는 Servlet Filter나 Spring Intercepter를 사용
>> HttpServletRequest 객체를 제공하기 때문에 HTTP 정보나 URL 정보에 접근하기 쉬움
>>> 예) HTTP Header Cookie -> 인증 / 특정 URL의 요청은 인증을 할 것이다 -> URL 정보 필요
  • Servlet Filter
- Servlet Filter는 보안, 로깅, 인코딩, 인증/인가 등 다양한 작업을 처리하기 위해 사용됨
- Servlet Filter 특징
> 1. 공통 관심사 로직 처리
>> 공통된 로직을 중앙 집중적으로 구현하여 재사용성이 높고 유지보수가 쉬움
>> 모든 요청이 하나의 입구를 통해 처리되어 일관성을 유지
> 2. HTTP 요청 및 응답 필터링
> 3. Filter Chain
>> 여러 개의 필터가 순차적으로 적용될 수 있음
>> filterChain.doFilter(request, response); 다음 필터로 제어를 전달
> 4. doFilter()
>> 실제 필터링 작업을 수행하는 주요 메소드로 필터가 처리할 작업을 정의
>> 다음 필터로 제어를 넘길지 여부를 결정
- Servlet Filter 적용
> Filter를 적용하면 Servlet이 호출되기 이전에 Filter를 항상 거침
> 공통 관심사를 필터에만 적용하면 모든 요청 or 응답에 적용
> Filter는 특정 URL Pattern에 적용할 수 있음
> Spring을 사용하는 경우 Servlet은 Dispatcher Servlet임
- 인증/인가 필터 동작
> 로그인 성공 -> 정상적으로 Controller 호출
> 로그인 실패 -> Filter에서 적절하지 않은 요청이면 Controller 호출X
- Filter Chain
> Filter는 Chain 형식으로 구성
> Filter는 개발자가 자유롭게 추가할 수 있음
> Filter는 순서를 지정하여 추가할 수 있음

Filter 2

  • Filter Interface
- Java Servlet에서 HTTP 요청과 응답을 가로채고, 이를 기반으로 다양한 처리 작업을 수행하는데 사용되는 Interface
- jakarta.servlet.Filter
> Filter Interface를 Implements하여 구현하고 Bean으로 등록하여 사용
>> Servlet Container가 Filter를 Singleton 객체로 생성 및 관리
- 주요 메서드
> 1. init()
>> Filter를 초기화하는 메서드
>> Servlet Container가 생성될 때 호출
>> default method이기 때문에 implements후 구현하지 않아도 됨
> 2. doFilter()
>> Client에서 요청이 올 때마다 doFilter() 메서드가 호출
>>> doFilter() 내부에 필터 로직(공통 관심사 로직)을 구현
>> WAS에서 doFilter()를 호출해주고 하나의 필터의 doFilter()가 통과된다면
>> Filter Chain에 따라서 순서대로 doFilter()를 호출
>> 더이상 doFilter()를 호출할 Filter가 없으면 Servlet이 호출
> 3. destroy()
>> 필터를 종료하는 메서드
>> Servlet Container가 종료될 때 호출
>> default method이기 때문에 implements 후 구현하지 않아도 됨
  • Servlet Filter 구현
- Filter 구현체
> 요청 URL을 Log로 출력하는 Filter
>> doFilter()는 더 이상 호출할 Filter가 없다면 Servlet을 호출
>> ServletRequest는 기능이 별로 없어서 대부분 기능이 많은 HttpServletRequest를 다운 캐스팅하여 사용
- Filter 등록
> setFilter()
>> 등록할 필터를 파라미터로 전달
> setOrder()
>> Filter는 Chain 형태로 동작
>> 실행될 Filter 들의 순서가 필요
>> 파라미터로 전달될 숫자에 따라 우선순위가 정해짐
>> 숫자가 낮을수록 우선순위가 높음
> addUrlPatterns()
>> 필터를 적용할 URL 패턴을 지정
>> 여러개 URL 패턴을 한번에 지정할 수 있음
>> 규칙은 Servlet URL pattern과 같음
> filterRegistrationBean.addUrlPatterns("/*")
>> 모든 Request는 Custom Filter를 항상 지나감
- Spring이 제공하는 URL Pattern은 Servlet과 다르게 더욱 세세하게 설정할 수 있음
- Postman 호출
> GET /test에 Mapping되는 Controller는 만들지 않음
> 존재하지 않는 URL Mapping이지만 Console에 URI가 log로 출력
>> 즉, Controller보다 앞에서 Filter가 동작
> doFilter() 로직이 끝나면 다음 Filter의 doFilter()가 호출
> 모든 Filter의 doFilter() 메서드가 정상적으로 실행되면 Controller를 호출
  • 정리
- Servlet Filter 정리
> 1. Filter를 사용하려면 Filter Interface를 Implements하여 구현
> 2. 구현한 Filter를 Bean으로 등록
> 3. HTTP 요청이 오면 doFilter() 메서드가 호출
>> ServletRequest는 기능이 별로 없어 HttpServletRequest로 다운 캐스팅해야함
> 4. chain.doFilter(request, response)
>> (순서를 설정해둔) 다음 필터가 있으면 FIlter를 호출
>> 다음 실행할 필터가 없으면 Servlet을 호출
>> 해당 메서드를 호출하지 않으면 다음 단계로 진행 X
>>> 다음 필터나 Servlet을 호출하지 않음
>> Filter를 등록하는 방법은 여러가지
>>> SpringBoot의 경우 FilterRegistrationBean을 사용
- @WebFilter 어노테이션은 필터 등록이 가능하지만 순서 조절이 안 돼 잘 사용 X

Servlet Filter 실습

  • Login Filter 실습
- 요구사항 1: Cookie/Session 방식으로 로그인 기능을 구현(가정)
- 요구사항 2: 로그인 인증은 Servlet Filter로 구현
- 요구사항 3: /, /user/singup, /login, /logout URL로 들어오는 요청은 인증 Filter 로직을 수행하지 않도록 해야함
- 1. Login Filter 만들기
- 2. Filter 등록하기
- Filter가 많아져도 성능적 문제 발생X. Filter 몇 개는 있어도 괜찮음.
- Spring MVC 구조를 보면 요청이 한번 들어올 때 수백, 수천 가지의 메서드가 호출되는 것을 확인할 수 있듯이 Filter로 발생하는 부하는 사소함.
> 이미 만들어져 있는 POST+/post API를 호출
> 로그인하지 않고 (session이 비어있는 채로 요청) 요청한다
> throw new RuntimeException("로그인 해주세요.") 실행
> 필터 로직이 순서대로 실행
>> 1. CustomFilter의 Log인 requestURI 출력
>> 2. LoginFilter의 Log인 로그인 필터 로직 실행 출력
  • 정리
- 실습 코드
1. WHITE_LIST
> a. LoginFilter를 적용하지 않을 URL 배열
2. isWhiteList()
> a. WHITE_LIST을 제외한 모든 경우에 인증 체크를 하도록 만든 메서드
3. throw new RuntimmeException()
> a. Login하지 않은 경우 즉, Session이 없는 경우 예외 처리
- 단일 책임 원칙
> 공통 관심사(로그인 인증)를 Servelt Filter로 해결
> 로그인 인증 로직이 바뀌거나 WHITE_LIST가 추가되어도 LoginFilter만 수정하면 됨 -> 단일 책임 원칙을 잘 지켰다
- JWT
> Spring Security는 JWT, OAuth와 같은 인증을 표준화된 방식으로 지원
> Spring Security를 사용하면 보안 결함의 위험을 줄이고 코드의 일관성을 유지
> Servlet Filter로도 JWT를 구현할 수 있지만 Spring Security를 사용하면 인증/인가 로직을 더 안전하고 관리하기 쉽게 구성할 수 있음

마무리

  • 이것만은 꼭 기억하기
- 1. Cookie
> 웹 브라우저에 저장되는 데이터
> 서버가 클라이언트 상태를 기억하도록 도와줌 (로그인 상태 유지 등에 활용)
> 보안에 취약(민감 정보 저장 X/ 사용자 임의 수정 가능)
- 2. Session
> 서버에서 중요한 정보를 보관하며 로그인을 유지하는 방법
>> SessioId를 탈취해도 민감정보가 없음
> 만료 시간을 설정해 탈취 문제를 최소화
>> HttpSession은 최근 Session을 요청한 시간을 기준으로 만료 시간을 유지
- 3. Token
> 인증/인가 과정에서 사용. 사용자 또는 시스템의 신원과 권한을 증명, 요청의 유효성 검증하는 데 사용되는 디지털 문자열
> Session과 달리 Client가 데이터(Token)을 저장
>> Stateless 기반으로 하여 확장성이 뛰어남
> Mobile과 같이 Cookie를 사용할 수 없는 경우에도 사용 가능
> Payload는 암호화되지 않음
> 만료 시간으로 Token 탈취를 대비함
- 4. JWT
> 인증에 필요한 정보들을 암호화시킨 JSON 형태의 Token
> Signature를 통해 Token을 안전하게 관리
> JWT의 목적은 정보 보호가 아닌 위조 방지
- 5. Filter
> 공통 관심 사항을 하나의 입구에서 처리
profile
Experience

0개의 댓글