관리 메뉴

seok의 패치노트

[Security] JWT - JWT를 이용한 인증처리 프로젝트2 본문

Spring/Security

[Security] JWT - JWT를 이용한 인증처리 프로젝트2

석석's 2021. 12. 9. 18:13

 

https://oneseok.tistory.com/71?category=933442 

 

[Security] JWT - JWT를 이용한 인증처리 프로젝트2

https://oneseok.tistory.com/70 [Security] JWT - JWT를 이용한 인증처리 프로젝트1 지난 시간까지 이 프로젝트를 만들기 전에 이론적인 내용을 공부해 보았다. RSA를 통한 서명 처리나 HMAC을 이용한 인증처리

oneseok.tistory.com

지난 시간에는 회원가입 처리 및 로그인 관련 파일들을 만들었다. 

하지만 formLogin 방식을 사용하고 있지 않기 때문에 "/login" 요청이 자동으로 활성화가 되지 않아 PrincipalDetailsService 가 동작하지 않고있다.

 

이번시간에는 PrincipalDetailsService 동작을 위한 필터를 만들 것이다.

 

1. 스프링 시큐리티 필터 체인


아래 사진과 같이 스프링 시큐리티에는 여러 필터들이 연결되어있다.

이번 JWT 토큰인증방식을 구현하기 위해서 "UsernamePasswordAuthenticationFilter" 와 "BasicAuthenticationFilter" 를 상속받은 클래스를 만들것이다.

 

 

  • 스프링 시큐리티의 인증 순서
  1. 사용자의 요청을 UsernamePasswordAuthenticationFilter(AuthenticationFilter) 가 가로챈다.
  2. UsernamePasswordAuthenticationFilter 는 사용자의 요청정보를 토대로 UsernamePasswordAuthenticationToken을 생성후 이걸 이용하여 인증용 객체인 Authentication을 생성한다.
  3. 인증을 위해 AuthenticationManager에게 인증용 객체 Authentication 을 전달한다.
  4. AuthenticationManager는 인증을 위해 AuthenticationProvider에게 Authentication 객체를 전달한다.
  5. AuthenticationProviderPrincipalDetailsService(UserDetailService) 에게 인증객체의 username을 넘겨 PrincipalDetails(UserDetails) 객체를 요구한다.
  6. PrincipalDetailsService는 username으로 DB에서 회원정보를 찾아 PrincipalDetails로 만든다.
  7. PrincipalDetailsServicePrincipalDetails를 반환한다.
  8. AuthenticationProvierAuthenticationPrincipalDetails를 가지고 인증을 수행하여 인증 성공시에 Authentication을 반환한다. (이때 반환되는 Authentication 에는 권한 , 인증 여부가 포함되어있다.)
  9. AuthenticationManagerAuthenticationProvier를 통해 인증에 성공한 Authentication 객체를 전달받아 반환함
  10. 인증된 Authentication 객체를 SecurityContextHolder에 담아 성공 시 AuthenticationSuccessHandler  를 호출하고 실패시에는 AuthenticationFailureHandler를 실행한다.

 

2. UsernamePasswordAuthenticationFilter를 상속받은 필터클래스 만들기


  • 필터를 상속받은 클래스를 만들자

      - 이 필터는 "/login" url로 요청이 오면 동작한다.

      - 이 필터는 로그인 인증을 위한 필터이다.

 

 

 //json 방식으로 전송한걸 받을때
 ObjectMapper om = new ObjectMapper(); 
 //request.getInputStream() 에는 사용자가 전달한 json 데이터가 들어있고 readValue메소드를 통해 매핑된다.
 User user = om.readValue(request.getInputStream(), User.class);
 

//인증을 위한 Authentication 객체를 만들기위해 UsernamePasswordAuthenticationToken 토큰을 생성한다.
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
  
//실질적인 인증처리를 담당한다.
//authenticationManager 로 로그인 시도를 하게되면 "PrincipalDetailsService" 의 "loadUserByUsername" 가 호출된다.
//로그인 시도가 정상적으로 이루어지면 인증된 Authentication 객체가 넘어오게된다,
Authentication authenticate = authenticationManager.authenticate(authenticationToken);

//시큐리티의 권한관리 로직을 편하게 이용하기 위해 PrincipalDetails 객체를 시큐리티 세션 영역에 넣기 위해 꺼낸다.
//JWT 토큰 방식을 사용하게 되면 세션방식은 사용하지 않아도 되지만 권한관리를 편하게 하기 위해 썼다.
PrincipalDetails principalDetails = (PrincipalDetails) authenticate.getPrincipal();

//리턴이 되면 인증이 완료된 authentication 객체가 자동으로 시큐리티 세션 영역에 저장이된다!!..
//세션이 계속 쌓이면 어떡하나 걱정스럽겠지만 일정 시간이 지나면 세션은 사라지게 되니 괜찮다.
return authentication

 

 

  • 필터 등록하기

     - 파라미터인 authenticationManager객체는 WebSecurityConfigurerAdapter가 가지고 있다.

 

3. JWT 설정


 

 

 

  • jwt 토큰 발급 로직 작성

     - JwtAuthenticationFilter 클래스에 이어서 작성하면되고 "successfulAuthentication" 메소드를 재정의할 것이다.

     - successfulAuthentication 메소드는 위에 attemptAuthentication 메소드에서 인증이 정상적으로 이루어 졌다면 이어서 자동으로 호출이된다!!

 

  •  payload 부분에는 간단하게 pk id와 username 정도만 넣고 signiture 부분에서 HMAC SHA512 알고리즘을 사용하여 secret-key와 함께 암호화 처리를 한다.
  • jwt 발급은 헤더로 response 한다.( {"Authorization": "Bearer <token>"} )

 

로그인 결과로 JWT 을 받아온 모습

 

 

 

4. 권한처리 필터 생성하기


권한이나 인증이 필요한 특정 주소를 요청했을때 시큐리티 필터중 BasciAuthenticationFilter 를 타게 되어있다 이 필터를 상속받아 확장클래스를 만들어 처리하는 로직을 작성할 것이다.

 

  • 권한,인증 처리 필터 클래스 생성하기

 

  • SecurityConfig에 필터 등록하기

 

 

  • 인증처리를위한 doFilterInternal 메소드 재정의하기
  • 현재 모든 요청에대해 이 메소드가 호출되게 되어있다 그래서 이곳에서 토큰값 여부에 따라 인증처리를 진행해주면된다.
  • 만약 Authentication 객체를 만들어 SecutiryContextHolder 영역에 저장하는 방법을 사용하지 않는다면 인터셉서를 통해 권한별로 걸러내는 작업을 따로 해줘야할 것이다(왠만하면 그냥 시큐리티에게 권한관리는 맡기자 , 편하니깐..)

 

 

 

 

5. 권한이 필요한 url 요청해보기


  • PostMan을 이용하여 "/api/v1/user" 를 요청해보기

 

 

  • "/api/v1/manager" , "/api/v1/admin"  요청해보기

* 현재 로그인한 user의 권한이 "ROLE_USER" 이기 때문에 403 권한에러가 나타난다.

 

 

 

6. 토큰 위변조 테스트


  • 발급받은 토큰을 JWT 홈페이지에서 변경하여 요청해보자

 

 

 

 

* HMAC SHA512 알고리즘을 통해 확인해 보았더니 서명이 일치하지 않는다는 익셉션을 발생키는걸 확인 할 수있다!!