Spring 4.1.2 (4.0.8 and 3.2.12) contains a Security Bugfix SPR-12354 that prevents the ResourceHttpRequestHandler
(the thing behind <mvc:resources>
) to load files from outside the the resource folder.
On the other hand: is JRebel (I use it with its default configuration). And it seams that JRebel do some magic to load the resources not from the wtp folder but directly form the "source" folder.
So after upgrading from Spring 3.2.11 to 3.2.12 (and an other similar Application from 4.0.7 to 4.0.8) Springs ResourceHttpRequestHandler
does not longer deliver the resource files that are "maintained" by JRebel. Instead is delivers a 404. The reason is that Spring compare the absolute file path of the configured resource folder with the absolute file path of the file that is going to be delivered. If the ResourceHttpRequestHandler
perceived that the file is outside of configured resource folder, then it assume that the url that was used to select the file is malicious. Therefore the ResourceHttpRequestHandler
and response with a 404 resource not found.
I expect that JRebel can been configured not to "maintain" js, png and css files, but I don't know how. And this is the question: How to configure JRebel that a Spring MVC Application (v 4.0.8) still deliver Resources with ResourceHttpRequestHandler
?
(I expect that almost every JRebel User is facing this problem after upgrading to Spring 4.1.2, 4.0.8 or 3.2.12).
(don't get me wrong, this is NOT a question how to manipulate Spring not to check that the files are outside of the configures resource folder. I have had a look at the source code and the observed behaviour is the behaviour that is intended by the authors of the Bug fix. - This question is about configuring JRebel)
Thank you for very good problem description!
Looks like this Spring change introduced the incompatibility into the JRebel. I am from JRebel team and will make sure this will be fixed!
As a workaround you can use <exclude>
tag in your rebel.xml <web>
element to tell JRebel not to touch these specific files. Here is more info of how to configure it http://manuals.zeroturnaround.com/jrebel/standalone/config.html#exclude
Other easier workaround is just removing the <web>
element altogether.
Based on smartman's anwser I configured rebel.xml as follows:
<classpath>
<dir name="${rebel.workspace.path}/myproject/target/classes">
</dir>
<dir name="${rebel.workspace.path}/myproject/target/test-classes">
</dir>
</classpath>
<web>
<link target="/">
<dir name="${rebel.workspace.path}/myproject/src/main/webapp">
<exclude name="resources/**"/>
</dir>
</link>
</web>
This will exclude all resources, but at least it will update the views (jsp- and tag-files). But when I do lots of frontend development, where I need to change controller, views and styles simultaniously, this configuration is useless and I have to turn off jrebel all together.
The problem is fixed in JRebel (I can not reproduce it with the current JRebel 6.1.1) - I guess it is fixed since 6.0.2 (23rd December 2014)
Fixed an issue where Spring ResourceHttpRequestHandler could not serve resources outside of webroot.
(JRebel Changelog https://zeroturnaround.com/software/jrebel/download/changelog/6-x/)
For the one that are interested how they solved it:
I can only guess because it is strange.
Spring 4.1.6 (that is the version I used for the test) has the class org.springframework.web.servlet.resource.PathResourceResolver
has the method checkResource(Resource resource, Resource location)
:
protected boolean checkResource(Resource resource, Resource location) throws IOException {
if (isResourceUnderLocation(resource, location)) {
return true;
}
if (getAllowedLocations() != null) {
for (Resource current : getAllowedLocations()) {
if (isResourceUnderLocation(resource, current)) {
return true;
}
}
}
return false;
}
The first if
: isResourceUnderLocation...
is the method that check whether or not the request is accessing a resource outside the configured resource folder
isResourceUnderLocation(Resource resource, Resource location) {
...
resourcePath = ((ServletContextResource) resource).getPath();
locationPath = StringUtils.cleanPath(((ServletContextResource) location).getPath());
...
if (!resourcePath.startsWith(locationPath)) {
return false;
}
...
}
When I use the debugger to check what is going on, while JRebel is active, then something strange happend: when the JVM hit the line if (isResourceUnderLocation(resource, location)) {
, then the method isResourceUnderLocation
gets not invoked!
So I came to the conclusion that JRebel does some bytecode manipulation to prevent that the check (and the whole isResourceUnderLocation
method) gets executed.
来源:https://stackoverflow.com/questions/26896900/resource-not-found-after-spring-4-1-2-update-when-deploy-with-jrebel-6-0-0