[Spring Security] OncePerRequestFilter.shouldNotFilter ๋ฉ”์„œ๋“œ ํšจ์œจ์ ์œผ๋กœ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•˜๊ธฐ

2024. 3. 23. 23:00ใ†Backend/Spring

์ด๋‹ฌ ์ดˆ๋ถ€ํ„ฐ ์ง€์ธ 3๋ถ„๊ณผ ํ•จ๊ป˜ ์Šคํ”„๋ง ํ† ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค.

 

GitHub - Tiketeer/Tiketeer-BE

Contribute to Tiketeer/Tiketeer-BE development by creating an account on GitHub.

github.com

ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์—”๋“œํฌ์ธํŠธ ๋ณดํ˜ธ ์ž‘์—…์„ ์œ„ํ•ด Spring Security์™€ JWT๋ฅผ ์ด์šฉ ์ค‘์ธ๋ฐ, ์ด๋ฅผ ์œ„ํ•ด Spring Security์˜ OncePerRequestFilter๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” JwtAuthFilter๋ฅผ ์•„๋ž˜์ฒ˜๋Ÿผ ๋”ฐ๋กœ ์ž‘์„ฑํ•ด์ค€ ๋’ค ํ•„ํ„ฐ ์ฒด์ธ์— ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
		FilterChain filterChain) throws ServletException, IOException {
		try {
			// Cookie ๋‚ด jwt ์ถ”์ถœ + verify + authentication ์ƒ์„ฑ
			SecurityContextHolder.getContext().setAuthentication(authentication);
			filterChain.doFilter(request, response);
		} catch (JwtException ex) {
			// ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ๋กœ์ง
		}
	}
	@Override
	protected boolean shouldNotFilter(HttpServletRequest request) {
		// ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์„ ์ผ€์ด์Šค์— ๋Œ€ํ•ด ์ ์šฉ ์—ฌ๋ถ€๋ฅผ true or false๋กœ ๋ฐ˜ํ™˜
	}
}

 

๋ฌธ์ œ ์ƒํ™ฉ

๋ฌธ์ œ๋Š” ์ „์ฒด ์š”์ฒญ ํ๋ฆ„(์„œ๋ธ”๋ฆฟ + ํ•„ํ„ฐ + ์ปจํŠธ๋กค๋Ÿฌ ~)์„ ํ…Œ์ŠคํŠธํ•˜๋Š” e2e ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์™€์ค‘์— ๋ฐœ์ƒํ•˜์˜€๋‹ค. 

์œ„ ํ”„๋กœ์ ํŠธ์—๋Š” GET /ticketings/{ticketingId} EP๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ, ํ•ด๋‹น EP์˜ ์š”๊ตฌ์‚ฌํ•ญ์€ ๋กœ๊ทธ์ธ์„ ํ•˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž๋„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด์—ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์œ„์˜ shouldNotFilter ๋ฉ”์„œ๋“œ์—์„œ๋Š” ํ•ด๋‹น EP์— ๋Œ€ํ•œ ํ˜ธ์ถœ์— ๋Œ€ํ•ด true๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์ž‘์„ฑํ•ด๋‘” ๋ฉ”์„œ๋“œ๋Š” ๊ทธ๋ ‡๊ฒŒ ๋™์ž‘ํ•˜์ง€ ์•Š์•˜๋‹ค.

ํ•ด๋‹น ๋ฉ”์„œ๋“œ์˜ ์ดˆ์•ˆ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค. (์ดํ•ด๋ฅผ ๋•๊ธฐ ์œ„ํ•ด ์กฐ๊ธˆ ์žฌ๊ตฌ์„ฑํ•˜์—ฌ ์‹ค์ œ ์†Œ์Šค์ฝ”๋“œ์™€๋Š” ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ)

@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
	String path = request.getRequestURI();
	return getAllPublicPaths.contains(path);
}

private List<String> getAllPublicPaths() {
	return List.of(
    	// ์ธ์ฆ์„ ์ƒ๋žตํ•  ๋‹ค๋ฅธ EP๋“ค ํฌํ•จ
        "/ticketings/*"
    );
}

์ž๋ฐ”์˜ List.contains ๋ฉ”์„œ๋“œ๋Š” ๊ฒฐ๊ตญ List๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ equals ํ†ตํ•ด ์ฐธ ๊ฑฐ์ง“ ํŒ์ •์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์‚ฌ์‹ค ์œ„์ฒ˜๋Ÿผ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋Š” ์˜๋„์™€ ์ „ํ˜€ ๋งž์ง€ ์•Š๋Š” ์™„์ „ํžˆ ์ž˜๋ชป๋œ ์ฝ”๋“œ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. (์ด๊ฑด ์‚ฌ์‹ค Spring Security ์ชฝ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์„ฑ ํด๋ž˜์Šค์˜ RequestMatcher์—์„œ ์‚ฌ์šฉํ•˜๋Š” Ant ํŒจํ„ด ๊ฐœ๋…์„ ์ฐฉ๊ฐํ•˜์—ฌ ๊ฐ€์ ธ์˜จ ์‹ค์ˆ˜์ด๋‹ค.)

 

์ˆ˜์ • ๋ฐฉ์•ˆ 1

์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ ์ œ์ผ ๋จผ์ € ๋– ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์•ˆ์€ ๊ฒฐ๊ตญ ์ •๊ทœํ‘œํ˜„์‹์ด๋‹ค.

์ด๋ฅผ ํ†ตํ•˜๋ฉด ์œ„์˜ shouldNotFilter ๋ฉ”์„œ๋“œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑ๋  ์ˆ˜ ์žˆ๋‹ค.

@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
	String path = request.getRequestURI();
	return getAllPublicPathRegexs.stream().anyMatch(regex -> 
			Pattern.compile(regex).matcher(input).matches()
	);
}

private List<String> getAllPublicPathRegexs() {
	return List.of(
    	// ์ธ์ฆ์„ ์ƒ๋žตํ•  ๋‹ค๋ฅธ EP๋“ค์˜ ์ •๊ทœ์‹ ํฌํ•จ
        "/ticketings/([\\w-]+)$"
    );
}

๋‹ค๋งŒ ํ•ด๋‹น ๋ฐฉ์‹์„ ๋„์ž…ํ•˜๋Š”๋ฐ๋Š” ํฌ๊ฒŒ ๋‘๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

๋ฌธ์ œ 1. ์œ ์ง€๋ณด์ˆ˜์˜ ์–ด๋ ค์›€

์œ„์ฒ˜๋Ÿผ ์ž‘์„ฑํ•  ๊ฒฝ์šฐ, ์˜๋„ํ•œ ๋ฐ”๋Œ€๋กœ ๋™์ž‘์‹œํ‚ฌ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ค์›Œ์ง„๋‹ค. ๋‚˜๋ฅผ ํฌํ•จํ•œ ํŒ€์›๋“ค์€ ๋ชจ๋‘ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์„ ์—”๋“œํฌ์ธํŠธ๋ฅผ ๊ฐœ๋ฐœํ•  ๋•Œ๋งˆ๋‹ค ์ •๊ทœ์‹์„ ํ•˜๋‚˜์”ฉ ์ž‘์„ฑํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜์˜ ์ง„์ž…์žฅ๋ฒฝ์œผ๋กœ ์ž‘์šฉํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์œผ๋ฉฐ, ๋ฌผ๋ก  ๊ตฌ๊ธ€๋ง ํ˜น์€ ์ƒ์„ฑํ˜• AI์˜ ํž˜์„ ๋นŒ๋ฆด ์ˆ˜๋Š” ์žˆ๊ฒ ์ง€๋งŒ ์ž‘์—… ํšจ์œจ์ด ์ž˜๋‚˜์˜ค์ง€๋Š” ์•Š์„ ๊ฒƒ์ด๋‹ค.

๋ฌธ์ œ 2. SecurityFilterChain์—์„œ ๊ด€๋ฆฌํ•˜๋Š” EP์™€์˜ ์ด์›ํ™”๋กœ ์ธํ•œ ๊ด€๋ฆฌํฌ์ธํŠธ ์ฆ๊ฐ€

์‚ฌ์‹ค shouldNotFilter์—์„œ ์ˆ˜ํ–‰ํ•˜๋ ค๋Š” ์ž‘์—…์€ ์ด๋ฏธ ์œ ์‚ฌํ•œ ๋ฐฉ์‹์œผ๋กœ SecurityFilterChain์„ ๊ตฌ์„ฑํ•˜๋Š” Configuration์— ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑ๋˜์–ด ์žˆ๋‹ค. (๊ฐ„์†Œํ™”ํ•ด์„œ ์ž‘์„ฑ)

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

	http.authorizeHttpRequests(req ->
		req.requestMatchers(GET, "/ticketings/*").permitAll()
	);

	return http.build();

}

๊ทธ๋Ÿฐ๋ฐ ํ•ด๋‹น ๋ถ€๋ถ„์—์„œ๋Š” ์ •๊ทœ์‹๋ณด๋‹ค ์กฐ๊ธˆ ๋” ๊ฐ„์†Œํ™”๋œ Ant ํŒจํ„ด์„ ์ž‘์„ฑํ•ด์„œ ๋„ฃ์–ด์ค€๋‹ค. ์ฆ‰, ํ•ด๋‹น ํ”„๋กœ์ ํŠธ๋Š” ํŠน์ • ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ธ๊ฐ€ ์—†์ด ๊ฐœ๋ฐฉํ•˜๊ธฐ ์œ„ํ•ด "Ant ํŒจํ„ด"๊ณผ "์ •๊ทœ์‹"์„ ๊ฐ๊ฐ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋Š” ๊ฐœ๋ฐœํ•˜๋Š”๋ฐ ์žˆ์–ด ๋ฌด์กฐ๊ฑด ์‹ค์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์ ์ด๊ณ , ๊ฐœ๋ฐœ ํšจ์œจ์„ฑ์„ ์ €ํ•ด์‹œํ‚ค๋Š” ์š”์ธ์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

 

์ˆ˜์ • ๋ฐฉ์•ˆ 2 (์ฑ„ํƒ)

๋‘๋ฒˆ์งธ ์ˆ˜์ • ๋ฐฉ์•ˆ์€ ๋ฐ”๋กœ Spring Security์˜ RequestMatcher๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์•ž์„  ๋ฌธ์ œ 2๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„๊นŒ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€,

FilterChain์—์„œ EP์˜ ์ธ๊ฐ€ ๊ด€๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” RequestMatcher๋ฅผ ๊ทธ๋ƒฅ ๊ฐ€์ ธ์™€์„œ ์“ธ ์ˆ˜๋Š” ์—†๋‚˜? 

๋ผ๋Š” ์•„์ด๋””์–ด์—์„œ ์ƒ๊ฐํ•ด๋‚ธ ๋ฐฉ์•ˆ์ด๋‹ค.

RequestMatcher๋Š” matches ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์— ๋ถˆ๊ณผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด์˜ ๊ตฌํ˜„์ฒด ์ค‘ AntPathRequestMatcher๋ฅผ ์‚ดํŽด๋ณด์ž.

// AntPathRequestMatcher.matches ๋กœ์ง
@Override
public boolean matches(HttpServletRequest request) {
	if (this.httpMethod != null && StringUtils.hasText(request.getMethod())
			&& this.httpMethod != HttpMethod.valueOf(request.getMethod())) {
		return false;
	}
	if (this.pattern.equals(MATCH_ALL)) {
		return true;
	}
	String url = getRequestPath(request);
	return this.matcher.matches(url);
}

private String getRequestPath(HttpServletRequest request) {
	if (this.urlPathHelper != null) {
		return this.urlPathHelper.getPathWithinApplication(request);
	}
	String url = request.getServletPath();
	String pathInfo = request.getPathInfo();
	if (pathInfo != null) {
		url = StringUtils.hasLength(url) ? url + pathInfo : pathInfo;
	}
	return url;
}

RequestMatcher๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑ๋œ matches ๋กœ์ง์„ ์‚ดํŽด๋ณด๋ฉด ์•„๋ž˜๋กœ ์š”์•ฝ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

  1. HttpMethod ์ผ์น˜ ์—ฌ๋ถ€ ํ™•์ธ
  2. pattern์ด MATCH_ALL(/**) ์ธ์ง€ ํ™•์ธ
  3. url ํš๋“ (servletPath + pathInfo, ์ด๋ฅผ ํ†ตํ•ด context-path๋Š” ๋ฌด์‹œ๋จ)
  4. this.matcher์—๊ฒŒ ์ผ์น˜ ํŒ์ • ์œ„์ž„ (์ด๋Š” ์ตœ์ข…์ ์œผ๋กœ AntPathMatcher์— ์œ„์ž„๋˜์–ด Ant ํŒจํ„ด์„ ํ†ตํ•ด ๋งค์นญ ํŒ์ •)

์ฆ‰, ์ด๋ฅผ ํ†ตํ•˜๋ฉด FilterChain๊ณผ OncePerRequestFilter์—์„œ ๋ชจ๋‘ Ant ํŒจํ„ด์„ ํ†ตํ•ด ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋‘ ๊ณณ์—์„œ ์‚ฌ์šฉํ•  ๊ฒฝ๋กœ ์ •๋ณด๋ฅผ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ๋œ๋‹ค. (context-path๋ฅผ ์‹ ๊ฒฝ ์•ˆ์จ๋„ ๋˜๋Š”๊ฑด ๋ค)

 

๊ตฌํ˜„

๊ตฌํ˜„์— ์•ž์„œ ๋…น์—ฌ๋‚ด์•ผํ•  ์ปจํ…์ŠคํŠธ๋ฅผ ์ •๋ฆฌํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  1. EP์˜ ๊ฒฝ๋กœ๋Š” Ant ํŒจํ„ด ํ˜•์‹์œผ๋กœ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌ
  2. ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ ์‚ฌ์šฉํ•˜๋Š” Role์˜ ์ข…๋ฅ˜๋Š” ๊ตฌ๋งค์ž, ํŒ๋งค์ž๊ฐ€ ์กด์žฌํ•˜๊ณ  ์—ฌ๊ธฐ์— ์ถ”๊ฐ€์ ์œผ๋กœ ๋กœ๊ทธ์ธ์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์€ "๋ฐฉ๋ฌธ์ž"์˜ ๊ฐœ๋…๋„ ์กด์žฌ (๋ฐฉ๋ฌธ์ž < ๊ตฌ๋งค์ž < ํŒ๋งค์ž ์ˆœ)
  3. ์ธ์ž๋กœ Role์„ ๋„˜๊ฒจ์ฃผ๋ฉด ๊ทธ์— ๋Œ€์‘๋˜๋Š” RequestMatcher ๋ฒŒํฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•จ
  4. ์ด ๋•Œ ๊ฒฝ๋กœ๋“ค์€ HttpMethod์™€ ํŒจํ„ด์œผ๋กœ ๊ตฌ๋ถ„๋จ

๊ทธ๋ฆฌ๊ณ  ์ด์— ๋”ฐ๋ผ ์ž‘์„ฑํ•œ ์‹ค์ œ ๋กœ์ง์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

@Component
public class RequestMatcherManager {
	/**
	 * if role == null, return permitAll Path
	 */
	public RequestMatcher getRequestMatchersByMinRole(@Nullable RoleEnum minRole) {
		return new OrRequestMatcher(REQUEST_INFO_LIST.stream()
			.filter(reqInfo -> {
				if (reqInfo.minRole() == null) {
					return minRole == null;
				}
				return reqInfo.minRole().equals(minRole);
			})
			.map(reqInfo -> new AntPathRequestMatcher(reqInfo.pattern(), reqInfo.method().name()))
			.toArray(AntPathRequestMatcher[]::new));
	}

	// ๊ธธ์ด ๋ฌธ์ œ ์ƒ ์ผ๋ถ€๋งŒ ์ž‘์„ฑ
	private static final List<RequestInfo> REQUEST_INFO_LIST = List.of(
		// member
		new RequestInfo(GET, "/members/*/purchases", RoleEnum.BUYER),
		new RequestInfo(GET, "/members/*/sale", RoleEnum.SELLER),

		// auth
		new RequestInfo(POST, "/auth/login", null),
		new RequestInfo(POST, "/auth/refresh", null),

		// ticketing
		new RequestInfo(GET, "/ticketings", null),
		new RequestInfo(GET, "/ticketings/*", null),
		new RequestInfo(POST, "/ticketings", RoleEnum.SELLER),

		// swagger
		new RequestInfo(GET, "/v3/api-docs/**", null),
		new RequestInfo(GET, "/swagger-ui.html", null),
		new RequestInfo(GET, "/swagger-ui/**", null)
	);

	private record RequestInfo(HttpMethod method, String pattern, RoleEnum minRole) {
	}
}

 

์ž‘์„ฑ๋œ ๋กœ์ง์— ๋Œ€ํ•ด ๊ฐ„๋žตํžˆ ์„ค๋ช…ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • ํผ๋ธ”๋ฆญ ๋ฉ”์„œ๋“œ๋Š” ๋ฐ˜ํ™˜ํ•  ๊ฒฝ๋กœ๋“ค์˜ ์ตœ์†Œ Role์„ ๋ฐ›๋„๋ก ํ–ˆ๋‹ค. ๋ฐฉ๋ฌธ์ž ๊ฐœ๋…๋„ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— nullable๋กœ ๋‘์—ˆ๋‹ค.
  • ๋‚ด๋ถ€์ ์œผ๋กœ ํ”„๋ผ์ด๋น— ๋ ˆ์ฝ”๋“œ์ธ RequestInfo๋ฅผ ํ†ตํ•ด EP ์ธ๊ฐ€ ๊ด€๋ฆฌ์— ํ•„์š”ํ•œ ์ •๋ณด๋“ค์„ ๋‹ด๋„๋ก ํ–ˆ๋‹ค. (๋ฉ”์„œ๋“œ - ๊ฒฝ๋กœ - ์ตœ์†Œ Role)
  • ๊ทธ๋ฆฌ๊ณ  ๊ฐœ๋ฐœ์ž๋“ค์ด EP๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋‘๊ณ , ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ด ์ค‘ ์ตœ์†Œ ๊ถŒํ•œ์ด ๋™์ผํ•œ ์›์†Œ๋“ค๋งŒ์„ ์ด์šฉํ•˜์—ฌ RequestMatcher๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค.
  • ์ด ๋•Œ ์—ฌ๋Ÿฌ RequestMatcher๋ฅผ ํ•˜๋‚˜๋กœ ๋ฌถ์–ด์ฃผ๋Š” OrRequestMatcher๋ฅผ ์ด์šฉํ•˜์—ฌ ํ•˜๋‚˜์˜ RequestMatcher๋กœ ์—ฌ๋Ÿฌ EP์˜ ์ •๋ณด๋ฅผ ๋‹ด์„ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค.

์œ„ ๊ตฌํ˜„์„ SecurityFilterChain ๊ตฌ์„ฑ ๋ฉ”์„œ๋“œ์™€ OncePerRequestFilter์˜ shouldNotFilter ๋ฉ”์„œ๋“œ์— ์ ์šฉํ•˜๋ฉด ๊ฐ๊ฐ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

// SecurityConfig ๊ตฌ์„ฑ ํด๋ž˜์Šค ๋‚ด ๋นˆ ๋“ฑ๋ก ๋ฉ”์„œ๋“œ
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
	// ๊ฐ„๋žตํ™”ํ•œ ์ž‘์„ฑ
	http.addFilterAfter(jwtAuthenticationFilter, BasicAuthenticationFilter.class)
		.authorizeHttpRequests(req ->
			req
				.requestMatchers(requestMatcherManager.getRequestMatchersByMinRole(null))
				.permitAll()
				.requestMatchers(requestMatcherManager.getRequestMatchersByMinRole(SELLER))
				.hasAnyAuthority(SELLER.name())
				.requestMatchers(requestMatcherManager.getRequestMatchersByMinRole(BUYER))
				.hasAnyAuthority(BUYER.name(), SELLER.name())
				.anyRequest().authenticated()
		);

	return http.build();
}
// OncePerRequestFilter๋ฅผ ๊ตฌํ˜„ํ•œ JwtAuthFilter์˜ ๋ฉ”์„œ๋“œ
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
	// ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์„ ๊ฒฝ๋กœ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฉ๋ฌธ์ž Role (null)
	return requestMatcherManager.getRequestMatchersByMinRole(null).matches(request);
}

์ด๋ฅผ ํ†ตํ•ด, GET /member/{memberId} ๊ฒฝ๋กœ๋ฅผ ์ž˜ ํ†ต๊ณผ์‹œํ‚ฌ ๋ฟ ์•„๋‹ˆ๋ผ, ์ด์ „์—๋Š” ๋‘ ๊ด€๋ฆฌ ์ง€์ ์„ ๊ฐ๊ฐ ๊ด€๋ฆฌํ–ˆ์–ด์•ผ ํ–ˆ๋˜ ๋ฌธ์ œ๋ฅผ ํ•จ๊ป˜ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๋˜ํ•œ ์ด์ „์—๋Š” context-path๋ฅผ ์‹ ๊ฒฝ์จ์คฌ์–ด์•ผ ํ–ˆ๋˜ ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ, AntPathRequestMatcher์˜ ๋‚ด๋ถ€๊ตฌํ˜„ ๋ฐฉ์‹์— ์˜ํ•ด ์ด์— ๋Œ€ํ•œ ๊ณ ๋ ค๋„ ํ•„์š”์—†์–ด์กŒ๋‹ค.