/* * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.boot.autoconfigure.security.oauth2; import java.net.URI; import java.util.Arrays; import java.util.Base64; import java.util.List; import com.fasterxml.jackson.databind.JsonNode; import org.junit.Test; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.oauth2.authserver.OAuth2AuthorizationServerConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.annotation.Order; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource; import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource; import org.springframework.security.access.method.MethodSecurityMetadataSource; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice; import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.oauth2.client.OAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestOperations; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.approval.ApprovalStore; import org.springframework.security.oauth2.provider.approval.ApprovalStoreUserApprovalHandler; import org.springframework.security.oauth2.provider.approval.TokenApprovalStore; import org.springframework.security.oauth2.provider.approval.UserApprovalHandler; import org.springframework.security.oauth2.provider.client.BaseClientDetails; import org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService; import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint; import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; /** * Verify Spring Security OAuth2 auto-configuration secures end points properly, accepts * environmental overrides, and also backs off in the presence of other * resource/authorization components. * * @author Greg Turnquist * @author Dave Syer */ public class OAuth2AutoConfigurationTests { private static final Class<?> RESOURCE_SERVER_CONFIG = OAuth2ResourceServerConfiguration.class; private static final Class<?> AUTHORIZATION_SERVER_CONFIG = OAuth2AuthorizationServerConfiguration.class; private AnnotationConfigServletWebServerApplicationContext context; @Test public void testDefaultConfiguration() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(AuthorizationAndResourceServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); this.context.getBean(AUTHORIZATION_SERVER_CONFIG); this.context.getBean(RESOURCE_SERVER_CONFIG); this.context.getBean(OAuth2MethodSecurityConfiguration.class); ClientDetails config = this.context.getBean(BaseClientDetails.class); AuthorizationEndpoint endpoint = this.context .getBean(AuthorizationEndpoint.class); UserApprovalHandler handler = (UserApprovalHandler) ReflectionTestUtils .getField(endpoint, "userApprovalHandler"); ClientDetailsService clientDetailsService = this.context .getBean(ClientDetailsService.class); ClientDetails clientDetails = clientDetailsService .loadClientByClientId(config.getClientId()); assertThat(AopUtils.isJdkDynamicProxy(clientDetailsService)).isTrue(); assertThat(AopUtils.getTargetClass(clientDetailsService).getName()) .isEqualTo(InMemoryClientDetailsService.class.getName()); assertThat(handler).isInstanceOf(ApprovalStoreUserApprovalHandler.class); assertThat(clientDetails).isEqualTo(config); verifyAuthentication(config); assertThat(this.context.getBeanNamesForType(OAuth2RestOperations.class)) .isEmpty(); } @Test public void methodSecurityExpressionHandlerIsConfiguredWithRoleHierarchyFromTheContext() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(RoleHierarchyConfiguration.class, AuthorizationAndResourceServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); PreInvocationAuthorizationAdvice advice = this.context .getBean(PreInvocationAuthorizationAdvice.class); MethodSecurityExpressionHandler expressionHandler = (MethodSecurityExpressionHandler) ReflectionTestUtils .getField(advice, "expressionHandler"); RoleHierarchy roleHierarchy = (RoleHierarchy) ReflectionTestUtils .getField(expressionHandler, "roleHierarchy"); assertThat(roleHierarchy).isSameAs(this.context.getBean(RoleHierarchy.class)); } @Test public void methodSecurityExpressionHandlerIsConfiguredWithPermissionEvaluatorFromTheContext() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(PermissionEvaluatorConfiguration.class, AuthorizationAndResourceServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); PreInvocationAuthorizationAdvice advice = this.context .getBean(PreInvocationAuthorizationAdvice.class); MethodSecurityExpressionHandler expressionHandler = (MethodSecurityExpressionHandler) ReflectionTestUtils .getField(advice, "expressionHandler"); PermissionEvaluator permissionEvaluator = (PermissionEvaluator) ReflectionTestUtils .getField(expressionHandler, "permissionEvaluator"); assertThat(permissionEvaluator) .isSameAs(this.context.getBean(PermissionEvaluator.class)); } @Test public void testEnvironmentalOverrides() { this.context = new AnnotationConfigServletWebServerApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, "security.oauth2.client.clientId:myclientid", "security.oauth2.client.clientSecret:mysecret", "security.oauth2.client.autoApproveScopes:read,write", "security.oauth2.client.accessTokenValiditySeconds:40", "security.oauth2.client.refreshTokenValiditySeconds:80"); this.context.register(AuthorizationAndResourceServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); ClientDetails config = this.context.getBean(ClientDetails.class); assertThat(config.getClientId()).isEqualTo("myclientid"); assertThat(config.getClientSecret()).isEqualTo("mysecret"); assertThat(config.isAutoApprove("read")).isTrue(); assertThat(config.isAutoApprove("write")).isTrue(); assertThat(config.isAutoApprove("foo")).isFalse(); assertThat(config.getAccessTokenValiditySeconds()).isEqualTo(40); assertThat(config.getRefreshTokenValiditySeconds()).isEqualTo(80); verifyAuthentication(config); } @Test public void testDisablingResourceServer() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(AuthorizationServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(0); assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(1); } @Test public void testClientIsNotResourceServer() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(ClientConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(0); assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(0); // Scoped target and proxy: assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(2); } @Test public void testCanUseClientCredentials() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(TestSecurityConfiguration.class, MinimalSecureWebApplication.class); EnvironmentTestUtils.addEnvironment(this.context, "security.oauth2.client.clientId=client", "security.oauth2.client.grantType=client_credentials"); ConfigurationPropertySources.attach(this.context.getEnvironment()); this.context.refresh(); OAuth2ClientContext bean = this.context.getBean(OAuth2ClientContext.class); assertThat(bean.getAccessTokenRequest()).isNotNull(); assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1); assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(1); } @Test public void testCanUseClientCredentialsWithEnableOAuth2Client() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(ClientConfiguration.class, MinimalSecureWebApplication.class); EnvironmentTestUtils.addEnvironment(this.context, "security.oauth2.client.clientId=client", "security.oauth2.client.grantType=client_credentials"); ConfigurationPropertySources.attach(this.context.getEnvironment()); this.context.refresh(); // The primary context is fine (not session scoped): OAuth2ClientContext bean = this.context.getBean(OAuth2ClientContext.class); assertThat(bean.getAccessTokenRequest()).isNotNull(); assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1); // Kind of a bug (should ideally be 1), but the cause is in Spring OAuth2 (there // is no need for the extra session-scoped bean). What this test proves is that // even if the user screws up and does @EnableOAuth2Client for client credentials, // it will still just about work (because of the @Primary annotation on the // Boot-created instance of OAuth2ClientContext). assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(2); } @Test public void testClientIsNotAuthCode() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MinimalSecureNonWebApplication.class); EnvironmentTestUtils.addEnvironment(context, "security.oauth2.client.clientId=client"); context.refresh(); assertThat(countBeans(context, ClientCredentialsResourceDetails.class)) .isEqualTo(1); context.close(); } @Test public void testDisablingAuthorizationServer() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(ResourceServerConfiguration.class, MinimalSecureWebApplication.class); EnvironmentTestUtils.addEnvironment(this.context, "security.oauth2.resource.jwt.keyValue:DEADBEEF"); ConfigurationPropertySources.attach(this.context.getEnvironment()); this.context.refresh(); assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1); assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(0); assertThat(countBeans(UserApprovalHandler.class)).isEqualTo(0); assertThat(countBeans(DefaultTokenServices.class)).isEqualTo(1); } @Test public void testResourceServerOverride() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(AuthorizationAndResourceServerConfiguration.class, CustomResourceServer.class, MinimalSecureWebApplication.class); this.context.refresh(); ClientDetails config = this.context.getBean(ClientDetails.class); assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(1); assertThat(countBeans(CustomResourceServer.class)).isEqualTo(1); assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1); verifyAuthentication(config); } @Test public void testAuthorizationServerOverride() { this.context = new AnnotationConfigServletWebServerApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, "security.oauth2.resourceId:resource-id"); this.context.register(AuthorizationAndResourceServerConfiguration.class, CustomAuthorizationServer.class, MinimalSecureWebApplication.class); this.context.refresh(); BaseClientDetails config = new BaseClientDetails(); config.setClientId("client"); config.setClientSecret("secret"); config.setResourceIds(Arrays.asList("resource-id")); config.setAuthorizedGrantTypes(Arrays.asList("password")); config.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("USER")); config.setScope(Arrays.asList("read")); assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(0); assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1); verifyAuthentication(config); } @Test public void testDefaultPrePostSecurityAnnotations() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(AuthorizationAndResourceServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); this.context.getBean(OAuth2MethodSecurityConfiguration.class); ClientDetails config = this.context.getBean(ClientDetails.class); DelegatingMethodSecurityMetadataSource source = this.context .getBean(DelegatingMethodSecurityMetadataSource.class); List<MethodSecurityMetadataSource> sources = source .getMethodSecurityMetadataSources(); assertThat(sources.size()).isEqualTo(1); assertThat(sources.get(0).getClass().getName()) .isEqualTo(PrePostAnnotationSecurityMetadataSource.class.getName()); verifyAuthentication(config); } @Test public void testClassicSecurityAnnotationOverride() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(SecuredEnabledConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); this.context.getBean(OAuth2MethodSecurityConfiguration.class); ClientDetails config = this.context.getBean(ClientDetails.class); DelegatingMethodSecurityMetadataSource source = this.context .getBean(DelegatingMethodSecurityMetadataSource.class); List<MethodSecurityMetadataSource> sources = source .getMethodSecurityMetadataSources(); assertThat(sources.size()).isEqualTo(1); assertThat(sources.get(0).getClass().getName()) .isEqualTo(SecuredAnnotationSecurityMetadataSource.class.getName()); verifyAuthentication(config, HttpStatus.OK); } @Test public void testJsr250SecurityAnnotationOverride() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(Jsr250EnabledConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); this.context.getBean(OAuth2MethodSecurityConfiguration.class); ClientDetails config = this.context.getBean(ClientDetails.class); DelegatingMethodSecurityMetadataSource source = this.context .getBean(DelegatingMethodSecurityMetadataSource.class); List<MethodSecurityMetadataSource> sources = source .getMethodSecurityMetadataSources(); assertThat(sources.size()).isEqualTo(1); assertThat(sources.get(0).getClass().getName()) .isEqualTo(Jsr250MethodSecurityMetadataSource.class.getName()); verifyAuthentication(config, HttpStatus.OK); } @Test public void testMethodSecurityBackingOff() { this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context.register(CustomMethodSecurity.class, TestSecurityConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); DelegatingMethodSecurityMetadataSource source = this.context .getBean(DelegatingMethodSecurityMetadataSource.class); List<MethodSecurityMetadataSource> sources = source .getMethodSecurityMetadataSources(); assertThat(sources.size()).isEqualTo(1); assertThat(sources.get(0).getClass().getName()) .isEqualTo(PrePostAnnotationSecurityMetadataSource.class.getName()); } @Test public void resourceServerConditionWhenJwkConfigurationPresentShouldMatch() throws Exception { this.context = new AnnotationConfigServletWebServerApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, "security.oauth2.resource.jwk.key-set-uri:http://my-auth-server/token_keys"); this.context.register(ResourceServerConfiguration.class, MinimalSecureWebApplication.class); this.context.refresh(); assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1); } /** * Connect to the oauth service, get a token, and then attempt some operations using * it. * @param config the client details. */ private void verifyAuthentication(ClientDetails config) { verifyAuthentication(config, HttpStatus.FORBIDDEN); } private void verifyAuthentication(ClientDetails config, HttpStatus finalStatus) { String baseUrl = "http://localhost:" + this.context.getWebServer().getPort(); TestRestTemplate rest = new TestRestTemplate(); // First, verify the web endpoint can't be reached assertEndpointUnauthorized(baseUrl, rest); // Since we can't reach it, need to collect an authorization token HttpHeaders headers = getHeaders(config); String url = baseUrl + "/oauth/token"; JsonNode tokenResponse = rest.postForObject(url, new HttpEntity<>(getBody(), headers), JsonNode.class); String authorizationToken = tokenResponse.findValue("access_token").asText(); String tokenType = tokenResponse.findValue("token_type").asText(); String scope = tokenResponse.findValues("scope").get(0).toString(); assertThat(tokenType).isEqualTo("bearer"); assertThat(scope).isEqualTo("\"read\""); // Now we should be able to see that endpoint. headers.set("Authorization", "BEARER " + authorizationToken); ResponseEntity<String> securedResponse = rest .exchange(new RequestEntity<Void>(headers, HttpMethod.GET, URI.create(baseUrl + "/securedFind")), String.class); assertThat(securedResponse.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(securedResponse.getBody()).isEqualTo( "You reached an endpoint " + "secured by Spring Security OAuth2"); ResponseEntity<String> entity = rest.exchange(new RequestEntity<Void>(headers, HttpMethod.POST, URI.create(baseUrl + "/securedSave")), String.class); assertThat(entity.getStatusCode()).isEqualTo(finalStatus); } private HttpHeaders getHeaders(ClientDetails config) { HttpHeaders headers = new HttpHeaders(); String token = new String(Base64.getEncoder().encode( (config.getClientId() + ":" + config.getClientSecret()).getBytes())); headers.set("Authorization", "Basic " + token); return headers; } private MultiValueMap<String, Object> getBody() { MultiValueMap<String, Object> body = new LinkedMultiValueMap<>(); body.set("grant_type", "password"); body.set("username", "foo"); body.set("password", "bar"); body.set("scope", "read"); return body; } private void assertEndpointUnauthorized(String baseUrl, TestRestTemplate rest) { URI uri = URI.create(baseUrl + "/secured"); ResponseEntity<String> entity = rest .exchange(new RequestEntity<Void>(HttpMethod.GET, uri), String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } private int countBeans(Class<?> type) { return countBeans(this.context, type); } private int countBeans(ApplicationContext context, Class<?> type) { return context.getBeanNamesForType(type).length; } @Configuration @Import({ UseFreePortEmbeddedContainerConfiguration.class, SecurityAutoConfiguration.class, DispatcherServletAutoConfiguration.class, OAuth2AutoConfiguration.class, WebMvcAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class }) protected static class MinimalSecureWebApplication { } @Configuration @Import({ SecurityAutoConfiguration.class, OAuth2AutoConfiguration.class }) protected static class MinimalSecureNonWebApplication { } @Configuration @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) protected static class TestSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("foo").password("bar").roles("USER"); } @Bean TestWebApp testWebApp() { return new TestWebApp(); } } @Configuration @EnableOAuth2Client protected static class ClientConfiguration extends TestSecurityConfiguration { } @Configuration @EnableAuthorizationServer @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) protected static class AuthorizationAndResourceServerConfiguration extends TestSecurityConfiguration { } @Configuration @EnableAuthorizationServer @EnableResourceServer @EnableGlobalMethodSecurity(securedEnabled = true) protected static class SecuredEnabledConfiguration extends TestSecurityConfiguration { } @Configuration @EnableAuthorizationServer @EnableResourceServer @EnableGlobalMethodSecurity(jsr250Enabled = true) protected static class Jsr250EnabledConfiguration extends TestSecurityConfiguration { } @Configuration @EnableAuthorizationServer protected static class AuthorizationServerConfiguration extends TestSecurityConfiguration { } @Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends TestSecurityConfiguration { } @RestController protected static class TestWebApp { @GetMapping("/securedFind") @PreAuthorize("#oauth2.hasScope('read')") public String secureFind() { return "You reached an endpoint secured by Spring Security OAuth2"; } @PostMapping("/securedSave") @PreAuthorize("#oauth2.hasScope('write')") public String secureSave() { return "You reached an endpoint secured by Spring Security OAuth2"; } } @Configuration protected static class UseFreePortEmbeddedContainerConfiguration { @Bean TomcatServletWebServerFactory webServerFactory() { return new TomcatServletWebServerFactory(0); } } @Configuration @EnableResourceServer protected static class CustomResourceServer extends ResourceServerConfigurerAdapter { private final ResourceServerProperties config; protected CustomResourceServer(ResourceServerProperties config) { this.config = config; } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { if (this.config.getId() != null) { resources.resourceId(this.config.getId()); } } @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated().and().httpBasic().and() .csrf().disable(); } } @Configuration @EnableAuthorizationServer protected static class CustomAuthorizationServer extends AuthorizationServerConfigurerAdapter { private final AuthenticationManager authenticationManager; protected CustomAuthorizationServer(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } @Bean public TokenStore tokenStore() { return new InMemoryTokenStore(); } @Bean public ApprovalStore approvalStore(final TokenStore tokenStore) { TokenApprovalStore approvalStore = new TokenApprovalStore(); approvalStore.setTokenStore(tokenStore); return approvalStore; } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory().withClient("client").secret("secret") .resourceIds("resource-id").authorizedGrantTypes("password") .authorities("USER").scopes("read") .redirectUris("http://localhost:8080"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore()) .authenticationManager(this.authenticationManager); } } @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) protected static class CustomMethodSecurity extends GlobalMethodSecurityConfiguration { @Override protected MethodSecurityExpressionHandler createExpressionHandler() { return new OAuth2MethodSecurityExpressionHandler(); } } @Configuration protected static class RoleHierarchyConfiguration { @Bean public RoleHierarchy roleHierarchy() { return mock(RoleHierarchy.class); } } @Configuration protected static class PermissionEvaluatorConfiguration { @Bean public PermissionEvaluator permissionEvaluator() { return mock(PermissionEvaluator.class); } } }