세션 로그인/회원가입
세션과 쿠키 ,코드스쿼드 - 루카스 강의정리 (비밀😛))
- 세션 : 로그인(인증) 이후, 사용자 별 정보 저장위한 자료구조 (메모리)
- 쿠키 : 무상태의 HTTP 에서 서버에서 이용하는 사용자 식별 수단으로 브라우저에서 저장하는 값
- 쿠키에 있는 session id 이용해서 서버가 클라이언트 식별, session_ db로부터 세션 정보 읽어 와서 인증, 인가 처리
logig/LoginService
- 회원가입 - signup()
UserService
- create 담당 (이미 존재하는 User 정보 검증 - UserService)
- 로그인 - login()
- 세션 값 ⭕ → 리턴(ok)
- 세션 값 ❌ → 비밀번호 체크 후 로그인 & 세션에 담고 리턴(ok)
- 로그아웃 - logout()
HttpSession - setAttribute(name, value);
- name : HttpSession의 여러 기능 중 내가 쓸 기능의 이름 정의
암호화
- 양방향 : 복호화 가능 (대칭키, 공개키 방식)
- 단방향 : 복호화 불가능 (해시함수)
패스워드 암호화 요구조건
- 복호화 불가능 : 해시함수 사용
- 하나의 패스워드 해시값이 노출되더라도, 다른 계정의 동일한 패스워드까지 탈취 당하면 안 된다.
- salt 사용
- ex. 동일 1234 -> 다른 해시값
- Brute-force 공격 대비가 가능해야 한다.
- 경우의 수를 무작정 투입해서 알아내려는 공격
- 연산속도가 너무 빠르면 보안상 안 좋을 수 있다.
- BCrypt 알고리즘 많이 사용
구현
🟢 strategy 패턴: User with Encryptor
@Transactional
public Optional<User> findPasswordMatchingUser(String email, String password) {
return userRepository.findByEmail(email)
.map(user -> user.isMatched(encryptor, password) ? user : null);
Spring Security
스프링 생태계에서 인증, 인가 개념을 최대한 쉽고 유연하게 구현할 수 있게 만들어진 framework
- 인증과 인가는 Spring Security의 궁극적으로 이루고자 하는 목표
인증
- 사용자는 누구일까? - 나는 누구이며, 누구임을 증명한다
- 한 번 의 로그인 후, 서비스 이용중에도 인증은 이루어 져야 한다.
- user A 로그인 후, user B 의 서비스 요청에 대해 인증으로 구분한다.
- 모든 요청 마다 인증을 해야 한다.
- 방법1. 모든 요청마다 ID, PW를 포함시켜 요청한다.
- 방법2. ID,PW 를 서버가 받으면, 난수형태의 KEY를 응답으로 전달하여, 요청마다 KEY를 포함한다.
인가
- 당신은 무엇을 할 수 있습니까?
- 인증 후, 리소스에 대한 권한 통제
- 클라이언트 요청한 작업의 허가 여부 확인하는 절차
- ex. 👺A가 😎B의 개인 정보/게시글/글쓰기 등 요청 → 인가 실패
세션
- User A 로그인(인증)
서버
: User 저장, 응답으로 JSESSIONID 반환
- User A 는 JSESSIONID 저장 = 쿠키 저장
- (로그인 이 후) 요청시 쿠키 전달
서버
: 쿠키로 해당 세션을 찾고, 세션으로 Security Context 찾아 인증, 인가 거쳐 요청받은 리소스 반환
세션의 장점
- 사용자 정보(세션)
- JSESSIONID
- 유의미한 값 X, 서버에서 세션 찾는 Key로만 활용
- JSESSIONID 탈취 ≠ 개인정보 탈취
- 세션하이제킹 공격은 주의
세션의 단점
- 서버에 세션 저장 공간 필요
- 분산 서버(클러스터 환경)에서 세션 공유 어려움
- 세션 서버를 별도로 두거나 서버간 세션 공유 방법 등 사용
- 하지만 쉬운 방법이 아니기에 토큰 이용
JWT
토큰으로 인증 하기, stateless
Json Web Token
Stateless
- 세션의 단점 해결방안으로 사용
- User A 로그인
서버
: 토큰 생성 후 저장X, 응답으로 토큰 반환
- User A 는 토큰 저장
- (로그인 이 후) 요청시 토큰 전달
- 분산환경에서 서버1 or 서버2 는 토큰을 의미 있는 값으로 해석 ➜ User A 인증
의미 있는 값
- 세션 방식에서 JSESSIONID 는 의미있는 값이 아니다. (key 로만 활용)
- 토큰은 유저를 설명할 수 있는 데이터를 포함 ➜ 의미 있는 값
토큰의 장점
- 세션관리 할 필요가 없어 별도의 저장소가 필요하지 않다.
- 서버 분산&클러스터 환경과 같은 확장성에 좋다.
토큰의 단점
- 제공된 토큰의 회수가 어렵다.
- 세션 : 서버에서 세션 삭제해서 브라우저의 JSESSIONID을 무용지물화
- 토큰 : 서버에 저장하지 않기에 제공된 토큰 회수 할 수 없다.
- 토큰이 한 번 생성 후, 저장하지 않기 때문에 삭제로 기존 토큰의 무용지물하는 등의 제어가 불가능
- 토큰의 유효 기간을 짧게 한다.
- 토큰은 의미있는 데이터를 포함하기 때문에, 민감정보를 토큰에 포함시키면 안된다.
JWT 구조
- JWT 검증에 필요한 정보 가진 객체
③ SIGNATURE
에 사용한 암호화 알고리즘 정보, KEY의 ID
정보 담음
- HEADER ➜ Json 변환 ➜ UTF-8 인코딩 ➜ Base64 URL-Safe 인코딩 한 값
- 암호화 된 값은 아니다
② PAYLOAD
- 인증에 필요한 데이터
- Claim : 데이터 각각의 필드들 (다른 필드도 추가 가능)
- sub : 대부분 username 포함 ➜ User 조회시 이용
- iat : 토큰 발행 시간
- exp : 토큰 만료 시간
- PAYLOAD ➜ Json 변환 ➜ UTF-8 인코딩 ➜ Base64 인코딩 (변경한 데이터일뿐)
- 암호화 ❌
③ SIGNATURE
- JWT 토큰의 진위여부 판단 용도
- 암호화 ⭕️: HEADER + PAYLOAD ➜ 비밀키(Secret Key)로 hashing ➜ Base64로 변경
SecretKey
: 절대 노출되선 안 된다.
Key Rolling
Secret Key를 여러개 둬서 Key 노출 대비
- 수시로 추가/삭제하여 변경으로 노출되어도 다른 Secret Key와 데이터 안전하게 한다.
여러개의 Secret Key 존재
➜ Secret Key 1개에 unique 한 ID(①HEADER의 kid) 연결
➜ JWT 토큰 생성시 ①HEADER에 kid 포함
➜ 서버에서 토큰 해석시 kid 로 Secret Key 찾아 ③SIGNATURE 검증
Springboot 구현시 - ArgumentResolver
- 스프링은 기본적으로 bean을 proxy 해서 다루고 있다.
- MethodParamer : Controller의 parameter를 메타 정의 해 놓은
JWT (루카스)
json web token, rfc 7519 산업계 표준
- 인증을 위한 방식이 아니다.
- 두 장소 간 데이터를 안전하게 표현하고 교환하기 위한 방법
- key 를 알아야만 위변조 가능
- 수평 확장시 공유 할 게 없어서 좋다.
- 보안에 좋은건 아니다.
OAuth 2
- Authorization Server : 권한 관리 서버로 Access Token, Refresh Token 발급 및 관리
- Resource Serve : Authorization Server와 관련하여 서비스 제공하는 서버
- Resource Owner : 리소스(계정)의 주인 (계정 소유자 = 일반 사용자)
- Client : Resource Owner 대리하여 리소스 요청하는 애플리케이션
- AccessToken : Client로부터 Resource Owner 식별하기 위해 Authorization Server 발급 한 키
- RefreshToken : Refreshing 하기위해 사용하는 키 (ex. AccessToken 만료 후 갱신 위해)
Next. JWT와 OAuth2구현한 로그인 과정... one day...
- 아래 참고내용 외에 Refresh Token, Access Token을 2개 같이 클라이언트로 보내는 것보다 다른 방법들을 알아보고, 개인적으로 구현
- 틀린 내용이나 좋은 내용들 공유 해주시면 감사하겠습니다. 👍
참고
로그인 쪽을 쭉 정리하고 계시는군요! 요악 잘하시네요~ 잘 읽고 갑니다 셀리~