navigation

Building microservices with Netflix OSS, Apache Kafka and Spring Boot – Part 4: Security

Building microservices with Netflix OSS, Apache Kafka and Spring Boot – Part 4: Security

by
February 20, 2018
frontpage, Java
3 Comments

After building our group of microservices, it seems the next step is spending some time for securing them. From my experience at Dreamix Spring security and JWT tokens is the recipe for success for a spring microservice project so I will follow it too. I separated this part in a new branch microservices/auth that may be found in GIT. You can find also the test requests exported from insomnia in the microservices_insomnia.json file in the project:

And here are the changes I did after the 3rd part of the blog post to accomplish the goal:

  • 1. Build auth server that will be able to:
    – Issue signed JWT
    – Save a new user when it is registered (USER_REGISTERED message is sent to Kafka)
  • 2. Update the gateway service to be able to:
    – Manage cookies where we will store the tokens (access and refresh).
    – Enrich the requests with some data (secret)
  • 3. Update User service to be able to:
    – Verify that the token is issued by our auth server
    – Secure the “get all users” operation to be accessible by registered users with admin rights (ROLE_ADMIN)
    – Secure the “find by id” operation to be accessible by registered users with user rights (ROLE_USER)

Auth-server

First we need to generate certificate public and private pair. It will be used by the auth server to sign the token, then by the resource servers (ms-user) to verify it.

Private key

Public key

Then we are going to create the authorization server as a separate spring boot project. Here the only new dependency we need is oauth2, plus the others: eureka discovery, config client, jpa, web, H2 and Kafka used in the previous parts.

/pom.xml

We are configuring the the OAuth 2.0 Authorization Server mechanism by adding the
@EnableAuthorizationServer annotation and implementing AuthorizationServerConfigurer. Spring provides the AuthorizationServerConfigurerAdapter implementation with empty configure() methods that we will overwrite. By default Spring security will provide access_token and refresh_token in UUID format that is expected to be verified by the resource providers (ms-user), using the auth server api. This may turn the auth server into a bottleneck if we have huge amount of requests to our services. That’s why we will make our auth server to issue signed JWTs that will contain all the information necessary to validate the user in the token itself. To achieve that we need JwtTokenStore, JwtAccessTokenConverter and DefaultTokenServices.

/AuthorizationConfig.java

To make the аuth server using database for the authentication we need to implement another set of classes provided by spring UserDetails, UserDetailsService and DaoAuthenticationProvider.

/Account.java

/AccountService.java

/SecurityConfig.java

There are also 6 tables that Spring needs for storing security data in the database. With spring boot we just need to put the queries in a schema.sql file in the resources and it will take care for the initialization. There will also add an insert of the client_id that will use for our requests.

/schema.sql

In the auth service will also have a consumer same as in the email service (ms-email) that will insert a new record in the auth-server database when the USER_CREATED message is sent to Kafka by the ms-user microservice. To be able to consume same event with different consumers (ms-mail and ms-auth) we need to have different consumer group-ids, so will need update of the configuration files:

/ms-auth.yml

/ms-mail.yml

To test if everything is fine, build and run the project:

1. Build and run the service. By default the auth server is configured to run at port 9999, and there are 2 users added – admin and user inserted on start. Both users has password “password”.
2. Encode the secret to Base64 format “trusted-app:secret” goes to dHJ1c3RlZC1hcHA6c2VjcmV0
3. Do a call to request the token for user:
curl –request POST \
–url http://localhost:9999/oauth/token \
–header ‘authorization: Basic dHJ1c3RlZC1hcHA6c2VjcmV0’ \
–header ‘content-type: multipart/form-data \
–form grant_type=password \
–form username=user \
–form password=password
4. Get the access_token result go to https://jwt.io/ and verify the signature with the public key
5. Do the same for the admin user ( –form username=admin \ )

User service

As in the previous parts there were no security mechanism. So there is a property in our configuration that needs to be changed now to enable it. In the config properties should set security.basic.enabled from false to true

/ms-user.yml

After enabling the security, need to “teach” the resource server (ms-user) how to verify the tokens we pass to it. It should be build the same way as the in the ms-auth AuthorizationServerConfigurerAdapter, using the public key we previously generated.

/ResourceConfig.java

Setting different levels of security for the the operation endpoints is very easy with Spring security. Just need enable it for the whole project with the @EnableGlobalMethodSecurity annotation.

And then to use it according the needs we have. In our case it will be

To secure the “find by id” operation to be accessible by registered users with ROLE_USER authority, and

to secure the “get all users” operation to be accessible by registered users with admin rights.

To test if everything is fine, build and run the project:

1. Build and run the service. By default the user service runs on port 8081 and has no registered users (the service user and admin are only in the auth service)
2. Register 1 new user

3. Do a “get all” request with the admin token

4. Do a “find by id” request with the user token:

5. Do a “get all” request with the user token, and verify you are getting 403:

Ms-gateway

In the gateway service will do 2 modifications.
First will add filtering for all POST request responses and will check if the body contains contain the refresh and access tokens. If we find them then will create 2 cookies. The refresh token cookie will be a little different than the access one. Its age will be bigger (30 days) while the access token cookie is 1h. Also will set different path for the refresh token. Will set it same as the endpoint for issuing tokens (/auth/oauth/token). That way the refresh token will not be sent with every request but only when we need to get a new access token (once / hour). Also will have an access token with fairly short live (if stolen) but still will not need to log-in every time it expires

Then will add filtering for any request to the auth server for issuing tokens (to oauth/token). And will add the secret to the request. This will prevent exposing the secret to the front end.

For any other requests (to services different from the auth server), the gateway will add Authorization header by extracting the token from the cookie

To test if everything is fine, build and run the project:

1. Build and run the service. By default the gateway service runs on port 8765. The easiest way to perform the next steps is to use some REST client that supports cookies. I personally prefer insomnia.
2. Do a call to request the token for user. See here you don’t need the Authorization header. It is added buy the gateway :

3. Do a “find by id” request with the user token in the cookie. Here you don’t need the Authorization header too, as the token it will be passed with cookie and will be added by the gateway :

 

You can find more information on the topic in the previous parts:

Building microservices with Netflix OSS, Apache Kafka and Spring Boot – Part 1: Service registry and Config server

Building microservices with Netflix OSS, Apache Kafka and Spring Boot – Part 2: Message Broker and Use

Building microservices with Netflix OSS, Apache Kafka and Spring Boot – Part 3: Email service and Gateway

Iskren Ivanov

Java Expert at Dreamix

More Posts

Do you want more great blogs like this?

Subscribe for Dreamix Blog now!