我有一个简单的StreamResource示例,其中单击下载锚点时,SpringSecurityContext神秘地消失了.基本上,当单击下载锚点时,将调用createInputStream方法来创建下载文件,但是执行此方法时,SecurityContext为null.下面是重现该问题的简化示例.
@H_403_6@public class HomeView extends VerticalLayout { public HomeView() { Anchor anchor = new Anchor(); anchor.add("DOWNLOAD"); anchor.setHref(new StreamResource("file",() -> createInputStream())); add(anchor); // SecurityContext returns correct value and is not null. System.err.println(SecurityContextHolder.getContext()); System.err.println("Thread name in constructor : " + Thread.currentThread().getName()); } private InputStream createInputStream() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { outputStream.write("text".getBytes()); } catch (IOException e) { e.printStackTrace(); } // SecurityContextHolder.getContext() returns null here. Why? System.err.println(SecurityContextHolder.getContext()); System.err.println("Thread name in createInputStream() : " + Thread.currentThread().getName()); return new ByteArrayInputStream(outputStream.toByteArray()); }}
执行此代码后,我得到以下消息.
@H_403_6@org.springframework.security.core.context.SecurityContextImpl@db426455: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@db426455: Principal: org.springframework.security.core.userdetails.User@983d0d8b...Rest omitted Thread name in constructor : http-nio-8080-exec-4 org.springframework.security.core.context.SecurityContextImpl@ffffffff: Null authentication Thread name in createInputStream() : http-nio-8080-exec-9
但是我发现解决此问题的一种方法是在createInputStream方法中手动设置SecurityContext.下面举一个例子.
@H_403_6@public class HomeView extends VerticalLayout { SecurityContext context; public HomeView() { Anchor anchor = new Anchor(); anchor.add("DOWNLOAD"); anchor.setHref(new StreamResource("file",() -> createInputStream())); add(anchor); // Save Context to a variable context = SecurityContextHolder.getContext(); System.err.println(SecurityContextHolder.getContext()); } private InputStream createInputStream() { // Set SecurityContext before accessing it. SecurityContextHolder.setContext(context); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { outputStream.write("text".getBytes()); } catch (IOException e) { e.printStackTrace(); } // SecurityContextHolder.getContext() no longer returns null. System.err.println(SecurityContextHolder.getContext()); return new ByteArrayInputStream(outputStream.toByteArray()); }}
最后我得到了这个问题.为什么在第一个示例中丢失了Spring SecurityContext,是否有更好的方法来解决此问题,或者我坚持第二个示例?
顺带一提,我意识到Vaadin的Upload组件也有同样的问题. SecurityContext在addSucceededListener回调方法中丢失.
我正在使用Vaadin 13.0.1和Spring Boot 2.1.3.
@H_403_6@@Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers( // Vaadin Flow static resources "/VAADIN/**",// This was the problematic spot //Rest of configuration omitted for simplicity }
问题是通过StreamResource或Upload Component动态创建的文件被映射到具有以下前缀/ VAADIN / dynamic / resource / **的URL.在上面的配置中,我们用/ VAADIN / **告诉Spring Security忽略所有以/ VAADIN /开头的请求.这导致Spring Security忽略所有指向动态创建的资源的HttpServletRequest,因为Vaadin使用/ VAADIN / dynamic / resource / ** url前缀映射它们.当Spring Security忽略HttpServletRequest时,SpringSecurityContext将为空.请参阅WebSecurity.ignoring()文档.
可以通过将/ VAADIN / **重命名为/ VAADIN / static / **来解决此问题.这将防止Spring Security忽略对动态资源的请求,因此SpringSecurityContext将在StreamResource和Upload回调方法中可用.下面是一个工作示例.
@H_403_6@@Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers( // Vaadin Flow static resources "/VAADIN/static/**",//Rest of configuration omitted for simplicity }