001/* 002 * Copyright 2002-2019 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; 022 023import javax.servlet.http.HttpServletRequest; 024 025import org.springframework.lang.Nullable; 026import org.springframework.util.AntPathMatcher; 027import org.springframework.util.Assert; 028import org.springframework.util.PathMatcher; 029import org.springframework.web.util.UrlPathHelper; 030 031/** 032 * Provide a per request {@link CorsConfiguration} instance based on a 033 * collection of {@link CorsConfiguration} mapped on path patterns. 034 * 035 * <p>Exact path mapping URIs (such as {@code "/admin"}) are supported 036 * as well as Ant-style path patterns (such as {@code "/admin/**"}). 037 * 038 * @author Sebastien Deleuze 039 * @since 4.2 040 */ 041public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource { 042 043 private final Map<String, CorsConfiguration> corsConfigurations = new LinkedHashMap<>(); 044 045 private PathMatcher pathMatcher = new AntPathMatcher(); 046 047 private UrlPathHelper urlPathHelper = new UrlPathHelper(); 048 049 @Nullable 050 private String lookupPathAttributeName; 051 052 053 /** 054 * Set the PathMatcher implementation to use for matching URL paths 055 * against registered URL patterns. Default is AntPathMatcher. 056 * @see org.springframework.util.AntPathMatcher 057 */ 058 public void setPathMatcher(PathMatcher pathMatcher) { 059 Assert.notNull(pathMatcher, "PathMatcher must not be null"); 060 this.pathMatcher = pathMatcher; 061 } 062 063 /** 064 * Shortcut to same property on underlying {@link #setUrlPathHelper UrlPathHelper}. 065 * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath 066 */ 067 public void setAlwaysUseFullPath(boolean alwaysUseFullPath) { 068 this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath); 069 } 070 071 /** 072 * Shortcut to same property on underlying {@link #setUrlPathHelper UrlPathHelper}. 073 * @see org.springframework.web.util.UrlPathHelper#setUrlDecode 074 */ 075 public void setUrlDecode(boolean urlDecode) { 076 this.urlPathHelper.setUrlDecode(urlDecode); 077 } 078 079 /** 080 * Optionally configure the name of the attribute that caches the lookupPath. 081 * This is used to make the call to 082 * {@link UrlPathHelper#getLookupPathForRequest(HttpServletRequest, String)} 083 * @param lookupPathAttributeName the request attribute to check 084 * @since 5.2 085 */ 086 public void setLookupPathAttributeName(@Nullable String lookupPathAttributeName) { 087 this.lookupPathAttributeName = lookupPathAttributeName; 088 } 089 090 /** 091 * Shortcut to same property on underlying {@link #setUrlPathHelper UrlPathHelper}. 092 * @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean) 093 */ 094 public void setRemoveSemicolonContent(boolean removeSemicolonContent) { 095 this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent); 096 } 097 098 /** 099 * Set the UrlPathHelper to use for resolution of lookup paths. 100 * <p>Use this to override the default UrlPathHelper with a custom subclass. 101 */ 102 public void setUrlPathHelper(UrlPathHelper urlPathHelper) { 103 Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); 104 this.urlPathHelper = urlPathHelper; 105 } 106 107 /** 108 * Set CORS configuration based on URL patterns. 109 */ 110 public void setCorsConfigurations(@Nullable Map<String, CorsConfiguration> corsConfigurations) { 111 this.corsConfigurations.clear(); 112 if (corsConfigurations != null) { 113 this.corsConfigurations.putAll(corsConfigurations); 114 } 115 } 116 117 /** 118 * Get the CORS configuration. 119 */ 120 public Map<String, CorsConfiguration> getCorsConfigurations() { 121 return Collections.unmodifiableMap(this.corsConfigurations); 122 } 123 124 /** 125 * Register a {@link CorsConfiguration} for the specified path pattern. 126 */ 127 public void registerCorsConfiguration(String path, CorsConfiguration config) { 128 this.corsConfigurations.put(path, config); 129 } 130 131 132 @Override 133 @Nullable 134 public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { 135 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, this.lookupPathAttributeName); 136 for (Map.Entry<String, CorsConfiguration> entry : this.corsConfigurations.entrySet()) { 137 if (this.pathMatcher.match(entry.getKey(), lookupPath)) { 138 return entry.getValue(); 139 } 140 } 141 return null; 142 } 143 144}