View Javadoc
1   /*
2    * Copyright 2013-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5    * the License. You may obtain a copy of the License at
6    *
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10   * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11   * specific language governing permissions and limitations under the License.
12   */
13  
14  package org.springframework.security.oauth2.config.annotation.web.configuration;
15  
16  import java.util.Collections;
17  import java.util.List;
18  import java.util.Set;
19  
20  import javax.annotation.PostConstruct;
21  
22  import org.springframework.beans.BeansException;
23  import org.springframework.beans.factory.BeanCreationException;
24  import org.springframework.beans.factory.BeanFactoryUtils;
25  import org.springframework.beans.factory.FactoryBean;
26  import org.springframework.beans.factory.annotation.Autowired;
27  import org.springframework.beans.factory.config.AbstractFactoryBean;
28  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
29  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
30  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
31  import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
32  import org.springframework.context.annotation.Bean;
33  import org.springframework.context.annotation.Configuration;
34  import org.springframework.context.annotation.Import;
35  import org.springframework.http.HttpMethod;
36  import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
37  import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration.TokenKeyEndpointRegistrar;
38  import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
39  import org.springframework.security.oauth2.provider.ClientDetailsService;
40  import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
41  import org.springframework.security.oauth2.provider.OAuth2RequestValidator;
42  import org.springframework.security.oauth2.provider.TokenGranter;
43  import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
44  import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
45  import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint;
46  import org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint;
47  import org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping;
48  import org.springframework.security.oauth2.provider.endpoint.RedirectResolver;
49  import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
50  import org.springframework.security.oauth2.provider.endpoint.TokenKeyEndpoint;
51  import org.springframework.security.oauth2.provider.endpoint.WhitelabelApprovalEndpoint;
52  import org.springframework.security.oauth2.provider.endpoint.WhitelabelErrorEndpoint;
53  import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
54  import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
55  import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
56  import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
57  import org.springframework.stereotype.Component;
58  
59  /**
60   * @author Dave Syer
61   *
62   */
63  @Configuration
64  @Import(TokenKeyEndpointRegistrar.class)
65  public class AuthorizationServerEndpointsConfiguration {
66  
67  	private AuthorizationServerEndpointsConfigureron/web/configurers/AuthorizationServerEndpointsConfigurer.html#AuthorizationServerEndpointsConfigurer">AuthorizationServerEndpointsConfigurer endpoints = new AuthorizationServerEndpointsConfigurer();
68  
69  	@Autowired
70  	private ClientDetailsService clientDetailsService;
71  
72  	@Autowired
73  	private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
74  
75  	@PostConstruct
76  	public void init() {
77  		for (AuthorizationServerConfigurer configurer : configurers) {
78  			try {
79  				configurer.configure(endpoints);
80  			} catch (Exception e) {
81  				throw new IllegalStateException("Cannot configure enpdoints", e);
82  			}
83  		}
84  		endpoints.setClientDetailsService(clientDetailsService);
85  	}
86  
87  	@Bean
88  	public AuthorizationEndpoint authorizationEndpoint() throws Exception {
89  		AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();
90  		FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
91  		authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
92  		authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator());
93  		authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
94  		authorizationEndpoint.setTokenGranter(tokenGranter());
95  		authorizationEndpoint.setClientDetailsService(clientDetailsService);
96  		authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices());
97  		authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
98  		authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
99  		authorizationEndpoint.setUserApprovalHandler(userApprovalHandler());
100 		authorizationEndpoint.setRedirectResolver(redirectResolver());
101 		return authorizationEndpoint;
102 	}
103 
104 	@Bean
105 	public TokenEndpoint tokenEndpoint() throws Exception {
106 		TokenEndpoint tokenEndpoint = new TokenEndpoint();
107 		tokenEndpoint.setClientDetailsService(clientDetailsService);
108 		tokenEndpoint.setProviderExceptionHandler(exceptionTranslator());
109 		tokenEndpoint.setTokenGranter(tokenGranter());
110 		tokenEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
111 		tokenEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
112 		tokenEndpoint.setAllowedRequestMethods(allowedTokenEndpointRequestMethods());
113 		return tokenEndpoint;
114 	}
115 
116 	@Bean
117 	public CheckTokenEndpoint checkTokenEndpoint() {
118 		CheckTokenEndpoint endpoint = new CheckTokenEndpoint(getEndpointsConfigurer().getResourceServerTokenServices());
119 		endpoint.setAccessTokenConverter(getEndpointsConfigurer().getAccessTokenConverter());
120 		endpoint.setExceptionTranslator(exceptionTranslator());
121 		return endpoint;
122 	}
123 
124 	@Bean
125 	public WhitelabelApprovalEndpoint whitelabelApprovalEndpoint() {
126 		return new WhitelabelApprovalEndpoint();
127 	}
128 
129 	@Bean
130 	public WhitelabelErrorEndpoint whitelabelErrorEndpoint() {
131 		return new WhitelabelErrorEndpoint();
132 	}
133 
134 	@Bean
135 	public FrameworkEndpointHandlerMapping oauth2EndpointHandlerMapping() throws Exception {
136 		return getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
137 	}
138 
139 	@Bean
140 	public FactoryBean<ConsumerTokenServices> consumerTokenServices() throws Exception {
141 		return new AbstractFactoryBean<ConsumerTokenServices>() {
142 
143 			@Override
144 			public Class<?> getObjectType() {
145 				return ConsumerTokenServices.class;
146 			}
147 
148 			@Override
149 			protected ConsumerTokenServices createInstance() throws Exception {
150 				return getEndpointsConfigurer().getConsumerTokenServices();
151 			}
152 		};
153 	}
154 
155 	/**
156 	 * This needs to be a <code>@Bean</code> so that it can be
157 	 * <code>@Transactional</code> (in case the token store supports them). If
158 	 * you are overriding the token services in an
159 	 * {@link AuthorizationServerConfigurer} consider making it a
160 	 * <code>@Bean</code> for the same reason (assuming you need transactions,
161 	 * e.g. for a JDBC token store).
162 	 * 
163 	 * @return an AuthorizationServerTokenServices
164 	 */
165 	@Bean
166 	public FactoryBean<AuthorizationServerTokenServices> defaultAuthorizationServerTokenServices() {
167 		return new AuthorizationServerTokenServicesFactoryBean(endpoints);
168 	}
169 
170 	public AuthorizationServerEndpointsConfigurer getEndpointsConfigurer() {
171 		if (!endpoints.isTokenServicesOverride()) {
172 			try {
173 				endpoints.tokenServices(endpoints.getDefaultAuthorizationServerTokenServices());
174 			}
175 			catch (Exception e) {
176 				throw new BeanCreationException("Cannot create token services", e);
177 			}
178 		}
179 		return endpoints;
180 	}
181 
182 	private Set<HttpMethod> allowedTokenEndpointRequestMethods() {
183 		return getEndpointsConfigurer().getAllowedTokenEndpointRequestMethods();
184 	}
185 
186 	private OAuth2RequestFactory oauth2RequestFactory() throws Exception {
187 		return getEndpointsConfigurer().getOAuth2RequestFactory();
188 	}
189 
190 	private UserApprovalHandler userApprovalHandler() throws Exception {
191 		return getEndpointsConfigurer().getUserApprovalHandler();
192 	}
193 
194 	private OAuth2RequestValidator oauth2RequestValidator() throws Exception {
195 		return getEndpointsConfigurer().getOAuth2RequestValidator();
196 	}
197 
198 	private AuthorizationCodeServices authorizationCodeServices() throws Exception {
199 		return getEndpointsConfigurer().getAuthorizationCodeServices();
200 	}
201 
202 	private WebResponseExceptionTranslator<OAuth2Exception> exceptionTranslator() {
203 		return getEndpointsConfigurer().getExceptionTranslator();
204 	}
205 
206 	private RedirectResolver redirectResolver() {
207 		return getEndpointsConfigurer().getRedirectResolver();
208 	}
209 
210 	private TokenGranter tokenGranter() throws Exception {
211 		return getEndpointsConfigurer().getTokenGranter();
212 	}
213 
214 	private String extractPath(FrameworkEndpointHandlerMapping mapping, String page) {
215 		String path = mapping.getPath(page);
216 		if (path.contains(":")) {
217 			return path;
218 		}
219 		return "forward:" + path;
220 	}
221 
222 	protected static class AuthorizationServerTokenServicesFactoryBean
223 			extends AbstractFactoryBean<AuthorizationServerTokenServices> {
224 
225 		private AuthorizationServerEndpointsConfigurer endpoints;
226 		
227 		protected AuthorizationServerTokenServicesFactoryBean() {
228 		}
229 
230 		public AuthorizationServerTokenServicesFactoryBean(
231 				AuthorizationServerEndpointsConfigurer endpoints) {
232 					this.endpoints = endpoints;
233 		}
234 
235 		@Override
236 		public Class<?> getObjectType() {
237 			return AuthorizationServerTokenServices.class;
238 		}
239 
240 		@Override
241 		protected AuthorizationServerTokenServices createInstance() throws Exception {
242 			return endpoints.getDefaultAuthorizationServerTokenServices();
243 		}
244 	}
245 
246 	@Component
247 	protected static class TokenKeyEndpointRegistrar implements BeanDefinitionRegistryPostProcessor {
248 
249 		private BeanDefinitionRegistry registry;
250 
251 		@Override
252 		public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
253 			String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory,
254 					JwtAccessTokenConverter.class, false, false);
255 			if (names.length > 0) {
256 				BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(TokenKeyEndpoint.class);
257 				builder.addConstructorArgReference(names[0]);
258 				registry.registerBeanDefinition(TokenKeyEndpoint.class.getName(), builder.getBeanDefinition());
259 			}
260 		}
261 
262 		@Override
263 		public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
264 			this.registry = registry;
265 		}
266 
267 	}
268 
269 }