spring boot와 flutter를 연동한 소셜로그인 구현(feat. spring security)-3

김가빈·2023년 12월 26일
0

springsecurity

목록 보기
22/23

이번에는 받아온 토큰을 바탕으로 accessToken을 검증해보도록 하겠다.

요청 시 access 토큰 검증

클라이언트는 요청을 보낼 때 마다 accessToken을 전달한다고 했다. 클라이언트는 이런 형태로 accessToken을 전달한다.

Authorization: bearer accessToken

서버에서는 filter를 돌 때 마다 accessToken을 검증하는 로직을 수행한다. 나는 이를 security filterchain에 customfilter를 추가해 구현하였다.


import java.io.IOException;
import java.util.Arrays;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import com.project.bookforeast.common.security.error.TokenErrorResult;
import com.project.bookforeast.common.security.error.TokenException;
import com.project.bookforeast.common.security.service.JwtUtil;
import com.project.bookforeast.common.security.service.SecurityService;
import com.project.bookforeast.user.dto.UserDTO;

import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {
	
	private final JwtUtil jwtUtil;
	private final SecurityService securityService;
	
	@Autowired
	public JwtAuthorizationFilter(JwtUtil jwtUtil, SecurityService securityService) {
		this.jwtUtil = jwtUtil;
		this.securityService = securityService;
	}
	
	
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		
		if(checkAccessTokenValid(request)) {
			filterChain.doFilter(request, response);
		} else {
			throw new RuntimeException("알 수 없는 오류가 발생했습니다.");
		}
	}



	private boolean checkAccessTokenValid(HttpServletRequest request) {
		String accessToken = jwtUtil.extractTokenFromHeader(request);
		if(!jwtUtil.validateAccessToken(accessToken)) {
			securityService.saveUserInSecurityContext(accessToken);
		}
		return true;
	}
	
	
	@Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        String[] excludePath = {
                "/api-docs/json",
                "/api-docs",
                "/api/u/v1/social-login",
                "/swagger-ui/",
                "/swagger-config",
                "/swagger.yaml", 
                "/requestBodies",
                "/swagger-",
                "/error"
        		};
        String path = request.getRequestURI();
        
        System.out.println(path);
        System.out.println(Arrays.toString(excludePath));
        boolean shouldNotFilter = Arrays.stream(excludePath).anyMatch(path::startsWith);
        
        return shouldNotFilter;
    }
}

주의 해야할 부분은 accessToken이 검증되고 나면 saveUserInSecurityContext로직을 호출시켜 유저 정보를 db에서 불러와 SecurityContext에 넣어줘야한다는 것이다. 그 이유는 이 글에서 참고해 보면 될 것 같다.

https://velog.io/@kgb/SecurityContextHolder.getContext.getAuthentication%EC%97%90%EC%84%9C-null%EC%9D%B4-%EB%9C%A8%EB%8A%94-%EB%AC%B8%EC%A0%9C

	public void saveUserInSecurityContext(String accessToken) {
		String socialId = jwtUtil.extractClaim(accessToken,  Claims::getSubject);
		String socialProvider = jwtUtil.extractClaim(accessToken, Claims::getIssuer);
		saveUserInSecurityContext(socialId, socialProvider);
	}
	
	
	private void saveUserInSecurityContext(String socialId, String socialProvider) {
		UserDetails userDetails = loadUserBySocialIdAndSocialProvider(socialId, socialProvider);
		Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
		UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, authorities);
		
		if(authentication != null) {
			SecurityContext context = SecurityContextHolder.createEmptyContext();
			context.setAuthentication(authentication);
			SecurityContextHolder.setContext(context);
		}
	}

아무튼 이렇게 코드를 작성해서 filter를 돌 때마다 accessToken을 검증하고 유저정보를 securityContext에 넣어주는 것까지 성공했다.

하지만 만약에 에러가 발생한다면? 특히 accessToken검증하는 부분에서 token이 만료될 경우에는 클라이언트에서 refreshToken을 보내주어야 한다. 이는 다음 글에서 알아보겠다.

profile
신입 웹개발자입니다.

0개의 댓글