본문 바로가기

내일배움캠프/Today I Learned

[TIL] 나의 마흔 번째 회고록

Today Error


팀 프로젝트 마무리 단계 2탄

 

팀프로젝트 최종 Test단계에서

Security와 관련된 Error를 팀원이 공유해주었다.

 

[ Error 내용 ]

회원가입 후 로그인 시 발행 되는 토큰이 '정상적인 토큰 형식'이 아닐 경우에 Exception처리가 되지 않고 아무런 Message를 띄워주지 않는다.

 

아~~^_^

(처음 에러 공유 받았을 때 표정)

SecurityConfig.java 파일에서 Exception처리를 따로 해주면 되겠네~

응..아니야ㅎㅎ

 

  @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.authorizeRequests()
                .antMatchers("/api/user/signup").permitAll()
                .antMatchers("/api/user/login").permitAll()
                .antMatchers("/api/**").hasAnyRole(UserRoleEnum.ADMIN.name(), UserRoleEnum.USER.name())
                .anyRequest().authenticated()
                .and().addFilterBefore(new JwtAuthFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class);

        http.formLogin().disable();

        return http.build();
    }

 

바로 코드 수정을 이해 SecurityConfig.java을 들여다봤다.

코드를 보면 SecurityFilterChain에서 발생한 Exception의 경우 throws Exception으로 

잡아주는 걸로 코드를 작성해놨었는데 

왜 아무런 Exception 메세지가 뜨지 않는거지?? 싶었다. 

 

잘못된 토큰일 경우 권한체크에서 걸리기 때문에

Filter도 타지않고

요청한 URL로 이동이 되지 않는다. 

 

Oh..그럼 예외처리를 어떻게 해야하는걸까?? 혼자 속앓이 하다가 

클래스에 제일 잘하는 단원에게 SOS를 청했다. 

 

 

< 코드를 수정하기 전 내가 작성한 코드 >

 @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        String token = jwtUtil.resolveToken(request);

        if(token != null){
            if(!jwtUtil.validateToken(token)){
                throw new CustomException(INVALID_TOKEN);
                return;
            }
            Claims claims = jwtUtil.getUserInfoFromToken(token);
            setAuthentication(claims.getSubject());
        }
        filterChain.doFilter(request, response);
    }

 

< 수정한 코드>

 @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        String token = jwtUtil.resolveToken(request);

        if(token != null){
            if(!jwtUtil.validateToken(token)){
                response.setContentType("application/json;charset=UTF-8");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

                JSONObject responseJson = new JSONObject();
                responseJson.put("message", "잘못된 토큰 값 입니다.");
                responseJson.put("code", 401);

                response.getWriter().print(responseJson);
                return;
            }
            Claims claims = jwtUtil.getUserInfoFromToken(token);
            setAuthentication(claims.getSubject());
        }
        filterChain.doFilter(request, response);
    }

 

그렇게 여러가지 실험을 하던 와중

JwtFilter.java 파일에 토큰의 유효성을 체크하는 

validateToken()이 false일 경우 response객체를 통해 

Message를 보여주는 부분을 추가로 구현해주었다. 

 

 

이렇게 하면 잘못된 토큰 값을 입력했을 경우 

JSON형태로 401 에러 코드와

"잘못된 토큰 값입니다" 에러 메세지가 발행되는 것을 확인했다. 

 

이제 끝?!!!

응...아냐^^

 

비정상적인 토큰 값 이외에도 

만약 토큰 값이 없을 경우에는 어떻게 Exception을 처리해줘야 할까??

 

 

 CustomAuthenticationEntryPoint.java 파일을 생성하여 

 

 스프링 시큐리티 컨텍스트 내에 존재하는

1. 인증절차가 실패할 경우

2. 인증헤더(Authorization)를 보내지 않게되는 경우

 

 401(UnAuthorized) 라는 응답값을 처리해주는 로직을 품고 있는

AuthenticationEntryPoint 인터페이스를 상속받아 코드를 수정해주었다.

 

Response에 401이 떨어질만한 에러가 발생할 경우 해당로직을 타게되어,

commence라는 메소드를 실행하여 error메세지를 보여줄 수 있게 된다.

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

        JSONObject responseJson = new JSONObject();
        responseJson.put("message", "토큰이 존재하지 않습니다.");
        responseJson.put("code", 401);

        response.getWriter().print(responseJson);
    }

 

CustomAuthenticationEntryPoint.java파일을 추가하고

SecurityConfig.java파일에는 토큰 값이 없을 경우 Exception이 발생 할 수 있도록 로직을 추가해주었다.

http.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint());

 

 

<수정된 SecurityConfig.java파일>

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.authorizeRequests()
                .antMatchers("/api/user/signup").permitAll()
                .antMatchers("/api/user/login").permitAll()
                .antMatchers("/api/**").hasAnyRole(UserRoleEnum.ADMIN.name(), UserRoleEnum.USER.name())
                .anyRequest().authenticated()
                .and().addFilterBefore(new JwtAuthFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class);

        http.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint());           //토큰이 없을 경우 발생

 

해당 부분을 혼자서 끙끙 되면서 힘들어하는 것보다 

배움을 함께 하고 있는 클래스 단원에게 물어보는 것이 현명하다는 생각이 들었다. 

충분히 혼자서 생각해보고 검색해본 전제 하에 

그 다음 방법으로 도움을 구해야 할 것이다. 

 

 

 

내일배움캠프에 참여하면서 

가장 공감하는 말 중에 하나가 있다. 

 

 

나와 가장 가까운 스승은 내 옆에 있는 동료이다



도움주신 관호님 태이님 성현님 감사합니다~★