Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
WebUtils |
|
| 3.1818181818181817;3.182 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | package org.apache.shiro.web.util; | |
20 | ||
21 | import org.apache.shiro.SecurityUtils; | |
22 | import org.apache.shiro.session.Session; | |
23 | import org.apache.shiro.subject.Subject; | |
24 | import org.apache.shiro.subject.support.DefaultSubjectContext; | |
25 | import org.apache.shiro.util.StringUtils; | |
26 | import org.apache.shiro.web.env.EnvironmentLoader; | |
27 | import org.apache.shiro.web.env.WebEnvironment; | |
28 | import org.apache.shiro.web.filter.AccessControlFilter; | |
29 | import org.slf4j.Logger; | |
30 | import org.slf4j.LoggerFactory; | |
31 | ||
32 | import javax.servlet.ServletContext; | |
33 | import javax.servlet.ServletRequest; | |
34 | import javax.servlet.ServletResponse; | |
35 | import javax.servlet.http.HttpServletRequest; | |
36 | import javax.servlet.http.HttpServletResponse; | |
37 | import java.io.IOException; | |
38 | import java.io.UnsupportedEncodingException; | |
39 | import java.net.URLDecoder; | |
40 | import java.util.Map; | |
41 | ||
42 | /** | |
43 | * Simple utility class for operations used across multiple class hierarchies in the web framework code. | |
44 | * <p/> | |
45 | * Some methods in this class were copied from the Spring Framework so we didn't have to re-invent the wheel, | |
46 | * and in these cases, we have retained all license, copyright and author information. | |
47 | * | |
48 | * @since 0.9 | |
49 | */ | |
50 | 0 | public class WebUtils { |
51 | ||
52 | //TODO - complete JavaDoc | |
53 | ||
54 | 1 | private static final Logger log = LoggerFactory.getLogger(WebUtils.class); |
55 | ||
56 | 1 | public static final String SERVLET_REQUEST_KEY = ServletRequest.class.getName() + "_SHIRO_THREAD_CONTEXT_KEY"; |
57 | 1 | public static final String SERVLET_RESPONSE_KEY = ServletResponse.class.getName() + "_SHIRO_THREAD_CONTEXT_KEY"; |
58 | ||
59 | /** | |
60 | * {@link org.apache.shiro.session.Session Session} key used to save a request and later restore it, for example when redirecting to a | |
61 | * requested page after login, equal to {@code shiroSavedRequest}. | |
62 | */ | |
63 | public static final String SAVED_REQUEST_KEY = "shiroSavedRequest"; | |
64 | ||
65 | /** | |
66 | * Standard Servlet 2.3+ spec request attributes for include URI and paths. | |
67 | * <p>If included via a RequestDispatcher, the current resource will see the | |
68 | * originating request. Its own URI and paths are exposed as request attributes. | |
69 | */ | |
70 | public static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri"; | |
71 | public static final String INCLUDE_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.include.context_path"; | |
72 | public static final String INCLUDE_SERVLET_PATH_ATTRIBUTE = "javax.servlet.include.servlet_path"; | |
73 | public static final String INCLUDE_PATH_INFO_ATTRIBUTE = "javax.servlet.include.path_info"; | |
74 | public static final String INCLUDE_QUERY_STRING_ATTRIBUTE = "javax.servlet.include.query_string"; | |
75 | ||
76 | /** | |
77 | * Standard Servlet 2.4+ spec request attributes for forward URI and paths. | |
78 | * <p>If forwarded to via a RequestDispatcher, the current resource will see its | |
79 | * own URI and paths. The originating URI and paths are exposed as request attributes. | |
80 | */ | |
81 | public static final String FORWARD_REQUEST_URI_ATTRIBUTE = "javax.servlet.forward.request_uri"; | |
82 | public static final String FORWARD_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.forward.context_path"; | |
83 | public static final String FORWARD_SERVLET_PATH_ATTRIBUTE = "javax.servlet.forward.servlet_path"; | |
84 | public static final String FORWARD_PATH_INFO_ATTRIBUTE = "javax.servlet.forward.path_info"; | |
85 | public static final String FORWARD_QUERY_STRING_ATTRIBUTE = "javax.servlet.forward.query_string"; | |
86 | ||
87 | /** | |
88 | * Default character encoding to use when <code>request.getCharacterEncoding</code> | |
89 | * returns <code>null</code>, according to the Servlet spec. | |
90 | * | |
91 | * @see javax.servlet.ServletRequest#getCharacterEncoding | |
92 | */ | |
93 | public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1"; | |
94 | ||
95 | /** | |
96 | * Return the path within the web application for the given request. | |
97 | * Detects include request URL if called within a RequestDispatcher include. | |
98 | * <p/> | |
99 | * For example, for a request to URL | |
100 | * <p/> | |
101 | * <code>http://www.somehost.com/myapp/my/url.jsp</code>, | |
102 | * <p/> | |
103 | * for an application deployed to <code>/mayapp</code> (the application's context path), this method would return | |
104 | * <p/> | |
105 | * <code>/my/url.jsp</code>. | |
106 | * | |
107 | * @param request current HTTP request | |
108 | * @return the path within the web application | |
109 | */ | |
110 | public static String getPathWithinApplication(HttpServletRequest request) { | |
111 | 7 | String contextPath = getContextPath(request); |
112 | 7 | String requestUri = getRequestUri(request); |
113 | 7 | if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) { |
114 | // Normal case: URI contains context path. | |
115 | 7 | String path = requestUri.substring(contextPath.length()); |
116 | 7 | return (StringUtils.hasText(path) ? path : "/"); |
117 | } else { | |
118 | // Special case: rather unusual. | |
119 | 0 | return requestUri; |
120 | } | |
121 | } | |
122 | ||
123 | /** | |
124 | * Return the request URI for the given request, detecting an include request | |
125 | * URL if called within a RequestDispatcher include. | |
126 | * <p>As the value returned by <code>request.getRequestURI()</code> is <i>not</i> | |
127 | * decoded by the servlet container, this method will decode it. | |
128 | * <p>The URI that the web container resolves <i>should</i> be correct, but some | |
129 | * containers like JBoss/Jetty incorrectly include ";" strings like ";jsessionid" | |
130 | * in the URI. This method cuts off such incorrect appendices. | |
131 | * | |
132 | * @param request current HTTP request | |
133 | * @return the request URI | |
134 | */ | |
135 | public static String getRequestUri(HttpServletRequest request) { | |
136 | 7 | String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE); |
137 | 7 | if (uri == null) { |
138 | 7 | uri = request.getRequestURI(); |
139 | } | |
140 | 7 | return normalize(decodeAndCleanUriString(request, uri)); |
141 | } | |
142 | ||
143 | /** | |
144 | * Normalize a relative URI path that may have relative values ("/./", | |
145 | * "/../", and so on ) it it. <strong>WARNING</strong> - This method is | |
146 | * useful only for normalizing application-generated paths. It does not | |
147 | * try to perform security checks for malicious input. | |
148 | * Normalize operations were was happily taken from org.apache.catalina.util.RequestUtil in | |
149 | * Tomcat trunk, r939305 | |
150 | * | |
151 | * @param path Relative path to be normalized | |
152 | * @return normalized path | |
153 | */ | |
154 | public static String normalize(String path) { | |
155 | 10 | return normalize(path, true); |
156 | } | |
157 | ||
158 | /** | |
159 | * Normalize a relative URI path that may have relative values ("/./", | |
160 | * "/../", and so on ) it it. <strong>WARNING</strong> - This method is | |
161 | * useful only for normalizing application-generated paths. It does not | |
162 | * try to perform security checks for malicious input. | |
163 | * Normalize operations were was happily taken from org.apache.catalina.util.RequestUtil in | |
164 | * Tomcat trunk, r939305 | |
165 | * | |
166 | * @param path Relative path to be normalized | |
167 | * @param replaceBackSlash Should '\\' be replaced with '/' | |
168 | * @return normalized path | |
169 | */ | |
170 | private static String normalize(String path, boolean replaceBackSlash) { | |
171 | ||
172 | 10 | if (path == null) |
173 | 0 | return null; |
174 | ||
175 | // Create a place for the normalized path | |
176 | 10 | String normalized = path; |
177 | ||
178 | 10 | if (replaceBackSlash && normalized.indexOf('\\') >= 0) |
179 | 0 | normalized = normalized.replace('\\', '/'); |
180 | ||
181 | 10 | if (normalized.equals("/.")) |
182 | 0 | return "/"; |
183 | ||
184 | // Add a leading "/" if necessary | |
185 | 10 | if (!normalized.startsWith("/")) |
186 | 0 | normalized = "/" + normalized; |
187 | ||
188 | // Resolve occurrences of "//" in the normalized path | |
189 | while (true) { | |
190 | 10 | int index = normalized.indexOf("//"); |
191 | 10 | if (index < 0) |
192 | 10 | break; |
193 | 0 | normalized = normalized.substring(0, index) + |
194 | normalized.substring(index + 1); | |
195 | 0 | } |
196 | ||
197 | // Resolve occurrences of "/./" in the normalized path | |
198 | while (true) { | |
199 | 11 | int index = normalized.indexOf("/./"); |
200 | 11 | if (index < 0) |
201 | 10 | break; |
202 | 1 | normalized = normalized.substring(0, index) + |
203 | normalized.substring(index + 2); | |
204 | 1 | } |
205 | ||
206 | // Resolve occurrences of "/../" in the normalized path | |
207 | while (true) { | |
208 | 11 | int index = normalized.indexOf("/../"); |
209 | 11 | if (index < 0) |
210 | 10 | break; |
211 | 1 | if (index == 0) |
212 | 0 | return (null); // Trying to go outside our context |
213 | 1 | int index2 = normalized.lastIndexOf('/', index - 1); |
214 | 1 | normalized = normalized.substring(0, index2) + |
215 | normalized.substring(index + 3); | |
216 | 1 | } |
217 | ||
218 | // Return the normalized path that we have completed | |
219 | 10 | return (normalized); |
220 | ||
221 | } | |
222 | ||
223 | ||
224 | /** | |
225 | * Decode the supplied URI string and strips any extraneous portion after a ';'. | |
226 | * | |
227 | * @param request the incoming HttpServletRequest | |
228 | * @param uri the application's URI string | |
229 | * @return the supplied URI string stripped of any extraneous portion after a ';'. | |
230 | */ | |
231 | private static String decodeAndCleanUriString(HttpServletRequest request, String uri) { | |
232 | 7 | uri = decodeRequestString(request, uri); |
233 | 7 | int semicolonIndex = uri.indexOf(';'); |
234 | 7 | return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri); |
235 | } | |
236 | ||
237 | /** | |
238 | * Return the context path for the given request, detecting an include request | |
239 | * URL if called within a RequestDispatcher include. | |
240 | * <p>As the value returned by <code>request.getContextPath()</code> is <i>not</i> | |
241 | * decoded by the servlet container, this method will decode it. | |
242 | * | |
243 | * @param request current HTTP request | |
244 | * @return the context path | |
245 | */ | |
246 | public static String getContextPath(HttpServletRequest request) { | |
247 | 7 | String contextPath = (String) request.getAttribute(INCLUDE_CONTEXT_PATH_ATTRIBUTE); |
248 | 7 | if (contextPath == null) { |
249 | 7 | contextPath = request.getContextPath(); |
250 | } | |
251 | 7 | if ("/".equals(contextPath)) { |
252 | // Invalid case, but happens for includes on Jetty: silently adapt it. | |
253 | 3 | contextPath = ""; |
254 | } | |
255 | 7 | return decodeRequestString(request, contextPath); |
256 | } | |
257 | ||
258 | /** | |
259 | * Find the Shiro {@link WebEnvironment} for this web application, which is typically loaded via the | |
260 | * {@link org.apache.shiro.web.env.EnvironmentLoaderListener}. | |
261 | * <p/> | |
262 | * This implementation rethrows an exception that happened on environment startup to differentiate between a failed | |
263 | * environment startup and no environment at all. | |
264 | * | |
265 | * @param sc ServletContext to find the web application context for | |
266 | * @return the root WebApplicationContext for this web app | |
267 | * @throws IllegalStateException if the root WebApplicationContext could not be found | |
268 | * @see org.apache.shiro.web.env.EnvironmentLoader#ENVIRONMENT_ATTRIBUTE_KEY | |
269 | * @since 1.2 | |
270 | */ | |
271 | public static WebEnvironment getRequiredWebEnvironment(ServletContext sc) | |
272 | throws IllegalStateException { | |
273 | ||
274 | 1 | WebEnvironment we = getWebEnvironment(sc); |
275 | 1 | if (we == null) { |
276 | 0 | throw new IllegalStateException("No WebEnvironment found: no EnvironmentLoaderListener registered?"); |
277 | } | |
278 | 1 | return we; |
279 | } | |
280 | ||
281 | /** | |
282 | * Find the Shiro {@link WebEnvironment} for this web application, which is typically loaded via | |
283 | * {@link org.apache.shiro.web.env.EnvironmentLoaderListener}. | |
284 | * <p/> | |
285 | * This implementation rethrows an exception that happened on environment startup to differentiate between a failed | |
286 | * environment startup and no environment at all. | |
287 | * | |
288 | * @param sc ServletContext to find the web application context for | |
289 | * @return the root WebApplicationContext for this web app, or <code>null</code> if none | |
290 | * @see org.apache.shiro.web.env.EnvironmentLoader#ENVIRONMENT_ATTRIBUTE_KEY | |
291 | * @since 1.2 | |
292 | */ | |
293 | public static WebEnvironment getWebEnvironment(ServletContext sc) { | |
294 | 1 | return getWebEnvironment(sc, EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY); |
295 | } | |
296 | ||
297 | /** | |
298 | * Find the Shiro {@link WebEnvironment} for this web application. | |
299 | * | |
300 | * @param sc ServletContext to find the web application context for | |
301 | * @param attrName the name of the ServletContext attribute to look for | |
302 | * @return the desired WebEnvironment for this web app, or <code>null</code> if none | |
303 | * @since 1.2 | |
304 | */ | |
305 | public static WebEnvironment getWebEnvironment(ServletContext sc, String attrName) { | |
306 | 1 | if (sc == null) { |
307 | 0 | throw new IllegalArgumentException("ServletContext argument must not be null."); |
308 | } | |
309 | 1 | Object attr = sc.getAttribute(attrName); |
310 | 1 | if (attr == null) { |
311 | 0 | return null; |
312 | } | |
313 | 1 | if (attr instanceof RuntimeException) { |
314 | 0 | throw (RuntimeException) attr; |
315 | } | |
316 | 1 | if (attr instanceof Error) { |
317 | 0 | throw (Error) attr; |
318 | } | |
319 | 1 | if (attr instanceof Exception) { |
320 | 0 | throw new IllegalStateException((Exception) attr); |
321 | } | |
322 | 1 | if (!(attr instanceof WebEnvironment)) { |
323 | 0 | throw new IllegalStateException("Context attribute is not of type WebEnvironment: " + attr); |
324 | } | |
325 | 1 | return (WebEnvironment) attr; |
326 | } | |
327 | ||
328 | ||
329 | /** | |
330 | * Decode the given source string with a URLDecoder. The encoding will be taken | |
331 | * from the request, falling back to the default "ISO-8859-1". | |
332 | * <p>The default implementation uses <code>URLDecoder.decode(input, enc)</code>. | |
333 | * | |
334 | * @param request current HTTP request | |
335 | * @param source the String to decode | |
336 | * @return the decoded String | |
337 | * @see #DEFAULT_CHARACTER_ENCODING | |
338 | * @see javax.servlet.ServletRequest#getCharacterEncoding | |
339 | * @see java.net.URLDecoder#decode(String, String) | |
340 | * @see java.net.URLDecoder#decode(String) | |
341 | */ | |
342 | @SuppressWarnings({"deprecation"}) | |
343 | public static String decodeRequestString(HttpServletRequest request, String source) { | |
344 | 14 | String enc = determineEncoding(request); |
345 | try { | |
346 | 14 | return URLDecoder.decode(source, enc); |
347 | 0 | } catch (UnsupportedEncodingException ex) { |
348 | 0 | if (log.isWarnEnabled()) { |
349 | 0 | log.warn("Could not decode request string [" + source + "] with encoding '" + enc + |
350 | "': falling back to platform default encoding; exception message: " + ex.getMessage()); | |
351 | } | |
352 | 0 | return URLDecoder.decode(source); |
353 | } | |
354 | } | |
355 | ||
356 | /** | |
357 | * Determine the encoding for the given request. | |
358 | * Can be overridden in subclasses. | |
359 | * <p>The default implementation checks the request's | |
360 | * {@link ServletRequest#getCharacterEncoding() character encoding}, and if that | |
361 | * <code>null</code>, falls back to the {@link #DEFAULT_CHARACTER_ENCODING}. | |
362 | * | |
363 | * @param request current HTTP request | |
364 | * @return the encoding for the request (never <code>null</code>) | |
365 | * @see javax.servlet.ServletRequest#getCharacterEncoding() | |
366 | */ | |
367 | protected static String determineEncoding(HttpServletRequest request) { | |
368 | 14 | String enc = request.getCharacterEncoding(); |
369 | 14 | if (enc == null) { |
370 | 14 | enc = DEFAULT_CHARACTER_ENCODING; |
371 | } | |
372 | 14 | return enc; |
373 | } | |
374 | ||
375 | /* | |
376 | * Returns {@code true} IFF the specified {@code SubjectContext}: | |
377 | * <ol> | |
378 | * <li>A {@link WebSubjectContext} instance</li> | |
379 | * <li>The {@code WebSubjectContext}'s request/response pair are not null</li> | |
380 | * <li>The request is an {@link HttpServletRequest} instance</li> | |
381 | * <li>The response is an {@link HttpServletResponse} instance</li> | |
382 | * </ol> | |
383 | * | |
384 | * @param context the SubjectContext to check to see if it is HTTP compatible. | |
385 | * @return {@code true} IFF the specified context has HTTP request/response objects, {@code false} otherwise. | |
386 | * @since 1.0 | |
387 | */ | |
388 | ||
389 | public static boolean isWeb(Object requestPairSource) { | |
390 | 16 | return requestPairSource instanceof RequestPairSource && isWeb((RequestPairSource) requestPairSource); |
391 | } | |
392 | ||
393 | public static boolean isHttp(Object requestPairSource) { | |
394 | 25 | return requestPairSource instanceof RequestPairSource && isHttp((RequestPairSource) requestPairSource); |
395 | } | |
396 | ||
397 | public static ServletRequest getRequest(Object requestPairSource) { | |
398 | 39 | if (requestPairSource instanceof RequestPairSource) { |
399 | 39 | return ((RequestPairSource) requestPairSource).getServletRequest(); |
400 | } | |
401 | 0 | return null; |
402 | } | |
403 | ||
404 | public static ServletResponse getResponse(Object requestPairSource) { | |
405 | 33 | if (requestPairSource instanceof RequestPairSource) { |
406 | 33 | return ((RequestPairSource) requestPairSource).getServletResponse(); |
407 | } | |
408 | 0 | return null; |
409 | } | |
410 | ||
411 | public static HttpServletRequest getHttpRequest(Object requestPairSource) { | |
412 | 23 | ServletRequest request = getRequest(requestPairSource); |
413 | 23 | if (request instanceof HttpServletRequest) { |
414 | 23 | return (HttpServletRequest) request; |
415 | } | |
416 | 0 | return null; |
417 | } | |
418 | ||
419 | public static HttpServletResponse getHttpResponse(Object requestPairSource) { | |
420 | 19 | ServletResponse response = getResponse(requestPairSource); |
421 | 19 | if (response instanceof HttpServletResponse) { |
422 | 19 | return (HttpServletResponse) response; |
423 | } | |
424 | 0 | return null; |
425 | } | |
426 | ||
427 | private static boolean isWeb(RequestPairSource source) { | |
428 | 15 | ServletRequest request = source.getServletRequest(); |
429 | 15 | ServletResponse response = source.getServletResponse(); |
430 | 15 | return request != null && response != null; |
431 | } | |
432 | ||
433 | private static boolean isHttp(RequestPairSource source) { | |
434 | 23 | ServletRequest request = source.getServletRequest(); |
435 | 23 | ServletResponse response = source.getServletResponse(); |
436 | 23 | return request instanceof HttpServletRequest && response instanceof HttpServletResponse; |
437 | } | |
438 | ||
439 | /** | |
440 | * Returns {@code true} if a session is allowed to be created for a subject-associated request, {@code false} | |
441 | * otherwise. | |
442 | * <p/> | |
443 | * <b>This method exists for Shiro's internal framework needs and should never be called by Shiro end-users. It | |
444 | * could be changed/removed at any time.</b> | |
445 | * | |
446 | * @param requestPairSource a {@link RequestPairSource} instance, almost always a | |
447 | * {@link org.apache.shiro.web.subject.WebSubject WebSubject} instance. | |
448 | * @return {@code true} if a session is allowed to be created for a subject-associated request, {@code false} | |
449 | * otherwise. | |
450 | */ | |
451 | public static boolean _isSessionCreationEnabled(Object requestPairSource) { | |
452 | 12 | if (requestPairSource instanceof RequestPairSource) { |
453 | 11 | RequestPairSource source = (RequestPairSource) requestPairSource; |
454 | 11 | return _isSessionCreationEnabled(source.getServletRequest()); |
455 | } | |
456 | 1 | return true; //by default |
457 | } | |
458 | ||
459 | /** | |
460 | * Returns {@code true} if a session is allowed to be created for a subject-associated request, {@code false} | |
461 | * otherwise. | |
462 | * <p/> | |
463 | * <b>This method exists for Shiro's internal framework needs and should never be called by Shiro end-users. It | |
464 | * could be changed/removed at any time.</b> | |
465 | * | |
466 | * @param request incoming servlet request. | |
467 | * @return {@code true} if a session is allowed to be created for a subject-associated request, {@code false} | |
468 | * otherwise. | |
469 | */ | |
470 | public static boolean _isSessionCreationEnabled(ServletRequest request) { | |
471 | 11 | if (request != null) { |
472 | 11 | Object val = request.getAttribute(DefaultSubjectContext.SESSION_CREATION_ENABLED); |
473 | 11 | if (val != null && val instanceof Boolean) { |
474 | 1 | return (Boolean) val; |
475 | } | |
476 | } | |
477 | 10 | return true; //by default |
478 | } | |
479 | ||
480 | /** | |
481 | * A convenience method that merely casts the incoming <code>ServletRequest</code> to an | |
482 | * <code>HttpServletRequest</code>: | |
483 | * <p/> | |
484 | * <code>return (HttpServletRequest)request;</code> | |
485 | * <p/> | |
486 | * Logic could be changed in the future for logging or throwing an meaningful exception in | |
487 | * non HTTP request environments (e.g. Portlet API). | |
488 | * | |
489 | * @param request the incoming ServletRequest | |
490 | * @return the <code>request</code> argument casted to an <code>HttpServletRequest</code>. | |
491 | */ | |
492 | public static HttpServletRequest toHttp(ServletRequest request) { | |
493 | 18 | return (HttpServletRequest) request; |
494 | } | |
495 | ||
496 | /** | |
497 | * A convenience method that merely casts the incoming <code>ServletResponse</code> to an | |
498 | * <code>HttpServletResponse</code>: | |
499 | * <p/> | |
500 | * <code>return (HttpServletResponse)response;</code> | |
501 | * <p/> | |
502 | * Logic could be changed in the future for logging or throwing an meaningful exception in | |
503 | * non HTTP request environments (e.g. Portlet API). | |
504 | * | |
505 | * @param response the outgoing ServletResponse | |
506 | * @return the <code>response</code> argument casted to an <code>HttpServletResponse</code>. | |
507 | */ | |
508 | public static HttpServletResponse toHttp(ServletResponse response) { | |
509 | 10 | return (HttpServletResponse) response; |
510 | } | |
511 | ||
512 | /** | |
513 | * Redirects the current request to a new URL based on the given parameters. | |
514 | * | |
515 | * @param request the servlet request. | |
516 | * @param response the servlet response. | |
517 | * @param url the URL to redirect the user to. | |
518 | * @param queryParams a map of parameters that should be set as request parameters for the new request. | |
519 | * @param contextRelative true if the URL is relative to the servlet context path, or false if the URL is absolute. | |
520 | * @param http10Compatible whether to stay compatible with HTTP 1.0 clients. | |
521 | * @throws java.io.IOException if thrown by response methods. | |
522 | */ | |
523 | public static void issueRedirect(ServletRequest request, ServletResponse response, String url, Map queryParams, boolean contextRelative, boolean http10Compatible) throws IOException { | |
524 | 3 | RedirectView view = new RedirectView(url, contextRelative, http10Compatible); |
525 | 3 | view.renderMergedOutputModel(queryParams, toHttp(request), toHttp(response)); |
526 | 3 | } |
527 | ||
528 | /** | |
529 | * Redirects the current request to a new URL based on the given parameters and default values | |
530 | * for unspecified parameters. | |
531 | * | |
532 | * @param request the servlet request. | |
533 | * @param response the servlet response. | |
534 | * @param url the URL to redirect the user to. | |
535 | * @throws java.io.IOException if thrown by response methods. | |
536 | */ | |
537 | public static void issueRedirect(ServletRequest request, ServletResponse response, String url) throws IOException { | |
538 | 3 | issueRedirect(request, response, url, null, true, true); |
539 | 3 | } |
540 | ||
541 | /** | |
542 | * Redirects the current request to a new URL based on the given parameters and default values | |
543 | * for unspecified parameters. | |
544 | * | |
545 | * @param request the servlet request. | |
546 | * @param response the servlet response. | |
547 | * @param url the URL to redirect the user to. | |
548 | * @param queryParams a map of parameters that should be set as request parameters for the new request. | |
549 | * @throws java.io.IOException if thrown by response methods. | |
550 | */ | |
551 | public static void issueRedirect(ServletRequest request, ServletResponse response, String url, Map queryParams) throws IOException { | |
552 | 0 | issueRedirect(request, response, url, queryParams, true, true); |
553 | 0 | } |
554 | ||
555 | /** | |
556 | * Redirects the current request to a new URL based on the given parameters and default values | |
557 | * for unspecified parameters. | |
558 | * | |
559 | * @param request the servlet request. | |
560 | * @param response the servlet response. | |
561 | * @param url the URL to redirect the user to. | |
562 | * @param queryParams a map of parameters that should be set as request parameters for the new request. | |
563 | * @param contextRelative true if the URL is relative to the servlet context path, or false if the URL is absolute. | |
564 | * @throws java.io.IOException if thrown by response methods. | |
565 | */ | |
566 | public static void issueRedirect(ServletRequest request, ServletResponse response, String url, Map queryParams, boolean contextRelative) throws IOException { | |
567 | 0 | issueRedirect(request, response, url, queryParams, contextRelative, true); |
568 | 0 | } |
569 | ||
570 | /** | |
571 | * <p>Checks to see if a request param is considered true using a loose matching strategy for | |
572 | * general values that indicate that something is true or enabled, etc.</p> | |
573 | * <p/> | |
574 | * <p>Values that are considered "true" include (case-insensitive): true, t, 1, enabled, y, yes, on.</p> | |
575 | * | |
576 | * @param request the servlet request | |
577 | * @param paramName @return true if the param value is considered true or false if it isn't. | |
578 | * @return true if the given parameter is considered "true" - false otherwise. | |
579 | */ | |
580 | public static boolean isTrue(ServletRequest request, String paramName) { | |
581 | 0 | String value = getCleanParam(request, paramName); |
582 | 0 | return value != null && |
583 | (value.equalsIgnoreCase("true") || | |
584 | value.equalsIgnoreCase("t") || | |
585 | value.equalsIgnoreCase("1") || | |
586 | value.equalsIgnoreCase("enabled") || | |
587 | value.equalsIgnoreCase("y") || | |
588 | value.equalsIgnoreCase("yes") || | |
589 | value.equalsIgnoreCase("on")); | |
590 | } | |
591 | ||
592 | /** | |
593 | * Convenience method that returns a request parameter value, first running it through | |
594 | * {@link StringUtils#clean(String)}. | |
595 | * | |
596 | * @param request the servlet request. | |
597 | * @param paramName the parameter name. | |
598 | * @return the clean param value, or null if the param does not exist or is empty. | |
599 | */ | |
600 | public static String getCleanParam(ServletRequest request, String paramName) { | |
601 | 0 | return StringUtils.clean(request.getParameter(paramName)); |
602 | } | |
603 | ||
604 | public static void saveRequest(ServletRequest request) { | |
605 | 0 | Subject subject = SecurityUtils.getSubject(); |
606 | 0 | Session session = subject.getSession(); |
607 | 0 | HttpServletRequest httpRequest = toHttp(request); |
608 | 0 | SavedRequest savedRequest = new SavedRequest(httpRequest); |
609 | 0 | session.setAttribute(SAVED_REQUEST_KEY, savedRequest); |
610 | 0 | } |
611 | ||
612 | public static SavedRequest getAndClearSavedRequest(ServletRequest request) { | |
613 | 0 | SavedRequest savedRequest = getSavedRequest(request); |
614 | 0 | if (savedRequest != null) { |
615 | 0 | Subject subject = SecurityUtils.getSubject(); |
616 | 0 | Session session = subject.getSession(); |
617 | 0 | session.removeAttribute(SAVED_REQUEST_KEY); |
618 | } | |
619 | 0 | return savedRequest; |
620 | } | |
621 | ||
622 | public static SavedRequest getSavedRequest(ServletRequest request) { | |
623 | 0 | SavedRequest savedRequest = null; |
624 | 0 | Subject subject = SecurityUtils.getSubject(); |
625 | 0 | Session session = subject.getSession(false); |
626 | 0 | if (session != null) { |
627 | 0 | savedRequest = (SavedRequest) session.getAttribute(SAVED_REQUEST_KEY); |
628 | } | |
629 | 0 | return savedRequest; |
630 | } | |
631 | ||
632 | /** | |
633 | * Redirects the to the request url from a previously | |
634 | * {@link #saveRequest(javax.servlet.ServletRequest) saved} request, or if there is no saved request, redirects the | |
635 | * end user to the specified {@code fallbackUrl}. If there is no saved request or fallback url, this method | |
636 | * throws an {@link IllegalStateException}. | |
637 | * <p/> | |
638 | * This method is primarily used to support a common login scenario - if an unauthenticated user accesses a | |
639 | * page that requires authentication, it is expected that request is | |
640 | * {@link #saveRequest(javax.servlet.ServletRequest) saved} first and then redirected to the login page. Then, | |
641 | * after a successful login, this method can be called to redirect them back to their originally requested URL, a | |
642 | * nice usability feature. | |
643 | * | |
644 | * @param request the incoming request | |
645 | * @param response the outgoing response | |
646 | * @param fallbackUrl the fallback url to redirect to if there is no saved request available. | |
647 | * @throws IllegalStateException if there is no saved request and the {@code fallbackUrl} is {@code null}. | |
648 | * @throws IOException if there is an error redirecting | |
649 | * @since 1.0 | |
650 | */ | |
651 | public static void redirectToSavedRequest(ServletRequest request, ServletResponse response, String fallbackUrl) | |
652 | throws IOException { | |
653 | 0 | String successUrl = null; |
654 | 0 | boolean contextRelative = true; |
655 | 0 | SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request); |
656 | 0 | if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase(AccessControlFilter.GET_METHOD)) { |
657 | 0 | successUrl = savedRequest.getRequestUrl(); |
658 | 0 | contextRelative = false; |
659 | } | |
660 | ||
661 | 0 | if (successUrl == null) { |
662 | 0 | successUrl = fallbackUrl; |
663 | } | |
664 | ||
665 | 0 | if (successUrl == null) { |
666 | 0 | throw new IllegalStateException("Success URL not available via saved request or via the " + |
667 | "successUrlFallback method parameter. One of these must be non-null for " + | |
668 | "issueSuccessRedirect() to work."); | |
669 | } | |
670 | ||
671 | 0 | WebUtils.issueRedirect(request, response, successUrl, null, contextRelative); |
672 | 0 | } |
673 | ||
674 | } |