Spring Security3源码分析-SecurityContextPersistenceFilter分析

通过观察Filter的名字,就能大概猜出来这个过滤器的作用,是的,持久化SecurityContext实例。这个过滤器位置是;

org.springframework.security.web.context.SecurityContextPersistenceFilter

废话不说,看源码


public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (request.getAttribute(FILTER_APPLIED) != null) {
// ensure that filter is only applied once per request
chain.doFilter(request, response);
return;
}

final boolean debug = logger.isDebugEnabled();

request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

if (forceEagerSessionCreation) {
HttpSession session = request.getSession();

if (debug && session.isNew()) {
logger.debug("Eagerly created session: " + session.getId());
}
}
//将request、response对象交给HttpRequestResponseHolder维持
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
//通过SecurityContextRepository接口的实现类装载SecurityContext实例
//HttpSessionSecurityContextRepository将产生SecurityContext实例的任务交给SecurityContextHolder.createEmptyContext()完成
//SecurityContextHolder再根据策略模式的不同,
//把任务再交给相应策略类完成SecurityContext的创建
//如果没有配置策略名称,则默认为
//ThreadLocalSecurityContextHolderStrategy,
//该类直接通过new SecurityContextImpl()创建实例
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

try {
//将产生的SecurityContext再通过SecurityContextHolder->
//ThreadLocalSecurityContextHolderStrategy设置到ThreadLocal中
SecurityContextHolder.setContext(contextBeforeChainExecution);
//继续把请求流向下一个过滤器执行
chain.doFilter(holder.getRequest(), holder.getResponse());

} finally {
//先从SecurityContextHolder获取SecurityContext实例
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
// Crucial removal of SecurityContextHolder contents - do this before anything else.
//再把SecurityContext实例从SecurityContextHolder中清空
SecurityContextHolder.clearContext();
//将SecurityContext实例持久化到session中
repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
if (debug) {
logger.debug("SecurityContextHolder now cleared, as request processing completed");
}
}
}

通过源码中的注释,应该可以看出来,这个Filter的作用主要是创建一个空的SecurityContext(如果session中没有SecurityContext实例),然后持久化到session中。

接下来看看repo.loadContext(holder);代码:


public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
HttpServletRequest request = requestResponseHolder.getRequest();
HttpServletResponse response = requestResponseHolder.getResponse();
HttpSession httpSession = request.getSession(false);
//从session中获取SecurityContext
SecurityContext context = readSecurityContextFromSession(httpSession);
//如果获取不到SecurityContext,新建一个空的SecurityContext实例
if (context == null) {
if (logger.isDebugEnabled()) {
logger.debug("No SecurityContext was available from the HttpSession: " + httpSession +". " +
"A new one will be created.");
}
context = generateNewContext();

}
//这里需要注意一下,response装饰器类重新包装了response
requestResponseHolder.setResponse(new SaveToSessionResponseWrapper(response, request,
httpSession != null, context.hashCode()));

return context;
}

进一步分析generateNewContext方法


SecurityContext generateNewContext() {
SecurityContext context = null;
//创建SecurityContext实例并返回
if (securityContextClass == null) {
context = SecurityContextHolder.createEmptyContext();

return context;
}

try {
context = securityContextClass.newInstance();
} catch (Exception e) {
ReflectionUtils.handleReflectionException(e);
}
return context;
}

实际上,SecurityContextHolder类也是把创建SecurityContext任务交给具体的SecurityContextHolderStrategy实现类处理,SecurityContextHolder类有一个静态初始化过程


static {
initialize();
}

…………
private static void initialize() {
if ((strategyName == null) || "".equals(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
//默认的SecurityContextHolderStrategy实现类为
//ThreadLocalSecurityContextHolderStrategy
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
} else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
} else if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
} else {
// Try to load a custom strategy
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
} catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}

initializeCount++;
}

现在来看ThreadLocalSecurityContextHolderStrategy源码


final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
//~ Static fields/initializers =====================================================================================

private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<SecurityContext>();

//~ Methods ========================================================================================================

public void clearContext() {
contextHolder.set(null);
}

public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();

if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}

return ctx;
}

public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder.set(context);
}
//直接new一个SecurityContextImpl对象,
//SecurityContextImpl类实现SecurityContext接口
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}

分析到这里,整个过程也清楚了。不过在filter原路返回时,还需要保存这个SecurityContext实例到session中,并且通过SecurityContextHolder将ThreadLocalSecurityContextHolderStrategy中ThreadLocal维持的SecurityContext实例清空。


//将SecurityContext实例持久化到session中
repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());

public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
//由于之前response装饰器类SaveToSessionResponseWrapper
//重新装饰了response
SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = (SaveContextOnUpdateOrErrorResponseWrapper)response;
// saveContext() might already be called by the response wrapper
// if something in the chain called sendError() or sendRedirect(). This ensures we only call it
// once per request.
if (!responseWrapper.isContextSaved() ) {
//SaveToSessionResponseWrapper保存SecurityContext实例
responseWrapper.saveContext(context);
}
}

SaveToSessionResponseWrapper的saveContext方法源码:


protected void saveContext(SecurityContext context) {
// See SEC-776
if (authenticationTrustResolver.isAnonymous(context.getAuthentication())) {
if (logger.isDebugEnabled()) {
logger.debug("SecurityContext contents are anonymous - context will not be stored in HttpSession. ");
}
return;
}

HttpSession httpSession = request.getSession(false);

if (httpSession == null) {
httpSession = createNewSessionIfAllowed(context);
}

// If HttpSession exists, store current SecurityContextHolder contents but only if
// the SecurityContext has actually changed (see JIRA SEC-37)
if (httpSession != null && context.hashCode() != contextHashBeforeChainExecution) {
//保存SecurityContext到session中
httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);

if (logger.isDebugEnabled()) {
logger.debug("SecurityContext stored to HttpSession: '" + context + "'");
}
}
}
    原文作者:Spring MVC
    原文地址: https://blog.csdn.net/Dead_Knight/article/details/84213887
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞