Thursday, 1 August 2019

Implementing OAuth 2.0 Authentication using SpringBoot

The OAuth 2.0 provider mechanism is responsible for exposing OAuth 2.0 protected resources. The configuration involves establishing the OAuth 2.0 clients that can access its protected resources independently or on behalf of a user.

The provider role in OAuth 2.0 is actually split between Authorization Service and Resource Service, and while these sometimes reside in the same application, with Spring Security OAuth you have the option to split them across two applications, and also to have multiple Resource Services that share an Authorization Service. 

Now we start looking in to how to Implement this oauth2.0.

Maven Dependencies.

Here is my pom.xml

Since we are going to have the authorization server and client on the same application added the below dependencies.

<dependencies>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


The following endpoints are required in the Spring Security filter chain in order to implement OAuth 2.0 Authorization Server:

1. Authorization end point. Where we get authorize by giving the proper username and password. /oauth/authorize

2.Token Endpoint.This is where we get the Token from the server./oauth/token

Authorization Server.

As you configure the Authorization Server, you have to consider the grant type that the client is to use to obtain an access token from the end-user (e.g. authorization code, user credentials, refresh token). The configuration of the server is used to provide implementations of the client details service and token services and to enable or disable certain aspects of the mechanism globally.

Create a class that extends AuthorizationServerConfigurerAdapter. Use the anotations @Configuration for making it as the configuration.
@EnableAuthorizationServer for the authorization.

Here we need to have configurer method for the below purpose.

ClientDetailsServiceConfigurer: a configurer that defines the client details service. Client details can be initialized, or you can just refer to an existing store.

eg:
@Override
    public void configure(
        ClientDetailsServiceConfigurer clients
    ) throws Exception {
        clients.inMemory()
            .withClient("client")
                .authorizedGrantTypes("password")
                .secret("{noop}secret")
                .scopes("all");
    }

AuthorizationServerSecurityConfigurer: defines the security constraints on the token endpoint.
eg:
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess(
"hasAuthority('ROLE_TRUSTED_CLIENT')");
}


AuthorizationServerEndpointsConfigurer: defines the authorization and token endpoints and the token services.
eg:

@Override
    public void configure(
        AuthorizationServerEndpointsConfigurer endpoints
    ) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }

Here is my Authorization code.

package com.searchendeca.oauthdemo.server;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("client")
.secret(passwordEncoder.encode("123456"))
.authorizedGrantTypes("password", "authorization_code", "refresh_token")
.authorities("READ_ONLY_CLIENT")
.scopes("all")
.resourceIds("oauth2")
.redirectUris("http://localhost:8081/login")
.accessTokenValiditySeconds(5000)
.refreshTokenValiditySeconds(50000);
}
//in the above method you can use the jdbc confguration and fetch the data from it. I am using the Inmemory database.

}

The above configuration also avaliable in the XML as well.

Resource Server

A Resource Server (can be the same as the Authorization Server or a separate application) serves resources that are protected by the OAuth2 token. Spring OAuth provides a Spring Security authentication filter that implements this protection.

eg:
@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests().antMatchers("/login").permitAll().and()
        // default protection for all resources (including /oauth/authorize)
            .authorizeRequests()
                .anyRequest().hasRole("USER")
        // ... more configuration, e.g. for form login
    }
here is Resource Server configuration.
package com.searchendeca.oauthdemo.server;


import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

@Configuration
@EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter 
{
@Override
public void configure(HttpSecurity http) throws Exception {
http
        .authorizeRequests()
        .antMatchers("/").permitAll()
        .antMatchers("/api/v1/**").authenticated();
}
}

Now we have to configure the security configuration as we did for the basic authentication.Here is my file.

package com.searchendeca.oauthdemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@Order(1)
public class ServerConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
          .antMatchers("/login", "/oauth/authorize")
          .and()
          .authorizeRequests()
          .anyRequest().authenticated()
          .and()
          .formLogin().permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
        .inMemoryAuthentication()
            .withUser("syed")
            .password(passwordEncoder().encode("123456"))
            .roles("USER");
    }
     
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){ 
        return new BCryptPasswordEncoder(); 
    }
}

When ever it asks for the authentication we can give the username as syed and password as 123456. 

Rest Controller.

package com.searchendeca.oauthdemo.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class RestController 
{
@Secured("ROLE_USER")
    @GetMapping(value = "/user/hello")
    public ResponseEntity<String> welcomeAppUser(@AuthenticationPrincipal User user) {
        return ResponseEntity.ok("Welcome User " + user.getUsername());
    }
}


Now its done build the application.

Access the 
Url: http://localhost:8080/oauth/authorize?client_id=client&response_type=code&scope=all

This is what we mentioned in the Authorization Server.you will get the login window give the username as syed and password as 123456. 

Once you give the next window takes to the authorization window give approve and it will give the code note it down.

eg We got http://localhost:8081/login?code=0Tccin

Next in the post man pass all this information.

Getting the access token.

http://localhost:8080/oauth/token

pass the below as headers.

Content-Type:application/x-www-form-urlencoded
authorization: Basic Y2xpZW50OjEyMzQ1Ng== // this is nothing but the base64 of client:123456 which we have mentoned in the authorization server.

Pass the below as formdata in body.

grant_type:authorization_code
code:0Tccin
redirect_uri:http://localhost:8081/login

you will get the response like below.

{
    "access_token": "afe1c541-b366-4439-bffb-1f06493c5a8e",
    "token_type": "bearer",
    "refresh_token": "7b5fbaea-cc34-4b23-a4ad-fab74ef646d0",
    "expires_in": 4999,
    "scope": "read_profile_info"
}


Pass the access token to the controller service.

http://localhost:8080/api/users/hello

with the below headers.

Authorization:Bearer afe1c541-b366-4439-bffb-1f06493c5a8e

Output: Welcome User syed

Happy Learning !!!!

Wednesday, 31 July 2019

Basic Authentication using the Spring boot

Spring Security
Spring Security provides comprehensive security services for J2EE-based enterprise software applications. 

Terms to get familiar in spring security.

Principal:means a user, device or some other system which can perform an action in your application.

Authentication:It is the process of establishing a principal is who they claim to be.

Authorization:It refers to the process of deciding whether a principal is allowed to perform an action within your application.

UserRoles: User Roles are created to make sure the services which are created are not accessed by all the users. It is like we provide the restriction to the user, so he / she cannot access the particular service.

class WebSecurityConfigurerAdapter:Provides a convenient base class for creating a WebSecurityConfigurer instance. The implementation allows customization by overriding methods.

Now we can jump in to creating the basic authentication using the Spring boot.

Create a project from spring initializer, add the dependencies like web and security.

Here is My Pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.searchendeca</groupId>
<artifactId>securehttp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>securehttp</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

Here is my Security Configuration.

package com.searchendeca.securehttp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user1").password("user1").roles("USER");
        auth.inMemoryAuthentication().withUser("user2").password("user2").roles("USER", "ADMIN");
        auth.inMemoryAuthentication().withUser("user3").password("user3").roles("USER");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().
                realmName("spring-app").
                and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
                and().csrf().disable().
                authorizeRequests().antMatchers("/user/**").permitAll().anyRequest().authenticated();

    }

    @Bean
    public NoOpPasswordEncoder passwordEncoder()
    {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }
}

Controller Class.

@RestController
public class RestApiController {

 @RolesAllowed({"ROLE_USER", "ROLE_ADMIN"})
 @GetMapping(value = "/user/message")
    public String greetUser() {
        return "Welcome User ";
    }
}
The Above main class can be accessed by both user and admin respectively.
Main Class.
package com.searchendeca.securehttp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = "com.searchendeca.*")
public class SecurehttpApplication {

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

}

Deploy the application in Tomcat

Execute and access the below Url:http://localhost:8080/user/message It asks for the user name and password, once if you enter username and password then you will be able to see the service working else you will not see this .

you can also encrypt the password in the following format(username:password) with base64 and pass as header Authorization.

eg: Authorization:Basic c3llZDp0ZXN0MTIz

Still it will work in the same way. Once you include the spring security module you can see while server startup. Using generated security password: 6a1abb58-cfd9-4e7a-bb1f-ae9f0950c8b6

If you are not defining the configure method then you can access the resources by giving the username as user and password as encrypted that was updated during the server startup.

Happy Learning!!!

Monday, 29 July 2019

Working with profiles in Spring Boot

When ever we work in any application maintaing the environment specific files are very much required. In spring this can be achieved by using the profiles.

Look at the Following example. I am trying to fetch the url property and print it using the controller. To achieve this create a property files named, in the following path /src/main/resources.

application-dev.properties

url=http://mydevhost:8080/home

application-stage.properties

url=http://myStagehost:8080/home

In the application.properties define the following .

url=http://myhost:8080/home

spring.profiles.active=stage

Create a service class where you read this value as below.

package com.searchendeca.application.services;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@Service
public class DemoService {
@Value("${url}")
private String Url;

public String getUrl() {
return Url;
}

public void setUrl(String url) {
Url = url;
}

}


Below is my Main class.

package com.searchendeca.application.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.searchendeca.application.services.DemoService;

@SpringBootApplication
public class DemoApplication {
@Bean
public DemoService getDemoService() {
return new DemoService();
}

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

@Controller
class Example {
@RequestMapping("/")
@ResponseBody
public String hello() {
return getDemoService().getUrl();
}
}

}

Now acces the Url: http://localhost:8080/

Below is the output.

http://myStagehost:8080/home

In some cases we can differentiate using the @profile anotation, this will work only the profile is passed as part of the VM argument or the application.properties

Happy Learning!!!


Sunday, 28 July 2019

Configuring Edge (proxy) server – Zuul

Zuul is an edge server or proxy server, and serves the requests of external applications such as UI client, Android/iOS app, or any third-party consumer of APIs offered by the product or service. Conceptually, it is a door to external applications.

Zuul allows dynamic routing and monitoring of requests. It also performs security operations like authentication. It can identify authentication requirements for each resource and reject any request that does not satisfy them.

In this Tutorial You’ll write a simple microservice application and then build a reverse proxy application that uses Netflix Zuul to forward requests to the service application. You’ll also see how to use Zuul to filter requests made through the proxy service.

Here I have created two projects Profile Service and Zuul Gateway. I am going to make all the changes to the Zuul Gateway Project. I can access any Profile Service using their port also using this proxy.

I will show here only the ZuulGateway Setup. InProfile Service just configure any sample service.

Gradle dependencies.

buildscript {
ext {
springBootVersion = '1.4.0.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
apply plugin: 'io.spring.dependency-management'

jar {
baseName = 'gateway'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
mavenCentral()
}


dependencies {
compile('org.springframework.cloud:spring-cloud-starter-zuul')
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.SR5"
}
}


eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}

Create a Filter class.

package com.example.sayhello;

import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.ZuulFilter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SimpleFilter extends ZuulFilter {

  private static Logger log = LoggerFactory.getLogger(SimpleFilter.class);

  @Override
  public String filterType() {
    return "pre";
  }

  @Override
  public int filterOrder() {
    return 1;
  }

  @Override
  public boolean shouldFilter() {
    return true;
  }

  @Override
  public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest request = ctx.getRequest();

    log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));

    return null;
  }

}

Inject the Filter class in the Main class. Also Enable Zuul Proxy.

package com.example.sayhello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
@EnableZuulProxy
public class SayHelloApplication {
@Bean
  public SimpleFilter simpleFilter() {
    return new SimpleFilter();
  }

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

}

Configuration.

Below are the configuration in the ZuulGateway.

/ZuulGateway/src/main/resources/application.properties

zuul.routes.profileService.url=http://localhost:8080

ribbon.eureka.enabled=false

server.port=8093

spring.application.name=ZuulGateway

Here profileservice is the name of the spring boot application. and Url is the Url of the Project profileService.

Now I can access the Profile service using the Following Url.

Original Url: http://localhost:8080/user/name/sarath

Proxy Url: http://localhost:8093/profileService/user/name/sarath

We get the Following log information from the filter class.

2019-07-28 19:08:53.410  INFO 20184 --- [io-8093-exec-10] com.example.sayhello.SimpleFilter        : GET request to http://localhost:8093/profileService/user/name/sarath

Happy Learning !!!! 

Circuit breaker – Hystrix

Hystrix tool is for circuit breaker operations, that is, latency and fault tolerance.Therefore, Hystrix stops cascading failures. Hystrix performs the real-time operations for monitoring the services and property changes, and supports concurrency.

Gradle Dependency.

buildscript {
ext {
springBootVersion = '2.1.6.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

bootJar {
baseName = 'user'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
mavenCentral()
}


dependencies {
compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix')
compile('org.springframework.cloud:spring-cloud-starter-netflix-ribbon')
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Finchley.SR2"
}
}

eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}

Main Class:

package com.example.sayhello;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@RestController
@EnableCircuitBreaker
public class SayHelloApplication {
@Bean
  RestTemplate restTemplate(){
    return new RestTemplate();
  }

  @Autowired
  RestTemplate restTemplate;
  
  @Autowired
  ProfileService profileService;
@Bean
public ProfileService getBookService() {
return new  ProfileService(restTemplate);
}

private static Logger log = LoggerFactory.getLogger(SayHelloApplication.class);

@RequestMapping("/callService")
  public Object invoke() {
    Object obj = getBookService().readProfileList();
    return obj;
  }

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

}

Service Class:

package com.example.sayhello;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.net.URI;

@Service
public class ProfileService {

  private final RestTemplate restTemplate;

  public ProfileService(RestTemplate rest) {
    this.restTemplate = rest;
  }

  @HystrixCommand(fallbackMethod = "adminProfile")
  public String readProfileList() {
    URI uri = URI.create("http://localhost:8180/user/name/sarath");
    return this.restTemplate.getForObject(uri, String.class);
  }

  public String adminProfile() {
  URI uri = URI.create("http://localhost:8080/user/name/Syed");
    return this.restTemplate.getForObject(uri, String.class);
  }

}

From the above class if the localhost:8180 is not accessible then It will access the localhost:8080. 
Thanks God We end up in seeing the alternate when the service is down.

Client Side Load Balancing with Ribbon and Spring Cloud

Microservice architecture is of no use if there is no inter-process or service communication. The Ribbon application provides this feature. Ribbon works with Eureka for load balancing and with Hystrix for fault tolerance or circuit breaker operations.

Ribbon also supports TCP and UDP protocols, apart from HTTP. It provides these protocol supports in both asynchronous and reactive models. It also provides the caching and batching capabilities.

In this Tutorial We are going to see the client side loadbalacing with ribbon and spring cloud. In this I have three projects as say-hello and other project named profileService, and profileService2 which is running with the profile related services. Now we will access this profileServie and return the response.

Follow the below steps.

1.Create a project named 'say-hello'.
2.Add the gradle dependencies.

buildscript {
ext {
springBootVersion = '2.1.6.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

bootJar {
baseName = 'user'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
mavenCentral()
}


dependencies {
compile('org.springframework.cloud:spring-cloud-starter-netflix-ribbon')
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Finchley.SR2"
}
}

eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}

3. Create a main class with the Following content.

package com.example.sayhello;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@RestController
@RibbonClient(name = "say-hello",configuration = RibbonConfiguration.class)
public class SayHelloApplication {
@Bean
@LoadBalanced
  RestTemplate restTemplate(){
    return new RestTemplate();
  }

  @Autowired
  RestTemplate restTemplate;
private static Logger log = LoggerFactory.getLogger(SayHelloApplication.class);

@RequestMapping("/callService")
  public Object invoke() {
    Object obj = this.restTemplate.getForObject("http://say-hello/user/name/sarath", String.class);
    return obj;
  }

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

}

4.Now we have created a service which Invokes my service and returns the Object. Next is to define the Configuration.

/say-hello/src/main/resources/application.yml

spring:
  application:
    name: say-hello

server:
  port: 8091
  
say-hello:
  ribbon:
    eureka:
      enabled: false
    listOfServers: localhost:8080,localhost:8180
    ServerListRefreshInterval: 15000
5.We should also define the ribbon configuration class.

RibbonConfiguration.class

package com.example.sayhello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.PingUrl;

public class RibbonConfiguration {

  @Autowired
  IClientConfig ribbonClientConfig;

  @Bean
  public IPing ribbonPing(IClientConfig config) {
    return new PingUrl();
  }

 }


Testing the Url.

When you call the http://localhost:8091/callService

It responds from 8080 and 8081 service respectively.

Congratulations we have implemented the Loadbalancer for the Application. The Implementation may differs calling directly or from another api.

Happy Learning !!!

Service Registry in MicroServices

The Service registry is a database populated with the information in how to dispatch requests to microservices instances.

In this tutorial we are using the Eureka Server is used for service discovery and registration.

For Setting up Follow the Below Steps:

Eureka Server.

For Configuring the server side follow the below steps.

1.Create a project named Discovery Server.
2.Add the Gradle dependencies.

Below is my File. /DiscoveryServer/build.gradle

plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.searchendeca.ecommerce'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
mavenCentral()
}

ext {
set('springCloudVersion', 'Greenwich.SR1')
}

dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}

3. Configurations related to the Eureka server.

Below is the file.

/DiscoveryServer/src/main/resources/application.properties

server.port=8761

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

//When the registry starts up it will complain that there are no replica nodes for the registry to //connect to disable the relevant logging.
logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF

4.Main Class.

You’ll first need a Eureka Service registry. You can use Spring Cloud’s @EnableEurekaServer to stand up a registry that other applications can talk to. This is a regular Spring Boot application with one annotation added to enable the service registry.

package com.searchendeca.ecommerce.DiscoveryServer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServerApplication {

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

}


Now Server side configuration are done and now its time to configure the client service. Its not necessary to be a separate project, instead your custom project where you have hosted your services.

Client Side.

For Configuring the client side follow the below steps.

1. My Project Named ProfileService
2.Add Gradle Dependencies.

Below is my File /ProfileService/build.gradle

plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.searchendeca.ecommerce'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
mavenCentral()
}

ext {
set('springCloudVersion', 'Greenwich.SR1')
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
compile("org.springframework.boot:spring-boot-starter-data-mongodb")
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}

3.Below is my configuration.

/ProfileService/src/main/resources/application.properties

spring.application.name=profileService

4.Below is the Main class where the client identifies.

/ProfileService/src/main/java/com/searchendeca/user/ProfileServiceApplication.java

package com.searchendeca.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient

public class ProfileServiceApplication {

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

}
Once this is setup your client service is identified in the eureka server.

Testing the application.



Access the Eureka server on http://localhost:8761/ you will see the service registered there.

Now Congratulations you have setup the discovery server and registered in it.



Happy Learning !!!