001/*
002 * Copyright 2002-2015 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.cors;
018
019import java.util.Collections;
020import java.util.LinkedHashMap;
021import java.util.Map;
022import javax.servlet.http.HttpServletRequest;
023
024import org.springframework.util.AntPathMatcher;
025import org.springframework.util.Assert;
026import org.springframework.util.PathMatcher;
027import org.springframework.web.util.UrlPathHelper;
028
029/**
030 * Provide a per request {@link CorsConfiguration} instance based on a
031 * collection of {@link CorsConfiguration} mapped on path patterns.
032 *
033 * <p>Exact path mapping URIs (such as {@code "/admin"}) are supported
034 * as well as Ant-style path patterns (such as {@code "/admin/**"}).
035 *
036 * @author Sebastien Deleuze
037 * @since 4.2
038 */
039public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource {
040
041        private final Map<String, CorsConfiguration> corsConfigurations = new LinkedHashMap<String, CorsConfiguration>();
042
043        private PathMatcher pathMatcher = new AntPathMatcher();
044
045        private UrlPathHelper urlPathHelper = new UrlPathHelper();
046
047
048        /**
049         * Set the PathMatcher implementation to use for matching URL paths
050         * against registered URL patterns. Default is AntPathMatcher.
051         * @see org.springframework.util.AntPathMatcher
052         */
053        public void setPathMatcher(PathMatcher pathMatcher) {
054                Assert.notNull(pathMatcher, "PathMatcher must not be null");
055                this.pathMatcher = pathMatcher;
056        }
057
058        /**
059         * Set if URL lookup should always use the full path within the current servlet
060         * context. Else, the path within the current servlet mapping is used if applicable
061         * (that is, in the case of a ".../*" servlet mapping in web.xml).
062         * <p>Default is "false".
063         * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
064         */
065        public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
066                this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
067        }
068
069        /**
070         * Set if context path and request URI should be URL-decoded. Both are returned
071         * <i>undecoded</i> by the Servlet API, in contrast to the servlet path.
072         * <p>Uses either the request encoding or the default encoding according
073         * to the Servlet spec (ISO-8859-1).
074         * @see org.springframework.web.util.UrlPathHelper#setUrlDecode
075         */
076        public void setUrlDecode(boolean urlDecode) {
077                this.urlPathHelper.setUrlDecode(urlDecode);
078        }
079
080        /**
081         * Set if ";" (semicolon) content should be stripped from the request URI.
082         * <p>The default value is {@code true}.
083         * @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean)
084         */
085        public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
086                this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent);
087        }
088
089        /**
090         * Set the UrlPathHelper to use for resolution of lookup paths.
091         * <p>Use this to override the default UrlPathHelper with a custom subclass.
092         */
093        public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
094                Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
095                this.urlPathHelper = urlPathHelper;
096        }
097
098        /**
099         * Set CORS configuration based on URL patterns.
100         */
101        public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) {
102                this.corsConfigurations.clear();
103                if (corsConfigurations != null) {
104                        this.corsConfigurations.putAll(corsConfigurations);
105                }
106        }
107
108        /**
109         * Get the CORS configuration.
110         */
111        public Map<String, CorsConfiguration> getCorsConfigurations() {
112                return Collections.unmodifiableMap(this.corsConfigurations);
113        }
114
115        /**
116         * Register a {@link CorsConfiguration} for the specified path pattern.
117         */
118        public void registerCorsConfiguration(String path, CorsConfiguration config) {
119                this.corsConfigurations.put(path, config);
120        }
121
122
123        @Override
124        public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
125                String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
126                for (Map.Entry<String, CorsConfiguration> entry : this.corsConfigurations.entrySet()) {
127                        if (this.pathMatcher.match(entry.getKey(), lookupPath)) {
128                                return entry.getValue();
129                        }
130                }
131                return null;
132        }
133
134}