spring-jwt-oauth인증/인가

ttomy·2022년 8월 5일
0

프로젝트에서 jwt를 이용한 회원 인증/인가를 만들기는 했지만 잘 파악하고 만든 것 같지 않다. 그래서 인증/인가에 대해 다시 정리하며 리팩토링하려 한다.

인증/인가의 차이

인증 :
네트워크나 서버에 접속할 때, 본인 여부와 정규 이용자 여부를 확인하는 방법. 일반적으로 사용자 아이디와 패스워드의 조합으로 본인을 특정한다.

인가 : 특정한 프로그램, 데이터 또는 시스템 서비스 따위에 접근할 수 있는 권한이 주어지는 것

즉 인증은 사용자를 특정하는 것이고, 인가는 접근권한을 허가하는 것이다.

oauth2의 4요소.

oauth2는 인증/인가를 위한 라이브러리인데, 이것이 성립하기 위해서는 4가지의 요소가 필요하다.

  1. Resource Owner : 사용자, 고객, 너, 혹은 나, 우리.
  2. Client : 리소스(데이터)에 접근하고자 하는 서비스.
  3. Authorization Server : 인가의 주체. 토큰을 발행해주는 것이 주역할.
  4. Resource Server : 인가서버가 발급한 토큰을 확인하고 리소스를 제공하는 역할.(Authorization Server와 Resource Server를 묶어서 OAuth Provider라고도 합니다)

우리가 게임에서 구글oauth로 인가를 받아 접속을 한다면
우리(Resource owner), 게임(Client), 구글의 서버(Authorization server, Resource Server)=OAuth Provider 이 된다.

oauth2 흐름


권한 부여 승인코드(authorization code) 방식의 grant를 사용하므로 Client부분은 일반적으로 Client측의 서버 부분이 될 것이다.

권한 부여 승인 코드(authorization code)가 전달되는 uri는 spring security에서는 기본설정은
(구글 provider의 경우)http://localhost:8080/login/oauth2/code/google
이거나 본인이 설정한다면
spring.security.oauth2.client.registration.google.redirectUri=http://localhost:8080/oauth2/callback/{registrationId}
과 같이 spring설정파일에 작성할 수 있다.

프론트서버-백엔드서버- authorization server에서는

  1. 프론트 쪽에서 백엔드의 authorizationendpoint에 접근
    -/oauth2/authorization/google 과 같은 uri에 접근

  2. authorization server에 제어권이 넘어가며 해당 provider의 로그인 페이지가 노출

  3. 사용자가 로그인

  4. 정상적으로 로그인이 되었다면 authorization code를 백엔드의 redirect-uri로 전송.
    ex)login/oauth2/code/google

  5. 백엔드에서 authorization code와 client-secret,유저 정보들을 함께 authorization server에 전송하며 access token을 요청

  6. 받은 정보들을 기반으로 access token생성 후 백엔드에 전달.

  7. 백엔드에서 access token을 포함시켜 최초로 인증을 요구했던 프론트 요청의 redirect uri로 이동.

  8. 프론트에서 받은 access token을 포함하며 추가적인 요청
    -> 인가 후 요청에 따른 자원 전달.

  • 의문점
    spring boot의 예제를 보았는데 authorization code를 받은 후 accesstoken을 요청하는 controller가 따로 없었다.
    AuthenticationSuccessHandler를 이용하면 authoroization server에 access token을 요청하지 않아도 백엔드에서 알아서 accesstoken을 생성해 프론트로 redirect시킬 수는 있다.
    하지만 예제에서는 어떻게 인증서버에 access token을 요청했을까?
    예제: https://spring.io/guides/tutorials/spring-boot-oauth2/

내 프로젝트에서의 흐름

  • 구글 소셜 로그인으로 진입시
  1. 프론트에서 authorizationendpoint진입.

  2. 구글 로그인 화면.

  3. 로그인 -> 로그인 성공 ->
    (userInfoEndPoint) customOauth2UserService 로 진입

  4. customOauth2UserService에서 Oauth2UserRequest를 기반으로 유저정보를 받아옴(loaduser).

  5. 이 유저가 DB에 존재하지않으면 새로운 유저로 판단해 db에 저장.
    이미 있던 유저인데 수정된 정보가 있으면 db에 갱신.

  6. successhandler실행 -> accesstoken만들어서 프론토로 redirect.

jwt

jwt를 통한 인증은 아래와 같은 흐름이다.

  • 클라이언트의 요청 - 서버의 토큰 생성 후 반환 - 클라이언트의 토큰을 동반한 요청 - 서버의 토큰 유효성 확인 - 확인결과에 따라 거부 or 인증 및 권한부여

//여기서 토큰의 생성

spring에서는 이를위해 일반적으로 다음과 같은 요소들를 구현해야 한다.
1. 클라이언트의 요청이 dispatcher servlet에 도달하기 전 요청을 확인
2. 요청에 따라 토큰 생성 및 반환
3. 요청에 토큰이 있다면 유효성 검증
4. 토큰이 유효할 경우 권한에 따른 접근 허가

이 요소들을 위의 spring security 요소들로 구현한다면

  • spring security의 구조
  1. authentication filter로 요청을 먼저 받음
  2. authenticationManager에서 요청에 토큰이 없다면 생성하고, 있다면 아래의 authenticationProvider에게 인증 위임.
  3. authenticationProvider에서 user정보를 가져와 암호화 했을 때의 signature이 요청의 signature과 동일한지 유효성 확인
  4. securityContext에 유저의 정보,권한 저장.

이렇게 구현이 가능하다.
사실 위에서 authenticationFilter로 뭉뚱그려 하나로 그려져 있지만 spring security는 servlet이 요청을 받기 전 많은 filterchain을 거친다.

이 필터들 중 어디에 내가 작성한 인증로직을 가진 커스텀필터를 위치시킬지 security에 설정을 해야 한다. 아래와 같이 addFilterBefore()을 이용한다.

  • ex)
@Override
protected void configure(HttpSecurity http) throws Exception {
     // ...
     http       
             .addFilterBefore(
             customAuthenticationTokenFilter(authenticationManagerBean()), 
             UsernamePasswordAuthenticationFilter.class);
}

참고:
https://wonyong-jang.github.io/spring/2020/08/20/Spring-Security-oauth2-jwt-1.html
https://velog.io/@sa833591/Spring-Security-5

이전: tokenAuthenticationFilter/ tokenProvider/customUserDetailService

Reference

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-jwt
https://velog.io/@agugu95/OAuth%EC%84%9C%EB%B2%84-%EA%B5%AC%ED%98%84%EC%9D%84-%EC%9C%84%ED%95%9C-Spring-Security-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0#spring-security%EC%99%80-rest-api
https://mygumi.tistory.com/10?category=642348
https://blinders.tistory.com/63 [글쓰는 개발자:티스토리]

0개의 댓글