Coverage Report - org.apache.shiro.web.util.WebUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
WebUtils
60%
95/156
45%
49/108
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  
 }