在 Spring Boot 3 和最新的 Spring Security 中访问返回 StreamingResponseBody 的 REST API 时出现 AccessDeniedException?

问题描述 投票:0回答:1

我创建了这个 api,用于流式传输 mp3 音频文件作为响应。

@GetMapping (value = "/api/user/stream/{songId}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public ResponseEntity<StreamingResponseBody> playSong(@PathVariable Long songId) throws IOException {

        File file = new File(projectPath + "\\assets\\audio\\DummySong" + ".mp3");
        FileInputStream in = new FileInputStream(file);

        StreamingResponseBody songStream = out -> {
            try {
                Thread.sleep(10);
                IOUtils.copy(in, out);
            } 
            catch (InterruptedException e) {
                LOGGER.error("Streaming Thread Interrupted - {}", e.getMessage());
            }
            
        };

        return ResponseEntity.ok()
                    .header(HttpHeaders.ACCEPT_RANGES, "128")
                    .header(HttpHeaders.CONTENT_TYPE, "audio/mpeg")
                    .contentLength(file.length())
                    .body(songStream);        
    }

Spring 安全配置

@Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {

        http.csrf(csrf -> csrf.disable());
        http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        http.authorizeHttpRequests((authorizeHttpRequests) ->
                authorizeHttpRequests
                    .requestMatchers("/auth/signin", "/auth/signup", "/docs/**").permitAll()
                    .requestMatchers("/api/admin/**").hasRole("ADMIN")
                    .requestMatchers("/api/artist/**").hasAnyRole("ARTIST", "ADMIN")
                    .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
            .anyRequest().authenticated()
        );

        http.exceptionHandling((exceptionHandling) ->
            exceptionHandling.authenticationEntryPoint((req, resp, e) -> {
                LOGGER.error("Error during auth: {}", e.getMessage());
                resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
            }));

        http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

一旦我使用 Postman 点击此 api,响应就会正确,并且歌曲也会按预期播放,但在服务器终端中我收到以下错误。

org.springframework.security.access.AccessDeniedException: Access Denied
        at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:98) ~[spring-security-web-6.1.2.jar:6.1.2]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.2.jar:6.1.2]
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) ~[spring-security-web-6.1.2.jar:6.1.2]
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) ~[spring-security-web-6.1.2.jar:6.1.2]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.2.jar:6.1.2]
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:91) ~[spring-security-web-6.1.2.jar:6.1.2]
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) ~[spring-security-web-6.1.2.jar:6.1.2]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.2.jar:6.1.2]
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) ~[spring-security-web-6.1.2.jar:6.1.2]

还有 作为根本原因

2023-08-17T18:35:48.233+05:30 ERROR 10528 --- [0.0-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with pat
h [] threw exception [Unable to handle the Spring Security Exception because the response is already committed.] with root cause.

请帮助我找出问题所在以及如何解决此问题。

mp3 access-denied spring-boot-3 streamingresponsebody spring-security-6
1个回答
0
投票

我已经解决了这个问题。下面的帖子中有详细的解决方案。 Spring Security 6 问题

我们必须在应用程序配置中创建一个

RequestAttributeSecurityContextRepository
的 bean。

@Bean
public RequestAttributeSecurityContextRepository getRequestAttributeSecurityContextRepository() {
    return new RequestAttributeSecurityContextRepository();
}

现在,我们必须在

JwtTokenFilter
类中设置上下文,如下所示。

class JwtTokenFilter extends OncePerRequestFilter {
    ...
    ...

    @Autowired
    private RequestAttributeSecurityContextRepository repo;

    public void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) {
        ....
        ....
        SecurityContext context = SecurityContextHolder.getContext();
        repo.setContext(context, req, res);
        filterChain.doFilter(req, res);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.