Spring Security3源码分析(5)-SecurityContextPersistenceFilter分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

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

org.springframework.security.web.context.SecurityContextPersistenceFilter

废话不说,看源码

Java代码

  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
  2. throws IOException, ServletException {
  3. HttpServletRequest request = (HttpServletRequest) req;
  4. HttpServletResponse response = (HttpServletResponse) res;
  5. if (request.getAttribute(FILTER_APPLIED) != null) {
  6. // ensure that filter is only applied once per request
  7. chain.doFilter(request, response);
  8. return;
  9. }
  10. final boolean debug = logger.isDebugEnabled();
  11. request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
  12. if (forceEagerSessionCreation) {
  13. HttpSession session = request.getSession();
  14. if (debug && session.isNew()) {
  15. logger.debug(“Eagerly created session: “ + session.getId());
  16. }
  17. }
  18. //将request、response对象交给HttpRequestResponseHolder维持
  19. HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
  20. //通过SecurityContextRepository接口的实现类装载SecurityContext实例
  21. //HttpSessionSecurityContextRepository将产生SecurityContext实例的任务交给SecurityContextHolder.createEmptyContext()完成
  22. //SecurityContextHolder再根据策略模式的不同,
  23. //把任务再交给相应策略类完成SecurityContext的创建
  24. //如果没有配置策略名称,则默认为
  25. //ThreadLocalSecurityContextHolderStrategy,
  26. //该类直接通过new SecurityContextImpl()创建实例
  27. SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
  28. try {
  29. //将产生的SecurityContext再通过SecurityContextHolder->
  30. //ThreadLocalSecurityContextHolderStrategy设置到ThreadLocal中
  31. SecurityContextHolder.setContext(contextBeforeChainExecution);
  32. //继续把请求流向下一个过滤器执行
  33. chain.doFilter(holder.getRequest(), holder.getResponse());
  34. } finally {
  35. //先从SecurityContextHolder获取SecurityContext实例
  36. SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
  37. // Crucial removal of SecurityContextHolder contents – do this before anything else.
  38. //再把SecurityContext实例从SecurityContextHolder中清空
  39. SecurityContextHolder.clearContext();
  40. //将SecurityContext实例持久化到session中
  41. repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
  42. request.removeAttribute(FILTER_APPLIED);
  43. if (debug) {
  44. logger.debug(“SecurityContextHolder now cleared, as request processing completed”);
  45. }
  46. }
  47. }

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

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

Java代码

  1. public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
  2. HttpServletRequest request = requestResponseHolder.getRequest();
  3. HttpServletResponse response = requestResponseHolder.getResponse();
  4. HttpSession httpSession = request.getSession(false);
  5. //从session中获取SecurityContext
  6. SecurityContext context = readSecurityContextFromSession(httpSession);
  7. //如果获取不到SecurityContext,新建一个空的SecurityContext实例
  8. if (context == null) {
  9. if (logger.isDebugEnabled()) {
  10. logger.debug(“No SecurityContext was available from the HttpSession: “ + httpSession +“. “ +
  11. “A new one will be created.”);
  12. }
  13. context = generateNewContext();
  14. }
  15. //这里需要注意一下,response装饰器类重新包装了response
  16. requestResponseHolder.setResponse(new SaveToSessionResponseWrapper(response, request,
  17. httpSession != null, context.hashCode()));
  18. return context;
  19. }

进一步分析generateNewContext方法

Java代码

  1. SecurityContext generateNewContext() {
  2. SecurityContext context = null;
  3. //创建SecurityContext实例并返回
  4. if (securityContextClass == null) {
  5. context = SecurityContextHolder.createEmptyContext();
  6. return context;
  7. }
  8. try {
  9. context = securityContextClass.newInstance();
  10. } catch (Exception e) {
  11. ReflectionUtils.handleReflectionException(e);
  12. }
  13. return context;
  14. }

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

Java代码

  1. static {
  2. initialize();
  3. }
  4. private static void initialize() {
  5. if ((strategyName == null) || “”.equals(strategyName)) {
  6. // Set default
  7. strategyName = MODE_THREADLOCAL;
  8. }
  9. //默认的SecurityContextHolderStrategy实现类为
  10. //ThreadLocalSecurityContextHolderStrategy
  11. if (strategyName.equals(MODE_THREADLOCAL)) {
  12. strategy = new ThreadLocalSecurityContextHolderStrategy();
  13. } else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
  14. strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
  15. } else if (strategyName.equals(MODE_GLOBAL)) {
  16. strategy = new GlobalSecurityContextHolderStrategy();
  17. } else {
  18. // Try to load a custom strategy
  19. try {
  20. Class<?> clazz = Class.forName(strategyName);
  21. Constructor<?> customStrategy = clazz.getConstructor();
  22. strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
  23. } catch (Exception ex) {
  24. ReflectionUtils.handleReflectionException(ex);
  25. }
  26. }
  27. initializeCount++;
  28. }

现在来看ThreadLocalSecurityContextHolderStrategy源码

Java代码

  1. final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
  2. //~ Static fields/initializers =====================================================================================
  3. private static final ThreadLocal contextHolder = new ThreadLocal();
  4. //~ Methods ========================================================================================================
  5. public void clearContext() {
  6. contextHolder.set(null);
  7. }
  8. public SecurityContext getContext() {
  9. SecurityContext ctx = contextHolder.get();
  10. if (ctx == null) {
  11. ctx = createEmptyContext();
  12. contextHolder.set(ctx);
  13. }
  14. return ctx;
  15. }
  16. public void setContext(SecurityContext context) {
  17. Assert.notNull(context, “Only non-null SecurityContext instances are permitted”);
  18. contextHolder.set(context);
  19. }
  20. //直接new一个SecurityContextImpl对象,
  21. //SecurityContextImpl类实现SecurityContext接口
  22. public SecurityContext createEmptyContext() {
  23. return new SecurityContextImpl();
  24. }
  25. }

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

Java代码

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

Java代码

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

SaveToSessionResponseWrapper的saveContext方法源码:

Java代码

  1. protected void saveContext(SecurityContext context) {
  2. // See SEC-776
  3. if (authenticationTrustResolver.isAnonymous(context.getAuthentication())) {
  4. if (logger.isDebugEnabled()) {
  5. logger.debug(“SecurityContext contents are anonymous – context will not be stored in HttpSession. “);
  6. }
  7. return;
  8. }
  9. HttpSession httpSession = request.getSession(false);
  10. if (httpSession == null) {
  11. httpSession = createNewSessionIfAllowed(context);
  12. }
  13. // If HttpSession exists, store current SecurityContextHolder contents but only if
  14. // the SecurityContext has actually changed (see JIRA SEC-37)
  15. if (httpSession != null && context.hashCode() != contextHashBeforeChainExecution) {
  16. //保存SecurityContext到session中
  17. httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
  18. if (logger.isDebugEnabled()) {
  19. logger.debug(“SecurityContext stored to HttpSession: ‘” + context + “‘”);
  20. }
  21. }
  22. }

来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring Security3源码分析(5)-SecurityContextPersistenceFilter分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏