Pac4j多间接认证场景下原始请求URL跳转处理方案咨询
问题根因
跳转逻辑失效的核心原因是原始请求保存逻辑被覆盖:
pac4j默认在未认证用户触发受保护路径的安全校验时,会把当前请求URL存入Session的SavedRequest对象,认证完成后自动读取该地址完成跳转。当前为每个认证客户端单独配置SecurityFilter、并将登录入口路径(/formLogin、/googleOidcLogin)绑定到安全过滤链的做法,会导致用户点击第三方登录入口时,/googleOidcLogin这个请求本身被识别为新的未认证请求,直接覆盖掉Session中之前保存的业务路径/private-url,最终认证完成后只能跳转到登录入口地址。
正确实现步骤
1. 配置统一安全过滤器覆盖业务受保护路径
移除原来分散的Form、Google OIDC独立安全过滤器,新增一个统一的SecurityFilter,只映射真正需要登录才能访问的业务路径,不要映射任何登录相关的入口路径:
<!-- 统一鉴权过滤器 --> <filter> <filter-name>AppSecurityFilter</filter-name> <filter-class>org.pac4j.jee.filter.SecurityFilter</filter-class> <init-param> <param-name>configFactory</param-name> <param-value>com.mycompany.authentication.Pac4jConfigFactory</param-value> </init-param> <!-- 配置所有支持的认证客户端,逗号分隔 --> <init-param> <param-name>clients</param-name> <param-value>FormClient,GoogleOidcClient</param-value> </init-param> <init-param> <param-name>authorizers</param-name> <param-value>isAuthenticated</param-value> </init-param> <!-- 未认证时默认跳转到表单登录页 --> <init-param> <param-name>defaultClient</param-name> <param-value>FormClient</param-value> </init-param> </filter> <filter-mapping> <filter-name>AppSecurityFilter</filter-name> <!-- 仅配置需要鉴权的业务路径,不要包含登录入口、静态资源等公开路径 --> <url-pattern>/private-url</url-pattern> <url-pattern>/private/*</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping>
2. 配置统一回调端点
所有认证方式(表单、OIDC)的认证完成回调都统一走CallbackFilter,不要给每个客户端单独分配回调逻辑:
<!-- 认证结果统一回调处理器 --> <filter> <filter-name>AuthCallbackFilter</filter-name> <filter-class>org.pac4j.jee.filter.CallbackFilter</filter-class> <init-param> <param-name>configFactory</param-name> <param-value>com.mycompany.authentication.Pac4jConfigFactory</param-value> </init-param> <!-- 无保存的原始请求时,认证完成默认跳转到首页 --> <init-param> <param-name>defaultUrl</param-name> <param-value>/index.html</param-value> </init-param> </filter> <filter-mapping> <filter-name>AuthCallbackFilter</filter-name> <url-pattern>/callback</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping>
同时在Pac4jConfigFactory中给所有客户端配置统一的回调地址为/callback,FormClient的登录页配置为公开可访问的自定义表单页面(比如/login.html,不需要加入安全过滤链)。
3. 配置第三方登录触发端点(可选)
如果需要保留独立的/googleOidcLogin这类第三方登录触发入口,给该端点单独配置SecurityFilter时,必须关闭请求保存开关,避免覆盖Session中已存的原始业务URL:
<!-- Google OIDC登录触发过滤器 --> <filter> <filter-name>GoogleLoginFilter</filter-name> <filter-class>org.pac4j.jee.filter.SecurityFilter</filter-class> <init-param> <param-name>configFactory</param-name> <param-value>com.mycompany.authentication.Pac4jConfigFactory</param-value> </init-param> <init-param> <param-name>clients</param-name> <param-value>GoogleOidcClient</param-value> </init-param> <init-param> <param-name>authorizers</param-name> <param-value>isAuthenticated</param-value> </init-param> <!-- 关键配置:访问该端点时不保存当前请求到Session,不覆盖原始业务URL --> <init-param> <param-name>saveRequestInSession</param-name> <param-value>false</param-value> </init-param> </filter> <filter-mapping> <filter-name>GoogleLoginFilter</filter-name> <url-pattern>/googleOidcLogin</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping>
如果不需要保留独立触发端点,也可以直接在表单登录页把Google登录按钮的链接设置为/callback?client_name=GoogleOidcClient,不需要额外配置过滤器,pac4j会自动识别参数调用对应客户端走认证流程,也不会覆盖已保存的原始请求。
运行逻辑验证
配置完成后整个流程会按预期执行:
- 用户未登录访问
/private-url,统一安全过滤器拦截后将该地址存入Session,跳转到表单登录页 - 用户选择账号密码登录:提交表单认证通过后,回调过滤器读取Session中保存的
/private-url完成跳转 - 用户选择Google登录:点击登录按钮触发OIDC流程时不会覆盖已存的原始地址,Google授权完成回调后,同样跳转到最开始访问的
/private-url
内容的提问来源于stack exchange,提问作者scharette

