View Javadoc
1   /*
2    * Copyright 2002-2013 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    *
8    * https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package org.springframework.security.oauth2.config.annotation.web.configurers;
17  
18  import java.util.Collections;
19  
20  import org.springframework.http.MediaType;
21  import org.springframework.security.access.expression.SecurityExpressionHandler;
22  import org.springframework.security.authentication.AuthenticationDetailsSource;
23  import org.springframework.security.authentication.AuthenticationEventPublisher;
24  import org.springframework.security.authentication.AuthenticationManager;
25  import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
26  import org.springframework.security.config.annotation.web.builders.HttpSecurity;
27  import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
28  import org.springframework.security.oauth2.provider.ClientDetailsService;
29  import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetailsSource;
30  import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager;
31  import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter;
32  import org.springframework.security.oauth2.provider.authentication.TokenExtractor;
33  import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
34  import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
35  import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
36  import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
37  import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
38  import org.springframework.security.oauth2.provider.token.TokenStore;
39  import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
40  import org.springframework.security.web.AuthenticationEntryPoint;
41  import org.springframework.security.web.DefaultSecurityFilterChain;
42  import org.springframework.security.web.FilterInvocation;
43  import org.springframework.security.web.access.AccessDeniedHandler;
44  import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
45  import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
46  import org.springframework.util.Assert;
47  import org.springframework.web.accept.ContentNegotiationStrategy;
48  import org.springframework.web.accept.HeaderContentNegotiationStrategy;
49  
50  import javax.servlet.http.HttpServletRequest;
51  
52  /**
53   *
54   * @author Rob Winch
55   * @author Dave Syer
56   * 
57   * @since 2.0.0
58   */
59  public final class ResourceServerSecurityConfigurer extends
60  		SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
61  
62  	private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
63  
64  	private AccessDeniedHandler accessDeniedHandler = new OAuth2AccessDeniedHandler();
65  
66  	private OAuth2AuthenticationProcessingFilter resourcesServerFilter;
67  
68  	private AuthenticationManager authenticationManager;
69  
70  	private AuthenticationEventPublisher eventPublisher = null;
71  
72  	private ResourceServerTokenServices resourceTokenServices;
73  
74  	private TokenStore tokenStore = new InMemoryTokenStore();
75  
76  	private String resourceId = "oauth2-resource";
77  
78  	private SecurityExpressionHandler<FilterInvocation> expressionHandler = new OAuth2WebSecurityExpressionHandler();
79  
80  	private TokenExtractor tokenExtractor;
81  
82  	private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
83  
84  	private boolean stateless = true;
85  
86  	public ResourceServerSecurityConfigurer() {
87  		resourceId(resourceId);
88  	}
89  
90  	private ClientDetailsService clientDetails() {
91  		return getBuilder().getSharedObject(ClientDetailsService.class);
92  	}
93  
94  	public TokenStore getTokenStore() {
95  		return tokenStore;
96  	}
97  
98  	/**
99  	 * Flag to indicate that only token-based authentication is allowed on these resources.
100 	 * @param stateless the flag value (default true)
101 	 * @return this (for fluent builder)
102 	 */
103 	public ResourceServerSecurityConfigurer stateless(boolean stateless) {
104 		this.stateless = stateless;
105 		return this;
106 	}
107 
108 	public ResourceServerSecurityConfigurer authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
109 		this.authenticationEntryPoint = authenticationEntryPoint;
110 		return this;
111 	}
112 
113 	public ResourceServerSecurityConfigurer accessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
114 		this.accessDeniedHandler = accessDeniedHandler;
115 		return this;
116 	}
117 
118 	public ResourceServerSecurityConfigurer tokenStore(TokenStore tokenStore) {
119 		Assert.state(tokenStore != null, "TokenStore cannot be null");
120 		this.tokenStore = tokenStore;
121 		return this;
122 	}
123 
124 	public ResourceServerSecurityConfigurer eventPublisher(AuthenticationEventPublisher eventPublisher) {
125 		Assert.state(eventPublisher != null, "AuthenticationEventPublisher cannot be null");
126 		this.eventPublisher = eventPublisher;
127 		return this;
128 	}
129 
130 	public ResourceServerSecurityConfigurer expressionHandler(
131 			SecurityExpressionHandler<FilterInvocation> expressionHandler) {
132 		Assert.state(expressionHandler != null, "SecurityExpressionHandler cannot be null");
133 		this.expressionHandler = expressionHandler;
134 		return this;
135 	}
136 
137 	public ResourceServerSecurityConfigurer tokenExtractor(TokenExtractor tokenExtractor) {
138 		Assert.state(tokenExtractor != null, "TokenExtractor cannot be null");
139 		this.tokenExtractor = tokenExtractor;
140 		return this;
141 	}
142 
143 	/**
144 	 * Sets a custom {@link AuthenticationDetailsSource} to use as a source
145 	 * of authentication details. The default is {@link OAuth2AuthenticationDetailsSource}.
146 	 *
147 	 * @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource} to use
148 	 * @return {@link ResourceServerSecurityConfigurer} for additional customization
149 	 */
150 	public ResourceServerSecurityConfigurer authenticationDetailsSource(
151 			AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
152 		Assert.state(authenticationDetailsSource != null, "AuthenticationDetailsSource cannot be null");
153 		this.authenticationDetailsSource = authenticationDetailsSource;
154 		return this;
155 	}
156 
157 	public ResourceServerSecurityConfigurer authenticationManager(AuthenticationManager authenticationManager) {
158 		Assert.state(authenticationManager != null, "AuthenticationManager cannot be null");
159 		this.authenticationManager = authenticationManager;
160 		return this;
161 	}
162 
163 	public ResourceServerSecurityConfigurer tokenServices(ResourceServerTokenServices tokenServices) {
164 		Assert.state(tokenServices != null, "ResourceServerTokenServices cannot be null");
165 		this.resourceTokenServices = tokenServices;
166 		return this;
167 	}
168 
169 	@Override
170 	public void init(HttpSecurity http) throws Exception {
171 		registerDefaultAuthenticationEntryPoint(http);
172 	}
173 
174 	@SuppressWarnings("unchecked")
175 	private void registerDefaultAuthenticationEntryPoint(HttpSecurity http) {
176 		ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = http
177 				.getConfigurer(ExceptionHandlingConfigurer.class);
178 		if (exceptionHandling == null) {
179 			return;
180 		}
181 		ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
182 		if (contentNegotiationStrategy == null) {
183 			contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
184 		}
185 		MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
186 				MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,
187 				MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA,
188 				MediaType.TEXT_XML);
189 		preferredMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
190 		exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint), preferredMatcher);
191 	}
192 
193 	public ResourceServerSecurityConfigurer resourceId(String resourceId) {
194 		this.resourceId = resourceId;
195 		if (authenticationEntryPoint instanceof OAuth2AuthenticationEntryPoint) {
196 			((OAuth2AuthenticationEntryPoint) authenticationEntryPoint).setRealmName(resourceId);
197 		}
198 		return this;
199 	}
200 
201 	@Override
202 	public void configure(HttpSecurity http) throws Exception {
203 
204 		AuthenticationManager oauthAuthenticationManager = oauthAuthenticationManager(http);
205 		resourcesServerFilter = new OAuth2AuthenticationProcessingFilter();
206 		resourcesServerFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
207 		resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager);
208 		if (eventPublisher != null) {
209 			resourcesServerFilter.setAuthenticationEventPublisher(eventPublisher);
210 		}
211 		if (tokenExtractor != null) {
212 			resourcesServerFilter.setTokenExtractor(tokenExtractor);
213 		}
214 		if (authenticationDetailsSource != null) {
215 			resourcesServerFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
216 		}
217 		resourcesServerFilter = postProcess(resourcesServerFilter);
218 		resourcesServerFilter.setStateless(stateless);
219 
220 		// @formatter:off
221 		http
222 			.authorizeRequests().expressionHandler(expressionHandler)
223 		.and()
224 			.addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class)
225 			.exceptionHandling()
226 				.accessDeniedHandler(accessDeniedHandler)
227 				.authenticationEntryPoint(authenticationEntryPoint);
228 		// @formatter:on
229 	}
230 
231 	private AuthenticationManager oauthAuthenticationManager(HttpSecurity http) {
232 		OAuth2AuthenticationManager oauthAuthenticationManager = new OAuth2AuthenticationManager();
233 		if (authenticationManager != null) {
234 			if (authenticationManager instanceof OAuth2AuthenticationManager) {
235 				oauthAuthenticationManager = (OAuth2AuthenticationManager) authenticationManager;
236 			}
237 			else {
238 				return authenticationManager;
239 			}
240 		}
241 		oauthAuthenticationManager.setResourceId(resourceId);
242 		oauthAuthenticationManager.setTokenServices(resourceTokenServices(http));
243 		oauthAuthenticationManager.setClientDetailsService(clientDetails());
244 		return oauthAuthenticationManager;
245 	}
246 
247 	private ResourceServerTokenServices resourceTokenServices(HttpSecurity http) {
248 		tokenServices(http);
249 		return this.resourceTokenServices;
250 	}
251 
252 	private ResourceServerTokenServices tokenServices(HttpSecurity http) {
253 		if (resourceTokenServices != null) {
254 			return resourceTokenServices;
255 		}
256 		DefaultTokenServices tokenServices = new DefaultTokenServices();
257 		tokenServices.setTokenStore(tokenStore());
258 		tokenServices.setSupportRefreshToken(true);
259 		tokenServices.setClientDetailsService(clientDetails());
260 		this.resourceTokenServices = tokenServices;
261 		return tokenServices;
262 	}
263 
264 	private TokenStore tokenStore() {
265 		Assert.state(tokenStore != null, "TokenStore cannot be null");
266 		return this.tokenStore;
267 	}
268 
269 	public AccessDeniedHandler getAccessDeniedHandler() {
270 		return this.accessDeniedHandler;
271 	}
272 
273 }