[에러노트]

hyewon jeong·2023년 1월 12일
0

에러노트

목록 보기
13/44

1 발생

블로그 프로젝트에 회원가입 및 로그인 구현 성공 후
Spring Security 적용하여 코드 설계를 하고 , 포스트맨을 통해 , 회원가입을 send 하니 아래에 같은 에러가 발생했다.


2 코드

java.lang.NullPointerException: Cannot invoke "com.example.myblog1.common.jwt.JwtUtil.resolveToken(javax.servlet.http.HttpServletRequest)" because "this.jwtUtil" is null

3 원인

@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
// @Secured 어노테이션 활성화//@Configuration 인스턴스에 @EnableMethodSecurity 애노테이션을 이용하면, 애노테이션을 기반으로 한 보안을 작동시킬 수 있다.
public class WebSecurityConfig {

    private JwtUtil jwtUtil;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

private JwtUtil jwtUtil; -> private final JwtUtil jwtutil;(o)
final키워드가 빠져서 의존성주입이 되지 않아 JwtUtil 이 null값으로 생긴 에러


4 해결

private JwtUtil jwtUtil; -> private final JwtUtil jwtutil;(o)

이 문제로 인해 스프링 시큐리티의 동작 원리에 대해 알 수 있었다.

📢 이 문제를 알기 전에는
회원가입을 할때는 Jwt토큰을 사용하지 않고 ,
WebSecurityConfig 클래스에
.antMatchers("/api/user/**").permitAll() 설정으로
인증, 인가 없이 가능하도록 했는데 왜 Jwt 에 널값으로 에러가 발생할까?

였는데 원리를 알고 나니 이해가 되었다.

permitAll() 메소드는 어떠한 보안 요구 없이 요청을 허용해준다.
뜻으로 애초에 SecurityFilter를 타지 않는다고 생각했는데 큰! 오류였다!!!!!!

스프링시큐리티 적용했으니 스프링시큐리티 아래에 코드들이 동작을 하게 되는데,

WebSecurityConfig 클래스에 
.antMatchers("/api/user/**").permitAll() 

을 하게 되면
🔑 커스텀한 JwtAuthFilter 의 doFilterInternal를 타게 되고
회원가입의 경우는 토큰이 널값이므로

filterChain.doFilter(request,response);

으로 다음 필터로 넘어가게 된다.
🔑즉 Claims info = jwtUtil.getUserInfoFromToken(token);
setAuthentication(info.getSubject());
인증 하지 않고 다음필터로 넘어가도록 설계되어 있는데
이때
permitAll() 메서드가 적용 되어 인증,인가 없이 요청을 허용되어 회원가입이 되는 것이다.

JwtAuthFilter

@Slf4j
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {

  private final JwtUtil jwtUtil;

  @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)){
              jwtExceptionHandler(response, "Token Error", HttpStatus.UNAUTHORIZED.value());
              return;
          }
          Claims info = jwtUtil.getUserInfoFromToken(token);
          setAuthentication(info.getSubject());
      }
      filterChain.doFilter(request,response);
  }

  public void setAuthentication(String username) {
      SecurityContext context = SecurityContextHolder.createEmptyContext();
      Authentication authentication = jwtUtil.createAuthentication(username);
      context.setAuthentication(authentication);

      SecurityContextHolder.setContext(context);
  }

요약

  • 스프링 시큐리티는 securityFilter 를 무조건 탄다.
  • permitAll( ) 은 securityFilter를 타고 , 인증*인가만 없이 허용한다는 뜻

.addFilterBefore(new JwtAuthFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class);

  • 의문❓ SecurityFilter 는 Chain 형식으로 많은 필터들이 연결되어 있는데
    내가 코드에 사용한 것은 커스텀한 JwtAuthFilter와 UsernamePasswordAuthenticationFilter 면 이거 두개의 필터만 타고 controller로 이동하는 건가?

🧻 Spring Security 를 적용하게 되면
아래와 같은 Spring SecurityFilterChain에 의해 다음필터 다음필터를 넘어가게 되고 , 설계한 커스텀 필터인 JwtAuthFilter 인식하여 필터 처리 후 controller로 넘어가게 된다.

profile
개발자꿈나무

0개의 댓글