세션과 JWT토큰에 대해서(Refresh Token) //나의 코드 확인하기
쿠키 ?: 서버가 사용자의 데이터를 브라우저에 넣음 브라우저(request)-><-(response)서버
response시, 모든 데이터와 사용자가 찾던 정보들을 response하는데,
이때 쿠키도 request,response를 반복하며 저장되고 쓰인다.
유효기간이 있다. 인증뿐만아니라 여러 정보들을 저장할 수 있다.
세션과 토큰
HTTP(웹사이트를 이용할 때 쓰는 프로토콜=stateless)
**stateless? 서버로 가는 모든 요청이 이전 request와 독립적이다.(메모리X)
request 이후, 서버는 정보를 저장하지 않음(즉, 요청할 때마다 세션을 이용해 사용자가 누구인지 알려야함)
ex)로그인
세션ID는 쿠키를 통해 DB-> Server->Browser(저장) 같은 웹사이트 내 다른 페이지로 이동하면 브라우저는 세션ID를 갖고 있는 쿠키를 서버에 보냄(자동) 서버는 세션ID와 함께 보내져오는 쿠키를 확인하고, 그 세션ID를 갖고 DB에서 유저를 찾음.
-> "환영합니다"
이후 다른 페이지로 이동하면, 이런 확인 동작이 계속 반복된다.
쿠키는 세션ID를 전달하기 위한 매개체일 뿐이다. 세션은 ios, androids, browser 모두 사용가능하지만 쿠키는 only 브라우저에서만 사용할 수 있다.
그래서 토큰을 사용한다. 서버에 '토큰'을 보냄.
세션은 현재 로그인한 유저들의 모든 세션ID를 DB에 저장해야한다.request가 있을때마다 쿠키와함께 세션ID로 DB에 일치하는 유저를 찾아야함.(유저가 많아질수록 DB리소스가 더 필요해짐)
세션: 세션ID만 줌(세션에 대한 정보는 세션DB에 저장되어있다.)
페이지를 요청하면 서버는 세션 ID를 DB에서 찾음.
새로운 기능을 추가할 때
ex)로그아웃 -> 세션 delete
인스타 로그인 디바이스 강제 삭제로 로그아웃
넷플이나 딪플처럼 계정 공유 인원을 제한할 수도 있다. 주로 redis(빠르고 저렴함)사용
->> 유저가 늘어날 수록 DB 사용 유지 비용이 커짐
-> 이때, JWT 토큰을 사용한다.
** JWT = Jason Web Token
(base64로 인코딩을한다.)
header -> 서명 방법
ex) {
"alg*":"*HS256";
"typ*":"*JWT"
}
claim (payload 속 각 부분)
payload (내용)
signature (서명)-> HMAC 대칭키를 사용한 단방향 암호화 사용
세션DB를 갖고있을 필요는없다. 또 서버가 인증 요청을 하기위해 바쁘게 움직일 필요도 없다.
쿠키는 공간의 제약이 있지만 JWT는 제약이 없어서 엄청 길어도 됨.
DB를 건드리지 않고 정보를 사인하고 전달함.
서버에 request,세션 ID와 비슷하게 사인된 정보/토큰을 서버에 보내야함.
서버는 토큰을 받으면 해당 사인이 유효한지 체크한 뒤, 유저로 인증함.
JWT : 서버가 유저를 인증하는 데에 필요한 정보를 토큰에 저장, 토큰 자체를 브라우저로 보낸다.
페이지 요청 시, 서버는 해당 토큰이 유효한지만 "검증" 하면 끝(로그인 할 때 한번만 확인하고 매번 DB를 거치지 않아서 세션DB사용 안함)
Single Sign-On, 인증, 권한
**JWT는 암호화되어있지않아서 누구나 볼 수 있다는 단점이 있다.
--> 강제 로그아웃을 할 수 없다! 서비스가 커지고 유저 계정 관리를 더 섬세하게 하고싶다면 세션으로 바꾸는 것이 나음.
JWT 로그아웃하는 방법: DB에 토큰 유효성을 서비스에서 부터 true false로 상태 바꾸면서 로그아웃 시킴.
[JWT를 세션대신 쓰는 이유 ]
-서버는 하나의 인스턴트로만 동작하지 않는다. 병렬서버(고가용성)로 운영하게 됨.
서버가 병렬로 운영되는 상황에서 세션을 사용하면, 각 서버간의 세션을 동기화하는 문제 발생(서버가 많아지면 세션동기는 더 어려워짐)
JWT를 사용하면 세션사용으로 인한 서버 부담도 줄면서 공유 세션에 대한 관리가 쉬워짐.
- 서버 부하 감소: 서버가 인증 세션을 관리할 필요가 없기 때문에 서버의 부하가 줄어든다.
- 확장성: 분산 시스템이나 로드 밸런싱 환경에서도 유용하다. 세션 기반 인증에서는 클라이언트가 매번 같은 서버에 요청을 보내야 하지만, JWT 기반에서는 어느 서버든 동일한 JWT를 검증할 수 있음
- 단일 진입점: API Gateway나 마이크로서비스 아키텍처에서, JWT는 각 서비스에 대한 인증을 관리할 필요 없이 단일 진입점에서 검증될 수 있음
세션기반 인증은세션 동기화 문제가 발생할 수 있음
token 테이블을 따로 만들어서 관리하기도 한다.
**
Refresh Token
jwt를 시크릿 키의 유효기간은 refresh-Token보다 짧게 주는게 일반적이다.
블록체인 및 암호화 관련 기초 수업을 들었던 적이 있는데, 이럴 때 쓰인다고 한다.
해쉬코드 어쩌구 이야기한것도 기억이 나긴한다
보안 쪽은 개발자 중에서도 최최최상위라고 생각하는데
딥하게 들어가면 엄청 어려운 분야같다..하하
< 내 코드 설명>
package lm.swith.user.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.filter.CorsFilter;
import lm.swith.user.token.JwtAuthenticationFilter;
import lombok.extern.slf4j.Slf4j;
@EnableWebSecurity
@Slf4j
@Configuration
public class WebSecurityConfig{
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.disable())
.csrf(csrf -> csrf.disable())
.httpBasic(httpHasic -> httpHasic.disable()) // token을 사용하르모 basic 인증 disable
.sessionManagement((session) -> session // session 기반이 아님을 선언
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorizeRequests ->
authorizeRequests
.requestMatchers(new AntPathRequestMatcher("/**")).permitAll()
)
.addFilterAfter(
jwtAuthenticationFilter,
CorsFilter.class);
// Disable CSRF protection
return http.build();
}
@Bean
static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
// 기본형식인 Security는 기본적으로 DelegatingPasswordEncoder를 BCryptPasswordEncoder() 형식으로 저장
}
}
JWT를 사용한 인증과 세션 없는(stateless) 보안 설정
- JwtAuthenticationFilter는 클라이언트가 요청에 포함한 JWT 토큰을 해석하고, 유효한 경우 해당 사용자를 인증
- Spring Security는 세션을 사용하지 않고 JWT로 요청을 인증하며, 이를 통해 무상태(stateless) 방식의 인증이 가능
- 비밀번호는 BCrypt 알고리즘을 사용해 안전하게 인코딩됨.
@EnableWebSecurity : Spring Security 설정을 활성화하는 어노테이션
@Slf4j : 로깅을 위한 어노테이션으로, lombok 라이브러리를 사용
@Configuration : Spring 설정 클래스임을 나타냄. Bean 정의나 설정이 들어갈 때 사용
JwtAuthenticationFilter : JWT 토큰을 검증하는 필터
cors(cors -> cors.disable()): CORS(Cross-Origin Resource Sharing)를 비활성화, 보안 정책에 따라 외부 도메인에서의 리소스 접근을 허용하지 않겠다는 의미
csrf(csrf -> csrf.disable()): CSRF(Cross-Site Request Forgery) 보호를 비활성화, JWT 기반 인증에서는 CSRF 토큰을 사용할 필요가 없기 때문에 이를 비활성화함
httpBasic(httpHasic -> httpHasic.disable()): Basic 인증을 비활성화 Basic 인증은 사용자의 ID와 비밀번호를 인코딩한 형태로 HTTP 요청 헤더에 포함시키지만, JWT 기반 인증을 사용하기 때문에 Basic 인증이 필요하지 않음
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) :세션 관리를 **무상태(stateless)**로 설정합니다. JWT 토큰을 사용하는 경우 서버는 세션을 유지할 필요가 없으므로, SessionCreationPolicy.STATELESS를 사용하여 세션을 아예 생성하지 않도록 합니다.
authorizeHttpRequests : 특정 URL 패턴에 대해 접근 권한을 설정.
**requestMatchers(new AntPathRequestMatcher("/**")).permitAll()**로 모든 URL에 대해 접근을 허용, 이 설정은 변경하여 인증이 필요한 URL을 특정할 수 있음
addFilterAfter(jwtAuthenticationFilter, CorsFilter.class) : JWT 인증 필터(jwtAuthenticationFilter)를 CorsFilter 이후에 추가 이 필터는 각 요청의 JWT 토큰을 검증하고, 유효한 경우 해당 사용자를 인증된 사용자로 처리
비밀번호 인코딩 방식으로 BCryptPasswordEncoder를 사용
개념 출처 :
https://youtu.be/tosLBcAX1vk?si=cP8avUR7tNFcxvAd
https://youtu.be/MPXoTJ4-NuA?si=KzdBcxbVwNlD0SUA
https://youtu.be/36lpDzQzVXs?si=67nGHazhrjXuV2q8