1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.servlet.resource;
18
19 import java.io.IOException;
20 import java.net.URLDecoder;
21 import java.util.Arrays;
22 import java.util.List;
23 import javax.servlet.http.HttpServletRequest;
24
25 import org.springframework.core.io.ClassPathResource;
26 import org.springframework.core.io.Resource;
27 import org.springframework.core.io.UrlResource;
28 import org.springframework.util.StringUtils;
29 import org.springframework.web.context.support.ServletContextResource;
30
31
32
33
34
35
36
37
38
39
40
41
42
43 public class PathResourceResolver extends AbstractResourceResolver {
44
45 private Resource[] allowedLocations;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public void setAllowedLocations(Resource... locations) {
66 this.allowedLocations = locations;
67 }
68
69 public Resource[] getAllowedLocations() {
70 return this.allowedLocations;
71 }
72
73
74 @Override
75 protected Resource resolveResourceInternal(HttpServletRequest request, String requestPath,
76 List<? extends Resource> locations, ResourceResolverChain chain) {
77
78 return getResource(requestPath, locations);
79 }
80
81 @Override
82 protected String resolveUrlPathInternal(String resourcePath, List<? extends Resource> locations,
83 ResourceResolverChain chain) {
84
85 return (getResource(resourcePath, locations) != null ? resourcePath : null);
86 }
87
88 private Resource getResource(String resourcePath, List<? extends Resource> locations) {
89 for (Resource location : locations) {
90 try {
91 if (logger.isTraceEnabled()) {
92 logger.trace("Checking location: " + location);
93 }
94 Resource resource = getResource(resourcePath, location);
95 if (resource != null) {
96 if (logger.isTraceEnabled()) {
97 logger.trace("Found match: " + resource);
98 }
99 return resource;
100 }
101 else if (logger.isTraceEnabled()) {
102 logger.trace("No match for location: " + location);
103 }
104 }
105 catch (IOException ex) {
106 logger.trace("Failure checking for relative resource - trying next location", ex);
107 }
108 }
109 return null;
110 }
111
112
113
114
115
116
117
118
119
120 protected Resource getResource(String resourcePath, Resource location) throws IOException {
121 Resource resource = location.createRelative(resourcePath);
122 if (resource.exists() && resource.isReadable()) {
123 if (checkResource(resource, location)) {
124 return resource;
125 }
126 else if (logger.isTraceEnabled()) {
127 logger.trace("Resource path=\"" + resourcePath + "\" was successfully resolved " +
128 "but resource=\"" + resource.getURL() + "\" is neither under the " +
129 "current location=\"" + location.getURL() + "\" nor under any of the " +
130 "allowed locations=" + Arrays.asList(getAllowedLocations()));
131 }
132 }
133 return null;
134 }
135
136
137
138
139
140
141
142
143
144
145
146 protected boolean checkResource(Resource resource, Resource location) throws IOException {
147 if (isResourceUnderLocation(resource, location)) {
148 return true;
149 }
150 if (getAllowedLocations() != null) {
151 for (Resource current : getAllowedLocations()) {
152 if (isResourceUnderLocation(resource, current)) {
153 return true;
154 }
155 }
156 }
157 return false;
158 }
159
160 private boolean isResourceUnderLocation(Resource resource, Resource location) throws IOException {
161 if (!resource.getClass().equals(location.getClass())) {
162 return false;
163 }
164 String resourcePath;
165 String locationPath;
166 if (resource instanceof UrlResource) {
167 resourcePath = resource.getURL().toExternalForm();
168 locationPath = StringUtils.cleanPath(location.getURL().toString());
169 }
170 else if (resource instanceof ClassPathResource) {
171 resourcePath = ((ClassPathResource) resource).getPath();
172 locationPath = StringUtils.cleanPath(((ClassPathResource) location).getPath());
173 }
174 else if (resource instanceof ServletContextResource) {
175 resourcePath = ((ServletContextResource) resource).getPath();
176 locationPath = StringUtils.cleanPath(((ServletContextResource) location).getPath());
177 }
178 else {
179 resourcePath = resource.getURL().getPath();
180 locationPath = StringUtils.cleanPath(location.getURL().getPath());
181 }
182 if(locationPath.equals(resourcePath)) {
183 return true;
184 }
185 locationPath = (locationPath.endsWith("/") || locationPath.isEmpty() ? locationPath : locationPath + "/");
186 if (!resourcePath.startsWith(locationPath)) {
187 return false;
188 }
189 if (resourcePath.contains("%")) {
190
191 if (URLDecoder.decode(resourcePath, "UTF-8").contains("../")) {
192 if (logger.isTraceEnabled()) {
193 logger.trace("Resolved resource path contains \"../\" after decoding: " + resourcePath);
194 }
195 return false;
196 }
197 }
198 return true;
199 }
200
201 }