본문 바로가기

프로젝트/Crossfit

Filter란 무엇인가?

 

Filter(필터)란? 바로 Controller로 들어가지 않고 Filter를 먼저거쳐 서버로직을 타고 Filter를 통과하지 못하면(검증되지 않으면) 아예 Controller는 거치지도 못하고 그냥 return 해버리게 하는 것. 즉, Filter를 거친것들만 Controller에서 작업하게 두는 것. 이번 프로젝트 같은 경우 Filter에서 JWT검증처리를 합니다.  검증을 하여 JWT의 payload에 들어있는 사용자정보인 이메일을 꺼내오게 됩니다. 그 이메일을 Controller에서 사용하게 되는데 필터에서 컨트롤러로 바로 넘겨줄 수는 없고 외부의 context라는 공간에 담아두고 필요할때 언제든 사용할수 있게 해줍니다.

 

아래는 프로젝트의 Filter가 구현된 JwtAuthenticationFilter클래스

package com.crossfit.webpageback.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import com.crossfit.webpageback.provider.JwtProvider;

import lombok.RequiredArgsConstructor;

//만든 JWT를 HTTP의 헤더인 Authorization: Bearer~~형식으로 클라이언트로 부터 날라오면 그것을 validate하여 필요한 부분을 
//처리해 줄 수 있는 지를 filter에서 하게 됩니다. 
@Component
@RequiredArgsConstructor // 필수 생성자를 만들어주는 Rombok의 어노테이션. 필수생성자인지는 final이 붙은 변수이면 필수 생성자를 만들어줌
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtProvider jwtProvider;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
                try{
                    String token=parseBearerToken(request);
                    //Authorization이없거나 Bearer가 아닌경우, 바로 return해버린다
                    if(token==null){
                        filterChain.doFilter(request, response);
                        return;
                    }
                    //여기까지 오면 Bearer토큰이라는 말이므로 provider에서 구현했던 validate함수를 이용하여 사용자의 email을 가져온다. 
                    String email=jwtProvider.validate(token);
                    if(email==null){
                        filterChain.doFilter(request, response);
                        return;
                    }
                    //여기까지 오게 되면 context에 등록해주어야 합니다. UsernamePasswordAuthenticationToken이라는 객체를 생성. 이객체는 사용자의 이름, 
                    //비밀번호, 권한을 포함함(아래에서와 같이 비밀번호는 우선null로 설정함. )
                    AbstractAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(email, null, AuthorityUtils.NO_AUTHORITIES);
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));//클라이언트측에서 인증요청한 것에 대해 세부설정하는 코드
    
                    //컨텍스트를 만드는 코드
                    SecurityContext securityContext=SecurityContextHolder.createEmptyContext();
                    securityContext.setAuthentication(authenticationToken);
    
                    SecurityContextHolder.setContext(securityContext);
    
                }catch(Exception exception){
                    exception.printStackTrace();
                }
                filterChain.doFilter(request, response);//다음 필터로 넘기는 코드
    }

    // request의 헤더를 가져와 그 헤더의 Authorization의 키를 찾고 bearer인증을 확인.
    // 토큰을 parsing해주는 것이므로 반환형은 String임
    private String parseBearerToken(HttpServletRequest request) {
        String authorization = request.getHeader("Authorization");
        boolean hasAuthorization = StringUtils.hasText(authorization);// http의 헤더중 authorization필드에 정보가 있는지를 확인
        if (!hasAuthorization)
            return null;
        boolean isBearer = authorization.startsWith("Bearer "); // (더 상세하게)Authorization의 값이 Bearer가 맞는지를 확인
        if (!isBearer)
            return null;
        // 여기까지 왔으면 Bearer토큰 방식이라는 말이므로 토큰을 뽑아냄.
        String token = authorization.substring(7);
        return token;
    }
}

 

다음으로는 이렇게 만들어진 Filter를 사용할수 있도록 Configuration등록을 해준다.

config폴더안에 인증설정과관련된 WebSecurityConfig파일을 생성함