Spring Security對於寫過Spring Boot的人應該是再熟悉不過了,這篇文主要紀錄我對於 ReactiveSecurityContextHolder的理解,原始碼版本為5.4.0-RC1

在聊原始碼前,我想先聊一下Context到底是什麼。

Context,中文譯作「上下文」,我對於上下文的理解就是物件作用的環境

打個比方,假設現在有一個物件叫做Weather,而假設Weather物件會有下雨、晴朗這兩種狀態,且EarthContext封裝其物件或者是強制其改變為某種狀態時,則可以說Weather的上下文是EarthContext

對於Spring Security來說,SecurityContext就是整個Spring Security應用的上下文。

SecurityContextHolder就是單純保存這個上下文而存在的。 在傳統Servlet應用SecurityContextHolder是存在多種保存上下文的策略,比方說GlobalSecurityContextHolderStrategyInheritableThreadLocalSecurityContextHolderStrategyThreadLocalSecurityContextHolderStrategy,但在ReactiveSecurityContextHolder中,並沒有多種策略去保存SecurityContext,唯一保存上下文的方法就是透過Reactor的上下文

以下為個人部分翻譯的源碼:

public class ReactiveSecurityContextHolder {
	private static final Class<?> SECURITY_CONTEXT_KEY = SecurityContext.class;

	/**
	 * 從 Reactor {@link Context} 取得 {@code Mono<SecurityContext>} 
	 * @return 回傳 {@code Mono<SecurityContext>}
	 */
	public static Mono<SecurityContext> getContext() {
        // 從Reactor的上下文中取得SecurityContext
		return Mono.subscriberContext()
			.filter( c -> c.hasKey(SECURITY_CONTEXT_KEY))
			.flatMap( c-> c.<Mono<SecurityContext>>get(SECURITY_CONTEXT_KEY));
	}

	/**
	 * 從 Reactor {@link Context} 清除 {@code Mono<SecurityContext>}
	 * @return 清除Reactor上下文,並回傳一個 Mono<Void>,若清除失敗,則報錯。
	 */
	public static Function<Context, Context> clearContext() {
        // 從Reactor的上下文中刪除SecurityContext
		return context -> context.delete(SECURITY_CONTEXT_KEY);
	}

	/**
	 * 創建含有 {@code Mono<SecurityContext>} 的 Reactor {@link Context}
	 * 可與其他 {@link Context} 合併 
	 * @param securityContext the {@code Mono<SecurityContext>} to set in the returned
	 * Reactor {@link Context}
	 * @return a Reactor {@link Context} that contains the {@code Mono<SecurityContext>}
	 */
	public static Context withSecurityContext(Mono<? extends SecurityContext> securityContext) {
		return Context.of(SECURITY_CONTEXT_KEY, securityContext);
	}

	/**
	 * A shortcut for {@link #withSecurityContext(Mono)}
	 * @param authentication the {@link Authentication} to be used
	 * @return a Reactor {@link Context} that contains the {@code Mono<SecurityContext>}
	 */
	public static Context withAuthentication(Authentication authentication) {
		return withSecurityContext(Mono.just(new SecurityContextImpl(authentication)));
	}
}

了解了 ReactiveSecurityContextHolder 主要在幹嘛,下一篇預計會介紹 SecurityContext 從何而來。