Okay, here we are. We have built an auth service who is responsible to authenticate and authorize our users and clients in a microservices architecture.

Now we will build the account service, this guy is responsible to create the users and will hold the business logic for this because we won’t put the business logic on the service responsible for the authentication/authorization. We may want to send account activation email or SMS, do other processing, so we will separate this logic.

In this third part, we will learn how to create and config a resource server with Spring Boot and Spring Cloud.

Creating the account-service project

As usual, we will use Spring Initializr to generate the project for us, create it like the image below.

Generating the account-service project

Adding the application’s configuration files

Rename the application.properties file to bootstrap.yml and add the following configuration:

spring:

application:

name: account-service

cloud:

config:

uri: http://localhost:8888

fail-fast: true

password: 1234

username: user

main:

allow-bean-definition-overriding: true #i dont remember why but i think there is a bug with spring cloud and OAuth2ClientContext

In the config service application, create the account-service.yml file to hold the configuration for auth service.

security:

oauth2:

client:

clientId: account-service

clientSecret: 1234

accessTokenUri: http://localhost:8081/uaa/oauth/token

grant-type: client_credentials

scope: server



server:

servlet:

context-path: /accounts

port: 8082



feign:

hystrix:

enabled: true

If you don’t know why these files are being created, I recommend you to read this article’s first part.

Open the file /resources/shared/application.yml in the config service project and add the following information:



eureka:

instance:

prefer-ip-address: false

client:

serviceUrl:

defaultZone: http://localhost:8761/eureka/



security:

oauth2:

resource:

user-info-uri: http://localhost:8080/uaa/user/current

token-info-uri: #add this information #This configuration file will hold common configs to be shared among all fileseureka:instance:prefer-ip-address: falseclient:serviceUrl:defaultZone: http://localhost:8761/eureka/security:oauth2:resource:user-info-uri: http://localhost:8080/uaa/user/currenttoken-info-uri: http:// localhost:8080 /uaa/oauth/check_token

This URL will provide us our UserDetails implementation from auth service in our resource servers.

Adding annotations

Our main class from account-service will be like this:

@SpringBootApplication

@EnableDiscoveryClient

@EnableOAuth2Sso

@EnableFeignClients

@EnableGlobalMethodSecurity(prePostEnabled = true)

public class AccountServiceApplication {

public static void main(String[] args) {

SpringApplication.run(AccountServiceApplication.class, args);

}

}

The @EnableOAuth2Sso will enable configuration for an OAuth2 client in a web application that uses Spring Security and wants to use the Authorization Code Grant from our auth-service and create a WebSecurityConfigurerAdapter with all paths secured. And the @EnableFeignClients scans for interfaces that declare they as feign clients.

Configuring the resource server

Create a class named ResourceServerConfig with the following content:

@Configuration

@EnableResourceServer

public class ResourceServerConfig extends ResourceServerConfigurerAdapter {



private final ResourceServerProperties sso;



private final OAuth2ClientContext oAuth2ClientContext;



@Autowired

public ResourceServerConfig(ResourceServerProperties sso, OAuth2ClientContext oAuth2ClientContext) {

this.sso = sso;

this.oAuth2ClientContext = oAuth2ClientContext;

}



@Bean

@ConfigurationProperties(prefix = "security.oauth2.client")

public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {

return new ClientCredentialsResourceDetails();

}



@Bean

public RequestInterceptor oauth2FeignRequestInterceptor() {

return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, clientCredentialsResourceDetails());

}



@Bean

public OAuth2RestOperations restTemplate(OAuth2ClientContext oauth2ClientContext) {

return new OAuth2RestTemplate(clientCredentialsResourceDetails(), oauth2ClientContext);

}



@Bean

@Primary

public ResourceServerTokenServices resourceServerTokenServices() {

return new UserInfoTokenServices(sso.getUserInfoUri(), sso.getClientId());

}



@Override

public void configure(HttpSecurity http) throws Exception {

http.authorizeRequests()

.antMatchers(HttpMethod.POST, "/").permitAll()

.antMatchers(HttpMethod.OPTIONS, "/").permitAll()

.anyRequest().authenticated();

}

}

In this class, we are configuring our resource server to permit any POST request to /accounts/ path, because who wants to create a new account is not logged in. And any other request needs to be authenticated.

Also, we are configuring the Feign to use an OAuh2RestTemplate to make authenticated requests.

Inserting account service client details in auth service

As we are creating another microservice, we need to register it as a client for our auth service.

Open the InitialValuesChangeLog class on the auth service project and add another ChangeSet .

@ChangeSet(order = "003", id = "insertAccountServiceClientDetails", author = "Marcus Hert Da Corégio")

public void insertAccountServiceClientDetails(MongoTemplate mongoTemplate) {

AuthClientDetails accountServiceClientDetails = new AuthClientDetails();

accountServiceClientDetails.setClientId("account-service");

accountServiceClientDetails.setClientSecret("$2a$10$fWNTd3H.u7G/aNROVQSifebOkZ2xzU5nUPOCI2Ld42M8E25/ljJqK");

accountServiceClientDetails.setScopes("server");

accountServiceClientDetails.setGrantTypes("refresh_token,client_credentials");



mongoTemplate.save(accountServiceClientDetails);

}

It’s almost the same as the browser client but has a different scope and grant types.

Creating the users endpoint in auth service

Here, I will list to you the changes that need to be made on auth service. I will not put the code here because it will become too messy, instead, I’ll attach the links for the class in the repository.

With this is hand, we can move to account service. If you have any questions about this, feel free to ask me in the comments section.

Creating the Feign client

The question now is how do we make a request to auth service from account service? For this we have Feign which is a Java to HTTP client binder, it makes easy to create interfaces for communication between our services.

Let’s start creating the AuthServiceFeignClient interface.

import com.marcusdacoregio.accountservice.dto.UserDto;

import com.marcusdacoregio.accountservice.dto.UserRegistrationDto;

import org.springframework.cloud.openfeign.FeignClient;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestBody;



@FeignClient(name = "auth-service")

public interface AuthServiceFeignClient {



@PostMapping(value = "/uaa/user")

UserDto createUser(@RequestBody UserRegistrationDto user);



}

The @FeignClient is an annotation for interfaces declaring that a REST client with that interface should be created. The name that we passed as value is the name of the microservice registered on our registry service, this way we don’t need to use the IP address which we won’t know in a microservices environment.

I’ve copied the DTOs from auth service, but you can create a submodule auth service, place the DTOs inside it, and add as a dependency on account service.

Now let’s create the AccountService interface and its implementation.