개발 일기

Filter 의 개념 (Spring Security - Google OAuth) 본문

카테고리 없음

Filter 의 개념 (Spring Security - Google OAuth)

flow123 2022. 2. 3. 22:44

Firebaes로 Google OAuth를 구현하면서 스프링 시큐리티에서 사용되는 주요 개념/메서드를 정리해둔다.

 

Spring Security Principal이란?

Authorization" refers to the process of deciding whether a principal is allowed to perform an action within your application. 

 

Principal: 시스템에서 무언가를 권한이 있는 사용자, 디바이스 혹은 시스템.

Authentication은 이 principal 이라는 객체가 어플리케이션에서 특정 행동을 할 수 있는 궈한이 있는지 확인하는 과정이다. 

 

VerifyIdToken이란? 

token 이 유효하다는 것을 증명하기 위해 쓰는 메서드. FirebaseI token을 분석(parse)하고 확인(verify) 해준다.

아래 세 가지 사항을 확인한다  

(1) token is correctely signed (토큰 서명이 잘 되었고) 
(2) has not expired (만료되지 않았으며) 

(3) it was issued to the Firebase project associated with this FirebaseAuth instance (해당 인스턴스와 연관된 firebase project에 발급된 게 맞는지도 확인해준다) 

 

param: A Firebase ID Token 

returns: The verified and decoded Token (Firebase) 

 

Filter Chain(필터 체인)이란? 

 

스프링에서는 필터 체인을 아래와 같이 정의한다. 

 

A FilterChain is an object provided by the servlet container to the developer giving a view into the invocation chain of a filtered request for a resource. Filters use the FilterChain to invoke the next filter in the chain, or if the calling filter is the last filter in the chain, to invoke the resource at the end of the chain.

 

정리하면

서블렛 컨테이너는 개발자에게 필터 체인이라는 객체를 제공한다.

필터체인을 사용하면, 리소스에 대해 filtered 된 요청 메소드 호출을 확인할 수 있다. 

필터는 filterchain 써서, 체인의 다음 필터를 호출한다.

만약 필터가 체인의 마지막 필터라면, 체인 끝에 마지막 리소스를 호출한다.

 

아래 예시는 현재 구현 중인 Filter 코드다. 

마지막에 filterChain.doFilter를 하는데 이 메서드를 써야지 해당 jwt filter를 처리하고 다음 필터로 넘어갈 수 있다. 

 

스프링 DispatchServelet의 구조를 보면 

Filter -> Interceptor -> Controller -> Service -> Repository를 거친다. 

의존성 주입이라고도 하는데, 이 프로세스를 스프링이 미리 만들어두었기 때문이다. 보통은 그 프로세스를 개발자가 짜지만, 스프링은 이미 체계를 잡아주기 때문에 제어의 역전이라고도 부른다. 

 

JwtFilter 코드 

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    FirebaseToken decodedToken;
    String header = request.getHeader("Authorization");
    if (header == null || !header.startsWith("Bearer ")) {
        setUnauthorizedResponse(response, "INVALID HEADER");
        return;
    }
    String token = header.substring(7);

    try{
        decodedToken = firebaseAuth.verifyIdToken(token);
    } catch (FirebaseAuthException e) {
        setUnauthorizedResponse(response, "INVALID TOKEN");
        return;
    }

    //USER를 가져와 SecurityContext에 저장
    try{
        UserDetails user = userDetailsService.loadUserByUsername(decodedToken.getUid());
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    } catch(NoSuchElementException e) {
        setUnauthorizedResponse(response, "USER_NOT_FOUND");
        return;
    }
    filterChain.doFilter(request, response);
 }

 

Comments