search
Example based on a service built as backend for some mobile apps

Spring Boot tutorial: REST services and microservices

Michael Gruczel
Spring Boot

© Shutterstock / Kunst Bilder

In this tutorial, Michael Gruczel uses a simple example to show how to set up a REST-based microservice with Spring Boot.

The times of Java EE application server and monolithic software architectures are nearly gone. Hardware is not getting faster anymore, but internet traffic is still increasing. Platforms have to support scaling out. Load must be distributed to several hosts. Microservice-based architectures can offer solutions for this requirement. Apart from the better scaling, microservices offer faster development cycles, dynamic scaling depending on load and improved failover behavior.

Instead of duplicating a full system in order to handle a higher demand, in microservices architectures, only the parts which need more resources need to be scaled. Software can be decoupled and maintenance of software is getting easier. Every developer who had to update a hibernate version in a monolithic application knows the pain of keeping a monolith up to date and reducing technical debt. With microservices, you can do this step by step, service by service. With an increasing number of services, every overhead needs to be minimized. Heavy application servers are not the tool to use for this. Web applications or services can be implemented by using embedded web servers for a while. Instead of installing full JEE profile application server and instead of deploying EAR or WAR files, a simple self-running jar can do the job. This is not new, but in order to create such services fast, a template or framework is needed. Such frameworks are prominent in other languages but for Java, there were only a few options available. That changed. With Dropwizard [1], the Play Framework [2] or Spring Boot [3] there are at least 3 frameworks which are heavily in use in the Java microservice world.

In this tutorial, I will use a simple example to show how Spring Boot can be used to set up a REST-based microservice with Spring Boot. This example is based on a service which was built as backend for some mobile apps. The shown code in this tutorial is simplified. The service itself offered a REST API for mobile apps by using other services that already exist in the background. That means the service only acts as a type of wrapper for other internal services. The service needs to be restricted from unauthorized access, means in this case that the service needs an authorization from the user and the client (a mobile App in this case).

That’s why we added OAuth2 [4] with JWT [5] as authorization system. We added additionally Swagger [6] for the documentation of the API. The service itself was deployed to production by using Docker [7].

How to set up an initial Spring Boot structure

Spring Boot is a framework designed to simplify the creation of new services. For the most simple use cases, the needed libraries are already bundled in the fitting combinations and versions in so-called spring starters. We don’t have to deploy our application into an application server, instead, we can run our application standalone or within a Docker container, since the application already contains a server.

In order to create a simple REST service, only a few lines of code are needed. Starting with one of the available Spring Boot examples or with the Spring Initializr (http://start.spring.io), we only have to add a Java DTO and annotate a controller and we have our first endpoint.

Listing 1

@RestController
public class RegistrationController {

    @RequestMapping(method = RequestMethod.POST,
                    value = "/register",
                    produces = APPLICATION_JSON_VALUE)
    public UserData register(@RequestBody User user) {
        ...
        if(usernameAlreadyExists) {
            throw new IllegalArgumentException("error.username");
        }
        ...
        return new UserData(...);
    }
    
    @ExceptionHandler
    void handleIllegalArgumentException(
                      IllegalArgumentException e,
                      HttpServletResponse response) throws IOException {

        response.sendError(HttpStatus.BAD_REQUEST.value());

    }
}

Listing 1 shows a controller. The controller includes a method for registration, which can be triggered by a POST request. The method processes JSON and returns JSON. The conversion from JSON to a Java DTO and vice versa is fully transparent for the Java developer. The configuration of parsers is handled by spring boot. Spring Boot supports Maven and Gradle. In the case of Gradle, the command bootRun would start the service.

Listing 2

...
 
public class User {

    private String mail;
    private String password;
    private String lastName;
    private String name;
    private String address;

    public Registration() {}

    //... getter and setter
}

Listing 3 shows another important concept of Spring Boot. A lot of extensions of an application can be done in Spring Boot just by adding a simple annotation in the main class. The underlying infrastructure behind the annotations is hidden. That’s great since more time can be invested in implementing the business logic instead of technology, but sometimes the magic behind spring boot features can be scary and it can take a lot of time to debug unexpected behavior. The annotation @SpringBootApplication is sufficient to start the application within an embedded tomcat. At least for small services, the complexity of setting up an application context by a mix of XML snippets, annotations, and code, is gone. The old impression about spring as a heavy and complex framework is not prominent anymore. After we started our service from this example, a registration can be triggered by a POST call. A simple example is shown in Listing 4.

Listing 3

...
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Listing 4

curl -X POST -H "Content-Type: application/json"
    http://localhost:8080/register -d
       '{
           "mail": "test@test.de",
           "password": "password",
           "lastName": "lastName",
           "name": "name",
           "address": "somewhere"
        }'

Spring Boot offers a simple way to map exceptions to HTTP status codes. This way we can easily guarantee that a certain type of exception always leads to the same error code. In this case (Listing 1) an exception handler catches the exception and returns a response which is compliant with the HTTP-Standards. A request with wrong parameters does not lead to a 500 error, instead, a 400 status code with a useful Error-ID will be returned. Listing 5 shows such a response. The same way we can, of course, implement GET, PUT, DELETE requests or process XML instead of JSON. Starting with a template or an existing example or the Initializr, we can immediately start writing code. The whole infrastructure, like the packaging of the JAR, the start of the HTTP-Server, the setup of libraries and other initialization efforts are solved from the beginning on. The initial setup of a new rest service takes minutes.

Listing 5

 {
       "timestamp":1458746952449,
       "status":400,
       "error":"Bad Request",
       "exception":"java.lang.IllegalArgumentException",
       "message":"error.username",
       "path":"/register"
    }

How to secure a REST API

Since our TEST service accepts user registrations, we have to think about protection of data. Before a user can retrieve his data, he has to authorize himself. In the world of a REST service, sessions in the classical sense do not exist. Every call has to be authorized to access the resource.

Several practices are common. Often, services are protected by Basic Auth. Basic Auth requires the client to send the username and password with each request (Base64 encoded as part of the header information). A sniffer could use that information to authorize his calls. Even if we would protect our service by SSL, we decided that Basic Auth was not the right approach for our service. Another method is to use a token, which will be changed with each request. A successful request would return the next token together with the response. In this case, parallel requests or errors could easily lead to a logout. That’s why we decided to use OAuth 2.0. OAuth 2.0 uses the password only for the initial login. The OAuth login will return 2 tokens. Future requests have to be executed with the first token (Access Token). This token replaces the password for a given period of time. If someone would be able to intercept the traffic, he could use that token for that timeframe until the token expires. As soon as the token expires, the client can retrieve a new token by using the second token (refresh token).

This concept does not force the client to send the real password all the time and parallel execution of calls is doable as well. Even if the access token expires, the client can assure that the user is logged in permanently by using the refresh token. The tokens send to the client need to be persisted in order to compare the token send by the client with the generated one. In our use case, we wanted to get rid of any database, since this service was only a wrapper for other microservices which contain the real logic.

In order to solve this, we had three options. We could use an in-memory database, which is shared between several instances. A second option would have been to use a load balancing which sends all requests from a session always to the same instance which stores the token locally (e.g. in memory). The first option would lead to unnecessary efforts, the second one breaks with the whole concept of cloud-native microservice architectures because then we would not have a stateless application anymore. Every downtime would mean a log out of customers. So we decided to use another approach. The JWT (JSON Web Tokens) extension on top of OAuth allows the authorization without storing the tokens. An access token is not just randomly generated, instead, the user ID, the expiry date, and other Metacloud-native is signed with a private key and added to the header information as Oauth token. This way every instance can verify the validity of the token and retrieve the user information without storing the token and the information is encrypted. JWT is fully compliant with the OAuth format, which means all OAuth 2 clients should be able to use JWT even without knowing that the token is a JWT token and not a classical OAuth 2.0 token. The format stays the same, the token is just a little bit longer. In our case, the client didn’t have to make any changes needed and even in the REST service, the needed adaptions were minimal. Instead of comparing the received token with the stored token, a JWT repository is called to verify the token. The repository decrypts the token and behaves equally to a repository which would use a database. The token contains the user ID but not the user data itself. That means the storage of user data must be solved independently. In our case, the service routed the user data via rest to a user service. Listing 6 shows the Spring Boot configuration needed to use OAuth 2.0 with JWT. It shows some additional configuration options as well.

Listing 6

@Configuration
public class OAuth2ServerConfiguration {
    ...
    @Bean
    public JwtAccessTokenConverter getTokenConverter() {
        JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();
        //  for asymmetric signing/verification use  
        //  tokenConverter.setKeyPair(...);
        tokenConverter.setSigningKey("aTokenSigningKey");
        tokenConverter.setVerifierKey("aTokenSigningKey");
        return tokenConverter;
    }
    ...
    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends
            ResourceServerConfigurerAdapter {

        ...
        @Override
        public void configure(HttpSecurity http) throws Exception {

            http.authorizeRequests()
                    .antMatchers("/register/**")
                    .permitAll()
                    .antMatchers("/user/**")
                    .access("#oauth2.hasScope('read')");
        }

    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends
            AuthorizationServerConfigurerAdapter {

        ...
        @Override
        public void configure(ClientDetailsServiceConfigurer clients)
            throws Exception {
                clients
                    .inMemory()
                    .withClient("aClient")
                    .authorizedGrantTypes("password", "refresh_token")
                    .authorities("USER")
                    .scopes("read", "write")
                    .resourceIds(RESOURCE_ID)
                    .secret("aSecret");
        }
        ...
    }

}

OAuth 2.0 enables authorization with a 3rd party. Even if this feature is unused in this case, we can limit the usage of our REST service to certain clients or partners. In the login phase, not only the username and password of the user must be transmitted, additionally, a client and a client password are needed. In our cases, the clients are the different apps. Spring Boot offers a simple roles and rights model. But we are not going into details in this tutorial. In our example, the registration is not secured, but accessing user data is only possible after a successful login. The login is, in this case, the request to retrieve an access OAuth-2.0 token. Listing 7 shows a login and the retrieval of user information after login with the OAuth token. Listing 8 is a possible response of the OAuth login including access token and refresh token. In order to complete the example, Listing 9 shows the controller.

Listing 7

 curl -vu aClient:aSecret -X POST 'http://localhost:8080/oauth/token?username=test@test.de&password=aPassword&grant_type=password'
curl -i -H "Authorization: Bearer eyJh...Fpao" http://localhost:8080/user

Listing 8

{ "access_token":"eyJh...Fpao",
  "token_type":"bearer",
  "refresh_token":"eyJh...4clI",
  "expires_in":43199,
  "scope":"read write",
  "jti":"6e0...b31"
}

Listing 9

@RestController
public class UserController {

    @RequestMapping(method = RequestMethod.GET,
                    value = "/user",
                    produces = APPLICATION_JSON_VALUE)
    public UserData getUser() {

        Authentication auth =  
                 SecurityContextHolder.getContext().getAuthentication();
        String userid = auth.getName();
        ...
    }
}

How to document a REST API

A maintainable documentation for a REST API needs to be as close to code as possible and, in an ideal world, would be generated from code (or the code should be generated from the API description). Swagger is a powerful framework which includes several tools and libraries around the topic of API documentation. One part of the toolset is, for example, a tool to generate code from an API description.

But more important for our use case is a lib, which generates a JSON documentation on base of the code at runtime. Another tool creates an executable HTML documentation out of the JSON documentation. The Swagger library often offers good results even with the default settings. Adding an executable documentation for a REST API can be done with a single annotation (@EnableSwagger2). Listing 10 is an example for this. By default, Swagger searches for all existing REST definitions within the app. In Listing 10, we can see additionally how you can restrict the API documentation to a subset of the existing API. In this case, only the user information and login will be visible in the documentation. Additionally, I’ve added a title and version information to the documentation.

Listing 10

@Configuration
@EnableSwagger2
@EnableAutoConfiguration
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
               .select()
               .apis(RequestHandlerSelectors.any())
               .paths(PathSelectors.regex("/user.*|/register.*|/oauth/token.*"))     
               //PathSelectors.any() for all
               .build().apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        ApiInfo apiInfo = new ApiInfo(
                "aTitle",
                "aDescription",
                "aVersion",
                "a url to terms and services",
                "aContact",
                "a License of API",
                "a license URL");
        return apiInfo;
    }

}

Spring Boot can automatically enrich the self-defined API with additional endpoints like for example health checks, metrics or debug information. These endpoints would be detected by Swagger automatically (if not restricted). This is very powerful, but you should think about protecting this information (e.g. by moving it to a different port behind a firewall).

Image 1 and 2 are the result of the configuration in Listing 10. Swagger detects nearly all endpoints. The configuration of OAuth 2.0 was not done within a controller but in a configuration class. Swagger was not able to fully detect all information needed for OAuth. That’s why I’ve added a header description to the methods in Listing 11. Without this description, the header information would not be shown in the executable Swagger-documentation (Image 2). Image 3 shows the result including the header information. The Swagger UI can be overwritten by adding a customized UI to the folder for static resources.

Listing 11

@ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization",
            value = "Bearer access_token",
            required = true,
            dataType = "string",
            paramType = "header"),
    })
    @RequestMapping(method = RequestMethod.GET,
                    value = "/user",
                    produces = APPLICATION_JSON_VALUE)
    public User getUser() {

        Authentication auth =  
                SecurityContextHolder.getContext().getAuthentication();
                User aUser =
                    userRepository.getUser(auth.getName());
        if(auth != null && aUser != null) {
            return aUser;
        } else {
            throw new IllegalArgumentException("error.username");
        }
    }

Generated Swagger UI

API documentation without header information

API documentation with header information

How to embed the application into a Docker container

Embedding a self-running jar into a Docker container is simple. Spring Boot supports Maven and Gradle. Since Gradle is leaner, easier to extend, easier to use and faster, I prefer Gradle for all my projects. Listing 12 shows how to package the Spring Boot application into a Docker container using a Gradle Docker Plugin. The Docker container only needs a Java runtime to run the jar.

Listing 13 is a simple Dockerfile. The Docker container is based on another container which already includes a Java runtime and adds our application as Jar. If you start the container the application will be listening on port 8080 and the same port will be exposed in our example. Listing 14 shows how to build and start the container.

Listing 12

...
buildscript {
    ...
    dependencies {
        ...
        classpath 'se.transmode.gradle:gradle-docker:1.2'
    }
}
...
apply plugin: 'docker'

group = 'agroup'
...
task buildDocker(type: Docker, dependsOn: build) {
    //push = true
    applicationName = jar.baseName
    println('Application:' + applicationName)
    println('Group:' + project.group)
    dockerfile = file('Dockerfile')
    doFirst {
        copy {
            from jar
            into stageDir
        }
    }
}

...

Listing 13

FROM frolvlad/alpine-oraclejdk8:latest
MAINTAINER <YOUR MAIL>
EXPOSE 8080
ADD spring-boot-app-service-example.jar /app/spring-boot-app-service-example.jar
ENTRYPOINT java -jar /app/spring-boot-app-service-example.jar --server.port=8080

Listing 14

$ ./gradlew buildDocker
$ docker run -p 8080:8080 -t agroup/spring-boot-app-service-example

Configuration

We have seen how to set up a REST-based microservice including authentication, documentation and how to embed it into a Docker container. The simplest way to configure a Spring Boot application is a properties file (application.properties) in the resource folder of the application. You can override properties (if needed) at startup of the application e.g. by a command like java -jar example.jar –spring.config.location=/configuration.properties. Changing a configuration of an application in a Docker container can be done by mounting a config file into the container. This can be part of your deployment automation. Using a configuration in a bean can be done by using a single annotation. Listing 15 shows such an example.

Listing 15

@Configuration
public class CustomerServiceProperties {

    @Value("${a.url}")
    private String url;

    public String getUrl() {
        return url;
    }
}

More complex setups might need a configuration change at runtime. Additionally, the different microservices have to find each other at runtime. Spring Boot can be easily enriched by a Eureka client. Eureka is an open source solution from Netflix. New services will register themselves at Eureka handing over IP, port information and Meta information about the service. All services can retrieve that information from Eureka in order to find other services. This concept is called service discovery. There are other service discovery solutions like Consul, which can be integrated into Spring Boot apps, but Eureka is probably the simplest way to do this.

Get your app cloud ready

After your application is embedded into a Docker container, you can use all available Docker tools to deploy your application to the cloud (like for example Kubernetes). Spring Boot is driven by Pivotal. Pivotal is the driver behind the enterprise PAAS solution Pivotal Cloud Foundry. Spring Boot apps can be easily integrated without Docker into Cloud Foundry solutions. Apart from some Meta information, these apps can be deployed to CloFoundrydry without adaptions. Cloud Foundry is available as open source or in different Enterprise Versions (e.g. Pivotal Could Foundry) and can be installed in your datacenter. There are public Cloud Foundry offerings (e.g. Pivotal Web Services and IBM Bluemix) as well.

Conclusion

Building Java-based microservices is simplified by Spring Boot for the most use cases. Unlike frameworks like Dropwizard, it is easier to use and offers a richer feature set. Spring Boot offers a huge amount of additional libraries and integrations like Ribbon, Zuul, Hystrix, integrations with databases like MongoDB, Redis, GemFire, Elasticsearch, Cassandra or Hazelcast, just to name a few.

With Maven and Gradle powerful and widely supported Build Systems are supported for Java developers, which are more common and easier to integrate into existing structures than the dedicated build systems for frameworks like the Play Framework. In comparison to classical Web applications, Spring Boot is lean. For the majority of projects, adding a dependency to your project is good enough to get a good result from the beginning, without adaption the default config.

But not everything is perfect in the Spring Boot ecosystem. If you want to adapt the setup of a library, it is very likely that you have to adapt the setup of other libraries as well. One example for this was the integration of OAuth. The header information was not automatically detected by Swagger. In order to embed Hystrix, you just have to add two annotations and the dependencies and all your metrics are automatically detected. If for example, you change the URL of your health check, you have to change the configuration of Hystrix as well. Debugging such issues hidden in the underlying Spring magic can take time, but the advantages offered by Spring Boot are worth it.

 


asap

Author
Michael Gruczel
As Advisory Consultant at Dell EMC, Michael Gruczel advises companies in Application Modernization, DevOps, and BigData. Part of this consultancy work is the full stack including steps like Discovery, Software Development, Testing, Infrastructure, Release, Deployment and Operation of Software.

Comments
comments powered by Disqus