1. Overview
In this tutorial, we’ll secure a REST API with OAuth2 and consume it from a simple client. The application we’re going to build out will consist of three modules:
- Authorization Server
- Resource Server
- Web Application
The simple project uses the implicit grant. The diagram below represents the flow used int this case:
I assume that you are familiar with the Spring and you know how to make the basic Spring application 🙂
2. The Authorization Server
Let’s start setting up an Authorization Server as a simple Spring Boot application. In order to do that we need to add some dependencies to our pom.xml file.
2.1. Maven Configuration
We’ll set up the following set of dependencies:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency>
2.2. @EnableAuthorizationServer
Next, we will configure our authorization server to use JwtTokenStore – as follows:
@Configuration @EnableAuthorizationServer public class OAuth2Config extends AuthorizationServerConfigurerAdapter { private final AuthenticationManager authenticationManager; @Autowired public OAuth2Config(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(tokenStore()) .tokenEnhancer(jwtTokenEnhancer()) .authenticationManager(authenticationManager); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security .tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()"); } @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtTokenEnhancer()); } @Bean protected JwtAccessTokenConverter jwtTokenEnhancer() { KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory (new ClassPathResource("jwt.jks"), "mySecretKey".toCharArray()); JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt")); return converter; } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("dummy-client") .secret("dummy-secret") .authorizedGrantTypes("client_credentials") .scopes("read", "write") .accessTokenValiditySeconds(60); } }
Note that the current OAuth2Config is working in memory, for a realistic use-case we may need to integrate it with Hazelcast or JDBC to access persistent data.
3. The Resource Server
Once the Authentication Server is up and running, we want to create a Service that only allows access to authenticated users. So, we will create another simple Spring Boot application.
3.1 The endpoint for getting the public key
In the application.properties file, we can set the endpoint for getting the public key of the certificate used to sign the token:
security.oauth2.resource.jwt.keyUri=http://localhost:8081/spring-oauth-server/oauth/token_key
3.2 @RestController
Next, let’s implement a simple controller exposing some resource.
With spring-oauth and spring-security we can use two different approaches for access control:
- by ROLES → .access(“hasRole(‘ROLE_RS_WRITE’)”)
- by SCOPES → .access(“#oauth2.hasScope(‘read’)”)
@PreAuthorize("#oauth2.hasScope('read')") @RequestMapping(value = "/", method = RequestMethod.GET) public Map<String, String> home() { return Collections.singletonMap("message", message); }
4. Web App – Implicit Grant
Let’s take a look at our web (client) application that uses the implicit grant. Our web application is a separate module that tries to access the resources server after obtaining an access token from the authorization server using implicit grant flow.
4.1 The applcation.properties
spring.aop.proxy-target-class=true security.oauth2.resource.jwt.keyUri=http://localhost:8081/spring-oauth-server/oauth/token_key resourceServerClient.accessTokenUri=http://localhost:8081/spring-oauth-server/oauth/token resourceServerClient.clientId=dummy-client resourceServerClient.clientSecret=dummy-secret
4.2 OAuthClientConfiguration
@Configuration public class OAuthClientConfiguration { @Bean @ConfigurationProperties("resourceServerClient") public ClientCredentialsResourceDetails getClientCredentialsResourceDetails() { return new ClientCredentialsResourceDetails(); } @Bean public OAuth2RestTemplate restTemplate() { AccessTokenRequest accessTokenRequest = new DefaultAccessTokenRequest(); return new OAuth2RestTemplate(getClientCredentialsResourceDetails(), new DefaultOAuth2ClientContext(accessTokenRequest)); } }
4.3 @RestController
@RestController public class MessageController { private final OAuth2RestTemplate resourceServerProxy; @Value("${resource.server.url}") private String resourceServer; @Autowired public MessageController(OAuth2RestTemplate resourceServerProxy) { this.resourceServerProxy = resourceServerProxy; } @RequestMapping(value = "/api/message", method = RequestMethod.GET) public Map getMessage() { return resourceServerProxy.getForObject(resourceServer, Map.class); } @RequestMapping(value = "/api/message", method = RequestMethod.POST) public void saveMessage(@RequestBody String newMessage) { resourceServerProxy.postForLocation(resourceServer, newMessage); } }
5. Conclusion
In this quick article we focused on setting up our Spring Security OAuth2 project to use JSON Web Tokens (JWT). The full implementation of this tutorial can be found in the github project.