Commit 09a29a72 authored by Phillip Webb's avatar Phillip Webb

Polish OAuth SSO

parent 31d6a0f1
...@@ -102,4 +102,15 @@ public class ConditionOutcome { ...@@ -102,4 +102,15 @@ public class ConditionOutcome {
public String toString() { public String toString() {
return (this.message == null ? "" : this.message); return (this.message == null ? "" : this.message);
} }
/**
* Return the inverse of the specified condition outcome.
* @param outcome the outcome to inverse
* @return the inverse of the condition outcome
* @since 1.3.0
*/
public static ConditionOutcome inverse(ConditionOutcome outcome) {
return new ConditionOutcome(!outcome.isMatch(), outcome.getMessage());
}
} }
...@@ -18,16 +18,12 @@ package org.springframework.boot.autoconfigure.condition; ...@@ -18,16 +18,12 @@ package org.springframework.boot.autoconfigure.condition;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
...@@ -41,18 +37,6 @@ import org.springframework.util.StringUtils; ...@@ -41,18 +37,6 @@ import org.springframework.util.StringUtils;
public abstract class SpringBootCondition implements Condition { public abstract class SpringBootCondition implements Condition {
private final Log logger = LogFactory.getLog(getClass()); private final Log logger = LogFactory.getLog(getClass());
public static boolean evaluateForClass(Class<?> annotated, ConditionContext context) {
Conditional conditional = AnnotationUtils.findAnnotation(annotated, Conditional.class);
StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(annotated);
for (Class<? extends Condition> type : conditional.value()) {
Condition condition = BeanUtils.instantiateClass(type);
if (condition.matches(context, metadata)) {
return true;
}
}
return false;
}
@Override @Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
......
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2013-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security; package org.springframework.boot.autoconfigure.security;
import java.io.IOException; import java.io.IOException;
...@@ -26,26 +27,27 @@ import org.springframework.security.web.AuthenticationEntryPoint; ...@@ -26,26 +27,27 @@ import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
/** /**
* AuthenticationEntryPoint that sends a 401 and Parameterized by the value of the * AuthenticationEntryPoint that sends a 401 and Parameterized by the value of the {@coe
* WWW-Authenticate header. Like the {@link BasicAuthenticationEntryPoint} but more * WWW-Authenticate} header. Like the {@link BasicAuthenticationEntryPoint} but more
* flexible. * flexible.
*
* @author Dave Syer
* *
* @author Dave Syer
* @since 1.3.0
*/ */
public class Http401AuthenticationEntryPoint implements AuthenticationEntryPoint { public class Http401AuthenticationEntryPoint implements AuthenticationEntryPoint {
private final String authenticateHeader; private final String headerValue;
public Http401AuthenticationEntryPoint(String authenticateHeader) { public Http401AuthenticationEntryPoint(String headerValue) {
this.authenticateHeader = authenticateHeader; this.headerValue = headerValue;
} }
@Override @Override
public void commence(HttpServletRequest request, HttpServletResponse response, public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException { AuthenticationException authException) throws IOException, ServletException {
response.setHeader("WWW-Authenticate", authenticateHeader); response.setHeader("WWW-Authenticate", this.headerValue);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
authException.getMessage()); authException.getMessage());
} }
}
\ No newline at end of file }
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -20,13 +20,14 @@ import org.springframework.beans.BeansException; ...@@ -20,13 +20,14 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.oauth2.authserver.SpringSecurityOAuth2AuthorizationServerConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.authserver.SpringSecurityOAuth2AuthorizationServerConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -37,16 +38,16 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R ...@@ -37,16 +38,16 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/** /**
* Spring Security OAuth2 top level auto-configuration beans * {@link EnableAutoConfiguration Auto-configuration} for Spring Security OAuth2.
* *
* @author Greg Turnquist * @author Greg Turnquist
* @author Dave Syer * @author Dave Syer
* @since 1.3.0
*/ */
@Configuration @Configuration
@ConditionalOnClass({ OAuth2AccessToken.class, WebMvcConfigurerAdapter.class }) @ConditionalOnClass({ OAuth2AccessToken.class, WebMvcConfigurerAdapter.class })
@Import({ SpringSecurityOAuth2AuthorizationServerConfiguration.class, @Import({ SpringSecurityOAuth2AuthorizationServerConfiguration.class,
OAuth2MethodSecurityConfiguration.class, OAuth2MethodSecurityConfiguration.class, OAuth2ResourceServerConfiguration.class,
OAuth2ResourceServerConfiguration.class,
OAuth2RestOperationsConfiguration.class }) OAuth2RestOperationsConfiguration.class })
@AutoConfigureBefore(WebMvcAutoConfiguration.class) @AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties(OAuth2ClientProperties.class) @EnableConfigurationProperties(OAuth2ClientProperties.class)
......
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -21,7 +21,10 @@ import java.util.UUID; ...@@ -21,7 +21,10 @@ import java.util.UUID;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
* Configuration properties for OAuth2 Client.
*
* @author Dave Syer * @author Dave Syer
* @since 1.3.0
*/ */
@ConfigurationProperties("spring.oauth2.client") @ConfigurationProperties("spring.oauth2.client")
public class OAuth2ClientProperties { public class OAuth2ClientProperties {
......
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -47,12 +47,13 @@ import org.springframework.security.oauth2.provider.client.BaseClientDetails; ...@@ -47,12 +47,13 @@ import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.TokenStore;
/** /**
* Auto-configure a Spring Security OAuth2 authorization server. Back off if another * Configuration for a Spring Security OAuth2 authorization server. Back off if another
* {@link AuthorizationServerConfigurer} already exists or if authorization server is not * {@link AuthorizationServerConfigurer} already exists or if authorization server is not
* enabled. * enabled.
* *
* @author Greg Turnquist * @author Greg Turnquist
* @author Dave Syer * @author Dave Syer
* @since 1.3.0
*/ */
@Configuration @Configuration
@ConditionalOnClass(EnableAuthorizationServer.class) @ConditionalOnClass(EnableAuthorizationServer.class)
...@@ -62,6 +63,9 @@ import org.springframework.security.oauth2.provider.token.TokenStore; ...@@ -62,6 +63,9 @@ import org.springframework.security.oauth2.provider.token.TokenStore;
public class SpringSecurityOAuth2AuthorizationServerConfiguration extends public class SpringSecurityOAuth2AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter { AuthorizationServerConfigurerAdapter {
private static final Log logger = LogFactory
.getLog(SpringSecurityOAuth2AuthorizationServerConfiguration.class);
@Autowired @Autowired
private BaseClientDetails details; private BaseClientDetails details;
...@@ -70,13 +74,10 @@ public class SpringSecurityOAuth2AuthorizationServerConfiguration extends ...@@ -70,13 +74,10 @@ public class SpringSecurityOAuth2AuthorizationServerConfiguration extends
@Autowired(required = false) @Autowired(required = false)
private TokenStore tokenStore; private TokenStore tokenStore;
@Configuration @Configuration
protected static class ClientDetailsLogger { protected static class ClientDetailsLogger {
private static final Log logger = LogFactory
.getLog(SpringSecurityOAuth2AuthorizationServerConfiguration.class);
@Autowired @Autowired
private OAuth2ClientProperties credentials; private OAuth2ClientProperties credentials;
......
/* /*
* Copyright 2015 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -28,21 +28,22 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur ...@@ -28,21 +28,22 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
/** /**
* Configuration for OAuth2 Single Sign On (SSO). If there is an existing * Enable OAuth2 Single Sign On (SSO). If there is an existing
* {@link WebSecurityConfigurerAdapter} provided by the user and annotated with * {@link WebSecurityConfigurerAdapter} provided by the user and annotated with
* <code>@EnableOAuth2Sso</code>, it is enhanced by adding an authentication filter and an * {@code @EnableOAuth2Sso}, it is enhanced by adding an authentication filter and an
* authentication entry point. If the user only has <code>@EnableOAuth2Sso</code> but not * authentication entry point. If the user only has {@code @EnableOAuth2Sso} but not on a
* on a WebSecurityConfigurerAdapter then one is added with all paths secured and with an * WebSecurityConfigurerAdapter then one is added with all paths secured and with an order
* order that puts it ahead of the default HTTP Basic security chain in Spring Boot. * that puts it ahead of the default HTTP Basic security chain in Spring Boot.
*
* @author Dave Syer
* *
* @author Dave Syer
* @since 1.3.0
*/ */
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
@EnableOAuth2Client @EnableOAuth2Client
@Import({ OAuth2SsoDefaultConfiguration.class, OAuth2SsoCustomConfiguration.class, ResourceServerTokenServicesConfiguration.class }) @Import({ OAuth2SsoDefaultConfiguration.class, OAuth2SsoCustomConfiguration.class,
ResourceServerTokenServicesConfiguration.class })
public @interface EnableOAuth2Sso { public @interface EnableOAuth2Sso {
} }
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.client; package org.springframework.boot.autoconfigure.security.oauth2.client;
import javax.annotation.Resource; import javax.annotation.Resource;
...@@ -49,8 +50,10 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; ...@@ -49,8 +50,10 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
/** /**
* @author Dave Syer * Configuration for OAuth2 Single Sign On REST operations.
* *
* @author Dave Syer
* @since 1.3.0
*/ */
@Configuration @Configuration
@ConditionalOnClass(EnableOAuth2Client.class) @ConditionalOnClass(EnableOAuth2Client.class)
...@@ -109,7 +112,7 @@ public class OAuth2RestOperationsConfiguration { ...@@ -109,7 +112,7 @@ public class OAuth2RestOperationsConfiguration {
@Bean @Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES) @Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public OAuth2ClientContext oauth2ClientContext() { public OAuth2ClientContext oauth2ClientContext() {
return new DefaultOAuth2ClientContext(accessTokenRequest); return new DefaultOAuth2ClientContext(this.accessTokenRequest);
} }
@Bean @Bean
......
/* /*
* Copyright 2015 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -44,9 +44,8 @@ import org.springframework.util.ReflectionUtils; ...@@ -44,9 +44,8 @@ import org.springframework.util.ReflectionUtils;
* {@link WebSecurityConfigurerAdapter} provided by the user and annotated with * {@link WebSecurityConfigurerAdapter} provided by the user and annotated with
* <code>@EnableOAuth2Sso</code>. The user-provided configuration is enhanced by adding an * <code>@EnableOAuth2Sso</code>. The user-provided configuration is enhanced by adding an
* authentication filter and an authentication entry point. * authentication filter and an authentication entry point.
*
* @author Dave Syer
* *
* @author Dave Syer
*/ */
@Configuration @Configuration
@Conditional(WebSecurityEnhancerCondition.class) @Conditional(WebSecurityEnhancerCondition.class)
...@@ -64,7 +63,8 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces ...@@ -64,7 +63,8 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces
@Override @Override
public void setImportMetadata(AnnotationMetadata importMetadata) { public void setImportMetadata(AnnotationMetadata importMetadata) {
configType = ClassUtils.resolveClassName(importMetadata.getClassName(), null); this.configType = ClassUtils
.resolveClassName(importMetadata.getClassName(), null);
} }
...@@ -77,11 +77,11 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces ...@@ -77,11 +77,11 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces
@Override @Override
public Object postProcessAfterInitialization(Object bean, String beanName) public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException { throws BeansException {
if (configType.isAssignableFrom(bean.getClass()) if (this.configType.isAssignableFrom(bean.getClass())
&& bean instanceof WebSecurityConfigurerAdapter) { && bean instanceof WebSecurityConfigurerAdapter) {
ProxyFactory factory = new ProxyFactory(); ProxyFactory factory = new ProxyFactory();
factory.setTarget(bean); factory.setTarget(bean);
factory.addAdvice(new SsoSecurityAdapter(beanFactory)); factory.addAdvice(new SsoSecurityAdapter(this.beanFactory));
bean = factory.getProxy(); bean = factory.getProxy();
} }
return bean; return bean;
...@@ -92,7 +92,7 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces ...@@ -92,7 +92,7 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces
private SsoSecurityConfigurer configurer; private SsoSecurityConfigurer configurer;
public SsoSecurityAdapter(BeanFactory beanFactory) { public SsoSecurityAdapter(BeanFactory beanFactory) {
configurer = new SsoSecurityConfigurer(beanFactory); this.configurer = new SsoSecurityConfigurer(beanFactory);
} }
@Override @Override
...@@ -102,8 +102,8 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces ...@@ -102,8 +102,8 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces
WebSecurityConfigurerAdapter.class, "getHttp"); WebSecurityConfigurerAdapter.class, "getHttp");
ReflectionUtils.makeAccessible(method); ReflectionUtils.makeAccessible(method);
HttpSecurity http = (HttpSecurity) ReflectionUtils.invokeMethod(method, HttpSecurity http = (HttpSecurity) ReflectionUtils.invokeMethod(method,
(WebSecurityConfigurerAdapter) invocation.getThis()); invocation.getThis());
configurer.configure(http); this.configurer.configure(http);
} }
return invocation.proceed(); return invocation.proceed();
} }
...@@ -127,6 +127,7 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces ...@@ -127,6 +127,7 @@ public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProces
return ConditionOutcome return ConditionOutcome
.noMatch("found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter"); .noMatch("found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
} }
} }
} }
/* /*
* Copyright 2015 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -32,12 +32,13 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur ...@@ -32,12 +32,13 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
/** /**
* If the user only has <code>@EnableOAuth2Sso</code> but not on a * Configuration for OAuth2 Single Sign On (SSO). If the user only has
* WebSecurityConfigurerAdapter then one is added with all paths secured and with an order * {@code @EnableOAuth2Sso} but not on a {@code WebSecurityConfigurerAdapter} then one is
* that puts it ahead of the default HTTP Basic security chain in Spring Boot. * added with all paths secured and with an order that puts it ahead of the default HTTP
* * Basic security chain in Spring Boot.
* @author Dave Syer
* *
* @author Dave Syer
* @since 1.3.0
*/ */
@Configuration @Configuration
@EnableConfigurationProperties(OAuth2SsoProperties.class) @EnableConfigurationProperties(OAuth2SsoProperties.class)
...@@ -57,13 +58,13 @@ public class OAuth2SsoDefaultConfiguration { ...@@ -57,13 +58,13 @@ public class OAuth2SsoDefaultConfiguration {
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests().anyRequest().authenticated(); http.antMatcher("/**").authorizeRequests().anyRequest().authenticated();
new SsoSecurityConfigurer(beanFactory).configure(http); new SsoSecurityConfigurer(this.beanFactory).configure(http);
} }
@Override @Override
public int getOrder() { public int getOrder() {
if (sso.getFilterOrder() != null) { if (this.sso.getFilterOrder() != null) {
return sso.getFilterOrder(); return this.sso.getFilterOrder();
} }
if (ClassUtils if (ClassUtils
.isPresent( .isPresent(
...@@ -80,6 +81,7 @@ public class OAuth2SsoDefaultConfiguration { ...@@ -80,6 +81,7 @@ public class OAuth2SsoDefaultConfiguration {
} }
private static class NeedsWebSecurityCondition extends SpringBootCondition { private static class NeedsWebSecurityCondition extends SpringBootCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
...@@ -95,6 +97,7 @@ public class OAuth2SsoDefaultConfiguration { ...@@ -95,6 +97,7 @@ public class OAuth2SsoDefaultConfiguration {
return ConditionOutcome return ConditionOutcome
.match("found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter"); .match("found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
} }
} }
} }
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,13 +13,16 @@ ...@@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.client; package org.springframework.boot.autoconfigure.security.oauth2.client;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
* @author Dave Syer * Configuration properties for OAuth2 Single Sign On (SSO).
* *
* @author Dave Syer
* @since 1.3.0
*/ */
@ConfigurationProperties("spring.oauth2.sso") @ConfigurationProperties("spring.oauth2.sso")
public class OAuth2SsoProperties { public class OAuth2SsoProperties {
...@@ -39,7 +42,7 @@ public class OAuth2SsoProperties { ...@@ -39,7 +42,7 @@ public class OAuth2SsoProperties {
private Integer filterOrder; private Integer filterOrder;
public String getLoginPath() { public String getLoginPath() {
return loginPath; return this.loginPath;
} }
public void setLoginPath(String loginPath) { public void setLoginPath(String loginPath) {
...@@ -47,7 +50,7 @@ public class OAuth2SsoProperties { ...@@ -47,7 +50,7 @@ public class OAuth2SsoProperties {
} }
public Integer getFilterOrder() { public Integer getFilterOrder() {
return filterOrder; return this.filterOrder;
} }
public void setFilterOrder(Integer filterOrder) { public void setFilterOrder(Integer filterOrder) {
......
/* /*
* Copyright 2015 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -36,7 +36,7 @@ class SsoSecurityConfigurer { ...@@ -36,7 +36,7 @@ class SsoSecurityConfigurer {
} }
public void configure(HttpSecurity http) throws Exception { public void configure(HttpSecurity http) throws Exception {
OAuth2SsoProperties sso = beanFactory.getBean(OAuth2SsoProperties.class); OAuth2SsoProperties sso = this.beanFactory.getBean(OAuth2SsoProperties.class);
// Delay the processing of the filter until we know the // Delay the processing of the filter until we know the
// SessionAuthenticationStrategy is available: // SessionAuthenticationStrategy is available:
http.apply(new OAuth2ClientAuthenticationConfigurer(oauth2SsoFilter(sso))); http.apply(new OAuth2ClientAuthenticationConfigurer(oauth2SsoFilter(sso)));
...@@ -46,9 +46,9 @@ class SsoSecurityConfigurer { ...@@ -46,9 +46,9 @@ class SsoSecurityConfigurer {
private OAuth2ClientAuthenticationProcessingFilter oauth2SsoFilter( private OAuth2ClientAuthenticationProcessingFilter oauth2SsoFilter(
OAuth2SsoProperties sso) { OAuth2SsoProperties sso) {
OAuth2RestOperations restTemplate = beanFactory OAuth2RestOperations restTemplate = this.beanFactory
.getBean(OAuth2RestOperations.class); .getBean(OAuth2RestOperations.class);
ResourceServerTokenServices tokenServices = beanFactory ResourceServerTokenServices tokenServices = this.beanFactory
.getBean(ResourceServerTokenServices.class); .getBean(ResourceServerTokenServices.class);
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter( OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(
sso.getLoginPath()); sso.getLoginPath());
...@@ -59,6 +59,7 @@ class SsoSecurityConfigurer { ...@@ -59,6 +59,7 @@ class SsoSecurityConfigurer {
private static class OAuth2ClientAuthenticationConfigurer extends private static class OAuth2ClientAuthenticationConfigurer extends
SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private OAuth2ClientAuthenticationProcessingFilter filter; private OAuth2ClientAuthenticationProcessingFilter filter;
public OAuth2ClientAuthenticationConfigurer( public OAuth2ClientAuthenticationConfigurer(
...@@ -68,12 +69,13 @@ class SsoSecurityConfigurer { ...@@ -68,12 +69,13 @@ class SsoSecurityConfigurer {
@Override @Override
public void configure(HttpSecurity builder) throws Exception { public void configure(HttpSecurity builder) throws Exception {
OAuth2ClientAuthenticationProcessingFilter ssoFilter = filter; OAuth2ClientAuthenticationProcessingFilter ssoFilter = this.filter;
ssoFilter.setSessionAuthenticationStrategy(builder ssoFilter.setSessionAuthenticationStrategy(builder
.getSharedObject(SessionAuthenticationStrategy.class)); .getSharedObject(SessionAuthenticationStrategy.class));
builder.addFilterAfter(ssoFilter, builder.addFilterAfter(ssoFilter,
AbstractPreAuthenticatedProcessingFilter.class); AbstractPreAuthenticatedProcessingFilter.class);
} }
} }
} }
\ No newline at end of file
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -30,22 +30,22 @@ import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecur ...@@ -30,22 +30,22 @@ import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecur
/** /**
* Auto-configure an expression handler for method-level security (if the user already has * Auto-configure an expression handler for method-level security (if the user already has
* <code>@EnableGlobalMethodSecurity</code>). * {@code @EnableGlobalMethodSecurity}).
* *
* @author Greg Turnquist * @author Greg Turnquist
* @author Dave Syer * @author Dave Syer
* @since 1.3.0
*/ */
@Configuration @Configuration
@ConditionalOnClass({ OAuth2AccessToken.class }) @ConditionalOnClass({ OAuth2AccessToken.class })
@ConditionalOnBean(GlobalMethodSecurityConfiguration.class) @ConditionalOnBean(GlobalMethodSecurityConfiguration.class)
public class OAuth2MethodSecurityConfiguration implements public class OAuth2MethodSecurityConfiguration implements BeanFactoryPostProcessor {
BeanFactoryPostProcessor {
@Override @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException { throws BeansException {
beanFactory OAuth2ExpressionHandlerInjectionPostProcessor processor = new OAuth2ExpressionHandlerInjectionPostProcessor();
.addBeanPostProcessor(new OAuth2ExpressionHandlerInjectionPostProcessor()); beanFactory.addBeanPostProcessor(processor);
} }
private static class OAuth2ExpressionHandlerInjectionPostProcessor implements private static class OAuth2ExpressionHandlerInjectionPostProcessor implements
......
/* /*
* Copyright 2013-2015 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,12 +13,24 @@ ...@@ -13,12 +13,24 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.resource; package org.springframework.boot.autoconfigure.security.oauth2.resource;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
/**
* Callback interface that can be used to provide additional configuration to the
* {@link JwtAccessTokenConverter}.
*
* @author Dave Syer
* @since 1.3.0
*/
public interface JwtAccessTokenConverterConfigurer { public interface JwtAccessTokenConverterConfigurer {
/**
* Configure the {@link JwtAccessTokenConverter}.
* @param converter the converter to configure
*/
void configure(JwtAccessTokenConverter converter); void configure(JwtAccessTokenConverter converter);
} }
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.security.oauth2.resource; package org.springframework.boot.autoconfigure.security.oauth2.resource;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
...@@ -27,13 +28,16 @@ import org.springframework.boot.autoconfigure.security.SecurityProperties; ...@@ -27,13 +28,16 @@ import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration.ResourceServerCondition; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration.ResourceServerCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationCondition; import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
...@@ -50,6 +54,7 @@ import org.springframework.util.StringUtils; ...@@ -50,6 +54,7 @@ import org.springframework.util.StringUtils;
* *
* @author Greg Turnquist * @author Greg Turnquist
* @author Dave Syer * @author Dave Syer
* @since 1.3.0
*/ */
@Configuration @Configuration
@Conditional(ResourceServerCondition.class) @Conditional(ResourceServerCondition.class)
...@@ -91,10 +96,13 @@ public class OAuth2ResourceServerConfiguration { ...@@ -91,10 +96,13 @@ public class OAuth2ResourceServerConfiguration {
} }
@ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class)
protected static class ResourceServerCondition extends SpringBootCondition implements protected static class ResourceServerCondition extends SpringBootCondition implements
ConfigurationCondition { ConfigurationCondition {
private static final String AUTHORIZATION_ANNOTATION = "org.springframework."
+ "security.oauth2.config.annotation.web.configuration."
+ "AuthorizationServerEndpointsConfiguration";
@Override @Override
public ConfigurationPhase getConfigurationPhase() { public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN; return ConfigurationPhase.REGISTER_BEAN;
...@@ -104,31 +112,48 @@ public class OAuth2ResourceServerConfiguration { ...@@ -104,31 +112,48 @@ public class OAuth2ResourceServerConfiguration {
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment(); Environment environment = context.getEnvironment();
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment); RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment,
"spring.oauth2.resource.");
String client = environment String client = environment
.resolvePlaceholders("${spring.oauth2.client.clientId:}"); .resolvePlaceholders("${spring.oauth2.client.clientId:}");
if (StringUtils.hasText(client)) { if (StringUtils.hasText(client)) {
return ConditionOutcome.match("found client id"); return ConditionOutcome.match("found client id");
} }
if (!resolver.getSubProperties("spring.oauth2.resource.jwt").isEmpty()) { if (!resolver.getSubProperties("jwt").isEmpty()) {
return ConditionOutcome.match("found JWT resource configuration"); return ConditionOutcome.match("found JWT resource configuration");
} }
if (StringUtils.hasText(resolver if (StringUtils.hasText(resolver.getProperty("user-info-uri"))) {
.getProperty("spring.oauth2.resource.userInfoUri"))) { return ConditionOutcome.match("found UserInfo "
return ConditionOutcome + "URI resource configuration");
.match("found UserInfo URI resource configuration");
} }
if (ClassUtils if (ClassUtils.isPresent(AUTHORIZATION_ANNOTATION, null)) {
.isPresent( if (AuthorizationServerEndpointsConfigurationBeanCondition
"org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration", .matches(context)) {
null)) { return ConditionOutcome.match("found authorization "
if (SpringBootCondition.evaluateForClass(ResourceServerCondition.class, context)) { + "server endpoints configuration");
return ConditionOutcome }
.match("found authorization server endpoints configuration"); }
return ConditionOutcome.noMatch("found neither client id nor "
+ "JWT resource nor authorization server");
}
}
@ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class)
private static class AuthorizationServerEndpointsConfigurationBeanCondition {
public static boolean matches(ConditionContext context) {
Class<AuthorizationServerEndpointsConfigurationBeanCondition> type = AuthorizationServerEndpointsConfigurationBeanCondition.class;
Conditional conditional = AnnotationUtils.findAnnotation(type,
Conditional.class);
StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(type);
for (Class<? extends Condition> conditionType : conditional.value()) {
Condition condition = BeanUtils.instantiateClass(conditionType);
if (condition.matches(context, metadata)) {
return true;
} }
} }
return ConditionOutcome return false;
.noMatch("found neither client id nor JWT resource nor authorization server");
} }
} }
......
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -30,8 +30,10 @@ import org.springframework.validation.Validator; ...@@ -30,8 +30,10 @@ import org.springframework.validation.Validator;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
/** /**
* @author Dave Syer * Configuration properties for OAuth2 Resources.
* *
* @author Dave Syer
* @since 1.3.0
*/ */
@ConfigurationProperties("spring.oauth2.resource") @ConfigurationProperties("spring.oauth2.resource")
public class ResourceServerProperties implements Validator, BeanFactoryAware { public class ResourceServerProperties implements Validator, BeanFactoryAware {
...@@ -71,7 +73,7 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware { ...@@ -71,7 +73,7 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
* The token type to send when using the userInfoUri. * The token type to send when using the userInfoUri.
*/ */
private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE; private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE;
private Jwt jwt = new Jwt(); private Jwt jwt = new Jwt();
public ResourceServerProperties() { public ResourceServerProperties() {
...@@ -133,7 +135,7 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware { ...@@ -133,7 +135,7 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
} }
public String getTokenType() { public String getTokenType() {
return tokenType; return this.tokenType;
} }
public void setTokenType(String tokenType) { public void setTokenType(String tokenType) {
...@@ -187,7 +189,8 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware { ...@@ -187,7 +189,8 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
} }
if (!StringUtils.hasText(resource.getUserInfoUri())) { if (!StringUtils.hasText(resource.getUserInfoUri())) {
errors.rejectValue("tokenInfoUri", "missing.tokenInfoUri", errors.rejectValue("tokenInfoUri", "missing.tokenInfoUri",
"Missing tokenInfoUri and userInfoUri and there is no JWT verifier key"); "Missing tokenInfoUri and userInfoUri and there is no "
+ "JWT verifier key");
} }
} }
} }
......
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,12 +13,16 @@ ...@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.resource; package org.springframework.boot.autoconfigure.security.oauth2.resource;
import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
...@@ -31,16 +35,18 @@ import org.springframework.social.connect.support.OAuth2ConnectionFactory; ...@@ -31,16 +35,18 @@ import org.springframework.social.connect.support.OAuth2ConnectionFactory;
import org.springframework.social.oauth2.AccessGrant; import org.springframework.social.oauth2.AccessGrant;
/** /**
* @author Dave Syer * {@link ResourceServerTokenServices} backed by Spring Social.
* *
* @author Dave Syer
* @since 1.3.0
*/ */
public class SpringSocialTokenServices implements ResourceServerTokenServices { public class SpringSocialTokenServices implements ResourceServerTokenServices {
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
private OAuth2ConnectionFactory<?> connectionFactory; private final OAuth2ConnectionFactory<?> connectionFactory;
private String clientId; private final String clientId;
public SpringSocialTokenServices(OAuth2ConnectionFactory<?> connectionFactory, public SpringSocialTokenServices(OAuth2ConnectionFactory<?> connectionFactory,
String clientId) { String clientId) {
...@@ -51,21 +57,20 @@ public class SpringSocialTokenServices implements ResourceServerTokenServices { ...@@ -51,21 +57,20 @@ public class SpringSocialTokenServices implements ResourceServerTokenServices {
@Override @Override
public OAuth2Authentication loadAuthentication(String accessToken) public OAuth2Authentication loadAuthentication(String accessToken)
throws AuthenticationException, InvalidTokenException { throws AuthenticationException, InvalidTokenException {
AccessGrant accessGrant = new AccessGrant(accessToken);
Connection<?> connection = connectionFactory.createConnection(new AccessGrant( Connection<?> connection = this.connectionFactory.createConnection(accessGrant);
accessToken));
UserProfile user = connection.fetchUserProfile(); UserProfile user = connection.fetchUserProfile();
return extractAuthentication(user); return extractAuthentication(user);
} }
private OAuth2Authentication extractAuthentication(UserProfile user) { private OAuth2Authentication extractAuthentication(UserProfile user) {
UsernamePasswordAuthenticationToken principal = new UsernamePasswordAuthenticationToken( String principal = user.getUsername();
user.getUsername(), "N/A", List<GrantedAuthority> authorities = AuthorityUtils
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); .commaSeparatedStringToAuthorityList("ROLE_USER");
principal.setDetails(user); OAuth2Request request = new OAuth2Request(null, this.clientId, null, true, null,
OAuth2Request request = new OAuth2Request(null, clientId, null, true, null, null, null, null, null, null);
null, null, null); return new OAuth2Authentication(request, new UsernamePasswordAuthenticationToken(
return new OAuth2Authentication(request, principal); principal, "N/A", authorities));
} }
@Override @Override
...@@ -73,4 +78,4 @@ public class SpringSocialTokenServices implements ResourceServerTokenServices { ...@@ -73,4 +78,4 @@ public class SpringSocialTokenServices implements ResourceServerTokenServices {
throw new UnsupportedOperationException("Not supported: read access token"); throw new UnsupportedOperationException("Not supported: read access token");
} }
} }
\ No newline at end of file
/* /*
* Copyright 2014-2015 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -25,15 +25,15 @@ import org.springframework.security.oauth2.client.OAuth2RestTemplate; ...@@ -25,15 +25,15 @@ import org.springframework.security.oauth2.client.OAuth2RestTemplate;
* authenticator (which is how the token gets attached to outgoing requests). The rest * authenticator (which is how the token gets attached to outgoing requests). The rest
* template that is being customized here is <i>only</i> used internally to carry out * template that is being customized here is <i>only</i> used internally to carry out
* authentication (in the SSO or Resource Server use cases). * authentication (in the SSO or Resource Server use cases).
*
* @author Dave Syer
* *
* @author Dave Syer
* @since 1.3.0
*/ */
public interface UserInfoRestTemplateCustomizer { public interface UserInfoRestTemplateCustomizer {
/** /**
* Customize the rest template before it is initialized. * Customize the rest template before it is initialized.
* *
* @param template the rest template * @param template the rest template
*/ */
void customize(OAuth2RestTemplate template); void customize(OAuth2RestTemplate template);
......
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,14 +13,17 @@ ...@@ -13,14 +13,17 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.resource; package org.springframework.boot.autoconfigure.security.oauth2.resource;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.client.OAuth2RestOperations; import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.OAuth2RestTemplate;
...@@ -32,13 +35,22 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; ...@@ -32,13 +35,22 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
/**
* {@link ResourceServerTokenServices} that uses a user info REST service.
*
* @author Dave Syer
* @since 1.3.0
*/
public class UserInfoTokenServices implements ResourceServerTokenServices { public class UserInfoTokenServices implements ResourceServerTokenServices {
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
private String userInfoEndpointUrl; private static final String[] PRINCIPAL_KEYS = new String[] { "user", "username",
"userid", "user_id", "login", "id", "name" };
private final String userInfoEndpointUrl;
private String clientId; private final String clientId;
private OAuth2RestOperations restTemplate; private OAuth2RestOperations restTemplate;
...@@ -48,7 +60,7 @@ public class UserInfoTokenServices implements ResourceServerTokenServices { ...@@ -48,7 +60,7 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
this.userInfoEndpointUrl = userInfoEndpointUrl; this.userInfoEndpointUrl = userInfoEndpointUrl;
this.clientId = clientId; this.clientId = clientId;
} }
public void setTokenType(String tokenType) { public void setTokenType(String tokenType) {
this.tokenType = tokenType; this.tokenType = tokenType;
} }
...@@ -60,31 +72,28 @@ public class UserInfoTokenServices implements ResourceServerTokenServices { ...@@ -60,31 +72,28 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
@Override @Override
public OAuth2Authentication loadAuthentication(String accessToken) public OAuth2Authentication loadAuthentication(String accessToken)
throws AuthenticationException, InvalidTokenException { throws AuthenticationException, InvalidTokenException {
Map<String, Object> map = getMap(this.userInfoEndpointUrl, accessToken);
Map<String, Object> map = getMap(userInfoEndpointUrl, accessToken);
if (map.containsKey("error")) { if (map.containsKey("error")) {
logger.debug("userinfo returned error: " + map.get("error")); this.logger.debug("userinfo returned error: " + map.get("error"));
throw new InvalidTokenException(accessToken); throw new InvalidTokenException(accessToken);
} }
return extractAuthentication(map); return extractAuthentication(map);
} }
private OAuth2Authentication extractAuthentication(Map<String, Object> map) { private OAuth2Authentication extractAuthentication(Map<String, Object> map) {
UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken( Object principal = getPrincipal(map);
getPrincipal(map), "N/A", List<GrantedAuthority> authorities = AuthorityUtils
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); .commaSeparatedStringToAuthorityList("ROLE_USER");
user.setDetails(map); OAuth2Request request = new OAuth2Request(null, this.clientId, null, true, null,
OAuth2Request request = new OAuth2Request(null, clientId, null, true, null, null, null, null, null, null);
null, null, null); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
return new OAuth2Authentication(request, user); principal, "N/A", authorities);
token.setDetails(map);
return new OAuth2Authentication(request, token);
} }
private Object getPrincipal(Map<String, Object> map) { private Object getPrincipal(Map<String, Object> map) {
String[] keys = new String[] { "user", "username", "userid", "user_id", "login", for (String key : PRINCIPAL_KEYS) {
"id", "name" };
for (String key : keys) {
if (map.containsKey(key)) { if (map.containsKey(key)) {
return map.get(key); return map.get(key);
} }
...@@ -97,22 +106,19 @@ public class UserInfoTokenServices implements ResourceServerTokenServices { ...@@ -97,22 +106,19 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
throw new UnsupportedOperationException("Not supported: read access token"); throw new UnsupportedOperationException("Not supported: read access token");
} }
@SuppressWarnings({ "unchecked" })
private Map<String, Object> getMap(String path, String accessToken) { private Map<String, Object> getMap(String path, String accessToken) {
logger.info("Getting user info from: " + path); this.logger.info("Getting user info from: " + path);
OAuth2RestOperations restTemplate = this.restTemplate; OAuth2RestOperations restTemplate = this.restTemplate;
if (restTemplate == null) { if (restTemplate == null) {
BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails(); BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails();
resource.setClientId(clientId); resource.setClientId(this.clientId);
restTemplate = new OAuth2RestTemplate(resource); restTemplate = new OAuth2RestTemplate(resource);
} }
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(accessToken); DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(accessToken);
token.setTokenType(tokenType); token.setTokenType(this.tokenType);
restTemplate.getOAuth2ClientContext().setAccessToken(token); restTemplate.getOAuth2ClientContext().setAccessToken(token);
@SuppressWarnings("rawtypes") return restTemplate.getForEntity(path, Map.class).getBody();
Map map = restTemplate.getForEntity(path, Map.class).getBody();
@SuppressWarnings("unchecked")
Map<String, Object> result = map;
return result;
} }
} }
\ No newline at end of file
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,9 +13,8 @@ ...@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.resource;
import static org.junit.Assert.assertNotNull; package org.springframework.boot.autoconfigure.security.oauth2.resource;
import java.util.Map; import java.util.Map;
...@@ -23,30 +22,34 @@ import org.junit.Test; ...@@ -23,30 +22,34 @@ import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import static org.junit.Assert.assertNotNull;
/** /**
* @author Dave Syer * Tests for {@link ResourceServerProperties}.
* *
* @author Dave Syer
*/ */
public class ResourceServerPropertiesTests { public class ResourceServerPropertiesTests {
private ResourceServerProperties properties = new ResourceServerProperties("client", "secret"); private ResourceServerProperties properties = new ResourceServerProperties("client",
"secret");
@Test @Test
@SuppressWarnings("unchecked")
public void json() throws Exception { public void json() throws Exception {
properties.getJwt().setKeyUri("http://example.com/token_key"); this.properties.getJwt().setKeyUri("http://example.com/token_key");
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(properties); String json = mapper.writeValueAsString(this.properties);
@SuppressWarnings("unchecked")
Map<String, Object> value = mapper.readValue(json, Map.class); Map<String, Object> value = mapper.readValue(json, Map.class);
@SuppressWarnings("unchecked")
Map<String, Object> jwt = (Map<String, Object>) value.get("jwt"); Map<String, Object> jwt = (Map<String, Object>) value.get("jwt");
assertNotNull("Wrong json: " + json, jwt.get("keyUri")); assertNotNull("Wrong json: " + json, jwt.get("keyUri"));
} }
@Test @Test
public void tokenKeyDerived() throws Exception { public void tokenKeyDerived() throws Exception {
properties.setUserInfoUri("http://example.com/userinfo"); this.properties.setUserInfoUri("http://example.com/userinfo");
assertNotNull("Wrong properties: " + properties, properties.getJwt().getKeyUri()); assertNotNull("Wrong properties: " + this.properties, this.properties.getJwt()
.getKeyUri());
} }
} }
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,11 +13,11 @@ ...@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.resource; package org.springframework.boot.autoconfigure.security.oauth2.resource;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
...@@ -38,13 +38,23 @@ import org.springframework.security.oauth2.provider.token.RemoteTokenServices; ...@@ -38,13 +38,23 @@ import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.social.connect.ConnectionFactoryLocator; import org.springframework.social.connect.ConnectionFactoryLocator;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
/** /**
* @author Dave Syer * Tests for {@link ResourceServerTokenServicesConfiguration}.
* *
* @author Dave Syer
*/ */
public class ResourceServerTokenServicesConfigurationTests { public class ResourceServerTokenServicesConfigurationTests {
private static String PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n"
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnGp/Q5lh0P8nPL21oMMrt2RrkT9"
+ "AW5jgYwLfSUnJVc9G6uR3cXRRDCjHqWU5WYwivcF180A6CWp/ireQFFBNowgc5XaA0kPpzE"
+ "tgsA5YsNX7iSnUibB004iBTfU9hZ2Rbsc8cWqynT0RyN4TP1RYVSeVKvMQk4GT1r7JCEC+T"
+ "Nu1ELmbNwMQyzKjsfBXyIOCFU/E94ktvsTZUHF4Oq44DBylCDsS1k7/sfZC2G5EU7Oz0mhG"
+ "8+Uz6MSEQHtoIi6mc8u64Rwi3Z3tscuWG2ShtsUFuNSAFNkY7LkLn+/hxLCu2bNISMaESa8"
+ "dG22CIMuIeRLVcAmEWEWH5EEforTg+QIDAQAB\n-----END PUBLIC KEY-----";
private ConfigurableApplicationContext context; private ConfigurableApplicationContext context;
private ConfigurableEnvironment environment = new StandardEnvironment(); private ConfigurableEnvironment environment = new StandardEnvironment();
...@@ -112,7 +122,7 @@ public class ResourceServerTokenServicesConfigurationTests { ...@@ -112,7 +122,7 @@ public class ResourceServerTokenServicesConfigurationTests {
@Test @Test
public void asymmetricJwt() { public void asymmetricJwt() {
EnvironmentTestUtils.addEnvironment(this.environment, EnvironmentTestUtils.addEnvironment(this.environment,
"spring.oauth2.resource.jwt.keyValue=" + publicKey); "spring.oauth2.resource.jwt.keyValue=" + PUBLIC_KEY);
this.context = new SpringApplicationBuilder(ResourceConfiguration.class) this.context = new SpringApplicationBuilder(ResourceConfiguration.class)
.environment(this.environment).web(false).run(); .environment(this.environment).web(false).run();
DefaultTokenServices services = this.context.getBean(DefaultTokenServices.class); DefaultTokenServices services = this.context.getBean(DefaultTokenServices.class);
...@@ -141,6 +151,7 @@ public class ResourceServerTokenServicesConfigurationTests { ...@@ -141,6 +151,7 @@ public class ResourceServerTokenServicesConfigurationTests {
PropertyPlaceholderAutoConfiguration.class }) PropertyPlaceholderAutoConfiguration.class })
@EnableConfigurationProperties(OAuth2ClientProperties.class) @EnableConfigurationProperties(OAuth2ClientProperties.class)
protected static class ResourceConfiguration { protected static class ResourceConfiguration {
} }
@Configuration @Configuration
...@@ -154,18 +165,17 @@ public class ResourceServerTokenServicesConfigurationTests { ...@@ -154,18 +165,17 @@ public class ResourceServerTokenServicesConfigurationTests {
return new ResourceServerProperties(this.credentials.getClientId(), return new ResourceServerProperties(this.credentials.getClientId(),
this.credentials.getClientSecret()); this.credentials.getClientSecret());
} }
} }
@Import({ FacebookAutoConfiguration.class, SocialWebAutoConfiguration.class }) @Import({ FacebookAutoConfiguration.class, SocialWebAutoConfiguration.class })
protected static class SocialResourceConfiguration extends ResourceConfiguration { protected static class SocialResourceConfiguration extends ResourceConfiguration {
@Bean @Bean
public EmbeddedServletContainerFactory embeddedServletContainerFactory() { public EmbeddedServletContainerFactory embeddedServletContainerFactory() {
return Mockito.mock(EmbeddedServletContainerFactory.class); return mock(EmbeddedServletContainerFactory.class);
} }
}
private static String publicKey = "-----BEGIN PUBLIC KEY-----\n" }
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnGp/Q5lh0P8nPL21oMMrt2RrkT9AW5jgYwLfSUnJVc9G6uR3cXRRDCjHqWU5WYwivcF180A6CWp/ireQFFBNowgc5XaA0kPpzEtgsA5YsNX7iSnUibB004iBTfU9hZ2Rbsc8cWqynT0RyN4TP1RYVSeVKvMQk4GT1r7JCEC+TNu1ELmbNwMQyzKjsfBXyIOCFU/E94ktvsTZUHF4Oq44DBylCDsS1k7/sfZC2G5EU7Oz0mhG8+Uz6MSEQHtoIi6mc8u64Rwi3Z3tscuWG2ShtsUFuNSAFNkY7LkLn+/hxLCu2bNISMaESa8dG22CIMuIeRLVcAmEWEWH5EEforTg+QIDAQAB\n"
+ "-----END PUBLIC KEY-----";
} }
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -15,14 +15,11 @@ ...@@ -15,14 +15,11 @@
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.resource; package org.springframework.boot.autoconfigure.security.oauth2.resource;
import static org.junit.Assert.assertEquals;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.OAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2ClientContext;
...@@ -30,42 +27,51 @@ import org.springframework.security.oauth2.client.OAuth2RestOperations; ...@@ -30,42 +27,51 @@ import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails; import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import static org.junit.Assert.assertEquals;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
/** /**
* @author Dave Syer * Tests for {@link UserInfoTokenServices}.
* *
* @author Dave Syer
*/ */
public class UserInfoTokenServicesTests { public class UserInfoTokenServicesTests {
private UserInfoTokenServices services = new UserInfoTokenServices( private UserInfoTokenServices services = new UserInfoTokenServices(
"http://example.com", "foo"); "http://example.com", "foo");
private BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails(); private BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails();
private OAuth2RestOperations template = Mockito.mock(OAuth2RestOperations.class);
private OAuth2RestOperations template = mock(OAuth2RestOperations.class);
private Map<String, Object> map = new LinkedHashMap<String, Object>(); private Map<String, Object> map = new LinkedHashMap<String, Object>();
@Before @Before
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
public void init() { public void init() {
resource.setClientId("foo"); this.resource.setClientId("foo");
Mockito.when( given(this.template.getForEntity(any(String.class), any(Class.class)))
template.getForEntity(Mockito.any(String.class), Mockito.any(Class.class))) .willReturn(new ResponseEntity<Map>(this.map, HttpStatus.OK));
.thenReturn(new ResponseEntity<Map>(map, HttpStatus.OK)); given(this.template.getAccessToken()).willReturn(
Mockito.when(template.getAccessToken()).thenReturn(new DefaultOAuth2AccessToken("FOO")); new DefaultOAuth2AccessToken("FOO"));
Mockito.when(template.getResource()).thenReturn(resource); given(this.template.getResource()).willReturn(this.resource);
Mockito.when(template.getOAuth2ClientContext()).thenReturn( given(this.template.getOAuth2ClientContext()).willReturn(
Mockito.mock(OAuth2ClientContext.class)); mock(OAuth2ClientContext.class));
} }
@Test @Test
public void sunnyDay() { public void sunnyDay() {
services.setRestTemplate(template); this.services.setRestTemplate(this.template);
assertEquals("unknown", services.loadAuthentication("FOO").getName()); assertEquals("unknown", this.services.loadAuthentication("FOO").getName());
} }
@Test @Test
public void userId() { public void userId() {
map.put("userid", "spencer"); this.map.put("userid", "spencer");
services.setRestTemplate(template); this.services.setRestTemplate(this.template);
assertEquals("spencer", services.loadAuthentication("FOO").getName()); assertEquals("spencer", this.services.loadAuthentication("FOO").getName());
} }
} }
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,11 +13,8 @@ ...@@ -13,11 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.sso;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; package org.springframework.boot.autoconfigure.security.oauth2.sso;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import javax.servlet.Filter; import javax.servlet.Filter;
...@@ -39,9 +36,14 @@ import org.springframework.test.web.servlet.MockMvc; ...@@ -39,9 +36,14 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/** /**
* @author Dave Syer * Tests for {@link OAuth2AutoConfiguration} with basic configuration.
* *
* @author Dave Syer
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class) @SpringApplicationConfiguration(classes = TestConfiguration.class)
...@@ -64,12 +66,13 @@ public class BasicOAuth2SsoConfigurationTests { ...@@ -64,12 +66,13 @@ public class BasicOAuth2SsoConfigurationTests {
@Before @Before
public void init() { public void init() {
mvc = MockMvcBuilders.webAppContextSetup(context).addFilters(filter).build(); this.mvc = MockMvcBuilders.webAppContextSetup(this.context)
.addFilters(this.filter).build();
} }
@Test @Test
public void homePageIsSecure() throws Exception { public void homePageIsSecure() throws Exception {
mvc.perform(get("/")).andExpect(status().isFound()) this.mvc.perform(get("/")).andExpect(status().isFound())
.andExpect(header().string("location", "http://localhost/login")); .andExpect(header().string("location", "http://localhost/login"));
} }
......
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,13 +13,8 @@ ...@@ -13,13 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.sso;
import static org.hamcrest.Matchers.startsWith; package org.springframework.boot.autoconfigure.security.oauth2.sso;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import javax.servlet.Filter; import javax.servlet.Filter;
...@@ -45,9 +40,16 @@ import org.springframework.web.bind.annotation.RequestMapping; ...@@ -45,9 +40,16 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import static org.hamcrest.Matchers.startsWith;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/** /**
* @author Dave Syer * Tests for {@link OAuth2AutoConfiguration} with custom configuration.
* *
* @author Dave Syer
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class) @SpringApplicationConfiguration(classes = TestConfiguration.class)
...@@ -70,24 +72,25 @@ public class CustomOAuth2SsoConfigurationTests { ...@@ -70,24 +72,25 @@ public class CustomOAuth2SsoConfigurationTests {
@Before @Before
public void init() { public void init() {
mvc = MockMvcBuilders.webAppContextSetup(context).addFilters(filter).build(); this.mvc = MockMvcBuilders.webAppContextSetup(this.context)
.addFilters(this.filter).build();
} }
@Test @Test
public void homePageIsBasicAuth() throws Exception { public void homePageIsBasicAuth() throws Exception {
mvc.perform(get("/")).andExpect(status().isUnauthorized()) this.mvc.perform(get("/")).andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate", startsWith("Basic"))); .andExpect(header().string("WWW-Authenticate", startsWith("Basic")));
} }
@Test @Test
public void uiPageIsSecure() throws Exception { public void uiPageIsSecure() throws Exception {
mvc.perform(get("/ui/")).andExpect(status().isFound()) this.mvc.perform(get("/ui/")).andExpect(status().isFound())
.andExpect(header().string("location", "http://localhost/login")); .andExpect(header().string("location", "http://localhost/login"));
} }
@Test @Test
public void uiTestPageIsAccessible() throws Exception { public void uiTestPageIsAccessible() throws Exception {
mvc.perform(get("/ui/test")).andExpect(status().isOk()) this.mvc.perform(get("/ui/test")).andExpect(status().isOk())
.andExpect(content().string("test")); .andExpect(content().string("test"));
} }
...@@ -112,6 +115,7 @@ public class CustomOAuth2SsoConfigurationTests { ...@@ -112,6 +115,7 @@ public class CustomOAuth2SsoConfigurationTests {
} }
} }
} }
} }
/* /*
* Copyright 2015 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -40,8 +40,8 @@ import org.springframework.context.annotation.Import; ...@@ -40,8 +40,8 @@ import org.springframework.context.annotation.Import;
@Import({ EmbeddedServletContainerAutoConfiguration.class, @Import({ EmbeddedServletContainerAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, ErrorMvcAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, SecurityAutoConfiguration.class })
SecurityAutoConfiguration.class }) @interface MinimalSecureWebConfiguration { public @interface MinimalSecureWebConfiguration {
} }
\ No newline at end of file
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -28,28 +28,30 @@ import org.springframework.boot.cli.compiler.DependencyCustomizer; ...@@ -28,28 +28,30 @@ import org.springframework.boot.cli.compiler.DependencyCustomizer;
* *
* @author Greg Turnquist * @author Greg Turnquist
* @author Dave Syer * @author Dave Syer
* @since 1.3.0
*/ */
public class SpringSecurityOAuth2CompilerAutoConfiguration extends CompilerAutoConfiguration { public class SpringSecurityOAuth2CompilerAutoConfiguration extends
CompilerAutoConfiguration {
@Override @Override
public boolean matches(ClassNode classNode) { public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableAuthorizationServer",
"EnableAuthorizationServer", "EnableResourceServer", "EnableOAuth2Client", "EnableOAuth2Sso"); "EnableResourceServer", "EnableOAuth2Client", "EnableOAuth2Sso");
} }
@Override @Override
public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException { public void applyDependencies(DependencyCustomizer dependencies)
dependencies.add("spring-security-oauth2").add("spring-boot-starter-web") throws CompilationFailedException {
.add("spring-boot-starter-security"); dependencies.add("spring-security-oauth2", "spring-boot-starter-web",
"spring-boot-starter-security");
} }
@Override @Override
public void applyImports(ImportCustomizer imports) throws CompilationFailedException { public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
imports imports.addImports("org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso");
.addImports( imports.addStarImports(
"org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso") "org.springframework.security.oauth2.config.annotation.web.configuration",
.addStarImports( "org.springframework.security.access.prepost");
"org.springframework.security.oauth2.config.annotation.web.configuration",
"org.springframework.security.access.prepost");
} }
} }
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,11 +16,6 @@ ...@@ -16,11 +16,6 @@
package org.springframework.boot.cli; package org.springframework.boot.cli;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.File; import java.io.File;
import java.net.URI; import java.net.URI;
...@@ -28,6 +23,11 @@ import org.junit.Ignore; ...@@ -28,6 +23,11 @@ import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/** /**
* Integration tests to exercise the samples. * Integration tests to exercise the samples.
* *
...@@ -72,8 +72,10 @@ public class SampleIntegrationTests { ...@@ -72,8 +72,10 @@ public class SampleIntegrationTests {
@Test @Test
public void oauth2Sample() throws Exception { public void oauth2Sample() throws Exception {
String output = this.cli.run("oauth2.groovy"); String output = this.cli.run("oauth2.groovy");
assertTrue("Wrong output: " + output, output.contains("spring.oauth2.client.clientId")); assertTrue("Wrong output: " + output,
assertTrue("Wrong output: " + output, output.contains("spring.oauth2.client.secret =")); output.contains("spring.oauth2.client.clientId"));
assertTrue("Wrong output: " + output,
output.contains("spring.oauth2.client.secret ="));
} }
@Test @Test
......
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,13 +13,15 @@ ...@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package sample;
package sample.secure.oauth2;
import java.util.Date;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType; import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
...@@ -33,18 +35,24 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; ...@@ -33,18 +35,24 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class Flight { public class Flight {
@Id @GeneratedValue(strategy = GenerationType.AUTO) @Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id; private Long id;
private String origin; private String origin;
private String destination; private String destination;
private String airline; private String airline;
private String flightNumber; private String flightNumber;
private Date date; private Date date;
private String traveler; private String traveler;
public Long getId() { public Long getId() {
return id; return this.id;
} }
public void setId(Long id) { public void setId(Long id) {
...@@ -52,7 +60,7 @@ public class Flight { ...@@ -52,7 +60,7 @@ public class Flight {
} }
public String getOrigin() { public String getOrigin() {
return origin; return this.origin;
} }
public void setOrigin(String origin) { public void setOrigin(String origin) {
...@@ -60,7 +68,7 @@ public class Flight { ...@@ -60,7 +68,7 @@ public class Flight {
} }
public String getDestination() { public String getDestination() {
return destination; return this.destination;
} }
public void setDestination(String destination) { public void setDestination(String destination) {
...@@ -68,7 +76,7 @@ public class Flight { ...@@ -68,7 +76,7 @@ public class Flight {
} }
public String getAirline() { public String getAirline() {
return airline; return this.airline;
} }
public void setAirline(String airline) { public void setAirline(String airline) {
...@@ -76,7 +84,7 @@ public class Flight { ...@@ -76,7 +84,7 @@ public class Flight {
} }
public String getFlightNumber() { public String getFlightNumber() {
return flightNumber; return this.flightNumber;
} }
public void setFlightNumber(String flightNumber) { public void setFlightNumber(String flightNumber) {
...@@ -84,7 +92,7 @@ public class Flight { ...@@ -84,7 +92,7 @@ public class Flight {
} }
public Date getDate() { public Date getDate() {
return date; return this.date;
} }
public void setDate(Date date) { public void setDate(Date date) {
...@@ -92,10 +100,11 @@ public class Flight { ...@@ -92,10 +100,11 @@ public class Flight {
} }
public String getTraveler() { public String getTraveler() {
return traveler; return this.traveler;
} }
public void setTraveler(String traveler) { public void setTraveler(String traveler) {
this.traveler = traveler; this.traveler = traveler;
} }
} }
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,7 +13,8 @@ ...@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package sample;
package sample.secure.oauth2;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
...@@ -26,15 +27,16 @@ import org.springframework.security.access.prepost.PreAuthorize; ...@@ -26,15 +27,16 @@ import org.springframework.security.access.prepost.PreAuthorize;
*/ */
public interface FlightRepository extends CrudRepository<Flight, Long> { public interface FlightRepository extends CrudRepository<Flight, Long> {
@PreAuthorize("#oauth2.hasScope('read')")
@Override @Override
@PreAuthorize("#oauth2.hasScope('read')")
Iterable<Flight> findAll(); Iterable<Flight> findAll();
@PreAuthorize("#oauth2.hasScope('read')")
@Override @Override
@PreAuthorize("#oauth2.hasScope('read')")
Flight findOne(Long aLong); Flight findOne(Long aLong);
@PreAuthorize("#oauth2.hasScope('write')")
@Override @Override
@PreAuthorize("#oauth2.hasScope('write')")
<S extends Flight> S save(S entity); <S extends Flight> S save(S entity);
} }
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package sample; package sample.secure.oauth2;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
...@@ -21,14 +21,11 @@ import org.springframework.security.config.annotation.method.configuration.Enabl ...@@ -21,14 +21,11 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
// @formatter:off
/** /**
* After you launch the app, you can seek a bearer token like this: * After you launch the app, you can seek a bearer token like this:
* *
* <pre> * <pre>
*
* curl localhost:8080/oauth/token -d "grant_type=password&scope=read&username=greg&password=turnquist" -u foo:bar * curl localhost:8080/oauth/token -d "grant_type=password&scope=read&username=greg&password=turnquist" -u foo:bar
*
* </pre> * </pre>
* *
* <ul> * <ul>
...@@ -91,16 +88,14 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.E ...@@ -91,16 +88,14 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.E
* @author Craig Walls * @author Craig Walls
* @author Greg Turnquist * @author Greg Turnquist
*/ */
// @formatter:on
@SpringBootApplication @SpringBootApplication
@EnableAuthorizationServer @EnableAuthorizationServer
@EnableResourceServer @EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true)
public class Application { public class SampleSecureOAuth2Application {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(Application.class, args); SpringApplication.run(SampleSecureOAuth2Application.class, args);
} }
} }
package sample; package sample.secure.oauth2;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
import java.util.Map; import java.util.Map;
...@@ -28,8 +19,20 @@ import org.springframework.test.web.servlet.MockMvc; ...@@ -28,8 +19,20 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import sample.secure.oauth2.SampleSecureOAuth2Application;
import sample.secure.oauth2.Flight;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
/** /**
* Series of automated integration tests to verify proper behavior of auto-configured, * Series of automated integration tests to verify proper behavior of auto-configured,
* OAuth2-secured system * OAuth2-secured system
...@@ -38,12 +41,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; ...@@ -38,12 +41,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration @WebAppConfiguration
@SpringApplicationConfiguration(classes = Application.class) @SpringApplicationConfiguration(classes = SampleSecureOAuth2Application.class)
@IntegrationTest("server.port:0") @IntegrationTest("server.port:0")
public class ApplicationTests { public class SampleSecureOAuth2ApplicationTests {
@Autowired @Autowired
WebApplicationContext context; WebApplicationContext context;
@Autowired @Autowired
FilterChainProxy filterChain; FilterChainProxy filterChain;
...@@ -53,85 +57,51 @@ public class ApplicationTests { ...@@ -53,85 +57,51 @@ public class ApplicationTests {
@Before @Before
public void setUp() { public void setUp() {
this.mvc = webAppContextSetup(this.context).addFilters(this.filterChain).build(); this.mvc = webAppContextSetup(this.context).addFilters(this.filterChain).build();
SecurityContextHolder.clearContext(); SecurityContextHolder.clearContext();
} }
@Test @Test
public void everythingIsSecuredByDefault() throws Exception { public void everythingIsSecuredByDefault() throws Exception {
this.mvc.perform(get("/").accept(MediaTypes.HAL_JSON))
this.mvc.perform(get("/").// .andExpect(status().isUnauthorized()).andDo(print());
accept(MediaTypes.HAL_JSON)).// / this.mvc.perform(get("/flights").accept(MediaTypes.HAL_JSON))
andExpect(status().isUnauthorized()).// .andExpect(status().isUnauthorized()).andDo(print());
andDo(print()); this.mvc.perform(get("/flights/1").accept(MediaTypes.HAL_JSON))
.andExpect(status().isUnauthorized()).andDo(print());
this.mvc.perform(get("/flights").// this.mvc.perform(get("/alps").accept(MediaTypes.HAL_JSON))
accept(MediaTypes.HAL_JSON)).// / .andExpect(status().isUnauthorized()).andDo(print());
andExpect(status().isUnauthorized()).//
andDo(print());
this.mvc.perform(get("/flights/1").//
accept(MediaTypes.HAL_JSON)).// /
andExpect(status().isUnauthorized()).//
andDo(print());
this.mvc.perform(get("/alps").//
accept(MediaTypes.HAL_JSON)).// /
andExpect(status().isUnauthorized()).//
andDo(print());
} }
@Test @Test
@Ignore @Ignore
// TODO: maybe show mixed basic + token auth on different resources?
public void accessingRootUriPossibleWithUserAccount() throws Exception { public void accessingRootUriPossibleWithUserAccount() throws Exception {
String header = "Basic " + new String(Base64.encode("greg:turnquist".getBytes()));
this.mvc.perform( this.mvc.perform(
get("/").// get("/").accept(MediaTypes.HAL_JSON).header("Authorization", header))
accept(MediaTypes.HAL_JSON).// .andExpect(
header("Authorization", header().string("Content-Type", MediaTypes.HAL_JSON.toString()))
"Basic " .andExpect(status().isOk()).andDo(print());
+ new String(Base64.encode("greg:turnquist"
.getBytes()))))
.//
andExpect(header().string("Content-Type", MediaTypes.HAL_JSON.toString()))
.//
andExpect(status().isOk()).//
andDo(print());
} }
@Test @Test
public void useAppSecretsPlusUserAccountToGetBearerToken() throws Exception { public void useAppSecretsPlusUserAccountToGetBearerToken() throws Exception {
String header = "Basic " + new String(Base64.encode("foo:bar".getBytes()));
// @formatter:off
MvcResult result = this.mvc MvcResult result = this.mvc
.perform( .perform(
post("/oauth/token"). post("/oauth/token").header("Authorization", header)
header("Authorization", .param("grant_type", "password").param("scope", "read")
"Basic " + new String(Base64.encode("foo:bar".getBytes()))). .param("username", "greg").param("password", "turnquist"))
param("grant_type", "password"). .andExpect(status().isOk()).andDo(print()).andReturn();
param("scope", "read").
param("username", "greg").
param("password", "turnquist")).
andExpect(status().isOk()).
andDo(print()).
andReturn();
// @formatter:on
Object accessToken = this.objectMapper.readValue( Object accessToken = this.objectMapper.readValue(
result.getResponse().getContentAsString(), Map.class).get("access_token"); result.getResponse().getContentAsString(), Map.class).get("access_token");
MvcResult flightsAction = this.mvc MvcResult flightsAction = this.mvc
.perform(get("/flights/1").// .perform(
accept(MediaTypes.HAL_JSON).// get("/flights/1").accept(MediaTypes.HAL_JSON).header(
header("Authorization", "Bearer " + accessToken)) "Authorization", "Bearer " + accessToken))
.// .andExpect(
andExpect(header().string("Content-Type", MediaTypes.HAL_JSON.toString())) header().string("Content-Type", MediaTypes.HAL_JSON.toString()))
.// .andExpect(status().isOk()).andDo(print()).andReturn();
andExpect(status().isOk()).//
andDo(print()).//
andReturn();
Flight flight = this.objectMapper.readValue(flightsAction.getResponse() Flight flight = this.objectMapper.readValue(flightsAction.getResponse()
.getContentAsString(), Flight.class); .getContentAsString(), Flight.class);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment