diff --git a/keyword/chapter10/image1.png b/keyword/chapter10/image1.png new file mode 100644 index 0000000..7277d17 Binary files /dev/null and b/keyword/chapter10/image1.png differ diff --git a/keyword/chapter10/image2.png b/keyword/chapter10/image2.png new file mode 100644 index 0000000..39fefa1 Binary files /dev/null and b/keyword/chapter10/image2.png differ diff --git a/keyword/chapter10/keyword.md b/keyword/chapter10/keyword.md new file mode 100644 index 0000000..163abd2 --- /dev/null +++ b/keyword/chapter10/keyword.md @@ -0,0 +1,124 @@ +## Spring Security +- 스프링 기반 웹 어플리케이션의 인증과 인가 처리를 담당하는 프레임워크 + +- 주요 기능 + - 인증 및 권한 관리 + - 사용자 로그인과 특정 리소스에 대한 권한을 검사한다. + - 폼 로그인, OAuth2, JWT 등 지원 + - 보안 설정 + - 요청 URL과 메서드에 대한 접근 제어를 설정할 수 있다. + - permitAll(모두 인가), hasRole(특정 권한을 지닌 경우만) + - 필터 체인 + - 모든 요청은 필터 체인을 통과하고, 각 필터는 특화된 작업을 수행한다. + - UsernamePasswordAuthenticationFilter, CsrfFilter + +- 주요 필터 + - SecurityContextPersistenceFilter : 세션으로부터 인증 정보 처리 + - UsernamePasswordAuthenticationFilter : ID/PW 검증 및 인증 처리 + - AnonymousAuthenticationFilter : 미인증 사용자를 익명으로 설정하여 접근 가능하도록 처리 + - BearerTokenAuthenticationFilter : JWT 토큰에 대한 인증 처리 + +- 시큐리티의 필터들 +- ![image1.png](image1.png) +1. WAS의 필터 처리 과정 중, DelegatingFilterProxy 가 가로챈다. +2. DelegatingFilterProxy는 FilterChainProxy로 처리를 위임한다. +3. FilterChainProxy는 시큐리티의 FilterChain 을 List 형태로 들고 있는 스프링 빈이다. +4. 시큐리티 의존성 추가 시, 기본으로 등록되는 FilterChain 내 필터를 몇 가지 살펴보면 다음과 같다. + - DisableEncodeUrlFilter : Response에 세션 id가 인코딩되는 보안 문제 방지용 필터 + - WebAsyncManagerIntegrationFilter : 서블릿에서 비동기 처리 시 동일한 SecurityContext를 참조하도록 하는 필터 + - SecurityContextHolderFilter : SecurityContextRepository 구현체로부터 세션정보를 가져와 SecurityContext에 할당하는 필터 + - HeaderWriterFilter : Response 헤더에 보안 설정 적용해주는 필터 + - CorsFilter : CORS 관련 설정 필터 + - CsrfFilter : csrf 공격 방지 필터 + - UsernamePasswordAuthenticationFilter : 로그인을 처리하는 필터, AbstractAuthenticationProcessingFilter를 상속한다. + - AnonymousFilter : 해당 필터까지 SecurityContext에 인증 정보가 담겨져 있지 않은 경우 익명 정보를 넣어주는 필터 + - AuthorizationFilter : 가장 마지막 필터, 최종 인가 처리 필터 + ... + +- AbstratAuthenticationFilter의 구현체 + - UsernamePasswordAuthenticationFilter + - OAuth2LoginAuthenticationFilter + - Saml2WebSsoAuthenticationFilter + - CasAuthenticationFilter + +- 커스텀 필터체인 등록방법 +```java +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Bean + public SecurityFilterChain customFilterChain(HttpSecurity http) throws Exception { + return http.build(); + } +} +``` +- 커스텀 필터체인을 등록하면 기본으로 등록되는 필터 중 일부는 등록되지 않음에 유의한다. (UsernameFilter 등등 ...) + +- 시큐리티 인증 과정 +![image2.png](image2.png) +1. UsernamePasswordFilter와 같은 AbstractAuthenticationProcessingFilter의 구현체가 오버라이딩한 attemptAuthentication() 메서드를 수행한다. +2. 해당 메서드는 AuthenticationManager에게 인증 작업을 위임하고 반환 값을 리턴하는 형식이다. +3. AuthenticationManager는 인증을 진행하는 인터페이스이며, 구현체는 ProviderManager이다. +4. ProviderManager는 여러개의 AuthenticationProvider를 가지고 있어서, 어떤 유형의 인증을 해야하는지를 support()로 확인하여 적절한 AuthenticationProvider 구현체를 선택해 인증을 처리하도록 한다. +5. AuthenticationProvider들은 내부적으로 UserDetailsService와 UserDetails를 사용한다. + + +## 인증(Authentication)과 인가(Authorization) +- 인증 + - 사용자가 누구인지 신원을 확인하는 과정 + - 로그인과 비밀번호를 받아 사용자를 확인 + - JWT와 같은 토큰을 받아 사용자를 확인 + +- 스프링 시큐리티의 인증과정 + - AbstractAuthenticationProcessingFilter -> AuthenticationManager -> AuthenticationProvider 의 과정을 의미한다. + - 반환된 Authentication 객체를 SecurityContextHolder에 저장 + - 사용자의 인증 정보와 권한을 담고 있어 이후 인가 필터(AuthorizationFilter)에서 사용됨 + +- 인가 + - 인증된 사용자가 무엇을 할 수 있는지 확인하는 과정 + - 권한에 따라 특정 리소스에 대한 액세스를 결정한다. + +- 스프링 시큐리티의 인가과정 + - AuthorizationFilter에서 Authentication에 담긴 권한을 바탕으로 리소스 접근 허용 여부를 결정한다. + - 실패 시 403 Forbidden 또는 401 Unauthorized (ExceptionTranslationFilter) + +- 인증 및 인가 세팅 +```java +@Bean +public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((requests) -> requests + .requestMatchers("/", "/home", "/signup", "/css/**").permitAll() // 인증이 필요하지 않음 + .requestMatchers("/admin/**").hasRole("ADMIN") // 인증 + ADMIN 권한이 필요함 + .anyRequest().authenticated() // 인증이 필요함 + ); + + return http.build(); +} +``` + + +## 세션과 토큰 +- 세션 + - 사용자에 대한 상태를 서버에 저장하는 방식 + - 각 사용자마다 고유 세션 ID 발급해주고, 이 값만 쿠키로 넘긴다. + - 상태 정보를 메모리나 DB에 저장하여야 하므로 서버 쪽 자원을 사용해야한다. + +- 토큰 + - 사용자에게 최소한의 인증 정보를 암호화 시켜 넘겨주고, 토큰을 API 호출 시 넘기고 서버가 검증하도록 하여 인증/인가 처리 + - 보통 사용하는 토큰은 JWT(Json Web Token)이다. + - 생성/검증 라이브러리를 지원하여 적용이 간편하다. + - 토큰 자체는 stateless 지만, 실제로 사용하는 경우에는 보안 상 이유로 리프레시 토큰을 서버 쪽에 저장해야한다. + + +## 액세스 토큰과 리프레시 토큰 +- 액세스 토큰 : API 요청 시 인증을 위해 사용하는 토큰 +- 리프레시 토큰 : 액세스 토큰이 만료되었을 때, 재발급을 위해 사용되는 토큰 + +- 토큰을 하나만 쓰면 안되나? + - 토큰이 탈취당하면 TTL 동안은 유효한 요청으로 처리된다. 따라서 유효기간이 짧은 토큰 하나, 해당 토큰을 재발급하주는 토큰 하나를 발급해주는 것이다. + +- RTR 기법 + - 리프레시 토큰을 사용할 때마다 '새로운 리프레시 토큰 + 액세스 토큰'을 함께 재발급 + - 보통 Redis에 리프레시 토큰을 저장하고, 발급 후 이전 리프레시 토큰은 폐기한다. + - 만약 동일한 리프레시 토큰을 재사용한다면 로그아웃을 시킨다. \ No newline at end of file diff --git a/mission/chapter10/jwt_1.png b/mission/chapter10/jwt_1.png new file mode 100644 index 0000000..b1ec60f Binary files /dev/null and b/mission/chapter10/jwt_1.png differ diff --git a/mission/chapter10/jwt_2.png b/mission/chapter10/jwt_2.png new file mode 100644 index 0000000..5f6c464 Binary files /dev/null and b/mission/chapter10/jwt_2.png differ diff --git a/mission/chapter10/jwt_3.png b/mission/chapter10/jwt_3.png new file mode 100644 index 0000000..012fc9b Binary files /dev/null and b/mission/chapter10/jwt_3.png differ diff --git a/mission/chapter10/kakao_1.png b/mission/chapter10/kakao_1.png new file mode 100644 index 0000000..c127d5c Binary files /dev/null and b/mission/chapter10/kakao_1.png differ diff --git a/mission/chapter10/kakao_2.png b/mission/chapter10/kakao_2.png new file mode 100644 index 0000000..39527f7 Binary files /dev/null and b/mission/chapter10/kakao_2.png differ diff --git a/mission/chapter10/kakao_3.png b/mission/chapter10/kakao_3.png new file mode 100644 index 0000000..887d24b Binary files /dev/null and b/mission/chapter10/kakao_3.png differ diff --git a/mission/chapter10/kakao_process.png b/mission/chapter10/kakao_process.png new file mode 100644 index 0000000..869de26 Binary files /dev/null and b/mission/chapter10/kakao_process.png differ diff --git a/mission/chapter10/mission.md b/mission/chapter10/mission.md new file mode 100644 index 0000000..8f0ecd9 --- /dev/null +++ b/mission/chapter10/mission.md @@ -0,0 +1,42 @@ +## 실습 1. 세션 방식 구현 +- ![session_1.png](session_1.png) +- ![session_2.png](session_2.png) + + +## 실습 2. JWT 방식 구현 +- ![jwt_1.png](jwt_1.png) +- ![jwt_2.png](jwt_2.png) +- ![jwt_3.png](jwt_3.png) + + +## 시니어 미션 1. 리프레시 토큰 구현 +- ![reissue_1.png](reissue_1.png) +- /reissue 구현 + - /reissue는 인증이 필요없는 재발급 용도이므로 permitAll()에 추가해주었다. + - 컨트롤러는 accessToken + refreshToken 2가지를 DTO로 받는다. + - refreshToken에는 보안 상 유효기간만 들어가도록 하는 것이 좋다. + - 따라서 서비스 코드에서는 accessToken을 통해 유저 정보를 파싱 해준다. + - 또한 refreshToken의 만료 여부를 판단하여 함께 재발급해주고, 응답 DTO로 넘겨주었다. + - 로그인 시에는 refreshToken도 함께 발행 및 DB에 저장하도록 기존 로그인 로직을 수정해주었다. + + +## 시니어 미션 2. SNS 로그인 구현 (카카오) +- ![kakao_process.png](kakao_process.png) +- 방법 1. 인가 코드를 백엔드 서버에서 얻는 방법 +- 방법 2. 인가 코드를 프론트엔드에서 얻고, 백엔드 서버로 전달하는 방법 + - 주로 선호되는 방법은 2번이다. 백엔드로 한번 추가적인 API 호출이 발생하지만, 프론트엔드에서 획득하면 페이지 전체를 새로고침하지 않고도 로그인 프로세스를 완료할 수 있어 사용자 경험을 부드럽게 할 수 있기에 선호된다. + +- 현재는 프론트를 직접 구현하기 힘들기 때문에 '방법 1'로 인가 코드를 직접 획득하였다. +- 로컬에서만 테스트할 것이므로 리다이렉트 경로는 'http://localhost:8080/auth/login/kakao' 로 카카오 디벨로퍼스에 등록해주었다. + +- 토큰 획득 +- ![kakao_1.png](kakao_1.png) +- 사용자가 로그인을 완료하면 'code=~~~' 쿼리 스트링이 전송되고, 이를 컨트롤러에서 받으면 된다. +- 받은 인가 코드로 액세스 토큰을 요청하고, 사용자 정보를 얻어와서 로그인을 처리하면 된다. + +- 로그인 완료 +- ![kakao_2.png](kakao_2.png) +- ![kakao_3.png](kakao_3.png) + +- 일단은 구현에만 신경썼기에 개선점을 생각해본다면, '최초 회원 or 기존 회원' 분기 처리하여 최초 회원이면 임시 토큰을 발행해 추가 정보를 입력하도록 하면 좋을 것 같다. +- 또한 실제 협업 환경에서는 백엔드에서 직접 인가코드를 얻기보다, 프론트엔드로부터 인가코드를 받아 회원 정보를 가져오도록 수정해야할 것이다. diff --git a/mission/chapter10/reissue_1.png b/mission/chapter10/reissue_1.png new file mode 100644 index 0000000..e978211 Binary files /dev/null and b/mission/chapter10/reissue_1.png differ diff --git a/mission/chapter10/session_1.png b/mission/chapter10/session_1.png new file mode 100644 index 0000000..90a6fea Binary files /dev/null and b/mission/chapter10/session_1.png differ diff --git a/mission/chapter10/session_2.png b/mission/chapter10/session_2.png new file mode 100644 index 0000000..9a018b1 Binary files /dev/null and b/mission/chapter10/session_2.png differ