Spring Security - 18. AnonymousAuthenticationFilter.

하쮸·2025년 3월 10일

1.AnonymousAuthenticationFilter

  • AnonymousAuthenticationFilter는 스프링 시큐리티 의존성을 추가하면 자동으로 등록되는 시큐리티 필터체인에 소속되어 있는 필터 중 하나임.
    • 이 필터의 목적은 SecurityContextHolder에 인증 객체가 없는지 확인하고 이를 채우는 역할.
      ('Anonymous'라는 값으로 넣어줌)
  • 사용자가 직접 커스텀 시큐리티필터체인을 등록하더라도 기본적으로 등록 됨.
public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {

	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
		.getContextHolderStrategy();

	private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();

	private String key;

	private Object principal;

	private List<GrantedAuthority> authorities;


	public AnonymousAuthenticationFilter(String key) {
		this(key, "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
	}


	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;
	}

	@Override
	public void afterPropertiesSet() {
		Assert.hasLength(this.key, "key must have length");
		Assert.notNull(this.principal, "Anonymous authentication principal must be set");
		Assert.notNull(this.authorities, "Anonymous authorities must be set");
	}

	@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);
	}

	private Supplier<SecurityContext> defaultWithAnonymous(HttpServletRequest request,
			Supplier<SecurityContext> currentDeferredContext) {
		return SingletonSupplier.of(() -> {
			SecurityContext currentContext = currentDeferredContext.get();
			return defaultWithAnonymous(request, currentContext);
		});
	}

	private SecurityContext defaultWithAnonymous(HttpServletRequest request, SecurityContext currentContext) {
		Authentication currentAuthentication = currentContext.getAuthentication();
		if (currentAuthentication == null) {
			Authentication anonymous = createAuthentication(request);
			if (this.logger.isTraceEnabled()) {
				this.logger.trace(LogMessage.of(() -> "Set SecurityContextHolder to " + anonymous));
			}
			else {
				this.logger.debug("Set SecurityContextHolder to anonymous SecurityContext");
			}
			SecurityContext anonymousContext = this.securityContextHolderStrategy.createEmptyContext();
			anonymousContext.setAuthentication(anonymous);
			return anonymousContext;
		}
		else {
			if (this.logger.isTraceEnabled()) {
				this.logger.trace(LogMessage.of(() -> "Did not set SecurityContextHolder since already authenticated "
						+ currentAuthentication));
			}
		}
		return currentContext;
	}

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

	public void setAuthenticationDetailsSource(
			AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
		Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
		this.authenticationDetailsSource = authenticationDetailsSource;
	}


	public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
		Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
		this.securityContextHolderStrategy = securityContextHolderStrategy;
	}

	public Object getPrincipal() {
		return this.principal;
	}

	public List<GrantedAuthority> getAuthorities() {
		return this.authorities;
	}

}

doFilter()

Supplier<SecurityContext> deferredContext = this.securityContextHolderStrategy.getDeferredContext();
  • SecurityContextHolder사용자 인증 정보(Authentication)가 저장되어 있는지 확인.
this.securityContextHolderStrategy
    .setDeferredContext(defaultWithAnonymous((HttpServletRequest) req, deferredContext));
  • defaultWithAnonymous() 메서드를 호출하여 현재 사용자가 인증되지 않은 경우, 익명 사용자(Anonymous) 인증 객체를 설정.

defaultWithAnonymous()

private Supplier<SecurityContext> defaultWithAnonymous(HttpServletRequest request,
        Supplier<SecurityContext> currentDeferredContext) {
    return SingletonSupplier.of(() -> {
        SecurityContext currentContext = currentDeferredContext.get();
        return defaultWithAnonymous(request, currentContext);
    });
}
  • 현재 SecurityContext를 가져와서 defaultWithAnonymous(request, currentContext)를 호출.

defaultWithAnonymous()

private SecurityContext defaultWithAnonymous(HttpServletRequest request, SecurityContext currentContext) {
    Authentication currentAuthentication = currentContext.getAuthentication();
    if (currentAuthentication == null) { // 현재 인증 정보가 없는 경우
        Authentication anonymous = createAuthentication(request);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(LogMessage.of(() -> "Set SecurityContextHolder to " + anonymous));
        } else {
            this.logger.debug("Set SecurityContextHolder to anonymous SecurityContext");
        }
        SecurityContext anonymousContext = this.securityContextHolderStrategy.createEmptyContext();
        anonymousContext.setAuthentication(anonymous);
        return anonymousContext;
    }
    return currentContext; // 인증 정보가 있으면 기존 컨텍스트 유지
}
  • SecurityContext에서 Authentication 객체를 가져옴.
    • null이면 익명 사용자 Authentication생성하고, SecurityContext에 저장.
    • 기존에 인증된 사용자가 있다면 아무런 작업도 하지 않고 원래 상태 유지.

2. 테스트

@Controller
public class TestController {

    @GetMapping("/")
    @ResponseBody
    public String test() {
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        String role = SecurityContextHolder.getContext().getAuthentication().getAuthorities().toString();

        System.out.println("username = " + username);
        System.out.println("role = " + role);
        return "test";
    }
}

profile
Every cloud has a silver lining.

0개의 댓글