中信国际inMotion项目Session id重复问题每日进度更新_20250304
中信国际inMotion项目Session id重复问题每日进度更新_20250304
- 分析涉及
Spring Session的各个filter源码。
详细排查过程
开源版本问题
根据之前排查的线索,重点排查登录过程中位于SessionRepositoryFilter以及CommonsRequestResponseLoggingFilter之间的filter。
CommonsRequestResponseLoggingFilter中,通过以下代码获取Session
#通过request对象尝试获取会话,如果会话不存在则返回null,不进行强制创建
HttpSession session = request.getSession(false);
这里的
request对象来源于Filter的HttpServletRequest入参,因此各个Filter可以通过包装Request对象实现session获取的自定义过程。
为了保证源码排查的严谨性,会通过两种手段确认排查结果:
-
本地源码Debug
-
在
UAT环境使用Arthus工具对Filter的入参进行监控。通过检查当前Filter的下一个Filter的入参类型确认排查结果。watch -b javax.servlet.Filter doFilter "{target.getClass(),params[0].getClass()}"
| Filter | 状态 | 备注 |
|---|---|---|
| OrderedFormContentFilter | 已排查 | |
| OrderedRequestContextFilter | 已排查 | |
| DelegatingFilterProxy | 已排查 | |
| WebAsyncManagerIntegrationFilter | 已排查 | |
| SecurityContextPersistenceFilter | 已排查 | |
| HeaderWriterFilter | 已排查 | |
| LogoutFilter | 已排查 | |
| RequestCacheAwareFilter | 已排查 | |
| SecurityContextHolderAwareRequestFilter | 已排查 | |
| AnonymousAuthenticationFilter | 已排查 | |
| SessionManagementFilter | ||
| ExceptionTranslationFilter | ||
| FilterSecurityInterceptor | ||
| ApplyHttpServletRequestWrapperFilter |
排查1. OrderedFormContentFilter
核心功能是对表单数据进行处理,特别是针对 application/x-www-form-urlencoded 和 multipart/form-data 类型的请求。OrderedFormContentFilter 会对原始的 HttpServletRequest 对象进行包装,生成一个新的请求对象。这个包装后的请求对象会对表单数据进行处理和优化,例如对请求参数进行排序,以确保数据的一致性和可预测性。这样,在后续的请求处理过程中,控制器方法可以直接从包装后的请求对象中获取经过处理的表单数据。
这里可以看到在有需要处理的参数时,会使用FormContentRequestWrapper对请求进行包装。
登录请求使用的Content-Type为:application/json,不满足条件,因此不会对Request进行包装。
- 本地debug
- UAT
排查2. OrderedRequestContextFilter
主要用于管理请求上下文信息,提供RequestContextHolder的支持。
在这个Filter中,未对HttpServletRequest对象进行包装。其它代码用于提供RequestContextHolder的支持,主要作用是在整个请求处理过程中保存和获取当前请求的上下文信息,为开发者提供了一种方便的方式来访问当前请求的相关数据,即使在无法直接获取 HttpServletRequest 对象的情况下也能做到。
- UAT
排查3. DelegatingFilterProxy
Spring 框架提供的一个特殊的过滤器,它在 Servlet 容器和 Spring 应用上下文之间起到了桥梁的作用。
这里可以看到请求被委托到另一个过滤器了
通过debug可以看到实际上被委托到:`
在FilterChainProxy中,则将Spring Security中的Filter加入到处理链中并执行,交给VirtualFilterChain处理。结合之前排查的线索,可以看到额外加入的Filter与线索中得到的结果是一致的,即额外加入的所有Filter都是属于Spring Security的。
在这个地方,可以看到通过firewall对request进行了二次包装成FirewalledRequest对象。继续跟踪,确认firewall使用的是StrictHttpFirewall。
继续查看getFirewalledRequest的实现
这个FirewalledRequest对原始的请求进行简单封装,并没有对getSession进行特殊处理。
排查4. WebAsyncManagerIntegrationFilter
主要用于在异步 Web 请求处理场景下集成 WebAsyncManager 与 Servlet 容器的异步处理机制,确保异步请求在整个处理流程中上下文信息的正确传递和管理。
这里是将异步管理器与当前的request进行绑定操作,传递的仍是之前的request对象。
- UAT
排查5. SecurityContextPersistenceFilter
负责安全上下文(SecurityContext)的持久化管理。
首先尝试从repo中恢复安全上下文,默认情况下使用:HttpSessionSecurityContextRepository实现。HttpSessionSecurityContextRepository会将包含用户身份和权限等信息的SecurityContext存储到HttpSession中。这样,在同一个会话期间的后续请求都可以从HttpSession中获取到该安全上下文,从而实现用户身份的持续跟踪和授权检查。
-
loadContext(HttpRequestResponseHolder requestResponseHolder):该方法用于从HttpSession中加载SecurityContext。它首先从HttpSession中获取已存储的SecurityContext,如果不存在,则创建一个新的空SecurityContext。
在这个步骤,可以看到对Request进行了包装成为:SaveToSessionRequestWrapper
该包装是为了处理异步模式下禁用自动保存安全上下文,也并没有对getSession进行重写。
-
saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response):此方法用于将SecurityContext保存到HttpSession中。它会将当前的SecurityContext存储到HttpSession,以便后续请求可以获取。
-
generateNewContext():用于创建一个新的空SecurityContext,当在HttpSession中未找到已存储的SecurityContext时会调用此方法。
排查6. HeaderWriterFilter
用于在 HTTP 响应中添加或修改特定的 HTTP 头信息,以此增强应用程序的安全性和功能性。
这里的逻辑比较简单,主要就是在请求结束前委托HeaderWriter将header写入响应中,有不同的实现,例如防止跨站脚本攻击(XSS)、缓存控制等。
这里通过HeaderWriterRequest对Request进行了包装,但是并没有对getSession进行重写。
排查7. LogoutFilter
用于处理用户的注销请求,清理用户的认证信息和会话状态,确保用户安全地退出系统。
这里的功能比较简单,先判断当前是否是logout
默认情况下,判断当前请求路径是否为:/logout。inmotion登录的地址为:/directbank/service/v1/login,不匹配,所以会跳过处理。
排查8. RequestCacheAwareFilter
用于处理用户在进行身份验证过程中被重定向到登录页面后,如何恢复原始请求的功能。
该Filter首先尝试从RequestCache中获取和本次请求相匹配的缓存请求,默认的实现是:HttpSessionRequestCache,基于Session的实现。
这里实际上就是尝试获取当前session如果能获取到则获取其中特定的attribute返回。缓存Request对象的写入位于ExceptionTranslationFilter的发送认证请求部分。
在inmotion登录场景中并没有重定向的需求,从排查的结果看也没有对Request进行封装。
- UAT
排查9. SecurityContextHolderAwareRequestFilter
用于将原始的 HttpServletRequest 请求对象包装成一个安全感知的请求对象,为后续的请求处理提供更便捷的安全信息访问方式。
通过RequestFactory对原始请求进行包装,默认的实现为:HttpServlet3RequestFactory。
该factory返回一个Servlet3SecurityContextHolderAwareRequestWrapper对象,该对象主要是提供了一些和登录、注销相关的方法,并未对getSession进行重写。
排查10. AnonymousAuthenticationFilter
在用户未进行显式身份验证时,为请求创建一个匿名身份,使得后续的安全处理流程能够统一处理已认证用户和未认证用户的请求。
如上所述,该filter仅用于创建匿名身份。
排查11. SessionManagementFilter
负责管理用户会话相关的安全问题,确保用户会话的安全性、有效性和一致性。主要提供以下能力:
- 会话并发控制
- 会话超时管理
- 会话固定攻击防护
特征统计
请求特征
| URI | 文件 1 中出现次数 | 文件 2 中出现次数 | 文件 3 中出现次数 |
|---|---|---|---|
| /uaa - coc - authorize/v1/devicebinding/pullDeviceMessage | 16 | 22 | 12 |
| /directbank/service/v1/td/landingInfo | 6 | 6 | 3 |
| /directbank/securities/v1/checkForEiao | 2 | 3 | 2 |
| /directbank/service/v1/customer/checkAddress | 3 | 3 | 2 |
| /uaa - coc - authorize/v1/devicebinding/svGetBindFlag | 4 | 3 | 2 |
| /directbank/service/v1/td/preparation | 7 | 2 | 1 |
| /directbank/service/v1/wm/promptAlert | 2 | 3 | 2 |
| /directbank/customer/v1/newFund | 6 | 3 | 1 |
| /directbank/service/v1/customer/cddInfoGet | 2 | 9 | 5 |
| /uaa - coc - authenticate/v1/login | 3 | 3 | 2 |
| /uaa - coc - authorize/v1/devicebinding/svGetDeviceUserID | 4 | 3 | 2 |
| /directbank/service/v1/customer/mainpageAuthFlag?type = getPermissions | 3 | 3 | 2 |
| /directbank/td/v1/rate/HKD?segment = true | 3 | 3 | 2 |
| /directbank/service/v1/td/opening | 2 | 2 | 1 |
| /directbank/account/v1/accounts | 3 | 3 | 2 |
| /directbank/account/v1/balances?realtime = false | 3 | 3 | 2 |
| /directbank/customer/v1/getBpInfo | 3 | 3 | 2 |
| /uaa - coc - idp/oauth/token?grant_type = client_credentials&scope = protected%20dbank&client_secret = 6d388860 - ac9c - 42bf - 81b7 - b0094fb1b71a&client_id = 2b439db7 - 496f - 4328 - b98b - 79318dd48cda | 3 | 3 | 2 |
| /directbank/service/v1/login | 3 | 3 | 2 |
| /directbank/service/v1/td/rate/HKD?segment = true | 3 | 3 | 2 |
| /directbank/account/v1/lostCard/getAvlCardList | 3 | 3 | 2 |
| /directbank/service/v1/getOfferByLocation | 3 | 3 | 2 |
| /directbank/service/v1/monopoly/checkEligibility | 3 | 3 | 2 |
| /directbank/service/v1/td/newFund | 3 | 3 | 2 |
| /directbank/service/v1/account/balanceSumm?acctType = ALL | 3 | 3 | 2 |
| /directbank/service/v1/ft/remitBindingList | 3 | 3 | 2 |
| /directbank/account/v1/campaign/registerInfo | 3 | 3 | 2 |
| /directbank/service/v1/getMyOfferInfo | 3 | 3 | 2 |
| /directbank/service/v1/customer/changePermission/qurey?type = ADDRESS,EMAIL,MOBILE,REACTIVATE | 3 | 3 | 2 |
| /directbank/service/v1/getMyTotalAssetInfo | 3 | 3 | 2 |
| /directbank/account/v1/accounts/TD | 2 | 2 | 1 |
| /directbank/account/v1/lostCard/getAbleToLostAndActStatus | 7 | 6 | 4 |
| /uaa - coc - idp/v1/user/login | 3 | 3 | 2 |
| /directbank/service/v1/account/accounts/additionalInfo | 6 | 6 | 5 |
| /directbank/monopoly/v1/checkEligibility | 3 | 3 | 2 |
| /directbank/service/v1/vcc/eligibility/etb | 3 | 3 | 2 |
| /directbank/td/v1/preparation | 7 | 2 | 1 |
| /directbank/td/v1/landingInfo | 6 | 6 | 3 |
| /directbank/service/v1/getMyTotalAssetInfo | 3 | 3 | 2 |
| /directbank/service/v1/lostCard/getAbleToLostAndActStatus | 7 | 6 | 4 |