404 for js files when using spring boot with vaadin
I have a problem when using spring security in a vaadin project with spring-boot. So I am using PdfViewer Addon to display PDF files. But I am getting the following error:
error:"Not Found"
message:"No message available"
path:"/APP/PUBLISHED/pdf.worker.js"
status:404
and my spring security config looks like this:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.defaultsDisabled()
.frameOptions().sameOrigin().and()
.csrf().disable() // Use Vaadin CSRF protection
.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/vaadinServlet/HEARTBEAT/**").permitAll()
.antMatchers("/vaadinServlet/UIDL/**").permitAll()
.antMatchers("/vaadinServlet/APP/PUBLISHED/**").permitAll()
.antMatchers("login?debug").permitAll()
.antMatchers("/#!pwdreset/*").permitAll()
.antMatchers("/pwdreset/*").permitAll()
.and()
.authorizeRequests()
.and()
.formLogin().loginPage("/#!login").permitAll()
.and()
.logout().logoutUrl("/#!login?logout").logoutSuccessUrl("/").permitAll().and()
.sessionManagement().sessionFixation().newSession();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/VAADIN/**");
}
So, while checking the downloaded files in Chrome, I see the / vaadinServlet / APP / PUBLISHED / folder , and there are all the required files.
Using Addon without spring Security works great, so any idea?
Update
It looks like it has nothing to do with spring security because I get similar behavior when testing the Addon on a new simple project. Seems to be a problem with spring boot.
To reproduce this issue you need ( complete project to download ):
- basic spring boot + vaadin app skeleton
- simple PDF and under
/webapp/files
- add-on PdfViewer in your computer and widgets compiled
- below simple user interface
@Theme("mytheme")
@SpringUI
@Widgetset("org.test.AppWidgetSet")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
String basepath = VaadinService.getCurrent().getBaseDirectory().getAbsolutePath();
File file = new File(basepath.concat("/files/test.pdf"));
if (file.exists()) {
PdfViewer pdfViewer = new PdfViewer(file);
Label info = new Label("File was found!");
layout.addComponents(info, pdfViewer);
} else {
Label info = new Label("no file found!");
layout.addComponent(info);
}
setContent(layout);
}
}
- Open chrome and developer tools tabbed
Network
, select an app and you will see one prompt forpdf.worker.js
.
source to share
TL; DR; version:
The second request is fired pdf.js
to download pdf.worker.js
, but it doesn't match the path /vaadinServlet/APP/PUBLISHED/pdf.worker.js
that is handled by the auto-configured one VaadinServlet
, it's easy /APP/PUBLISHED/pdf.worker.js
.
The simplest solution I could come up with is a controller that redirects the request VaadinServlet
:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class PdfJsRedirectController {
private static final String WORKER_JS_INCORRECT_PATH = "/APP/PUBLISHED/pdf.worker.js";
private static final String WORKER_JS_CORRECT_FORWARD_PATH = "forward:/vaadinServlet/APP/PUBLISHED/pdf.worker.js";
@RequestMapping(value = WORKER_JS_INCORRECT_PATH)
public String forwardWorkerJsRequestToVaadin() {
return WORKER_JS_CORRECT_FORWARD_PATH;
}
}
Detailed version:
So, I spent some time debugging and it turned out to be a combination of bad configurations:
- spring listen for dispatcher servlets
- vaadin spring listening servlets
- a pdf.js bugfix / woraround
What happens is spring logs requests DispatcherServlet
to serve /*
. and Vaadin registers a VaadinSpringServlet
for paths /vaadinServlet/*
and /VAADIN
:
ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
ServletRegistrationBean : Mapping servlet: 'springVaadinServlet' to [/vaadinServlet/*, /VAADIN/*]
To coexist with the dispatcher servlet and be able to serve requests to "/ *" as well, Vaadin also registers a forwarder for paths UI
. For example, requests for /MyUI
will be redirected to /vaadinServlet/MyUI
(see VaadinServletConfiguration
sources for details ).
Everything is working fine so far and you shouldn't have any problems. Just like you said looking at the hrome dev-tools all the files js
are there, so what's wrong? If you look at the requests made while accessing your application, you will notice that there are actually 2 requests for pdf.worker.js
- the first one successful, and the one that gives you a 404:
The column Initiator
for the call that fails, in fact pdf.js:2344
, and if you set a breakpoint, you can see that workerSrc=/APP/PUBLISHED/pdf.worker.js
, the value that is defined in pl.pdfviewer.client.ui.PdfViewer.java
. You may have to scroll horizontally a little to see the code, so I formatted it below:
public native void loadResourcePdf(String fileName, VPdfViewer instance)/*-{
var pdfviewer = instance.@pl.pdfviewer.client.ui.VPdfViewer::jsObject;
pdfviewer.work = false;
if ((pdfviewer.fileName == null || pdfviewer.fileName != fileName) && fileName != null) {
$wnd.PDFJS.disableStream = true;
======> $wnd.PDFJS.workerSrc = 'APP/PUBLISHED/pdf.worker.js';
$wnd.PDFJS.getDocument(fileName).then(function (pdf) {
pdfviewer.pdfFile = pdf;
pdfviewer.fileName = fileName;
pdfviewer.pageCount = pdf.numPages;
if (pdfviewer.pageNumber == 0 && pdf.numPages > 0) {
pdfviewer.pageNumber = 1;
}
pdfviewer.showPdfPage(pdfviewer.pageNumber);
});
}
}-*/;
In a normal environment Vaadin /APP/PUBLISHED/pdf.worker.js
will work out of the box, but now we have changed a little so we need some tweaks. At the bottom, we can take a similar approach to what Vaadin's auto-setup does and redirect the request /APP/PUBLISHED/pdf.worker.js
to /vaadinServlet/APP/PUBLISHED/pdf.worker.js
and finally overcome this issue. For brevity, the redirect controller can be seen at the beginning of this post.
source to share