Spring Boot调用员工认证接口/employees/token返回403 Forbidden的问题求助
各位大佬好,我最近在做一个Spring Boot的REST应用,里面有访客和员工两种用户角色,他们采用不同的认证方式,但最终都会生成JWT令牌。为了区分管理这两类认证逻辑,我用XML配置了3个独立的SecurityFilterChain:一个对应员工的认证接口,一个对应访客的认证接口,第三个用来处理其他需要JWT认证的API。目前还没做自定义授权逻辑,用的是Spring Security的默认配置。
现在遇到了一个很头疼的问题:调用员工认证接口/employees/token时,总是返回403 Forbidden。我Debug后发现,认证是成功的,SecurityContextHolder里也生成了正确的Authentication对象,但请求在过滤器链的处理过程中出了问题:
请求会依次经过3个AuthorizationFilter(分别对应3个SecurityFilterChain)。奇怪的是,第一个匹配/employees/token的AuthorizationFilter已经成功完成授权了,但请求并没有终止,反而继续走到下一个对应/visitors/token的AuthorizationFilter,在这里授权直接失败,抛出AccessDeniedException,最后重定向到/error接口。
更诡异的是,我发现Tomcat默认的ApplicationFilterChain末尾也挂载了这3个AuthorizationFilter,同时它们还存在于Spring的FilterChainProxy里的每个DefaultSecurityFilterChain实例中。
下面是我的环境信息和配置细节,麻烦各位帮我分析下问题出在哪:
环境信息
- Java版本:Amazon Corretto 17
- Spring Boot版本:3.5.3
- 安全依赖:
spring-boot-starter-oauth2-resource-server
Spring Security XML配置
<!-- ####################### Spring Security Beans ####################### --> <bean id="visitorDetailsService" class="com.zum.vms.service.VisitorService" /> <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> <bean name="publicKey" class="com.zum.vms.util.SecurityUtils" factory-method="loadPublicKey"> <constructor-arg value="${vms.security.auth.jwt.key.public}" /> </bean> <bean name="privateKey" class="com.zum.vms.util.SecurityUtils" factory-method="loadPrivateKey"> <constructor-arg value="${vms.security.auth.jwt.key.private}" /> </bean> <bean name="jwtEncoder" class="com.zum.vms.util.SecurityUtils" factory-method="jwtEncoder"> <constructor-arg ref="publicKey" /> <constructor-arg ref="privateKey" /> </bean> <bean name="jwtDecoder" class="com.zum.vms.util.SecurityUtils" factory-method="jwtDecoder"> <constructor-arg ref="publicKey" /> </bean> <bean id="totpAuthenticationProvider" class="com.zum.vms.security.auth.TotpAuthenticationProvider"/> <security:authentication-manager id="employeeAuthenticationManager"> <security:authentication-provider user-service-ref="employeeService"> <security:password-encoder ref="passwordEncoder" /> </security:authentication-provider> </security:authentication-manager> <security:authentication-manager id="visitorAuthenticationManager"> <security:authentication-provider ref="totpAuthenticationProvider"/> </security:authentication-manager> <!-- to enable regex matching add request-matcher="regex" attribute --> <security:http name="employeeSecurityChain" pattern="/employees/token" authentication-manager-ref="employeeAuthenticationManager" create-session="stateless" use-expressions="true"> <!-- Run url trailing slash checks before authentication logic --> <security:custom-filter ref="trailingSlashRedirectFilter" before="BASIC_AUTH_FILTER"/> <!-- Disabling Cross site request forgery protection--> <security:csrf disabled="true" /> <!-- Require authentication for generating jwt token /employees/token --> <security:intercept-url pattern="/employees/token" access="isAuthenticated()" /> <security:http-basic /> </security:http> <security:http name="visitorSecurityChain" pattern="/visitors/token" authentication-manager-ref="visitorAuthenticationManager" create-session="stateless" use-expressions="true"> <!-- Run url trailing slash checks before authentication logic --> <security:custom-filter ref="trailingSlashRedirectFilter" before="BASIC_AUTH_FILTER"/> <!-- Disabling Cross site request forgery protection--> <security:csrf disabled="true" /> <!-- 原配置内容截断,保留已有的配置片段 -->
有没有大佬遇到过类似的问题?或者能帮我分析下为什么请求会经过所有的SecurityFilterChain,而不是匹配到第一个符合条件的就终止处理?
内容来源于stack exchange

