Language/Java

Spring Security - jwt (회원가입 / 로그인) 에러 수정

쿠키오빠 2025. 2. 17. 22:56
반응형

 

그간 정처기 시험 준비로 약 한 달간 블로그를 작성하지 못하였다.

 

아쉬운대로 학원에서 22시까지 복습, 그 이후에 시험 준비를 하였다. (나름 열심히 살았다.)

 

아직 실기가 남았지만, 그 전까지 빡시게 기록해보려고 한다.


이번 프로젝트에서는 어쩌다보니 회원가입 / 로그인 기능을 맡게 되었는데, 

 

스프링 시큐리티와 세션 방식이 아닌 jwt 토큰 방식으로 구현하게 되었다.

 

회원 가입은 오랜 시간 걸리지 않고, 암호화 하여 insert 하는 데 성공하였다.

 

하지만 로그인 시, jwt 토큰이 생성되지 않는 문제가 내 발목을 잡았다.

 

아직 프론트 단 연결 전, postman을 이용하여 테스트를 진행했다.

 

위 사진과 같이, 200ok 싸인과 응답 메시지가 내가 설정한대로 객체 형태로(key, value) 반환은 되고 있다.

 

중요한 건, 토큰이 생성되지 않았다는 것.


결론부터 얘기하자면 request 요청에 따른 url 설정을 두 군데로 잡아 놔서, 정상적으로 로직이 수행되지 않고 토큰도 생성되지 않았던 것.

 

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

    CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(authenticationManager());
    System.out.println(" SecurityFilterChain 설정 시작");

    http.cors(Customizer.withDefaults()) // cors 관련 설정 추가해야 프론트에서 보낼 때 받을 수 있다.
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/api/v1/auth/signup", "/api/v1/product/*", "/api/v1/product", "/api/v1/rental/*", "/api/v1/rental", "/api/v1/auth/login").permitAll() // 로그인 및 회원가입 허용
                    .anyRequest().authenticated()
            )
            .addFilter(customAuthenticationFilter) // 커스텀 로그인 필터 추가
            .addFilterBefore(jwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class) // JWT 인증 필터 추가
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // JWT 사용 시 세션 비활성화

    System.out.println(" SecurityFilterChain 설정 완료");
    return http.build();
}

 

 webSecurityConfig 클래스에  securityFilterChain의 로직인데 요청 url을 가로채서 대신 수행하는 역할을 한다.

(따로 컨트롤러가 없다.)

여기서 cors 설정, crsf 설정 이후, 요청 url에 대해 permitAll() 로 허가하고 있다.

 

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
    System.out.println(" jwtAuthorizationFilter - 요청 처리 중: " + request.getRequestURI());
    /*
    * 권한이 필요없는 리소스 (여기에 로그인 필요 없는 rest-api url 넣어야 함)
    * */

    List<String> roleLeessList = Arrays.asList(
            "/api/v1/product/\\d+",
            "/api/v1/product/\\w+",
            "/api/v1/product",
            "/api/product",
            "/api/v1/auth/login",
            "/api/v1/auth/signup",
            "/api/v1/inquiry",
            "/api/v1/inquiry/product",
            "/api/v1/inquiry/regist",
            "/api/v1/inquiry/delete",
            "/api/v1/inquiry/owner",
            "/api/v1/inquiry/modify",
            "/api/v1/inquiry/wait",
            "/api/v1/inquiry/complete",
            "/api/v1/rental",
            "/api/v1/rental/user",
            "/api/v1/rental/admin",
            "/api/v1/rental/owner",
            "/api/v1/review/product/\\d+",
            "/api/v1/product/search?s=\\w+",
            "/api/v1/review",
            "/api/v1/review/\\d++",
            "/api/v1/review/(\\d+)?offset=\\d+",
            "/swagger-ui/(.*)",        //swagger 설정
            "/swagger-ui/index.html",  //swagger 설정
            "/v3/api-docs",              //swagger 설정
            "/v3/api-docs/(.*)",         //swagger 설정
            "/swagger-resources",        //swagger 설정
            "/swagger-resources/(.*)"    //swagger 설정
    );

    if(roleLeessList.stream().anyMatch(uri -> roleLeessList.stream().anyMatch(pattern -> Pattern.matches(pattern, request.getRequestURI())))){
        chain.doFilter(request,response);
        return;
    }

문제는 doFilterInternal 메소드에서도 권한이 필요 없는 url 설정을 따로 해주고 있기 때문에 중복 처리가 되어, 제대로 로직이 돌지 않았다는 점이다.

 

보통은 securityFilterChain에서 요청 url에 대해 처리하지만, 일단 협업을 위해 관리할 url이 많다보니 doFilterInternal에 구현하는 방향으로 정했다.

 


<문제 해결하기>

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(authenticationManager());
        System.out.println(" SecurityFilterChain 설정 시작");

        http.cors(Customizer.withDefaults()) // cors 관련 설정 추가해야 프론트에서 보낼 때 받을 수 있다.
                // ☆ 지금까지 토큰 반환 안 됐던 이유! : doFilterInternal에서 url에 대해 설정하고 있는데 여기서 추가로 설정하고 있으므로 로직이 안 돌아감.
                .csrf(csrf -> csrf.disable())
//                .authorizeHttpRequests(auth -> auth
//                        .requestMatchers("/api/v1/auth/signup", "/api/v1/product/*", "/api/v1/product", "/api/v1/rental/*", "/api/v1/rental", "/api/v1/auth/login").permitAll() // 로그인 및 회원가입 허용
//                        .anyRequest().authenticated()
//                )
                .addFilter(customAuthenticationFilter) // 커스텀 로그인 필터 추가
                .addFilterBefore(jwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class) // JWT 인증 필터 추가
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // JWT 사용 시 세션 비활성화

        System.out.println(" SecurityFilterChain 설정 완료");
        return http.build();
    }

이와 같이 요청 url에 대해 주석만 해주고 post 요청을 보내봤다.

 

해결 ^ㅅ^

 

마감 기한은 다가오는데 기능 구현을 얼마 못 할까바 마음만 급했던 것 같다.

 

급할수록, 더 자세히 보고 뜯어보는 습관을 들여야겠다.

 

이제 로그인 검증을 하고, userInfo에 프론트단에서 필요한 데이터를 담아 보내볼 예정!

 

오늘도 고생 많았다.

반응형