From e22a3b0ca73ed1325804eea910e88f985dfefb33 Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:41:28 +0800 Subject: [PATCH] fix(auth): project request context under forwarded prefix Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- .../skillhub/filter/AuthContextFilter.java | 10 ++++++- .../filter/AuthContextFilterTest.java | 28 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/AuthContextFilter.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/AuthContextFilter.java index 1c16fe89..a53ee303 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/AuthContextFilter.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/AuthContextFilter.java @@ -59,7 +59,7 @@ public class AuthContextFilter extends OncePerRequestFilter { HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - if (!routeSecurityPolicyRegistry.shouldProjectRequestContext(request.getRequestURI())) { + if (!routeSecurityPolicyRegistry.shouldProjectRequestContext(requestPath(request))) { filterChain.doFilter(request, response); return; } @@ -88,6 +88,14 @@ public class AuthContextFilter extends OncePerRequestFilter { filterChain.doFilter(request, response); } + private String requestPath(HttpServletRequest request) { + String servletPath = request.getServletPath(); + if (servletPath != null && !servletPath.isBlank()) { + return servletPath; + } + return request.getRequestURI(); + } + private boolean isInactiveUser(String userId) { if (!enforceActiveUserCheck) { return false; diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/filter/AuthContextFilterTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/filter/AuthContextFilterTest.java index f3d2d117..c796bab6 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/filter/AuthContextFilterTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/filter/AuthContextFilterTest.java @@ -120,6 +120,34 @@ class AuthContextFilterTest { verify(filterChain).doFilter(request, response); } + @Test + void forwardedPrefixApiRequest_shouldUseServletPathForContextProjection() throws Exception { + PlatformPrincipal principal = new PlatformPrincipal("user-3", "Cara", "cara@example.com", null, "local", Set.of("USER")); + UserAccount user = new UserAccount("user-3", "Cara", "cara@example.com", null); + user.setStatus(UserStatus.ACTIVE); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContextPath("/skillhub"); + request.setRequestURI("/skillhub/api/web/me/namespaces"); + request.setServletPath("/api/web/me/namespaces"); + request.getSession(true).setAttribute("platformPrincipal", principal); + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken(principal, null, List.of()) + ); + + MockHttpServletResponse response = new MockHttpServletResponse(); + FilterChain filterChain = mock(FilterChain.class); + + when(userAccountRepository.findById("user-3")).thenReturn(java.util.Optional.of(user)); + when(namespaceMemberRepository.findByUserId("user-3")).thenReturn(List.of()); + + filter.doFilter(request, response, filterChain); + + assertEquals("user-3", request.getAttribute("userId")); + assertTrue(((java.util.Map) request.getAttribute("userNsRoles")).isEmpty()); + verify(filterChain).doFilter(request, response); + } + @Test void anonymousRequest_shouldPassThroughWithoutLoadingUserContext() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest();