Build Secure Microservices in Your Spring REST API
Here's how a secure Spring REST API can reduce some of the pain points when building a microservices architecture.
Join the DZone community and get the full member experience.
Join For FreeUsers ask us for new features, bug fixes, and changes in domain logic for our applications every day. As any project (especially a monolith) grows, it often becomes difficult to maintain, and the barrier to entry for a new developer joining the project gets higher and higher.
In this tutorial, I’ll walk you through building a secure Spring REST API that tries to solve for some of these pain points using a microservices architecture.
In a typical microservices architecture, you divide your application into several apps that can be more easily maintained and scaled, use different stacks, and support more teams working in parallel. But microservices are the simple solution to every scaling and maintenance problem.
Microservices also present a number of architectural challenges that must be addressed:
- How those services communicate?
- How should communication failures and availability be handled?
- How can a user’s requests be traced between services?
- And, how should you handle user authorization to access a single service?
Let’s dig in and find out how to address these challenges when building a Spring REST API.
Secure Your Spring REST API With OAuth 2.0
In OAuth 2.0, a resource server is a service designed to handle domain-logic requests and does not have any kind of login workflow or complex authentication mechanism: It receives a pre-obtained access token that guarantees a user have grant permission to access the server and delivers the expected response.
In this post, you are going to build a simple Resource Server with Spring Boot and Okta to demonstrate how easy it is. You will implement a simple Resource Server that will receive and validate a JWT Token.
Add a Resource Server Your Spring REST API
This example uses Okta to handle all authentication process. You can register for a free-forever developer account that will enable you to create as many users and applications you need.
I have set up some things so we can get started easily. Please clone the following resource repository and go to startup
tag, as follows:
git clone -b startup https://github.com/oktadeveloper/okta-secure-spring-rest-api-example secure-spring-rest-api
cd secure-spring-rest-api
This project has the following structure:
$ tree .
.
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── net
│ │ └── dovale
│ │ └── okta
│ │ └── secure_rest_api
│ │ ├── HelloWorldController.java
│ │ ├── SecureRestApiApplication.java
│ │ └── SecurityConfig.java
│ └── resources
│ └── application.properties
└── test
└── java
└── net
└── dovale
└── okta
└── secure_rest_api
└── SecureRestApiApplicationTests.java
14 directories, 9 files
I created it using the excellent Spring Initializr and adding Web
and Security
dependencies. Spring Initializr provides an easy way to create a new Spring Boot service with some common auto-discovered dependencies. It also adds the Maven Wrapper: So, you use the command mvnw
instead of mvn
, the tool will detect if you have the designated Maven version and, if not, it will download and run the specified command.
The file HelloWorldController
is a simple @RestController
that outputs “Hello World.”
In a terminal, you can run the following command and see Spring Boot start:
mvnw spring-boot:run
TIP: If this command doesn’t work for you, try ./mvnw spring-boot:run
instead.
Once it finishes loading, you’ll have a REST API ready and set to deliver to you a glorious Hello World message!
> curl http://localhost:8080/
Hello World
TIP: The curl
command is not available by default for Windows users. You can download it from here.
Now, you need to properly create a protected Resource Server.
Set Up an OAuth 2.0 Resource Server
In the Okta dashboard, create an application of type Service it indicates a resource server that does not have a login page or any way to obtain new tokens.
Click Next, type the name of your service, and then click Done. You will be presented with a screen similar to the one below. Copy and paste your Client ID and Client Secret for later. They will be useful when you are configuring your application.
Now, let’s code something!
Edit the pom.xml
file and add dependencies for Spring Security and Okta. They will enable all the Spring AND Okta OAuth 2.0 goodness you need:
<!-- security - begin -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>com.okta.spring</groupId>
<artifactId>okta-spring-boot-starter</artifactId>
<version>0.6.1</version>
</dependency>
<!-- security - end -->
By simply adding this dependency, your code is going to be like a locked house without a key. No one can access your API until you provide a key to your users. Run the command below again.
mvnw spring-boot:run
Now, try to access the Hello World resource:
> curl http://localhost:8080/
{"timestamp":"2018-11-30T01:35:30.038+0000","status":401,"error":"Unauthorized","message":"Unauthorized","path":"/"}
Add Spring Security to Your REST API
Spring Boot has a lot of classpath magic and is able to discover and automatically configure dependencies. Since you have added Spring Security, it automatically secured your resources. Now, you need to configure Spring Security so you can properly authenticate the requests.
NOTE: If you are struggling, you can check the modifications in Git branch
step-1-security-dependencies
.
For that, you need to modify application.properties
as follows (use client_id and client_secret provided by Okta dashboard to your application):
okta.oauth2.issuer=https://okta.okta.com/oauth2/default
okta.oauth2.clientId={clientId}
okta.oauth2.clientSecret={clientSecret}
okta.oauth2.scopes=openid
Spring Boot uses annotations and code for configuring your application so you do not need to edit super boring XML files. This means you can use the Java compiler to validate your configuration!
I usually create a configuration in different classes, each one has its own purpose. Create the class net.dovale.okta.secure_rest_api.SecurityConfig
as follows:
package net.dovale.okta.secure_rest_api;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
@EnableWebSecurity
@EnableResourceServer
public class SecurityConfig {}
Allow me to explain what the annotations here do:
@EnableWebSecurity
tells Spring we are going to use Spring Security to provide web security mechanisms@EnableResourceServer
is a convenient annotation that enables request authentication through OAuth 2.0 tokens. Normally, you would provide aResourceServerConfigurer
bean, but Okta’s Spring Boot starter conveniently provides one for you.
That’s it! Now, you have a completely configured and secured Spring REST API without any boilerplate!
Run Spring Boot again and check it with cURL.
mvnw spring-boot:run
# in another shell
curl http://localhost:8080/
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
The message changed, but you still without access… why? Because now the server is waiting for an authorization
header with a valid token. In the next step, you’ll create an access token and use it to access your API.
NOTE: Check the Git branch
step-2-security-configuration
if you have any doubt.
Generate Tokens in Your Spring REST API
So… how do you obtain a token? A resource server has no responsibility to obtain valid credentials: It will only check if the token is valid and proceed with the method execution.
An easy way to achieve a token to generate one using OpenID Connect <debugger/>.
First, you’ll need to create a new Web application in Okta:
Set the Login redirect URIs field to https://oidcdebugger.com/debug
and Grant Type Allowed to Hybrid
. Click Done and copy the client ID for the next step.
Now, on the OpenID Connect website, fill the form in like the picture below (do not forget to fill in the client ID for your recently created Okta web application):
Submit the form to start the authentication process. You’ll receive an Okta login form if you are not logged in or you’ll see the screen below with your custom token.
The token will be valid for one hour so you can do a lot of testing with your API. It’s simple to use the token — just copy it and modify the curl command to use it as follows:
> export TOKEN=${YOUR_TOKEN}
> curl http://localhost:8080 -H "Authorization: Bearer $TOKEN"
Hello World
Add OAuth 2.0 Scopes
OAuth 2.0 scopes is a feature that lets users decide if the application will be authorized to make something restricted. For example, you could have “read” and “write” scopes. If an application needs the write scope, it should ask the user this specific scope. These can be automatically handled by Okta’s authorization server.
As a resource server, it can have different endpoints with different scope for each one. Next, you are going to learn how to set different scopes and how to test them.
Add a new annotation to your SecurityConfig
class:
@EnableWebSecurity
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {}
The new @EnableGlobalMethodSecurity(prePostEnabled = true)
annotation tells Spring to use AOP-like method security, and prePostEnabled = true
will enable pre and post annotations. Those annotations will enable us to define security programmatically for each endpoint.
Now, make changes to HelloWorldController.java
to create a scope-protected endpoint:
import org.springframework.security.access.prepost.PreAuthorize;
import java.security.Principal;
...
@PreAuthorize("#oauth2.hasScope('profile')")
@GetMapping("/protected/")
public String helloWorldProtected(Principal principal) {
return "Hello VIP " + principal.getName();
}
Pay attention to @PreAuthorize("#oauth2.hasScope('profile')")
. It says: Before running this method, verify the request has authorization for the specified Scope. The #oauth2
bit is added by OAuth2SecurityExpressionMethods (check the other methods available) Spring class and is added to your classpath through the spring-cloud-starter-oauth2
dependency.
OK! After a restart, your server will be ready! Make a new request to the endpoint using your current token:
> curl http://localhost:8080/protected/ -H "Authorization: Bearer $TOKEN"
{"error":"access_denied","error_description":"Access is denied"}
Since your token does not have the desired scope, you’ll receive an access is denied
message. To fix this, head back over to OIDC Debugger and add the new scope.
Try again using the newly obtained token:
> curl http://localhost:8080/protected/ -H "Authorization: Bearer $TOKEN"
Hello VIP raphael@dovale.net
That’s it! If you are in doubt of anything, check the latest repository branch finished_sample
.
TIP: Since
profile
is a common OAuth 2.0 scope, you don’t need to change anything in your authorization server. Need to create a custom scope? See this Simple Token Authentication for Java Apps.
Learn More About Spring and REST APIs
In this tutorial, you learned how to use Spring (Boot) to create a resource server and seamlessly integrate it with OAuth 2.0. Both Spring and REST API’s are huge topics, with lots to discuss and learn.
The source code for this tutorial is available on GitHub.
Here are some other posts that will help you further your understanding of both Spring and REST API security:
- What the Heck is OAuth?
- Secure Server-to-Server Communication with Spring Boot and OAuth 2.0
- Spring Boot 2.1: Outstanding OIDC, OAuth 2.0, and Reactive API Support
- Add User Authentication to Your Spring Boot App in 15 Minutes
Like what you learned today? Follow us on Twitter, like us on Facebook, check us out on LinkedIn, and subscribe to our YouTube channel.
Create a Secure Spring REST API was originally published to the Okta developer blog on December 18, 2018 by Raphael do Vale.
Published at DZone with permission of Lindsay Brunner, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments