【问题标题】:spring-boot Error: Exceeded maxRedirects. Probably stuck in a redirect loopspring-boot 错误:超过最大重定向。可能陷入重定向循环
【发布时间】:2020-05-31 20:58:48
【问题描述】:

我正在尝试在 Spring Boot 中执行 JWT 身份验证,并且请求卡在重定向循环中。

JWTAuthenticationProvider

@Component
public class JwtAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public boolean supports(Class<?> authentication) {
        return (JwtAuthenticationToken.class.isAssignableFrom(authentication));
    }

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails,
            UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    }

    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
        String token = jwtAuthenticationToken.getToken();

        JwtParsedUser parsedUser = jwtUtil.parseToken(token);

        if (parsedUser == null) {
            throw new JwtException("JWT token is not valid");
        }
        UserDetails user = User.withUsername(parsedUser.getUserName()).password("temp_password").authorities(parsedUser.getRole()).build();
        return user;
    }

JwtAuthenticationFilter

public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super("/**");
        this.setAuthenticationManager(authenticationManager);
    }

    @Override
    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
        return true;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        String header = request.getHeader("Authorization");

        if (header == null || !header.startsWith("Bearer ")) {
            throw new JwtException("No JWT token found in request headers");
        }

        String authToken = header.substring(7);

        JwtAuthenticationToken authRequest = new JwtAuthenticationToken(authToken);

        return getAuthenticationManager().authenticate(authRequest);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request,

            HttpServletResponse response, FilterChain chain, Authentication authResult)
            throws IOException, ServletException {
        super.successfulAuthentication(request, response, chain, authResult);

        chain.doFilter(request, response);
    }

}

安全配置

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationProvider jwtAuthenticationProvider;

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(jwtAuthenticationProvider);

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable().authorizeRequests().antMatchers("/secured-resource-1/**", "/secured-resource-2/**")
                .hasRole("ADMIN").antMatchers("/secured-resource-2/**").hasRole("ADMIN").and().formLogin()
                .successHandler(new AuthenticationSuccessHandler()).and().httpBasic().and().exceptionHandling()
                .accessDeniedHandler(new CustomAccessDeniedHandler()).authenticationEntryPoint(getBasicAuthEntryPoint())
                .and()
                .addFilterBefore(new JwtAuthenticationFilter(authenticationManager()),
                        FilterSecurityInterceptor.class)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Bean
    public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint() {
        return new CustomBasicAuthenticationEntryPoint();
    }
}

主控制器

@RestController
public class MainController {
    @Autowired
    private JwtUtil jwtUtil;

    @GetMapping("/secured-resource-1")
    public String securedResource1() {
        return "Secured resource1";
    }
}

当我使用有效的 JWT 令牌访问端点时,代码进入从过滤器到提供程序类的循环并以错误结束:

Exceeded maxRedirects. Probably stuck in a redirect loop http://localhost:8000/ error. 

调试日志显示以下错误:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: Cannot call sendError() after the response has been committed] with root cause

java.lang.IllegalStateException: Cannot call sendError() after the response has been committed

任何建议我在这里缺少什么。 提前致谢。

【问题讨论】:

    标签: spring spring-boot spring-security jwt-auth


    【解决方案1】:

    我相信这是因为你实际上没有为 bean JwtAuthenticationFilter 设置 AuthenticationSuccessHandler,因为它实际上并没有设置它会继续循环 super 和 chain 以及稍后需要错误时由于响应已经写入super() chain.doFilter 将失败,因为一旦写入响应就不能再次写入,因此会出现错误call sendError() after the response has been committed

    在设置此之前在您的 SecurityConfiguration 中更正此问题

    .addFilterBefore(new JwtAuthenticationFilter(authenticationManager()),
                            FilterSecurityInterceptor.class)
    

    实例化过滤器并像这样设置它的成功管理器

    JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager()),FilterSecurityInterceptor.class);
    
    jwtAuthenticationFilter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
    

    现在使用上面的变量来设置过滤器。 这是一个很好的参考项目:https://gitlab.com/palmapps/jwt-spring-security-demo/-/tree/master/

    【讨论】:

    • 谢谢斯里尼瓦斯。你解释得真好。这解决了问题。也感谢您的参考项目。
    【解决方案2】:

    我用另一种方法解决了这个问题。 在 JwtAuthenticationFilter 类中,我们需要在上下文中设置身份验证对象并调用 chain.doFilter。由于我们已经覆盖了实现,因此可以跳过调用 super.successfulAuthentication。

     @Override
       protected void successfulAuthentication(HttpServletRequest request,
    
       HttpServletResponse response, FilterChain chain, Authentication authResult)
                    throws IOException, ServletException {
                //super.successfulAuthentication(request, response, chain, authResult);
                SecurityContextHolder.getContext().setAuthentication(authResult);
                chain.doFilter(request, response);
            }
    
        public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
            super("/**");
            this.setAuthenticationManager(authenticationManager);
            //this.setAuthenticationSuccessHandler(new JwtAuthenticationSuccessHandler());
        }
    

    【讨论】:

      猜你喜欢
      • 2021-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-03
      • 1970-01-01
      • 2015-07-09
      相关资源
      最近更新 更多