이해

Spring Security의 동작 원리 + JWT + Oauth2까지

일일일코_장민기 2024. 4. 19. 13:38
728x90

1. 클라이언트가 어플리케이션에 요청(JSP에서 로그인)

1.1 AJAX를 통해 1차 검증

- ajax 컨트롤러 확인

1.2 서블릿필터에 의해 시큐리티필터로 시큐리티 작업이 위임

- mainLogin에서 login post로 요청

1.2.1 여러 시큐리티 필터 중에서 UsernamePasswordAuthenticationFilter에서 인증을 처리

1.2.2 로그인필터가 UsernamePasswordAuthenticationFilter를 상속 받아 사용

-> 로그인필터에서 처리

 

2. UsernamePasswordAuthenticationFilter(이하 로그인필터)attemptAuthentication에서 username password를 추출하여 UsernamePasswordAuthenticationToken을 만듦

1.1 UsernamePasswordAuthenticationToken에는 pricipalcredential이 들어감

1.1.1 pricipal은 주체 à 사용자의 식별 정보(주로 사용자의 이름 / 사용자의 이메일 주소 / 고유 식별자)

1.1.2 credential은 인증 자격을 증명하는 정보(주로 패스워드)

1.1.3 null 자리는 사용자의 권한(authorities)가 들어가는 자리(보통 사용자의 권한 정보를 포함하는 컬렉션 / 배열)

-> 정보를 제공할 수 없는 경우에는 null

 

3. 로그인 필터에서 AuthenticationManager로 인증 객체를 전달

3.1 securityConfigBean으로 등록된 것을 확인

 

4 AuthenticationFilter AuthenticationManager(ProviderManager 인터페이스 사용)로 인증 객체(principal) 전달(유저 아이디 // 토큰)

 

5. ProviderManager는 인증을 위해 AuthenticationProvider에게 인증 객체를 전달

 

6. AuthenticationProvider는 인증 객체 정보를 UserDetailsService로 전달

6.1 SpringSecurityService

 

7. 사용자 정보와 일치하는 UserDetails 객체를 AuthenticationProvider로 전달

 

8. AuthenticationProvider은 전달받은 UserDetails를 인증

8.1 ProviderManager에게 권한 (Authorities) 을 담은 검증된 인증 객체를 전달

 

9. ProviderManager는 검증된 인증 객체를 AuthenticationFilter에게 전달

 

10. AuthenticationFilter는 검증된 인증 객체를 SecurityContextHolder SecurityContext에 저장

10.1 SecurityContext는 인증된 사용자와 해당 사용자의 보안 정보를 포함하는 객체

-> 해당 사용자의 보안 상태를 관리함

-> 유저가 해당 유저의 세션에 연결되어 있는 동안 유지되며, 이후의 요청이 발생할 때마다 사용

Ex) ROLE_MEMBER 유저가 ROLE_ADMIN에게만 허용된 페이지에 들어가려고 할 경우, SC는 사용자의 인증 정보를 확인하기 위해 SecurityContext에서 사용자의 인증 객체를 가져옴

--> 사용자의 인증 객체에 포함된 권한 정보를 확인

--> 접근 거부

 

 

============ ============ ============ ============ ============ ============ ============

 

JWT

 

1. security.formLogin().disable();:

- 일반 로그인폼이 아닌 JWT를 사용하기 위한 커스텀 로그인폼을 사용

 

 

2. security.addFilterBefore(new JWTFilter(jwtUtil), LoginFilter.class);

2.1 addFilterBefore

- 특정 필터보다 앞에 새로운 필터를 추가하고자 할 때 사용

- 그 이후의 모든 필터에 대한 적용을 변경

 

2.2 jwtUtil

- JWTFilter에서 쓸 함수 짬통

Ex) getUsername

1. Jwts.parser()

- JWT를 파싱하기 위한 JWT 파서를 생성합니다.

2. verifyWith(key)

- 파싱된 JWT의 서명을 검증하기 위한 키를 설정합니다. 이 키를 사용하여 JWT의 서명이 올바른지 확인합니다.

3. build()

- JWT 파서를 빌드하여 JWT 파싱 및 검증을 수행하는 JwtParser 객체를 생성합니다.

4. parseSignedClaims(token)

- 파싱된 JWT에서 서명된 클레임을 추출합니다. 이 과정에서 서명이 검증되어야 합니다.

5. getPayload()

- 추출된 서명된 클레임에서 페이로드를 가져옵니다. 페이로드에는 사용자가 정의한 클레임(: 사용자 이름, 역할 등)이 포함됩니다.

6. get("username", String.class)

- 페이로드에서 "username"이라는 클레임을 추출합니다. 이 클레임은 사용자의 식별자를 나타냅니다. 이때, 해당 클레임의 값을 문자열(String) 형태로 가져옵니다.

 

2.3 JWTFilter

2.3.1 filterChain.doFilter(request, response);가 작동되면 LoginFilter 작동

 

2.4 LoginFilter

2.4.1 attemptAuthentication함수 작동

2.4.2 AuthenticationManager 작동

 

 

3. addFilterAt(LoginFilter)

- 특정한 위치에 새로운 필터를 추가하여 필터 체인을 정확하게 제어

3.1 AuthenticationManager의 결과에 따라 unsuccessfulAuthentication / successfulAuthentication 작동

- 해당 함수는 인증 결과가 실패 / 성공일 때 작동되는 함수

 

3.2 unsuccessfulAuthentication

- response.sendRedirect를 통해 url 이동

 

3.3 successfulAuthentication

3.3.1 authentication.getPrincipal()에서 아이디와 권한 정보 추출

3.3.2 JWT 생성

3.3.3 쿠키 생성

- Path=/acorn:    /acorn 이하의 요청에서만 사용 가능

- HttpOnly:        JavaScript에서 쿠키에 접근하는 것을 방지

- secure:           요청이 HTTPS로 전송된 경우에만, 쿠키에 Secure 플래그를 추가

                      쿠키는 암호화된 통신에서만 전송

 

 

4. JWTFilter(doFilterInternal)

- 클라이언트로부터 HTTP 요청이 서버에 도달할 때 호출(뭐 할 때 마다 호출)

4.1 AuthToken 찾기

 

4.2 AuthToken을 바탕으로 springSecurityUser 만들기

 

4.3 인증 객체(Authentication)를 생성

- UsernamePasswordAuthenticationToken(springSecurityUser, null, springSecurityUser.getAuthorities()

(principal / credential / Authorities)

 

4.4 생성된 인증 객체를 SecurityContextHolder에 설정

 

 

============ ============ ============ ============ ============ ============ ============

 

OAuth2

oauth의 의미

- 구글, 페이스북, 트위터와 같은 다양한 플랫폼의 특정한 사용자 데이터에 접근하기 위해 제3자 클라이언트(우리의 서비스)가 사용자의 접근 권한을 위임(Delegated Authorization)받을 수 있는 표준 프로토콜

 - 우리의 서비스가 우리 서비스를 이용하는 유저의 타사 플랫폼 정보에 접근하기 위해서 권한을 타사 플랫폼으로부터 위임 받는 것

 

1. JSP에서 각 소셜 로그인 인증 호출

/ [ ContextPath ] /oauth2/authorization/ [ client-name ] :     oauth2 기본 주소 설정 방식

-      client-nameproperties에서 설정

 

2. callback(각 소셜 사이트에서 일관되게 설정)

.redirectionEndpoint(endpoint -> endpoint.baseUri("/login/oauth2/code/**"))

 

3.  .userInfoEndpoint(endpoint -> endpoint.userService(oAuthService))(callBack 받은 데이터 사용)

3.1  JSON 데이터 취득

3.2  각 소셜사이트 별로 데이터 추출

3.3  회원가입 여부에 따른 로직 진행

3.4  OAuthUser 객체를 생성해서 Return

 

-- 기존 loginFilter와 동일

4. AuthenticationManager(ProviderManager 인터페이스 사용)로 인증 객체(principal) 전달

5. ProviderManager는 인증을 위해 AuthenticationProvider에게 인증 객체를 전달

6. AuthenticationProvider는 인증 객체 정보를 UserDetailsService로 전달

6.1 SpringSecurityService

7. 사용자 정보와 일치하는 UserDetails 객체를 AuthenticationProvider로 전달

8. AuthenticationProvider은 전달받은 UserDetails를 인증

8.1 ProviderManager에게 권한 (Authorities) 을 담은 검증된 인증 객체를 전달

9. ProviderManager는 검증된 인증 객체를 AuthenticationFilter에게 전달

10. AuthenticationFilter는 검증된 인증 객체를 SecurityContextHolder SecurityContext에 저장

11. LoginSuccessHandler / LoginfailureHandler 작동

- LoginSuccessHandler:    loginFilte 기능과 역할이 동일(다만 상속 / 인터페이스가 다름)