View Javadoc
1   /*
2    * Copyright 2002-2013 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of 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,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.springframework.security.oauth2.config.annotation.web.configuration;
17  
18  import java.util.Collections;
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.Map;
22  
23  import javax.servlet.http.HttpServletRequest;
24  
25  import org.springframework.aop.framework.Advised;
26  import org.springframework.beans.factory.annotation.Autowired;
27  import org.springframework.context.ApplicationContext;
28  import org.springframework.context.annotation.Configuration;
29  import org.springframework.core.Ordered;
30  import org.springframework.security.authentication.AnonymousAuthenticationProvider;
31  import org.springframework.security.authentication.AuthenticationEventPublisher;
32  import org.springframework.security.config.annotation.web.builders.HttpSecurity;
33  import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
34  import org.springframework.security.config.http.SessionCreationPolicy;
35  import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
36  import org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping;
37  import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
38  import org.springframework.security.oauth2.provider.token.TokenStore;
39  import org.springframework.security.web.util.matcher.RequestMatcher;
40  import org.springframework.util.ReflectionUtils;
41  
42  /**
43   * @author Dave Syer
44   * 
45   */
46  @Configuration
47  public class ResourceServerConfiguration extends WebSecurityConfigurerAdapter implements Ordered {
48  
49  	private int order = 3;
50  
51  	@Autowired(required = false)
52  	private TokenStore tokenStore;
53  
54  	@Autowired(required = false)
55  	private AuthenticationEventPublisher eventPublisher;
56  
57  	@Autowired(required = false)
58  	private Map<String, ResourceServerTokenServices> tokenServices;
59  
60  	@Autowired
61  	private ApplicationContext context;
62  
63  	private List<ResourceServerConfigurer> configurers = Collections.emptyList();
64  
65  	@Autowired(required = false)
66  	private AuthorizationServerEndpointsConfiguration endpoints;
67  
68  	@Override
69  	public int getOrder() {
70  		return order;
71  	}
72  
73  	public void setOrder(int order) {
74  		this.order = order;
75  	}
76  
77  	/**
78  	 * @param configurers the configurers to set
79  	 */
80  	@Autowired(required = false)
81  	public void setConfigurers(List<ResourceServerConfigurer> configurers) {
82  		this.configurers = configurers;
83  	}
84  
85  	private static class NotOAuthRequestMatcher implements RequestMatcher {
86  
87  		private FrameworkEndpointHandlerMapping mapping;
88  
89  		public NotOAuthRequestMatcher(FrameworkEndpointHandlerMapping mapping) {
90  			this.mapping = mapping;
91  		}
92  
93  		@Override
94  		public boolean matches(HttpServletRequest request) {
95  			String requestPath = getRequestPath(request);
96  			for (String path : mapping.getPaths()) {
97  				if (requestPath.startsWith(mapping.getPath(path))) {
98  					return false;
99  				}
100 			}
101 			return true;
102 		}
103 
104 		private String getRequestPath(HttpServletRequest request) {
105 			String url = request.getServletPath();
106 
107 			if (request.getPathInfo() != null) {
108 				url += request.getPathInfo();
109 			}
110 
111 			return url;
112 		}
113 
114 	}
115 
116 	@Override
117 	protected void configure(HttpSecurity http) throws Exception {
118 		ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer();
119 		ResourceServerTokenServices services = resolveTokenServices();
120 		if (services != null) {
121 			resources.tokenServices(services);
122 		}
123 		else {
124 			if (tokenStore != null) {
125 				resources.tokenStore(tokenStore);
126 			}
127 			else if (endpoints != null) {
128 				resources.tokenStore(endpoints.getEndpointsConfigurer().getTokenStore());
129 			}
130 		}
131 		if (eventPublisher != null) {
132 			resources.eventPublisher(eventPublisher);
133 		}
134 		for (ResourceServerConfigurer configurer : configurers) {
135 			configurer.configure(resources);
136 		}
137 		// @formatter:off
138 		http.authenticationProvider(new AnonymousAuthenticationProvider("default"))
139 		// N.B. exceptionHandling is duplicated in resources.configure() so that
140 		// it works
141 		.exceptionHandling()
142 				.accessDeniedHandler(resources.getAccessDeniedHandler()).and()
143 				.sessionManagement()
144 				.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
145 				.csrf().disable();
146 		// @formatter:on
147 		http.apply(resources);
148 		if (endpoints != null) {
149 			// Assume we are in an Authorization Server
150 			http.requestMatcher(new NotOAuthRequestMatcher(endpoints.oauth2EndpointHandlerMapping()));
151 		}
152 		for (ResourceServerConfigurer configurer : configurers) {
153 			// Delegates can add authorizeRequests() here
154 			configurer.configure(http);
155 		}
156 		if (configurers.isEmpty()) {
157 			// Add anyRequest() last as a fall back. Spring Security would
158 			// replace an existing anyRequest() matcher with this one, so to
159 			// avoid that we only add it if the user hasn't configured anything.
160 			http.authorizeRequests().anyRequest().authenticated();
161 		}
162 	}
163 
164 	private ResourceServerTokenServices resolveTokenServices() {
165 		if (tokenServices == null || tokenServices.size() == 0) {
166 			return null;
167 		}
168 		if (tokenServices.size() == 1) {
169 			return tokenServices.values().iterator().next();
170 		}
171 		if (tokenServices.size() == 2) {
172 			// Maybe they are the ones provided natively
173 			Iterator<ResourceServerTokenServices> iter = tokenServices.values().iterator();
174 			ResourceServerTokenServices one = iter.next();
175 			ResourceServerTokenServices two = iter.next();
176 			if (elementsEqual(one, two)) {
177 				return one;
178 			}
179 		}
180 		return context.getBean(ResourceServerTokenServices.class);
181 	}
182 
183 	private boolean elementsEqual(Object one, Object two) {
184 		// They might just be equal
185 		if (one == two) {
186 			return true;
187 		}
188 		Object targetOne = findTarget(one);
189 		Object targetTwo = findTarget(two);
190 		return targetOne == targetTwo;
191 	}
192 
193 	private Object findTarget(Object item) {
194 		Object current = item;
195 		while (current instanceof Advised) {
196 			try {
197 				current = ((Advised) current).getTargetSource().getTarget();
198 			}
199 			catch (Exception e) {
200 				ReflectionUtils.rethrowRuntimeException(e);
201 			}
202 		}
203 		return current;
204 	}
205 
206 }