1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.lang.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.owasp.encoder.Encode;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 import javax.servlet.ServletContext;
34 import javax.servlet.ServletRequest;
35 import javax.servlet.ServletResponse;
36 import javax.servlet.http.HttpServletRequest;
37 import javax.servlet.http.HttpServletResponse;
38 import java.io.IOException;
39 import java.io.UnsupportedEncodingException;
40 import java.net.URLDecoder;
41 import java.util.Map;
42
43
44
45
46
47
48
49
50
51 @SuppressWarnings({"checkstyle:MethodCount", "checkstyle:JavadocVariable"})
52 public final class WebUtils {
53
54 public static final String SERVLET_REQUEST_KEY = ServletRequest.class.getName() + "_SHIRO_THREAD_CONTEXT_KEY";
55 public static final String SERVLET_RESPONSE_KEY = ServletResponse.class.getName() + "_SHIRO_THREAD_CONTEXT_KEY";
56
57 public static final String ALLOW_BACKSLASH = "org.apache.shiro.web.ALLOW_BACKSLASH";
58
59
60
61
62
63
64 public static final String SAVED_REQUEST_KEY = "shiroSavedRequest";
65
66
67
68
69
70
71 public static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri";
72 public static final String INCLUDE_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.include.context_path";
73 public static final String INCLUDE_SERVLET_PATH_ATTRIBUTE = "javax.servlet.include.servlet_path";
74 public static final String INCLUDE_PATH_INFO_ATTRIBUTE = "javax.servlet.include.path_info";
75 public static final String INCLUDE_QUERY_STRING_ATTRIBUTE = "javax.servlet.include.query_string";
76
77
78
79
80
81
82 public static final String FORWARD_REQUEST_URI_ATTRIBUTE = "javax.servlet.forward.request_uri";
83 public static final String FORWARD_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.forward.context_path";
84 public static final String FORWARD_SERVLET_PATH_ATTRIBUTE = "javax.servlet.forward.servlet_path";
85 public static final String FORWARD_PATH_INFO_ATTRIBUTE = "javax.servlet.forward.path_info";
86 public static final String FORWARD_QUERY_STRING_ATTRIBUTE = "javax.servlet.forward.query_string";
87
88
89
90
91
92
93
94 public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
95
96 private static boolean isAllowBackslash = Boolean.getBoolean(ALLOW_BACKSLASH);
97
98 private static final Logger LOGGER = LoggerFactory.getLogger(WebUtils.class);
99
100 private WebUtils() {
101 }
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 public static String getPathWithinApplication(HttpServletRequest request) {
119 return normalize(removeSemicolon(getServletPath(request) + getPathInfo(request)));
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 @Deprecated
137 public static String getRequestUri(HttpServletRequest request) {
138 String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
139 if (uri == null) {
140 uri = request.getRequestURI();
141 }
142 return normalize(decodeAndCleanUriString(request, uri));
143 }
144
145 private static String getServletPath(HttpServletRequest request) {
146 String servletPath = (String) request.getAttribute(INCLUDE_SERVLET_PATH_ATTRIBUTE);
147 return servletPath != null ? servletPath : valueOrEmpty(request.getServletPath());
148 }
149
150 private static String getPathInfo(HttpServletRequest request) {
151 String pathInfo = (String) request.getAttribute(INCLUDE_PATH_INFO_ATTRIBUTE);
152 return pathInfo != null ? pathInfo : valueOrEmpty(request.getPathInfo());
153 }
154
155 private static String valueOrEmpty(String input) {
156 if (input == null) {
157 return "";
158 }
159 return input;
160 }
161
162
163
164
165
166
167
168
169
170
171
172
173 public static String normalize(String path) {
174 return normalize(path, isAllowBackslash);
175 }
176
177
178
179
180
181
182
183
184
185
186
187
188
189 @SuppressWarnings({"checkstyle:CyclomaticComplexity", "checkstyle:NPathComplexity"})
190 private static String normalize(String path, boolean replaceBackSlash) {
191
192 if (path == null) {
193 return null;
194 }
195
196
197 String normalized = path;
198
199 if (replaceBackSlash && normalized.indexOf('\\') >= 0) {
200 normalized = normalized.replace('\\', '/');
201 }
202
203 if (normalized.equals("/.")) {
204 return "/";
205 }
206
207
208 if (!normalized.startsWith("/")) {
209 normalized = "/" + normalized;
210 }
211
212
213 while (true) {
214 int index = normalized.indexOf("//");
215 if (index < 0) {
216 break;
217 }
218 normalized = normalized.substring(0, index) + normalized.substring(index + 1);
219 }
220
221
222 while (true) {
223 int index = normalized.indexOf("/./");
224 if (index < 0) {
225 break;
226 }
227 normalized = normalized.substring(0, index) + normalized.substring(index + 2);
228 }
229
230
231 while (true) {
232 int index = normalized.indexOf("/../");
233 if (index < 0) {
234 break;
235 }
236 if (index == 0) {
237
238 return (null);
239 }
240 int index2 = normalized.lastIndexOf('/', index - 1);
241 normalized = normalized.substring(0, index2) + normalized.substring(index + 3);
242 }
243
244
245 return (normalized);
246
247 }
248
249
250
251
252
253
254
255
256
257 private static String decodeAndCleanUriString(HttpServletRequest request, String uri) {
258 uri = decodeRequestString(request, uri);
259 return removeSemicolon(uri);
260 }
261
262 private static String removeSemicolon(String uri) {
263 int semicolonIndex = uri.indexOf(';');
264 return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri);
265 }
266
267
268
269
270
271
272
273
274
275
276 public static String getContextPath(HttpServletRequest request) {
277 String contextPath = (String) request.getAttribute(INCLUDE_CONTEXT_PATH_ATTRIBUTE);
278 if (contextPath == null) {
279 contextPath = request.getContextPath();
280 }
281 contextPath = normalize(decodeRequestString(request, contextPath));
282 if ("/".equals(contextPath)) {
283
284 contextPath = "";
285 }
286 return contextPath;
287 }
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302 public static WebEnvironment getRequiredWebEnvironment(ServletContext sc)
303 throws IllegalStateException {
304
305 WebEnvironment we = getWebEnvironment(sc);
306 if (we == null) {
307 throw new IllegalStateException("No WebEnvironment found: no EnvironmentLoaderListener registered?");
308 }
309 return we;
310 }
311
312
313
314
315
316
317
318
319
320
321
322
323
324 public static WebEnvironment getWebEnvironment(ServletContext sc) {
325 return getWebEnvironment(sc, EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY);
326 }
327
328
329
330
331
332
333
334
335
336 public static WebEnvironment getWebEnvironment(ServletContext sc, String attrName) {
337 if (sc == null) {
338 throw new IllegalArgumentException("ServletContext argument must not be null.");
339 }
340 Object attr = sc.getAttribute(attrName);
341 if (attr == null) {
342 return null;
343 }
344 if (attr instanceof RuntimeException) {
345 throw (RuntimeException) attr;
346 }
347 if (attr instanceof Error) {
348 throw (Error) attr;
349 }
350 if (attr instanceof Exception) {
351 throw new IllegalStateException((Exception) attr);
352 }
353 if (!(attr instanceof WebEnvironment)) {
354 throw new IllegalStateException("Context attribute is not of type WebEnvironment: " + attr);
355 }
356 return (WebEnvironment) attr;
357 }
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373 @SuppressWarnings("deprecation")
374 public static String decodeRequestString(HttpServletRequest request, String source) {
375 String enc = determineEncoding(request);
376 try {
377 return URLDecoder.decode(source, enc);
378 } catch (UnsupportedEncodingException ex) {
379 if (LOGGER.isWarnEnabled()) {
380 LOGGER.warn("Could not decode request string [" + Encode.forHtml(source)
381 + "] with encoding '" + Encode.forHtml(enc)
382 + "': falling back to platform default encoding; exception message: " + ex.getMessage());
383 }
384 return URLDecoder.decode(source);
385 }
386 }
387
388
389
390
391
392
393
394
395
396
397
398
399 protected static String determineEncoding(HttpServletRequest request) {
400 String enc = request.getCharacterEncoding();
401 if (enc == null) {
402 enc = DEFAULT_CHARACTER_ENCODING;
403 }
404 return enc;
405 }
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421 public static boolean isWeb(Object requestPairSource) {
422 return requestPairSource instanceof RequestPairSource && isWeb((RequestPairSource) requestPairSource);
423 }
424
425 public static boolean isHttp(Object requestPairSource) {
426 return requestPairSource instanceof RequestPairSource && isHttp((RequestPairSource) requestPairSource);
427 }
428
429 public static ServletRequest getRequest(Object requestPairSource) {
430 if (requestPairSource instanceof RequestPairSource) {
431 return ((RequestPairSource) requestPairSource).getServletRequest();
432 }
433 return null;
434 }
435
436 public static ServletResponse getResponse(Object requestPairSource) {
437 if (requestPairSource instanceof RequestPairSource) {
438 return ((RequestPairSource) requestPairSource).getServletResponse();
439 }
440 return null;
441 }
442
443 public static HttpServletRequest getHttpRequest(Object requestPairSource) {
444 ServletRequest request = getRequest(requestPairSource);
445 if (request instanceof HttpServletRequest) {
446 return (HttpServletRequest) request;
447 }
448 return null;
449 }
450
451 public static HttpServletResponse getHttpResponse(Object requestPairSource) {
452 ServletResponse response = getResponse(requestPairSource);
453 if (response instanceof HttpServletResponse) {
454 return (HttpServletResponse) response;
455 }
456 return null;
457 }
458
459 private static boolean isWeb(RequestPairSource source) {
460 ServletRequest request = source.getServletRequest();
461 ServletResponse response = source.getServletResponse();
462 return request != null && response != null;
463 }
464
465 private static boolean isHttp(RequestPairSource source) {
466 ServletRequest request = source.getServletRequest();
467 ServletResponse response = source.getServletResponse();
468 return request instanceof HttpServletRequest && response instanceof HttpServletResponse;
469 }
470
471
472
473
474
475
476
477
478
479
480
481
482
483 public static boolean isSessionCreationEnabled(Object requestPairSource) {
484 if (requestPairSource instanceof RequestPairSource) {
485 RequestPairSource source = (RequestPairSource) requestPairSource;
486 return isSessionCreationEnabled(source.getServletRequest());
487 }
488
489 return true;
490 }
491
492
493
494
495
496
497
498
499
500
501
502
503 public static boolean isSessionCreationEnabled(ServletRequest request) {
504 if (request != null) {
505 Object val = request.getAttribute(DefaultSubjectContext.SESSION_CREATION_ENABLED);
506 if (val != null && val instanceof Boolean) {
507 return (Boolean) val;
508 }
509 }
510
511 return true;
512 }
513
514
515
516
517
518
519
520
521
522
523
524
525
526 public static HttpServletRequest toHttp(ServletRequest request) {
527 return (HttpServletRequest) request;
528 }
529
530
531
532
533
534
535
536
537
538
539
540
541
542 public static HttpServletResponse toHttp(ServletResponse response) {
543 return (HttpServletResponse) response;
544 }
545
546
547
548
549
550
551
552
553
554
555
556
557 public static void issueRedirect(ServletRequest request, ServletResponse response, String url,
558 Map queryParams, boolean contextRelative,
559 boolean http10Compatible) throws IOException {
560 RedirectView view = new RedirectView(url, contextRelative, http10Compatible);
561 view.renderMergedOutputModel(queryParams, toHttp(request), toHttp(response));
562 }
563
564
565
566
567
568
569
570
571
572
573 public static void issueRedirect(ServletRequest request, ServletResponse response, String url) throws IOException {
574 issueRedirect(request, response, url, null, true, true);
575 }
576
577
578
579
580
581
582
583
584
585
586
587 public static void issueRedirect(ServletRequest request,
588 ServletResponse response, String url, Map queryParams) throws IOException {
589 issueRedirect(request, response, url, queryParams, true, true);
590 }
591
592
593
594
595
596
597
598
599
600
601
602
603 public static void issueRedirect(ServletRequest request,
604 ServletResponse response, String url, Map queryParams, boolean contextRelative)
605 throws IOException {
606 issueRedirect(request, response, url, queryParams, contextRelative, true);
607 }
608
609
610
611
612
613
614
615
616
617
618
619 @SuppressWarnings("checkstyle:BooleanExpressionComplexity")
620 public static boolean isTrue(ServletRequest request, String paramName) {
621 String value = getCleanParam(request, paramName);
622 return value != null
623 && (value.equalsIgnoreCase("true")
624 || value.equalsIgnoreCase("t")
625 || value.equalsIgnoreCase("1")
626 || value.equalsIgnoreCase("enabled")
627 || value.equalsIgnoreCase("y")
628 || value.equalsIgnoreCase("yes")
629 || value.equalsIgnoreCase("on"));
630 }
631
632
633
634
635
636
637
638
639
640 public static String getCleanParam(ServletRequest request, String paramName) {
641 return StringUtils.clean(request.getParameter(paramName));
642 }
643
644 public static void saveRequest(ServletRequest request) {
645 Subject subject = SecurityUtils.getSubject();
646 Session session = subject.getSession();
647 HttpServletRequest httpRequest = toHttp(request);
648 SavedRequest savedRequest = new SavedRequest(httpRequest);
649 session.setAttribute(SAVED_REQUEST_KEY, savedRequest);
650 }
651
652 public static SavedRequest getAndClearSavedRequest(ServletRequest request) {
653 SavedRequest savedRequest = getSavedRequest(request);
654 if (savedRequest != null) {
655 Subject subject = SecurityUtils.getSubject();
656 Session session = subject.getSession();
657 session.removeAttribute(SAVED_REQUEST_KEY);
658 }
659 return savedRequest;
660 }
661
662 public static SavedRequest getSavedRequest(ServletRequest request) {
663 SavedRequest savedRequest = null;
664 Subject subject = SecurityUtils.getSubject();
665 Session session = subject.getSession(false);
666 if (session != null) {
667 savedRequest = (SavedRequest) session.getAttribute(SAVED_REQUEST_KEY);
668 }
669 return savedRequest;
670 }
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691 public static void redirectToSavedRequest(ServletRequest request, ServletResponse response, String fallbackUrl)
692 throws IOException {
693 String successUrl = null;
694 boolean contextRelative = true;
695 SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request);
696 if (savedRequest != null && savedRequest.getMethod().equalsIgnoreCase(AccessControlFilter.GET_METHOD)) {
697 successUrl = savedRequest.getRequestUrl();
698 contextRelative = false;
699 }
700
701 if (successUrl == null) {
702 successUrl = fallbackUrl;
703 }
704
705 if (successUrl == null) {
706 throw new IllegalStateException("Success URL not available via saved request or via the "
707 + "successUrlFallback method parameter. One of these must be non-null for "
708 + "issueSuccessRedirect() to work.");
709 }
710
711 WebUtils.issueRedirect(request, response, successUrl, null, contextRelative);
712 }
713
714 public static boolean isAllowBackslash() {
715 return isAllowBackslash;
716 }
717
718
719
720
721
722
723 public static void reloadSystemProperties() {
724 isAllowBackslash = Boolean.getBoolean(ALLOW_BACKSLASH);
725 }
726
727 }