001/* 002 * Copyright 2012-2018 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 * http://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.boot.autoconfigure.security.servlet; 018 019import java.util.EnumSet; 020import java.util.LinkedHashSet; 021import java.util.List; 022import java.util.Set; 023import java.util.function.Supplier; 024import java.util.stream.Collectors; 025import java.util.stream.Stream; 026 027import javax.servlet.http.HttpServletRequest; 028 029import org.springframework.boot.autoconfigure.security.StaticResourceLocation; 030import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; 031import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher; 032import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 033import org.springframework.security.web.util.matcher.OrRequestMatcher; 034import org.springframework.security.web.util.matcher.RequestMatcher; 035import org.springframework.util.Assert; 036 037/** 038 * Used to create a {@link RequestMatcher} for static resources in commonly used 039 * locations. Returned by {@link PathRequest#toStaticResources()}. 040 * 041 * @author Madhura Bhave 042 * @author Phillip Webb 043 * @since 2.0.0 044 * @see PathRequest 045 */ 046public final class StaticResourceRequest { 047 048 static final StaticResourceRequest INSTANCE = new StaticResourceRequest(); 049 050 private StaticResourceRequest() { 051 } 052 053 /** 054 * Returns a matcher that includes all commonly used {@link StaticResourceLocation 055 * Locations}. The 056 * {@link StaticResourceRequestMatcher#excluding(StaticResourceLocation, StaticResourceLocation...) 057 * excluding} method can be used to remove specific locations if required. For 058 * example: <pre class="code"> 059 * PathRequest.toStaticResources().atCommonLocations().excluding(StaticResourceLocation.CSS) 060 * </pre> 061 * @return the configured {@link RequestMatcher} 062 */ 063 public StaticResourceRequestMatcher atCommonLocations() { 064 return at(EnumSet.allOf(StaticResourceLocation.class)); 065 } 066 067 /** 068 * Returns a matcher that includes the specified {@link StaticResourceLocation 069 * Locations}. For example: <pre class="code"> 070 * PathRequest.toStaticResources().at(StaticResourceLocation.CSS, StaticResourceLocation.JAVA_SCRIPT) 071 * </pre> 072 * @param first the first location to include 073 * @param rest additional locations to include 074 * @return the configured {@link RequestMatcher} 075 */ 076 public StaticResourceRequestMatcher at(StaticResourceLocation first, 077 StaticResourceLocation... rest) { 078 return at(EnumSet.of(first, rest)); 079 } 080 081 /** 082 * Returns a matcher that includes the specified {@link StaticResourceLocation 083 * Locations}. For example: <pre class="code"> 084 * PathRequest.toStaticResources().at(locations) 085 * </pre> 086 * @param locations the locations to include 087 * @return the configured {@link RequestMatcher} 088 */ 089 public StaticResourceRequestMatcher at(Set<StaticResourceLocation> locations) { 090 Assert.notNull(locations, "Locations must not be null"); 091 return new StaticResourceRequestMatcher(new LinkedHashSet<>(locations)); 092 } 093 094 /** 095 * The request matcher used to match against resource {@link StaticResourceLocation 096 * Locations}. 097 */ 098 public static final class StaticResourceRequestMatcher 099 extends ApplicationContextRequestMatcher<DispatcherServletPath> { 100 101 private final Set<StaticResourceLocation> locations; 102 103 private volatile RequestMatcher delegate; 104 105 private StaticResourceRequestMatcher(Set<StaticResourceLocation> locations) { 106 super(DispatcherServletPath.class); 107 this.locations = locations; 108 } 109 110 /** 111 * Return a new {@link StaticResourceRequestMatcher} based on this one but 112 * excluding the specified locations. 113 * @param first the first location to exclude 114 * @param rest additional locations to exclude 115 * @return a new {@link StaticResourceRequestMatcher} 116 */ 117 public StaticResourceRequestMatcher excluding(StaticResourceLocation first, 118 StaticResourceLocation... rest) { 119 return excluding(EnumSet.of(first, rest)); 120 } 121 122 /** 123 * Return a new {@link StaticResourceRequestMatcher} based on this one but 124 * excluding the specified locations. 125 * @param locations the locations to exclude 126 * @return a new {@link StaticResourceRequestMatcher} 127 */ 128 public StaticResourceRequestMatcher excluding( 129 Set<StaticResourceLocation> locations) { 130 Assert.notNull(locations, "Locations must not be null"); 131 Set<StaticResourceLocation> subset = new LinkedHashSet<>(this.locations); 132 subset.removeAll(locations); 133 return new StaticResourceRequestMatcher(subset); 134 } 135 136 @Override 137 protected void initialized( 138 Supplier<DispatcherServletPath> dispatcherServletPath) { 139 this.delegate = new OrRequestMatcher( 140 getDelegateMatchers(dispatcherServletPath.get())); 141 } 142 143 private List<RequestMatcher> getDelegateMatchers( 144 DispatcherServletPath dispatcherServletPath) { 145 return getPatterns(dispatcherServletPath).map(AntPathRequestMatcher::new) 146 .collect(Collectors.toList()); 147 } 148 149 private Stream<String> getPatterns(DispatcherServletPath dispatcherServletPath) { 150 return this.locations.stream().flatMap(StaticResourceLocation::getPatterns) 151 .map(dispatcherServletPath::getRelativePath); 152 } 153 154 @Override 155 protected boolean matches(HttpServletRequest request, 156 Supplier<DispatcherServletPath> context) { 157 return this.delegate.matches(request); 158 } 159 160 } 161 162}