
#冲刺创作新星#SecurityContextPersistenceFilter 过滤器链 原创
SecurityContextPersistenceFilter
SecurityContextPersistenceFilter是Springsecurity链中第二道防线,位于WebAsyncManagerIntegrationFilter之后,作用是为了存储SecurityContext而设计的。
SecurityContextPersistenceFilter主要做两件事:
- 当请求到来时,从HttpSession中获取SecurityContext并存入SecurityContextHolder中,这样在同一个请求的后续处理过程中,通过SecurityContextHolder获取数据
- 当一个请求处理完毕时,从SecurityContextHolder中获取SecurityContext并存入HttpSession中,方便下一个请求到来时,再从HTTPSession中拿来使用,同时擦除SecurityContextHolder中的登录信息。
将SpringContext存入HttpSession,或者从HttpSession中加载数据转化为SpringContext对象,这些事情都是由SecurityContextRepository的实现类完成。
SecurityContextRepository接口有三个实现类:
- TestSecurityContextRepository:为单元测试提供支持
- NullSecurityContextRepository:未做SpringContext的存储工作
- HttpSessionSecurityContextRepository:Springsecurity的默认使用类,实现了将SpringContext存储到HttpSession中以及从HttpSession中加载SecurityContext出来。
HttpSessionSecurityContextRepository
HttpSessionSecurityContextRepository定义的关于请求和封装的两个内部类
SaveToSessionResponseWrapper
它是对HttpServletResponse的扩展,
-
HttpServletResponseWrapper实现了HttpServletResponse接口,它是HttpServletResponse的装饰类,利用HttpServletResponseWrapper可以方便操作参数和输出流等。
-
OnCommittedResponseWrapper 继承HttpServletResponseWrapper,对其功能进行了增强,当HttpServletResponse的sendError sendRedirect flushBuffer等方法被调用的时候,doOnResponseCommitted()方法会被调用,这个方法里可以做一些数据的保存操作。OnCommittedResponseWrapper中的onResponseCommitted是抽象方法,实现类在SaveContextOnUpdateOrErrorResponseWrapper中
-
SaveContextOnUpdateOrErrorResponseWrapper 继承OnCommittedResponseWrapper,并对onResponseCommitted方法做了实现。在SaveContextOnUpdateOrErrorResponseWrapper中声明一个contextSaved变量,表示SecurityContext是否已经存储成功。当HttpServletResponse提交时,会调用onResponseCommitted方法,在onResponseCommitted中调用saveContext方法,并将contextSaved设置为true,表示已经存储。saveContext是抽闲方法,在SaveToSessionResponseWrapper中实现。
-
SaveToSessionResponseWrapper
saveContext方法:主要用来保存SecurityContext,如果authentication对象为null或者它是一个匿名对象,则不需要保存SecurityContext;同时如果httpSession不为null并且authBeforeExecution不为null,就从httpSession中将保存的登录用户数据移除,主要是为了防止开发者在注销成功的回调中继续调用chain.doFilter方法,进而导致原始的登录信息无法清除;如果httpSession为null,则去创建一个HttpSession对象;最后,如果SpringContext发生了变化,或者httpSession中没有保存SpringContext,则调用httpSession中的setAttribute方法将SpringContext保存起来。
这就是HttpSessionSecurityContextRepository封装的SaveToSessionResponseWrapper对象,一个核心功能就是在HttpServletResponse提交的时候,将SecurityContext保存到HttpSession中。
SaveToSessionRequestWrapper
作用是禁止在异步Servlet提交时,自动保存SecurityContext。为什么要禁止?
在异步Servlet中,当任务执行完毕后,HttpServletResponse会自动提交,在提交过程中会自动保存SecurityContext到HttpSession中,由于子线程无法获取用户信息,导致保存失败。如果使用异步Servlet,默认情况会禁用HttpServletResponse提交时自动保存SecurityContext,而是由SecurityContextPersistenceFilter中保存SecurityContext。
HttpSessionSecurityContextRepository源码:
- loadContext:调用readSecurityContextFromSession获取SecurityContext对象。如果为null,调用generateNewContext生成空的SecurityContext对象,并构造请求和响应的装饰类存入requestResponseHolder中。
- saveContext:保存SecurityContext
SecurityContextPersistenceFilter源码
核心方法是doFilter方法:
- 首先从request中获取FILTER_APPLIED属性,不为null放行,为null设置上属性值为true。确保该请求只执行一次这个过滤器
- forceEagerSessionCreation表示是否在过滤器链执行前确保会话有效,比较耗费资源,默认是false
- 构造HttpRequestResponseHolder对象,将HttpServletRequest HttpServletResponse存入。
- 调用repo.loadContext加载SecurityContext
- SecurityContext放入SecurityContextHolder中,这样就可以通过SecurityContextHolder来获取当前用户信息了
- 调用chain.doFilter方法执行下一个过滤链,此时传递的是SaveToSessionRequestWrapper SaveToSessionResponseWrapper的实例。
- 请求处理完毕后,在finally模块中,获取最新的SecurityContext,然后清空SecurityContextHolder中的数据。再调用repo.saveContext保存SecurityContext
- 移除FILTER_APPLIED属性
总结:
SecurityContextPersistenceFilter 先从HttpSession中读取SecurityContext出来,存入SecurityContextHolder以备后续使用,当请求离开SecurityContextPersistenceFilter的时候,获取最新的SecurityContext并存入HttpSession中,同时清空SecurityContextHolder中的登录信息
