1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.security.oauth.provider.filter;
18
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.springframework.beans.factory.InitializingBean;
22 import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.context.MessageSource;
24 import org.springframework.context.MessageSourceAware;
25 import org.springframework.context.support.MessageSourceAccessor;
26 import org.springframework.security.core.Authentication;
27 import org.springframework.security.core.AuthenticationException;
28 import org.springframework.security.core.SpringSecurityMessageSource;
29 import org.springframework.security.core.context.SecurityContextHolder;
30 import org.springframework.security.oauth.common.OAuthConsumerParameter;
31 import org.springframework.security.oauth.common.OAuthException;
32 import org.springframework.security.oauth.common.signature.*;
33 import org.springframework.security.oauth.provider.ConsumerAuthentication;
34 import org.springframework.security.oauth.provider.ConsumerCredentials;
35 import org.springframework.security.oauth.provider.ConsumerDetails;
36 import org.springframework.security.oauth.provider.ConsumerDetailsService;
37 import org.springframework.security.oauth.provider.InvalidOAuthParametersException;
38 import org.springframework.security.oauth.provider.OAuthAuthenticationDetails;
39 import org.springframework.security.oauth.provider.OAuthProcessingFilterEntryPoint;
40 import org.springframework.security.oauth.provider.OAuthProviderSupport;
41 import org.springframework.security.oauth.provider.OAuthVersionUnsupportedException;
42 import org.springframework.security.oauth.provider.nonce.ExpiringTimestampNonceServices;
43 import org.springframework.security.oauth.provider.nonce.OAuthNonceServices;
44 import org.springframework.security.oauth.provider.token.OAuthProviderToken;
45 import org.springframework.security.oauth.provider.token.OAuthProviderTokenServices;
46 import org.springframework.util.Assert;
47
48 import javax.servlet.*;
49 import javax.servlet.http.HttpServletRequest;
50 import javax.servlet.http.HttpServletResponse;
51 import java.io.IOException;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.List;
55 import java.util.Map;
56
57
58
59
60
61
62 public abstract class OAuthProviderProcessingFilter implements Filter, InitializingBean, MessageSourceAware {
63
64
65
66
67 public static final String OAUTH_PROCESSING_HANDLED = "org.springframework.security.oauth.provider.OAuthProviderProcessingFilter#SKIP_PROCESSING";
68
69 private final Log log = LogFactory.getLog(getClass());
70 private final List<String> allowedMethods = new ArrayList<String>(Arrays.asList("GET", "POST"));
71 private OAuthProcessingFilterEntryPointEntryPoint.html#OAuthProcessingFilterEntryPoint">OAuthProcessingFilterEntryPoint authenticationEntryPoint = new OAuthProcessingFilterEntryPoint();
72 protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
73 private String filterProcessesUrl = "/oauth_filter";
74 private OAuthProviderSupport providerSupport = new CoreOAuthProviderSupport();
75 private OAuthSignatureMethodFactory signatureMethodFactory = new CoreOAuthSignatureMethodFactory();
76 private OAuthNonceServices nonceServices = new ExpiringTimestampNonceServices();
77 private boolean ignoreMissingCredentials = false;
78 private OAuthProviderTokenServices tokenServices;
79
80 private ConsumerDetailsService consumerDetailsService;
81
82 public void afterPropertiesSet() throws Exception {
83 Assert.notNull(consumerDetailsService, "A consumer details service is required.");
84 Assert.notNull(tokenServices, "Token services are required.");
85 }
86
87 public void init(FilterConfig ignored) throws ServletException {
88 }
89
90 public void destroy() {
91 }
92
93 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
94 HttpServletRequest request = (HttpServletRequest) servletRequest;
95 HttpServletResponse response = (HttpServletResponse) servletResponse;
96
97 if (!skipProcessing(request)) {
98 if (requiresAuthentication(request, response, chain)) {
99 if (!allowMethod(request.getMethod().toUpperCase())) {
100 if (log.isDebugEnabled()) {
101 log.debug("Method " + request.getMethod() + " not supported.");
102 }
103
104 response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
105 return;
106 }
107
108 try {
109 Map<String, String> oauthParams = getProviderSupport().parseParameters(request);
110
111 if (parametersAreAdequate(oauthParams)) {
112
113 if (log.isDebugEnabled()) {
114 StringBuilder builder = new StringBuilder("OAuth parameters parsed: ");
115 for (String param : oauthParams.keySet()) {
116 builder.append(param).append('=').append(oauthParams.get(param)).append(' ');
117 }
118 log.debug(builder.toString());
119 }
120
121 String consumerKey = oauthParams.get(OAuthConsumerParameter.oauth_consumer_key.toString());
122 if (consumerKey == null) {
123 throw new InvalidOAuthParametersException(messages.getMessage("OAuthProcessingFilter.missingConsumerKey", "Missing consumer key."));
124 }
125
126
127 ConsumerDetails consumerDetails = getConsumerDetailsService().loadConsumerByConsumerKey(consumerKey);
128 if (log.isDebugEnabled()) {
129 log.debug("Consumer details loaded for " + consumerKey + ": " + consumerDetails);
130 }
131
132
133 validateOAuthParams(consumerDetails, oauthParams);
134 if (log.isDebugEnabled()) {
135 log.debug("Parameters validated.");
136 }
137
138
139 String token = oauthParams.get(OAuthConsumerParameter.oauth_token.toString());
140 String signatureMethod = oauthParams.get(OAuthConsumerParameter.oauth_signature_method.toString());
141 String signature = oauthParams.get(OAuthConsumerParameter.oauth_signature.toString());
142 String signatureBaseString = getProviderSupport().getSignatureBaseString(request);
143 ConsumerCredentialsder/ConsumerCredentials.html#ConsumerCredentials">ConsumerCredentials credentials = new ConsumerCredentials(consumerKey, signature, signatureMethod, signatureBaseString, token);
144
145
146 ConsumerAuthenticationnsumerAuthentication.html#ConsumerAuthentication">ConsumerAuthentication authentication = new ConsumerAuthentication(consumerDetails, credentials, oauthParams);
147 authentication.setDetails(createDetails(request, consumerDetails));
148
149 Authentication previousAuthentication = SecurityContextHolder.getContext().getAuthentication();
150 try {
151
152 SecurityContextHolder.getContext().setAuthentication(authentication);
153
154
155 validateSignature(authentication);
156
157
158 authentication.setSignatureValidated(true);
159
160
161 request.setAttribute(OAUTH_PROCESSING_HANDLED, Boolean.TRUE);
162
163 if (log.isDebugEnabled()) {
164 log.debug("Signature validated.");
165 }
166
167
168 onValidSignature(request, response, chain);
169 }
170 finally {
171
172 resetPreviousAuthentication(previousAuthentication);
173 }
174 }
175 else if (!isIgnoreInadequateCredentials()) {
176 throw new InvalidOAuthParametersException(messages.getMessage("OAuthProcessingFilter.missingCredentials", "Inadequate OAuth consumer credentials."));
177 }
178 else {
179 if (log.isDebugEnabled()) {
180 log.debug("Supplied OAuth parameters are inadequate. Ignoring.");
181 }
182 chain.doFilter(request, response);
183 }
184 }
185 catch (AuthenticationException ae) {
186 fail(request, response, ae);
187 }
188 catch (ServletException e) {
189 if (e.getRootCause() instanceof AuthenticationException) {
190 fail(request, response, (AuthenticationException) e.getRootCause());
191 }
192 else {
193 throw e;
194 }
195 }
196 }
197 else {
198 if (log.isDebugEnabled()) {
199 log.debug("Request does not require authentication. OAuth processing skipped.");
200 }
201
202 chain.doFilter(servletRequest, servletResponse);
203 }
204 }
205 else {
206 if (log.isDebugEnabled()) {
207 log.debug("Processing explicitly skipped.");
208 }
209
210 chain.doFilter(servletRequest, servletResponse);
211 }
212 }
213
214
215
216
217
218
219
220 protected boolean parametersAreAdequate(Map<String, String> oauthParams) {
221 return oauthParams.containsKey(OAuthConsumerParameter.oauth_consumer_key.toString());
222 }
223
224 protected void resetPreviousAuthentication(Authentication previousAuthentication) {
225 SecurityContextHolder.getContext().setAuthentication(previousAuthentication);
226 }
227
228
229
230
231
232
233
234
235 protected Object createDetails(HttpServletRequest request, ConsumerDetails consumerDetails) {
236 return new OAuthAuthenticationDetails(request, consumerDetails);
237 }
238
239
240
241
242
243
244
245 protected boolean allowMethod(String method) {
246 return allowedMethods.contains(method);
247 }
248
249
250
251
252
253
254 protected void validateSignature(ConsumerAuthentication authentication) throws AuthenticationException {
255 SignatureSecret secret = authentication.getConsumerDetails().getSignatureSecret();
256 String token = authentication.getConsumerCredentials().getToken();
257 OAuthProviderToken authToken = null;
258 if (token != null && !"".equals(token)) {
259 authToken = getTokenServices().getToken(token);
260 }
261
262 String signatureMethod = authentication.getConsumerCredentials().getSignatureMethod();
263 OAuthSignatureMethod method;
264 try {
265 method = getSignatureMethodFactory().getSignatureMethod(signatureMethod, secret, authToken != null ? authToken.getSecret() : null);
266 }
267 catch (UnsupportedSignatureMethodException e) {
268 throw new OAuthException(e.getMessage(), e);
269 }
270
271 String signatureBaseString = authentication.getConsumerCredentials().getSignatureBaseString();
272 String signature = authentication.getConsumerCredentials().getSignature();
273 if (log.isDebugEnabled()) {
274 log.debug("Verifying signature " + signature + " for signature base string " + signatureBaseString + " with method " + method.getName() + ".");
275 }
276 method.verify(signatureBaseString, signature);
277 }
278
279
280
281
282
283
284
285
286
287
288
289 protected abstract void onValidSignature(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException;
290
291
292
293
294
295
296
297
298
299 protected void validateOAuthParams(ConsumerDetails consumerDetails, Map<String, String> oauthParams) throws InvalidOAuthParametersException {
300 String version = oauthParams.get(OAuthConsumerParameter.oauth_version.toString());
301 if ((version != null) && (!"1.0".equals(version))) {
302 throw new OAuthVersionUnsupportedException("Unsupported OAuth version: " + version);
303 }
304
305 String realm = oauthParams.get("realm");
306 realm = realm == null || "".equals(realm) ? null : realm;
307 if ((realm != null) && (!realm.equals(this.authenticationEntryPoint.getRealmName()))) {
308 throw new InvalidOAuthParametersException(messages.getMessage("OAuthProcessingFilter.incorrectRealm",
309 new Object[]{realm, this.getAuthenticationEntryPoint().getRealmName()},
310 "Response realm name '{0}' does not match system realm name of '{1}'"));
311 }
312
313 String signatureMethod = oauthParams.get(OAuthConsumerParameter.oauth_signature_method.toString());
314 if (signatureMethod == null) {
315 throw new InvalidOAuthParametersException(messages.getMessage("OAuthProcessingFilter.missingSignatureMethod", "Missing signature method."));
316 }
317
318 String signature = oauthParams.get(OAuthConsumerParameter.oauth_signature.toString());
319 if (signature == null) {
320 throw new InvalidOAuthParametersException(messages.getMessage("OAuthProcessingFilter.missingSignature", "Missing signature."));
321 }
322
323 String timestamp = oauthParams.get(OAuthConsumerParameter.oauth_timestamp.toString());
324 if (timestamp == null) {
325 throw new InvalidOAuthParametersException(messages.getMessage("OAuthProcessingFilter.missingTimestamp", "Missing timestamp."));
326 }
327
328 String nonce = oauthParams.get(OAuthConsumerParameter.oauth_nonce.toString());
329 if (nonce == null) {
330 throw new InvalidOAuthParametersException(messages.getMessage("OAuthProcessingFilter.missingNonce", "Missing nonce."));
331 }
332
333 try {
334 getNonceServices().validateNonce(consumerDetails, Long.parseLong(timestamp), nonce);
335 }
336 catch (NumberFormatException e) {
337 throw new InvalidOAuthParametersException(messages.getMessage("OAuthProcessingFilter.invalidTimestamp", new Object[]{timestamp}, "Timestamp must be a positive integer. Invalid value: {0}"));
338 }
339
340 validateAdditionalParameters(consumerDetails, oauthParams);
341 }
342
343
344
345
346
347
348
349 protected void validateAdditionalParameters(ConsumerDetails consumerDetails, Map<String, String> oauthParams) {
350 }
351
352
353
354
355
356
357
358 protected void onNewTimestamp() throws AuthenticationException {
359 throw new InvalidOAuthParametersException(messages.getMessage("OAuthProcessingFilter.timestampNotNew", "A new timestamp should not be used in a request for an access token."));
360 }
361
362
363
364
365
366
367
368
369
370
371 protected void fail(HttpServletRequest request, HttpServletResponse response, AuthenticationException failure) throws IOException, ServletException {
372 SecurityContextHolder.getContext().setAuthentication(null);
373
374 if (log.isDebugEnabled()) {
375 log.debug(failure);
376 }
377
378 authenticationEntryPoint.commence(request, response, failure);
379 }
380
381
382
383
384
385
386
387
388
389 protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
390
391 String uri = request.getRequestURI();
392 int pathParamIndex = uri.indexOf(';');
393
394 if (pathParamIndex > 0) {
395
396 uri = uri.substring(0, pathParamIndex);
397 }
398
399 if ("".equals(request.getContextPath())) {
400 return uri.endsWith(filterProcessesUrl);
401 }
402
403 boolean match = uri.endsWith(request.getContextPath() + filterProcessesUrl);
404 if (log.isDebugEnabled()) {
405 log.debug(uri + (match ? " matches " : " does not match ") + filterProcessesUrl);
406 }
407 return match;
408 }
409
410
411
412
413
414
415
416 protected boolean skipProcessing(HttpServletRequest request) {
417 return ((request.getAttribute(OAUTH_PROCESSING_HANDLED) != null) && (Boolean.TRUE.equals(request.getAttribute(OAUTH_PROCESSING_HANDLED))));
418 }
419
420
421
422
423
424
425 public OAuthProcessingFilterEntryPoint getAuthenticationEntryPoint() {
426 return authenticationEntryPoint;
427 }
428
429
430
431
432
433
434 @Autowired (required = false)
435 public void setAuthenticationEntryPoint(OAuthProcessingFilterEntryPoint authenticationEntryPoint) {
436 this.authenticationEntryPoint = authenticationEntryPoint;
437 }
438
439
440
441
442
443
444 public ConsumerDetailsService getConsumerDetailsService() {
445 return consumerDetailsService;
446 }
447
448
449
450
451
452
453 @Autowired
454 public void setConsumerDetailsService(ConsumerDetailsService consumerDetailsService) {
455 this.consumerDetailsService = consumerDetailsService;
456 }
457
458
459
460
461
462
463 public OAuthNonceServices getNonceServices() {
464 return nonceServices;
465 }
466
467
468
469
470
471
472 @Autowired (required = false)
473 public void setNonceServices(OAuthNonceServices nonceServices) {
474 this.nonceServices = nonceServices;
475 }
476
477
478
479
480
481
482 public OAuthProviderTokenServices getTokenServices() {
483 return tokenServices;
484 }
485
486
487
488
489
490
491 @Autowired
492 public void setTokenServices(OAuthProviderTokenServices tokenServices) {
493 this.tokenServices = tokenServices;
494 }
495
496
497
498
499
500
501 public String getFilterProcessesUrl() {
502 return filterProcessesUrl;
503 }
504
505
506
507
508
509
510 public void setFilterProcessesUrl(String filterProcessesUrl) {
511 this.filterProcessesUrl = filterProcessesUrl;
512 }
513
514
515
516
517
518
519 public void setMessageSource(MessageSource messageSource) {
520 this.messages = new MessageSourceAccessor(messageSource);
521 }
522
523
524
525
526
527
528 public OAuthProviderSupport getProviderSupport() {
529 return providerSupport;
530 }
531
532
533
534
535
536
537 @Autowired (required = false)
538 public void setProviderSupport(OAuthProviderSupport providerSupport) {
539 this.providerSupport = providerSupport;
540 }
541
542
543
544
545
546
547 public OAuthSignatureMethodFactory getSignatureMethodFactory() {
548 return signatureMethodFactory;
549 }
550
551
552
553
554
555
556 @Autowired (required = false)
557 public void setSignatureMethodFactory(OAuthSignatureMethodFactory signatureMethodFactory) {
558 this.signatureMethodFactory = signatureMethodFactory;
559 }
560
561
562
563
564
565
566 public boolean isIgnoreInadequateCredentials() {
567 return ignoreMissingCredentials;
568 }
569
570
571
572
573
574
575 public void setIgnoreMissingCredentials(boolean ignoreMissingCredentials) {
576 this.ignoreMissingCredentials = ignoreMissingCredentials;
577 }
578
579
580
581
582
583
584 public void setAllowedMethods(List<String> allowedMethods) {
585 this.allowedMethods.clear();
586 if (allowedMethods != null) {
587 for (String allowedMethod : allowedMethods) {
588 this.allowedMethods.add(allowedMethod.toUpperCase());
589 }
590 }
591 }
592 }