OAuth2登录后调用接口返回403无权限问题求助
问题描述
使用Spring Security 5实现登录功能后,登录成功调用其他接口始终返回403 Unauthorized。自定义的TokenAuthenticationFilter的doFilterInternal方法仅在登录请求时触发,后续请求不会执行该过滤器逻辑;后续请求会进入org.springframework.security.web.session.SessionManagementFilter#doFilter,但此时安全上下文、认证信息及会话均不存在。
相关配置代码
SecurityConfig.java
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity( securedEnabled = true, jsr250Enabled = true, prePostEnabled = true ) public class SecurityConfig { @Autowired private CustomUserDetailsService customUserDetailsService; @Autowired private CustomOAuth2UserService customOAuth2UserService; @Autowired private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler; @Autowired private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler; @Autowired private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository; @Bean public TokenAuthenticationFilter tokenAuthenticationFilter() { return new TokenAuthenticationFilter(); } /* 默认Spring OAuth2使用HttpSessionOAuth2AuthorizationRequestRepository保存授权请求, 但因为服务是无状态的,不能存在会话中,所以改用Base64编码的Cookie存储 */ @Bean public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() { return new HttpCookieOAuth2AuthorizationRequestRepository(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager( AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .cors() .and() .csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests() .requestMatchers("/auth/login").permitAll() .anyRequest().authenticated(); return http.build(); } }
HttpCookieOAuth2AuthorizationRequestRepository.java
@Component public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> { public static final String OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME = "oauth2_auth_request"; public static final String REDIRECT_URI_PARAM_COOKIE_NAME = "redirect_uri"; private static final int cookieExpireSeconds = 180; @Override public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) { return CookieUtils.getCookie(request, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME) .map(cookie -> CookieUtils.deserialize(cookie, OAuth2AuthorizationRequest.class)) .orElse(null); } @Override public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request, HttpServletResponse response) { if (authorizationRequest == null) { CookieUtils.deleteCookie(request, response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME); CookieUtils.deleteCookie(request, response, REDIRECT_URI_PARAM_COOKIE_NAME); return; } CookieUtils.addCookie(response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME, CookieUtils.serialize(authorizationRequest), cookieExpireSeconds); String redirectUriAfterLogin = request.getParameter(REDIRECT_URI_PARAM_COOKIE_NAME); if (StringUtils.isNotBlank(redirectUriAfterLogin)) { CookieUtils.addCookie(response, REDIRECT_URI_PARAM_COOKIE_NAME, redirectUriAfterLogin, cookieExpireSeconds); } } @Override public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request, HttpServletResponse response) { return this.loadAuthorizationRequest(request); } // @Override // public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request) { // return this.loadAuthorizationRequest(request); // } public void removeAuthorizationRequestCookies(HttpServletRequest request, HttpServletResponse response) { CookieUtils.deleteCookie(request, response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME); CookieUtils.deleteCookie(request, response, REDIRECT_URI_PARAM_COOKIE_NAME); } }
问题原因
- 自定义Token过滤器未加入过滤器链:当前
SecurityFilterChain配置仅定义了授权规则,但未将TokenAuthenticationFilter添加到Spring Security的过滤器链中。导致登录后的请求无法被token校验逻辑处理,无法构建安全上下文。 - 无状态会话配置要求:因为设置了
SessionCreationPolicy.STATELESS,服务不会通过会话保存认证状态,必须依赖TokenAuthenticationFilter在每次请求时校验token并注入认证信息,否则请求会因无认证信息返回403。
解决方案
修改SecurityConfig中的filterChain方法,将自定义的TokenAuthenticationFilter添加到过滤器链中,通常放在UsernamePasswordAuthenticationFilter之前,确保token校验在用户名密码认证逻辑之后、其他授权逻辑之前执行。
修改后的filterChain代码:
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .cors() .and() .csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests() .requestMatchers("/auth/login").permitAll() .anyRequest().authenticated() .and() // 添加自定义Token过滤器,放在UsernamePasswordAuthenticationFilter之前 .addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); }
额外检查点
- 确保
TokenAuthenticationFilter继承自OncePerRequestFilter,保证每个请求都会触发一次过滤器逻辑。 - 确认
TokenAuthenticationFilter的doFilterInternal方法正确实现了token提取、校验及认证信息注入逻辑,示例如下:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 从请求头或Cookie中获取token,示例从Authorization头获取Bearer token String authorizationHeader = request.getHeader("Authorization"); String token = null; if (StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith("Bearer ")) { token = authorizationHeader.substring(7); } // 校验token并设置安全上下文 if (StringUtils.hasText(token) && tokenValidator.validateToken(token)) { Authentication authentication = tokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); } filterChain.doFilter(request, response); }
- 登录成功后需正确返回token,后续请求需携带该token(如放在
Authorization: Bearer <token>请求头中)。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

