티스토리 뷰

 

 

처음엔 URL로 다 막거나 열어

 

스프링 시큐리티(Spring Security) Config를 설정하다보면 인증할 경로, 인증안하고 열어둘 경로를 세팅했다.

SecurityConfig.java

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

	// 중간 생략

    http.authorizeHttpRequests((auth) -> auth
            .requestMatchers("/api/v1/users/**").permitAll()
            .requestMatchers("/api/v1/admin/**").authenticated()
            .requestMatchers("/api/v1/auth/**").permitAll()
            .anyRequest().authenticated()
    );
}

이렇게 세팅하다보니 Controller 클래스 마다 관리하게 되는 느낌이라 되게 편했다.

 

하지만, 한 클래스 내에 누구라도 호출할 수 있어야하는 API 엔드포인드가 몇개 생기기 시작했다.

 

 

 

 

어라? 화이트 리스트를 세팅하여 예외를 둬

 

이제는 화이트 리스트로 열어둘 API를 모두 작성하게 되었다.

SecurityConfig.java

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

    // 허용 URL
    final String[] WHITE_LIST_URL = {
            "/api/v1/users",
            "/api/v1/users/nickname/check",
            "/api/v1/users/email/check",
            ..
    };
    // 스웨거 허용 URL
    final String[] SWAGGER_LIST_URL = {
            "/swagger-ui/**",
            "/v3/api-docs/**",
            "/swagger-resources/**",
            "/webjars/**"
    };
    // 인증 필수 URL
    final String[] AUTH_REQUIRED_URL = {
            "/api/v1/admin/**",
            ..
    };

    http.authorizeHttpRequests((auth) -> auth
            .requestMatchers(AUTH_REQUIRED_URL).authenticated()     // 인증 필수 URL
            .requestMatchers(WHITE_LIST_URL).permitAll()       // 누구나 접근 가능
            .requestMatchers(SWAGGER_LIST_URL).permitAll()       // 누구나 접근 가능
            .anyRequest().authenticated()                // 이외의 경로 모두 인증 필요
    );
}

 

지금은 아주 극히 일부분이지만

개발을 하며 기능이 추가되고 열어둬야하는 API와 인증 필수인 API의 URL이 계속 추가가 되어 되게 이마트 영수증마냥  길어지기 시작했다.

그리고 계속해서 SecurityConfig 파일을 수정하기는 싫었다.

 

무한도전 박명수
[그림1] 무한도전 박명수

 

 

 

 

안되겠다! 구조를 바꿔!

 

전략은 이랬다.

 

  1. 모든 요청에는 기본적으로 인증이 필요
  2. 우리 프로젝트의 정책인 /api/v1/{domain} 이런 구조이므로 /api/v1/**을 전부 열어주기
  3. 정말로 열어야 하는 URL 경로만 화이트 리스트로 등록

흑백요리사 안성재 셰프
[그림1] 흑백요리사 안성재 셰프

 

 

 

어노테이션 추가

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
	// 생략
}

지금 여기서 중요한 것은 @EnableMethodSecurity 를 추가해야한다.

 

 

인증 URL 설정 (SecurityConfig.java)

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

    // 허용 URL
    final String[] WHITE_LIST_URL = {
            "/api/v1/**"
    };
    // 스웨거 허용 URL
    final String[] SWAGGER_LIST_URL = {
            "/swagger-ui/**",
            "/v3/api-docs/**",
            "/swagger-resources/**",
            "/webjars/**"
    };

    http.authorizeHttpRequests((auth) -> auth
            .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            .requestMatchers(WHITE_LIST_URL).permitAll()                // 누구나 접근 가능
            .requestMatchers(SWAGGER_LIST_URL).permitAll()              // 스웨거 허용
            .anyRequest().authenticated()                               // 이외의 경로 모두 인증 필요
    );
}

이상한 경로로 들어오지 못하도록 기본적인 요청은 인증이 필요하도록 막아둔 상태다.

즉, 굵직한 정상적인 경로로 다 들어와!! 검문은 나중에 할게!! 

 

역시 확실히 연예인 다이어트 급으로 갑자기 홀쭉해 졌구만유

 

 

Tip) OPTIONS 메서드를 열어둔 이유는 CORS Preflight 요청 때문에 열어야 한다.

 

 

컨트롤러에서 사용 (전부 허용)

@Operation(
        summary = "이메일 중복 체크",
        description = "이메일이 이미 사용중인지 중복체크를 합니다."
)
@PermitAll
@GetMapping("/email/check")
public SuccessRes<Void> checkDuplicateEmail(
	@ParameterObject @Validated @ModelAttribute UserEmailCheckReq req) {
    userApiFacade.checkDuplicateEmail(req);
    return SuccessRes.of();
}

@PermitAll 을 붙여 전부 허용한다는 것을 명시해 주었다.

다른 개발자가 보고 바로 파악할 수 있는게 목적이고

또 다른 목적은 나중에 내 자신이 봤을 때도 바로 파악하기 위함이다.  ^__^7

 

 

컨트롤러에서 사용 (권한 사용)

@PreAuthorize("hasRole('ADMIN')")
@GetMapping
public SuccessRes<PageResponse<AdminUserListRes>> getUsers(
        @ModelAttribute AdminUserSearchReq req) {
    return SuccessRes.of(adminApiFacade.getUsers(req));
}

이것 처럼 관리자 역할(권한)이 있어야하는 API 일 경우에는

@PreAuthorize("hasRole('??')")를 붙이거나 @PreAuthorize("hasAnyRole('??', '??2)") 를 붙여서 사용하면 된다.

 

@PreAuthorize를 명시하면 아 이건 권한이 필요한 API이고, 필터에서 판단하기 때문에 인증 절차가 걸린다.

 

 

컨트롤러에서 사용 (JWT 기반 현재 사용자로 사용)

@Operation(
        summary = "내 사용자 정보 조회",
        description = "사용자가 가지고 있는 토큰 기반으로 사용자 정보를 조회합니다.",
        security = @SecurityRequirement(name = "JWT")
)
@GetMapping("/profile")
public SuccessRes<UserDetailRes> getProfile(@CurrentUser Long id) {
    return SuccessRes.of(userApiFacade.getProfile(id));
}

@CurrnetUser 어노테이션은 내가 만든 것이며, Resolever를 만들어 세팅해 두었다.

 

그래서 JWT안에 있는 정보를 추출하는데 먼저 필터로 걸러내기 때문에!!
Security 요청은 허용했지만!! Filter에서 막혀 인증이 필요하다라는 문구가 나온다!!

 

 

 

 

마무리

 

내가 원하는 SecurityConfig는 더 이상 건들이지, 수정하지 않아도 되며,

메서드 별로 관리할 수 있게 되었다.

 

정말로 너무 거대해지면 너무 많은 API를 하나하나 컨트롤하고, 관리하기에는 어렵겠지만

그건 4~5년 후에 이야기고..

 

지금 상황에 맞춰서 최선을 다하며 최적의 개선을 하고자 한다.

 

 

 

감사합니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함