spring-security 如何使用Spring在auth重定向响应中添加一个头?

x8goxv8g  于 2022-11-11  发布在  Spring
关注(0)|答案(2)|浏览(223)

对于Sping Boot 与htmx的集成,如果传入的请求是由htmx完成的,并且用户不再登录,我需要一种方法来添加一个标头。
在正常的流程中,用户被重定向到登录页面。但是,当有一个由htmx完成的请求时,这是一个 AJAX 请求,重定向不会发生。
建议的解决方案是,如果请求中有HX-Request标头,服务器应该在响应中放置一个HX-Refresh: true标头,这将使htmx进行一次完整的页面刷新。
我的安全配置如下所示:

@Configuration
public class WebSecurityConfiguration {
    private final ClientRegistrationRepository clientRegistrationRepository;

    public WebSecurityConfiguration(ClientRegistrationRepository clientRegistrationRepository) {
        this.clientRegistrationRepository = clientRegistrationRepository;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests(registry -> {
            registry.mvcMatchers("/actuator/info", "/actuator/health").permitAll();
            registry.mvcMatchers("/**").hasAuthority(Roles.ADMIN);
            registry.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll();
            registry.anyRequest().authenticated();
        });
        http.oauth2Client();
        http.oauth2Login();
        http.logout(logout -> logout.logoutSuccessHandler(oidcLogoutSuccessHandler()));

        return http.build();
    }

    private LogoutSuccessHandler oidcLogoutSuccessHandler() {
        OidcClientInitiatedLogoutSuccessHandler logoutSuccessHandler = new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository);

        // Sets the location that the End-User's User Agent will be redirected to
        // after the logout has been performed at the Provider
        logoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}");

        return logoutSuccessHandler;
    }
}

我尝试使用筛选器:

public Filter htmxFilter() {
        return new Filter() {
            @Override
            public void doFilter(ServletRequest servletRequest,
                                 ServletResponse servletResponse,
                                 FilterChain filterChain) throws IOException, ServletException {
                HttpServletRequest request = (HttpServletRequest) servletRequest;
                HttpServletResponse response = (HttpServletResponse) servletResponse;

                filterChain.doFilter(servletRequest, servletResponse);
                String htmxRequestHeader = request.getHeader("HX-Request");
                System.out.println("htmxRequestHeader = " + htmxRequestHeader);
                System.out.println(response.getStatus());
                if (htmxRequestHeader != null
                        && response.getStatus() == 302) {
                    System.out.println("XXXXXXXXXXX");
                    response.setHeader("HX-Refresh", "true");
                }
            }
        };
    }

但是response.getStatus()从来都不是302(尽管我可以在Chrome中看到302响应状态)。
我也试过拦截器:

@Bean
    public HandlerInterceptor htmxHandlerInterceptor() {
        return new HandlerInterceptor() {

            @Override
            public void postHandle(HttpServletRequest request,
                                   HttpServletResponse response,
                                   Object handler,
                                   ModelAndView modelAndView) throws Exception {
                boolean htmxRequest = request.getHeader("HX-Request") != null;
                String htmxRequestHeader = request.getHeader("HX-Request");
                System.out.println("htmxRequestHeader = " + htmxRequestHeader);
                System.out.println(response.getStatus());

                if( htmxRequest && response.getStatus() == 302) {
                    response.setHeader("HX-Refresh", "true");
                }
            }
        };
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeInterceptor());
        registry.addInterceptor(htmxHandlerInterceptor());//.order(Ordered.HIGHEST_PRECEDENCE);
    }

其中也不起作用,没有302响应状态。
我也尝试了注解掉的order(Ordered.HIGHEST_PRECEDENCE),但这并没有任何区别。
还有其他选择吗?

vsaztqbk

vsaztqbk1#

当一个请求到达一个受保护的端点并且它没有被验证时,SpringSecurity执行它的AuthenticationEntryPoints接口来开始一个验证方案。
您可以建立自己的AuthenticationEntryPoint,将信头和委派加入到LoginUrlAuthenticationEntryPoint(或您正在使用的其他实作)。

@Bean
SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
    http
       //...
       .exceptionHandling(exception -> exception
           .authenticationEntryPoint(new HxRefreshHeaderAuthenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")))
       );
    return http.build();
}

public class HxRefreshHeaderAuthenticationEntryPoint implements AuthenticationEntryPoint {

    private final AuthenticationEntryPoint delegate;

    public HxRefreshHeaderAuthenticationEntryPoint(AuthenticationEntryPoint delegate) {
        this.delegate = delegate;
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {

        // Add the header

        this.delegate.commence(request, response, authException);
    }
}
noj0wjuj

noj0wjuj2#

您需要确保您的过滤器在任何Spring Security过滤器之前运行。请参见SecurityProperties.DEFAULT_FILTER_ORDER或HttpSecurity#addFilterBefore

相关问题