Post

[Spring Security] anonymous

[Spring Security] anonymous

πŸ“Œ 읡λͺ… μ‚¬μš©μž ν‘œν˜„ λ°©λ²•μ˜ λ³€ν™”

이전 인증 μ‹œμŠ€ν…œμ—μ„œλŠ” μΈμ¦λ˜μ§€ μ•ŠλŠ” μ‚¬μš©μžλ₯Ό null 둜 ν‘œν˜„ν•˜λŠ” κ²½μš°κ°€ λ§Žμ•˜λ‹€. κ·ΈλŸ¬λ‚˜ μ΄λŠ” μ½”λ“œμ— user == null κ³Ό 같은 μˆ˜λ§Žμ€ 검증 둜직 μž‘μ„±μ˜ 배경이 λ˜μ—ˆμœΌλ©°, NullPointerException 을 항상 κ³ λ €ν•΄μ•Ό ν–ˆλ‹€.

μ΄λŸ¬ν•œ 문제λ₯Ό Spring SecurityλŠ” β€˜μ‘΄μž¬ν•˜μ§€ μ•ŠμŒβ€™μ΄λΌλŠ” μƒνƒœλ₯Ό μ•„λ¬΄λŸ° λ™μž‘μ„ ν•˜μ§€ μ•Šμ§€λ§Œ μ‹€μ œ 객체인 AnonymousAuthenticationToken 으둜 ν‘œν˜„ν•œλ‹€.

인증된 μ‚¬μš©μžλŠ” UsernamePasswordAuthenticationToken 으둜 ν™•μΈν•œλ‹€.

이λ₯Ό 톡해 SecurityContextHolder κ°€ 항상 Authentication 객체λ₯Ό ν¬ν•¨ν•˜κ²Œ λœλ‹€. 즉, 인증 객체λ₯Ό κ°€μ Έμ˜¬ λ•Œ κ·Έ 리턴 값이 μ ˆλŒ€ null 이 λ˜μ§€ μ•ŠλŠ”λ‹€λŠ” λΆˆλ³€μ„±μ„ 보μž₯ν•˜κ²Œ λœλ‹€.

πŸ“Œ AnonymousAuthenticationFilter

AnonymousAuthenticationFilter λŠ” 인증 λ©”μ»€λ‹ˆμ¦˜ 이후 λ‹€λ₯Έ 인증 필터듀이 인증을 μˆ˜ν–‰ν•˜μ§€ λͺ»ν–ˆμ„ λ•Œ, 즉 SecurityContext 에 인증 객체가 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” 경우 λ™μž‘ν•œλ‹€. AnonymousAuthenticationFilter λŠ” SecurityContext κ°€ λΉ„μ–΄μžˆλ‹€λ©΄ AnonymousAuthenticationToken을 μƒμ„±ν•˜μ—¬ context에 μ €μž₯ν•œλ‹€.

1
2
3
4
5
6
7
8
public AnonymousAuthenticationFilter(String key, Object principal, List<GrantedAuthority> authorities) {
	Assert.hasLength(key, "key cannot be null or empty");
	Assert.notNull(principal, "Anonymous authentication principal must be set");
	Assert.notNull(authorities, "Anonymous authorities must be set");
	this.key = key;
	this.principal = principal;
	this.authorities = authorities;
}

μ΄ˆκΈ°μ— ν•„ν„°κ°€ μƒμ„±λ˜λ©΄ μ„Έ κ°€μ§€ 정보가 μ„€μ •λœλ‹€. key λŠ” AnonymousAuthenticationToken 을 μ‹λ³„ν•˜λŠ” κ³ μœ ν•œ 킀이닀. principal μ—λŠ” μ–΄λ–€ μ‚¬μš©μžκ°€ μ‘΄μž¬ν•˜λŠ”μ§€ λͺ…μ‹œν•˜λ©°, κΈ°λ³Έκ°’μœΌλ‘œ anonymous κ°€ μ„ΈνŒ…λœλ‹€. authorities μ—λŠ” 읡λͺ… μ‚¬μš©μžκ°€ κ°€μ§€λŠ” κΆŒν•œμ„ λͺ…μ‹œν•˜λ©°, κΈ°λ³Έκ°’μœΌλ‘œ ROLE_ANONYMOUS λ₯Ό κ°€μ§„λ‹€.

1
2
3
4
5
6
7
8
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
		throws IOException, ServletException {
	Supplier<SecurityContext> deferredContext = this.securityContextHolderStrategy.getDeferredContext();
	this.securityContextHolderStrategy
		.setDeferredContext(defaultWithAnonymous((HttpServletRequest) req, deferredContext));
	chain.doFilter(req, res);
}

이후 μ‚¬μš©μžμ˜ μš”μ²­μ€ doFilter λ©”μ„œλ“œλ‘œ λ“€μ–΄μ˜€κ²Œ λœλ‹€. ν•„ν„°μ˜ 핡심 λ‘œμ§μ€ defaultWithAnonymous λ©”μ„œλ“œμ— μ‘΄μž¬ν•œλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private SecurityContext defaultWithAnonymous(HttpServletRequest request, SecurityContext currentContext) {
	Authentication currentAuthentication = currentContext.getAuthentication();
	if (currentAuthentication == null) {
		Authentication anonymous = createAuthentication(request);
		
		// ...
		
		SecurityContext anonymousContext = this.securityContextHolderStrategy.createEmptyContext();
		anonymousContext.setAuthentication(anonymous);
		return anonymousContext;
	}
	else {
	
		// ...
	}
	return currentContext;
}

λ¨Όμ € getAuthentication λ©”μ„œλ“œλ₯Ό 톡해 SecurityContext 에 이미 Authentication 객체가 μ‘΄μž¬ν•˜λŠ”μ§€ ν™•μΈν•œλ‹€. μ•žμ„  필터듀이 이미 μ‚¬μš©μžλ₯Ό μΈμ¦ν–ˆλŠ”μ§€ ν™•μΈν•˜κ²Œ μœ„ν•΄μ„œμ΄λ‹€. λ§Œμ•½ μ‘΄μž¬ν•œλ‹€λ©΄ ν•„ν„°λŠ” μ•„λ¬΄λŸ° λ™μž‘μ„ μˆ˜ν–‰ν•˜μ§€ μ•Šκ³  λ„˜μ–΄κ°„λ‹€.

λ§Œμ•½ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄, 즉 ν•΄λ‹Ή μ‚¬μš©μžκ°€ μΈμ¦λ˜μ§€ μ•Šμ•˜λ‹€λ©΄ createAuthentication λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ AnonymousAuthenticationToken 을 μƒμ„±ν•œλ‹€,

1
2
3
4
5
6
protected Authentication createAuthentication(HttpServletRequest request) {
	AnonymousAuthenticationToken token = new AnonymousAuthenticationToken(this.key, this.principal,
			this.authorities);
	token.setDetails(this.authenticationDetailsSource.buildDetails(request));
	return token;
}

ν•„ν„°μ˜ μƒμ„±μžμ—μ„œ μ„€μ •λœ key, principal, authorities λ₯Ό 톡해 토큰을 μƒμ„±ν•˜κ³  λ‹€λ₯Έ μ„ΈλΆ€ 정보듀을 토큰에 μΆ”κ°€ν•œ ν›„ λ§Œλ“€μ–΄μ§„ 토큰을 λ¦¬ν„΄ν•œλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private SecurityContext defaultWithAnonymous(HttpServletRequest request, SecurityContext currentContext) {
	Authentication currentAuthentication = currentContext.getAuthentication();
	if (currentAuthentication == null) {
		// ...
		
		SecurityContext anonymousContext = this.securityContextHolderStrategy.createEmptyContext();
		anonymousContext.setAuthentication(anonymous);
		return anonymousContext;
	}
	else {
	
		// ...
	}
	return currentContext;
}

이후 토큰을 넣을 SecurityContext λ₯Ό μƒμ„±ν•œ ν›„ 토큰을 λ„£λŠ”λ‹€. μ΄λ ‡κ²Œ μƒμ„±λœ SecurityContext λ₯Ό λ¦¬ν„΄ν•˜μ—¬ ν˜„μž¬ μš”μ²­μ˜ SecuritContextHolder 에 μ„€μ •λœλ‹€.

πŸ“Œ AnonymousAuthenticationToken

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// AnonymousAuthenticationFilter.java
protected Authentication createAuthentication(HttpServletRequest request) {
	AnonymousAuthenticationToken token = new AnonymousAuthenticationToken(this.key, this.principal,
			this.authorities);
	token.setDetails(this.authenticationDetailsSource.buildDetails(request));
	return token;
}

private AnonymousAuthenticationToken(Integer keyHash, Object principal,
		Collection<? extends GrantedAuthority> authorities) {
	super(authorities);
	Assert.isTrue(principal != null && !"".equals(principal), "principal cannot be null or empty");
	Assert.notEmpty(authorities, "authorities cannot be null or empty");
	this.keyHash = keyHash;
	this.principal = principal;
	setAuthenticated(true);
}

필터에 λͺ…μ‹œλœ key, principal, authorities λ₯Ό μ„ΈνŒ…ν•˜κ³ , authenticated ν•„λ“œλ₯Ό μ„€μ •ν•œλ‹€. 이 ν•„λ“œμ˜ 값은 true 둜 μ„€μ •λ˜λŠ”λ°, μ΄λŠ” μ‚¬μš©μžκ°€ μ„±κ³΅μ μœΌλ‘œ λ‘œκ·ΈμΈν•˜μ˜€λ‹€λŠ” μ˜λ―Έκ°€ μ•„λ‹ˆλΌ ν•΄λ‹Ή Authentication 객체가 Spring Securityκ°€ μ‹ λ’°ν•˜λŠ” μœ νš¨ν•œ κ°μ²΄λΌλŠ” 것을 μ˜λ―Έν•œλ‹€.

1
2
3
public Object getCredentials() {
	return "";
}

ν† ν°μ˜ credentials μ—λŠ” 빈 λ¬Έμžμ—΄μ΄ μ„ΈνŒ…λ˜λŠ”λ°, μ΄λŠ” 증λͺ…ν•  자격 증λͺ…이 μ—†μŒμ„ μ˜λ―Έν•œλ‹€.

πŸ“Œ μ£Όμ˜μ‚¬ν•­ 및 νŠΉμ§•

읡λͺ… μ‚¬μš©μžμ—κ²Œ κ³Όλ„ν•œ κΆŒν•œμ„ λΆ€μ—¬ν•˜μ§€ μ•Šλ„λ‘ ν•œλ‹€. λ‹Ήμ—°ν•œ 뢀뢄이긴 ν•˜λ‹€. 인증된 μ‚¬μš©μžμ™€μ˜ 경계λ₯Ό λͺ…ν™•νžˆ ν•΄μ•Ό ν•œλ‹€.

AnonymousAuthenticationToken은 μ΅œμ†Œν•œμ˜ μ •λ³΄λ§Œ λ³΄κ΄€ν•˜λ―€λ‘œ λ©”λͺ¨λ¦¬μ μœΌλ‘œ νš¨μœ¨μ μ΄λ‹€. λ˜ν•œ 토큰이 μ„Έμ…˜μ—μ„œ κ΄€λ¦¬λ˜μ§€ μ•ŠμœΌλ―€λ‘œ μ„Έμ…˜ 곡간을 μ°¨μ§€ν•˜μ§€ μ•ŠλŠ”λ‹€.

읡λͺ… μ‚¬μš©μž κ΄€λ ¨ μ²˜λ¦¬λŠ” instanceof λ‚˜ authorities λ₯Ό ν™•μΈμœΌλ‘œ κ°„λ‹¨νžˆ μ΄λ£¨μ–΄μ§ˆ 수 μžˆλ‹€.

This post is licensed under CC BY 4.0 by the author.