spring security 설정 (1) - config

진병욱·2023년 9월 16일
post-thumbnail

네이버 로그인 구현을 하려고 하면서 가장 난항을 겪었던 부분이다.
어떤식으로 하는 느낌이다라는 것만 인지하고 있었고 직접 제대로 구현을 해보고 이해를 하려고 했던 것은 처음이었고, 게다가 SNS 로그인까지 연동이 되다보니 좀 더 여려웠던 것 같았다.
그래서 좀 더 직접적인 샘플코드와 설명을 필요로 했는데 서치를 하다가 아래의 블로그를 찾아서 관련된 코드를 작성하는데 좀 더 수월했다.
https://wildeveloperetrain.tistory.com/252

해당 블로그에는 깃헙도 나와있어서, 깃헙의 코드를 베이스로 세팅을 해주고 필요한 부분들은 커스터마이징을 해주었다.
그 중에서도 security의 메인이 되는 config 설정을 보면 아래와 같이 세팅을 해 주었다.

public class WebSecurityConfigure {

    private final CustomOAuth2UserService customOAuth2UserService;
    private final JwtTokenProvider jwtTokenProvider;
    private final CookieAuthorizationRequestRepository cookieAuthorizationRequestRepository;
    private final OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
    private final OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
    private final CustomUserDetailsService customUserDetailsService;

    @Value("${url.frontend}")
    private String FRONTEND_BASE_URL;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        log.debug("filterChain => ");
        log.debug("   {}", http.toString());
        //httpBasic, csrf, formLogin, rememberMe, logout, session disable
        http
//                .cors()
//                .and()
                .csrf().disable()
                .formLogin().disable()
                .rememberMe().disable()
                .httpBasic().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);


        //요청에 대한 권한 설정
        http.authorizeRequests()
                .antMatchers("/","/api-docs/**","/api/**","/api/swagger-ui/index.html", "/api/swagger.html","/js/**","/css/**").permitAll()
                .anyRequest().authenticated();

        //oauth2Login
        http.oauth2Login()
                .authorizationEndpoint()
                .baseUri("/api/oauth2/authorize")  // 소셜 로그인 url
                .authorizationRequestRepository(cookieAuthorizationRequestRepository)  // 인증 요청을 cookie 에 저장
                .and()
                .redirectionEndpoint()
                .baseUri("/api/oauth2/callback/*")  // 소셜 인증 후 redirect url
                .and()
                .userInfoEndpoint()
                .userService(customOAuth2UserService)  // 회원 정보 처리
                //userService()OAuth2 인증 과정에서 Authentication 생성에 필요한 OAuth2User 를 반환하는 클래스를 지정한다.
                .and()
                .successHandler(oAuth2AuthenticationSuccessHandler)
                .failureHandler(oAuth2AuthenticationFailureHandler);

        http.logout()
                .logoutUrl("/api/member/logout")
                .deleteCookies("JSESSIONID")
                .clearAuthentication(true)
                .logoutSuccessHandler((request, response, authentication) ->
                        response.sendRedirect(FRONTEND_BASE_URL)
                );


        //jwt filter 설정
        http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, customUserDetailsService), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

위와 같이 세팅을 해 주었다.

http 기본 설정

  • csrf().disable() : csrf 비활성화
  • formLogin().disable() : 폼 로그인 비활성화
  • rememberMe().disable() : 로그인 유지 비활성화
  • httpBasic().disable() : http 기본 인증 비활성화
  • SessionCreationPolicy.STATELESS : 상태 미저장 세션관리 사용

권한 설정

  • antMatachers("").permitAll() : 해당 링크에 대해서는 접근 권한 OK
  • anyRequest().authenticated() : 위의 링크를 제외한 요청은 인증 필요

oauth2 로그인 설정

  • authorizationEndpoint().baseUri("") : 소셜 로그인 요청 uri
  • authorizationRequestRepository(cookieAuthorizationRequestRepository) : 인증 요청을 쿠키에 저장
  • redirectionEndpoint().baseUri("") : 소셜 로그인 완료 후 redirect uri
  • .userInfoEndpoint().userService(customOAuth2UserService) : 회원 인증 정보 처리 서비스 지정
  • successHandler() : 인증 완료 처리 handler
  • failureHandler() : 인증 실패 처리 handler

로그아웃 설정

  • logoutUrl("") : 로그아웃 요청을 받을 Url을 지정
  • deleteCookies("") : 쿠키 삭제
  • clearAuthentication : 인증 정보 삭제
  • logoutSuccessHandler((request, response, authentication) -> response.sendRedirect(FRONTEND_BASE_URL)) : 로그아웃 성공시 handler, 성공시 front로 redirect

jwt 필터 설정

  • http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, customUserDetailsService), UsernamePasswordAuthenticationFilter.class)

(+ 2023.11.07 추가 사항)
기존에는 spring boot 2.7.14 를 사용했었지만, 프로젝트를 진행하면서 spring boot를 3.1.4로 사용하면서 spring security가 spring boot 3점대부터 6점대로 변경되었다.
그러면서 기존에 사용하던 .disable을 사용하지 않고, 람다 함수를 사용하도록 변경되었다.

public class WebSecurityConfigure {

    private final CustomOAuth2UserService customOAuth2UserService;
    private final JwtTokenProvider jwtTokenProvider;
    private final CookieAuthorizationRequestRepository cookieAuthorizationRequestRepository;
    private final OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
    private final OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
    private final CustomUserDetailsService customUserDetailsService;

    @Value("${url.frontend}")
    private String FRONTEND_BASE_URL;

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

    // CORS 관련 설정
    @Bean
    public CorsConfigurationSource configurationSource () {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(false);
        config.setAllowedOrigins(Arrays.asList("*"));
        config.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "DELETE"));
        config.setAllowedHeaders(Arrays.asList("*"));
        config.setMaxAge(3600 * 6L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        log.debug("filterChain => ");
        log.debug("   {}", http.toString());

        // 기타 보안 설정
        // 람다 함수 및 추상 메소드 사용
        http
                .cors(cors -> cors.configurationSource(configurationSource()))
                .csrf(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .rememberMe(AbstractHttpConfigurer::disable)
                .httpBasic(AbstractHttpConfigurer::disable)
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));


        //요청에 대한 권한 설정
        http.authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/member/login", "/api/member/join", "/api/game/file/game-download", "/api/oauth2/**", "/api/**", "/swagger-ui/**", "/api-docs/**", "/api/mail/send").permitAll()
                .requestMatchers("/api/admin").hasAuthority(Role.ROLE_ADMIN.name())
                .anyRequest().authenticated()
        );


        //oauth2Login
        http.oauth2Login(oauth2-> oauth2
                .authorizationEndpoint(auth->auth.baseUri("/api/oauth2/authorize")
                        .authorizationRequestRepository(cookieAuthorizationRequestRepository))
                .redirectionEndpoint(redirect -> redirect.baseUri("/api/oauth2/callback/*"))
                .userInfoEndpoint(user -> user.userService(customOAuth2UserService))
                .successHandler(oAuth2AuthenticationSuccessHandler)
                .failureHandler(oAuth2AuthenticationFailureHandler)
        );


        // 로그아웃 관련 설정
        http.
                logout(logout -> logout.logoutUrl("/api/user/auth/logout").permitAll()
                        .deleteCookies("JSESSIONID")
                        .logoutSuccessHandler((request, response, authentication) ->
                                response.sendRedirect(FRONTEND_BASE_URL))
                );


        //jwt filter 설정
        http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, customUserDetailsService), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}
profile
새로운 기술을 접하는 것에 망설임이 없고, 부족한 것이 있다면 항상 배우고자 하는 열정을 가지고 있습니다!

0개의 댓글