Вы находитесь на странице: 1из 67

Contents

1. Microservices – A Primer

2. Spring Boot REST Crud API

3. Swagger Documentation for REST API

4. Error Handling and Logging

5. Consuming RESTFul Webservice

6. Producing and Consuming SOAP webservice

7. Handle uncaught exceptions

8. Angular JS User Interface for Spring Boot REST API

9. Spring Boot security in web application

10.Deploying Spring Boot application using docker

11.References
1. Microservices – A Primer

What is Microservices?

Wikipedia definition says

Microservices is a variant of the service-oriented architecture (SOA) architectural style that

structures an application as a collection of loosely coupled services.

But there is no official definition of Microservices by industry standards. It’s recent phenomenon

in software industry to architect the new softwares which should be light weight, easier to deploy

and scale, easier to refactor individually and could work independently.

To understand in details, you can definitely read Martin Fowler’s Microservices or Chris

Richardson’s Microservices.

We will not be covering this post in detail as compared to link I have posted here. Microservices

are small services that can run independently, but can also easily communicate with other services.

Microservice Architecture vs Monolithic Architecture

In traditional monolithic architecture style, there is a single application with single code base. An

application contains number of modules which are interrelated and can have external

dependencies. It’s a multi-tier enterprise application and has been used to build software for long.

Microservice architecture style was born out of need to build an application that could easily be

supported for mobile applications. Older style was not easy to support for mobile and new
generation way to handling of data. Any large enterprise application can be easily built using

microservices architecture style.

How to identify Microservice Architecture Pattern?

A simple ground rule of microservice architecture pattern is to build a standalone service that can

be run without depending on any other service. That means for a large application can have more

than one services talking to each other, communicating with their own databases, but still

performing the business logic. Databases are used to ensure loose coupling of services.

A large enterprise e-commerce application can consist of following services

1. Backend service REST API to manage data

1. Account Service

2. Shipment Service

3. Inventory Service

2. Runtime service to handle runtime and backend data to process business logic

3. Logging service

4. Error Handling service

5. Session service

UI for the e-commerce application can be built independently to use backend services to show/edit

data.

By standards, there are few rules to identify microservices patterns

1. Decomposition by business capability


2. Database per service pattern

3. API gateway pattern

4. Client-side discovery and Server-side discovery

Pros and Cons of Microservices

Pros

1. Deployability – They can independently be deployed.

2. Reliability – A fault in the service can only bring down that service, depending on handling

in application, rest of the application can still be accessed.

3. Scalability – Each microservice can be scaled depending on requirements using clusters

and grids.

4. Availability – Dispatching the patch or newer version of service requires less downtime

compared to regular monolithic application.

5. Management – Easier to manage

6. Design and Development – Each service can be developed independently and helps

developer to manage the service easily without worrying about other services.

Cons

1. Performance – All services involved in application have to communicate with each other

over network and that could hamper the performance.

2. Testability – Automated tests are harder to manage and run.

3. Memory usage – Possible duplicate data across services and lot of duplication in cache.
2. Spring Boot REST CRUD API
In this chapter, we will learn how to write a CRUD Rest API using Spring Boot. Spring boot

provides some cool features to create a production ready Spring application that can be deployed

as a war file on any environment. Part of this book, we will create a microservice using Spring

Boot and deploy on docker. Let’s start with creation of a simple REST API.

What you’ll need

1. Eclipse Mars.2 Release

2. Java version 1.8

3. MySQL 5.0 or higher

4. Maven 3.0 or higher

What we’ll cover

In this lesson, we will cover following items

1. Create a Maven project

2. Assemble pom file for all dependencies

3. Create entity classes

4. Create business logic to handle data

5. Create a rest controller

6. Run the API in tomcat


Create a Maven project

As first step, let’s create a maven project in eclipse. You can create this by going into File > New

> Maven Project.

Select Archtype as maven-archtype-webapp.

Enter artifactid as benefits and groupid as com.betterjavacode

Assemble pom file for all dependencies

We will be using spring-boot and all the required dependencies including spring-data. Spring data

JPA provides lot of useful enhancements that can be seamlessly used with spring-boot project.

Spring-data will cover the data access layer which is basically implements persistence. Once we

use spring-data, we don’t have to add any external hibernate or eclipselink JPA APIs. Also some

of the data access repositories provided by spring-data makes implementing data access layer code

less worrisome.

<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.betterjavacode</groupId>
<artifactId>Benefits</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>Benefits Maven Webapp</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
<finalName>Benefits</finalName>
</build>
</project>

Create entity classes

We will be creating a rest api for Benefits service which will have companies and users as main

objects. We are only covering basic data model classes at the moment, but as part of the series

we will develop a web application. Each company will company profile and each user will

userprofile. So we will have four basic entities Company, CompanyProfile, User, UserProfile.

package com.betterjavacode.benefits.entities;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity(name = "Company")
@Table(name = "company")
public class Company implements Serializable {

/**
*
*/
private static final long serialVersionUID = 1L;

public Company(int id, String name, String type, String ein,


CompanyProfile cp) {
super();
this.id = id;
this.name = name;
this.type = type;
this.ein = ein;
this.cp = cp;
}
public Company() {
super();
}

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

@Column
private String name;

@Column
private int statusid;

@OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)


@JoinColumn(name = "companyprofileid")
private CompanyProfile cp;

@Column
private String type;

@Column
private String ein;

public int getId() {


return id;
}

public void setId(int id) {


this.id = id;
}

public String getName() {


return name;
}

public void setName(String name) {


this.name = name;
}

public int getStatusid() {


return statusid;
}

public void setStatusid(int statusid) {


this.statusid = statusid;
}

public CompanyProfile getCp() {


return cp;
}

public void setCp(CompanyProfile cp) {


this.cp = cp;
}
public String getType() {
return type;
}

public void setType(String type) {


this.type = type;
}

public String getEin() {


return ein;
}

public void setEin(String ein) {


this.ein = ein;
}

package com.betterjavacode.benefits.entities;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Feature;

@Entity(name = "User")
@Table(name = "user")
public class User implements Serializable {

/**
*
*/
private static final long serialVersionUID = 1L;

public User() {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

@Column
private Date createdate;

@Column(name = "email", nullable = false, unique = true)


private String email;

@Column(name = "firstname", length = 255)


private String firstname;

@Column(name = "middlename", length = 255)


private String middlename;

@Column(name = "lastname", length = 255)


private String lastname;

@Column(name = "username", length = 100)


private String username;

@Column(name = "jobtitle", length = 255)


private String jobtitle;

@Column(name = "password_hash", nullable = false)


private String passwordHash;

@Column(name = "enabled")
private int enabled;

@OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)


@JoinColumn(name = "userprofileid")
private UserProfile userprofile;

@JsonFormat(with = Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name =
"user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
private List<Role> roles;

public int getId() {


return id;
}

public void setId(int id) {


this.id = id;
}

public Date getCreatedate() {


return createdate;
}

public void setCreatedate(Date createdate) {


this.createdate = createdate;
}

public String getEmail() {


return email;
}

public void setEmail(String email) {


this.email = email;
}

public String getFirstname() {


return firstname;
}

public void setFirstname(String firstname) {


this.firstname = firstname;
}

public String getMiddlename() {


return middlename;
}

public void setMiddlename(String middlename) {


this.middlename = middlename;
}

public String getLastname() {


return lastname;
}

public void setLastname(String lastname) {


this.lastname = lastname;
}

public String getUsername() {


return username;
}

public void setUsername(String username) {


this.username = username;
}

public String getJobtitle() {


return jobtitle;
}

public void setJobtitle(String jobtitle) {


this.jobtitle = jobtitle;
}

public UserProfile getUserprofile() {


return userprofile;
}

public void setUserprofile(UserProfile userprofile) {


this.userprofile = userprofile;
}

public String getPasswordHash() {


return passwordHash;
}
public void setPasswordHash(String passwordHash) {
this.passwordHash = passwordHash;
}

public List<Role> getRoles() {


return roles;
}

public void setRoles(List<Role> roles) {


this.roles = roles;
}

public int getEnabled() {


return enabled;
}

public void setEnabled(int enabled) {


this.enabled = enabled;
}

Create business logic to handle the data

Part of our architecture for REST API, we will have following three layers

1. Rest layer

2. Business object layer

3. Data access layer

So in Business object layer, we will implement all the managers which will handle processing of

rest requests to create, update, read or delete the data. In subsequent posts, we will enhance this

layer to handle logging, error handling and more.

package com.betterjavacode.benefits.managers;

import java.util.List;

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

import com.betterjavacode.benefits.entities.User;
import com.betterjavacode.benefits.interfaces.UserManager;
import com.betterjavacode.benefits.repositories.UserRepository;
public class UserManagerImpl implements UserManager {

private UserRepository userRepository;

@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public User createUser(User u) {
if (u != null) {
User user = userRepository.save(u);
return user;
} else {
return null;
}
}

@Override
public User updateUser(User u) {
// TODO Auto-generated method stub
return null;
}

@Override
public User getUser(int id) {
User user = userRepository.findOne(id);
if (user == null) {
return null;
}
return user;
}

@Override
public List getAllUsers() {
List userList = (List) userRepository.findAll();
return userList;
}

@Override
public void deleteUser(int guid) {
// TODO Auto-generated method stub
User user = userRepository.findOne(guid);
if (user == null) {
return;
}
userRepository.delete(user);
}

}
Create a REST controller

One of the best uses of Spring boot is to create rest API and the feature it offers for the same is to
use REST controller. Spring-boot offers an annotation for the same as @RestController.

package com.betterjavacode.benefits.controller;

import java.util.List;

import javax.ws.rs.core.Response;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.betterjavacode.benefits.entities.User;
import com.betterjavacode.benefits.interfaces.UserManager;

@RestController
@RequestMapping("benefits/v1")
public class UserService {

@Autowired
UserManager userMgr;

@RequestMapping(value = "/users/", method = RequestMethod.POST)


public User createUser(User user) {
User u = userMgr.createUser(user);
return u;
}

@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)


public User getUser(@PathVariable("id") int id) {
User u = userMgr.getUser(id);
return u;
}

@RequestMapping(value = "/users/", method = RequestMethod.GET)


public List getAllUsers() {
List cList = userMgr.getAllUsers();
return cList;
}

@RequestMapping(value = "/users/", method = RequestMethod.PUT)


public User updateUser(User user) {
User u = userMgr.updateUser(user);
return u;
}

@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)


public Response deleteUser(@PathVariable("id") int id) {
userMgr.deleteUser(id);
return Response.status(Response.Status.OK).build();
}
}

package com.betterjavacode.benefits.controller;

import java.util.List;

import javax.ws.rs.core.Response;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.betterjavacode.benefits.entities.Company;
import com.betterjavacode.benefits.interfaces.CompanyManager;

@RestController
@RequestMapping("benefits/v1")
public class CompanyService {

@Autowired
CompanyManager compMgr;

@RequestMapping(value = "/companies/", method = RequestMethod.POST)


public Company createCompany(Company company) {
Company c = compMgr.createCompany(company);
return c;
}

@RequestMapping(value = "/companies/{id}", method = RequestMethod.GET)


public Company getCompany(@PathVariable("id") int id) {
Company c = compMgr.getCompany(id);
return c;
}

@RequestMapping(value = "/companies/", method = RequestMethod.GET)


public List getAllCompanies() {
List cList = compMgr.getAllCompanies();
return cList;
}

@RequestMapping(value = "/companies/", method = RequestMethod.PUT)


public Company updateCompany(Company company) {
Company c = compMgr.updateCompany(company);
return c;
}

@RequestMapping(value = "/companies/{id}", method = RequestMethod.DELETE)


public Response deleteCompany(@PathVariable("id") int id) {
compMgr.deleteCompany(id);
return Response.status(Response.Status.OK).build();
}
}

Run the API in tomcat

We are using embedded tomcat in this Spring-boot project. So once we are done building and

installing the code through maven, we can run the project through eclipse or standalone war file in

tomcat. For our demo purposes, we will run this application through eclipse, which will start

embedded tomcat.

If we execute the url http://localhost:8080/benefits/v1/users/1 – it will display json for user data as

below
3. Swagger Documentation for REST API
In this lesson, we will show how to add swagger documentation to Spring boot rest API. We

learned how to create a Spring Boot REST API in previous chapter. In Microservices’ world, these

days documenting your API is a standard norm. Swagger provides a handy interface and a simple

way to build these documentations that any client can test any moment. They don’t need to have

all the services on their environment.

What is Swagger?

Swagger was intended to provide a standard, language-agnostic interface to REST APIs which

allow anyone to understand the capabilities of a service without any source code, documentation

of source code. You can find more details about Swagger here.

How to add swagger documentation?

In our previous lesson, we added Spring boot REST API. We will add swagger documentation to

the same REST API.

1. Add Maven dependencies

To start with, let’s add maven dependencies for swagger jars.

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
<scope>compile</scope>
</dependency>

2. Add Swagger bean in configuration

In our main starting Application class, we will add few configurations for setting up a bean which

will handle swagger documentation. In below code, I show what I have added in Application.java

file. Basically, we have created a new bean of type Docket which takes care of swagger

configuration.

@EnableSwagger2
@SpringBootApplication(scanBasePackages = { "com.betterjavacode.benefits" })
public class Application {

public static void main(String[] args) {


SpringApplication.run(Application.class, args);
}

@Bean
public Docket benefitsApi() {
return new Docket(DocumentationType.SWAGGER_2).groupName("Benefits")
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.pathMapping("/");
}

private ApiInfo apiInfo() {


return new ApiInfoBuilder().title("Benefits REST Service")
.description(" A simple REST service for Benefits software ")
.contact(new Contact("Yogesh Mali", "https://betterjavacode.com",
""))
.version("1.0")
.build();
}
}
3. Show Swagger documentation

Now once we have added the configuration, we can build our project with maven clean install.

After successful build, run the project from eclipse as a Java application. We will access swagger

documentation from URL http://localhost:8080/swagger-ui.html . This will look like below:


4. Error Handling and Logging
In previous lessons, I wrote about how to create a spring boot REST API chapter 2 and how to add

swagger documentation for REST API chapter 3. In this lesson, we will add error handling and

logging to our REST API. Error handling and Logging are two different ideas, so I will divide this

post in two sections.

1. Logging

In most production applications, logging is critical and it is used for multiple purposes. Few of

those uses are debugging the production issues or auditing for the application. Over the years,

different logging libraries have evolved to use in java based applications. slf4j is the most popular

framework as it provides a simple abstraction layer to any kind of logging framework.

In our tutorial for this application, we will be using log4j2 which is the most recent and advance

logging library out there. It provides lot of useful features for performance, support for multiple

APIs, advance filtering, automatic reloading of configurations etc. We will not cover any of these

in this article, if you are interested to read about log4j2 libraries, read here.

Add log4j2 library in application –

To use log4j2, we will add the maven dependency to our project’s pom file. This should look like

below

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>

Add log4j2 configuration file

To enable logging, we will have to add a configuration file in our application. This configuration

file can be XML, JSON or YAML file. We will be using a XML file log4j2.xml which will look

like below

<?xml version="1.0" encoding="UTF-8"?>


<Configuration status="INFO">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level
%logger{36} - %msg%n" />
</Console>
<File name="BenefitsFile" fileName="benefits.log" append="true">
<PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level
%logger{36} - %msg%n"/>
</File>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
<AppenderRef ref="BenefitsFile"/>
</Root>
</Loggers>
</Configuration>

So we are using Console and BenefitsFile as two loggers which will log into a console and file

respectively. We are setting log level to DEBUG. If you log any messages with a level lower than

DEBUG, they will be logged into console or file. We will have to add a file benefits.log in

classpath to achieve this logging in file. Log pattern is with date time, log level, class from which

log is originating and log message.


Add logging in application code

Once we have required logging libraries and logging configuration adjusted, we can add logging

in our code to capture this logging during runtime execution. In one of the managers

CompanyManagerImpl, we will add a logger.

public static final Logger LOGGER =


LogManager.getLogger(CompanyManagerImpl.class);

@Override
public List<Company> getAllCompanies()
{
LOGGER.info(" Enter >> getAllCompanies() ");
List<Company> cList = (List<Company>) companyRepository.findAll();
LOGGER.info(" Exit << getAllCompanies() ");
return cList;
}

Now once we execute our spring boot application, we can capture the logs in console or file. The

file will be benefits.log.

2. Error Handling

We will not write about exceptions in detail as it has been covered in this post Exceptions. We

will create our own custom exception which will be extended from WebApplicationException

which jersey library provides.

This will look like below:

package com.betterjavacode.benefits.utilities;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;

public class InvalidRequestException extends WebApplicationException {

/**
*
*/
private static final long serialVersionUID = 1L;
private int errorcode = 00; // 00 indicates - no error

public InvalidRequestException() {

public InvalidRequestException(int errorcode, String message) {


super(Response.status(Response.Status.BAD_REQUEST)
.entity(message).build());
this.errorcode = errorcode;
}

public InvalidRequestException(int errorcode, String message, Throwable


cause) {
super(cause, Response.status(Response.Status.BAD_REQUEST)
.entity(message).build());
this.errorcode = errorcode;
}
}

Now we can use this custom exception in our managers when we want to throw an error message

to indicate if there is anything wrong with client request. Similarly we can build another exception

to show if there is anything wrong on server side. Following snippet shows from

CompanyManagerImpl where we have shown how to throw this exception.

@Override
public Company getCompany(int guid)
{
LOGGER.info(" Enter >> getCompany() ");
Company company = companyRepository.findOne(guid);
if (company == null) {
LOGGER.info(" Exit << createCompany() ");
throw new InvalidRequestException(400, "Company not found");
}
LOGGER.info(" Exit << getCompany() ");
return company;
}

In this lesson, we showed how to handle logging and errors in a REST API.
5. Consuming RESTFul Webservice
Continuing the series of lessons on Spring Boot, in this post, we will investigate how to consume

a REST API service we built previously. This is a short lesson, but we will show how to read the

data and how to post the data with some of the features Spring Boot offers to consume a REST

service for client side. Eventual goal is to use this feature to call our rest service during runtime to

use the data from database to display on views which a user will be able to see.

Purpose

Purpose of this post is to read company data from Company REST API and also to create a

company by posting company data using the same REST API.

Build a client

To consume a rest service programmatically, Spring provides a feature called RestTemplate.

RestTemplate is easiest way for client to interact with server side code with just one line of code.

In our client code, we will need a RestTemplate object, REST service URL. Since this is a sample

we are building, we will be adding a main method in this class to run this client side of the code.

In real life scenarios, during runtime, a client code will call rest template to get server side data,

use that data to massage or display to user on user interface.

RestTemplate restTemplate = new RestTemplate();


String resourceAPI_URL = "http://localhost:8080/benefits/v1/companies/{id}";
Company company = restTemplate.getForObject(resourceAPI_URL, Company.class,
1);
This code is showing that we are calling REST service to read company data for company with id

that a client will pass.

Similarly, we will have another request to post the data on server side to create a company. The

code for that will look like below:

String resourceAPI_POSTURL = "http://localhost:8080/benefits/v1/companies/";


Company comp = new Company();
comp.setName("XYZ Company");
comp.setStatusid(1);
comp.setType("Corporation");
comp.setEin("9343423232");
Company newcomp = restTemplate.postForObject(resourceAPI_POSTURL, comp,
Company.class);

In this post, we showed how to use RestTemplate a feature that spring boot provides to consume

a REST service.
6. Producing and Consuming SOAP Webservice

In last few lessons, we have covered following

1. Spring Boot REST CRUD API

2. Swagger Documentation for Spring Boot REST API

3. Error Handling and logging in Spring Boot REST API

4. Consuming RESTful Webservice

In this lesson, we will describe how to create a SOAP webservice from our existing Spring Boot

REST API. This SOAP webservice will provide us user data from the database which is we have

connected through Spring-data in Spring REST API.

1. Requirements

1. Eclipse Mars2

2. Maven 3.1 and above

3. Spring 1.4 and above

4. Java 7

5. Tomcat 8

2. SOAP Web Service

We will use our existing Spring Boot REST API to build an application that will act as a SOAP

web service to provide users data. For a given user id, web service will return user data.
Let’s create a schema file in src/main/resources directory and maven will create java classes based

on this schema file.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="https://betterjavacode.com/benefits/soap"
targetNamespace="https://betterjavacode.com/benefits/soap"
elementFormDefault="qualified">
<xs:element name="getUserRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="id" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getUserResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="user" type="tns:user"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="user">
<xs:sequence>
<xs:element name="id" type="xs:int"/>
<xs:element name="firstname" type="xs:string"/>
<xs:element name="middlename" type="xs:string"/>
<xs:element name="lastname" type="xs:string"/>
<xs:element name="username" type="xs:string"/>
<xs:element name="createdate" type="xs:date"/>
<xs:element name="jobtitle" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>

3. Update Maven dependencies

Now to generate classes from schema, we have to make sure we have all the right dependencies in

our pom.xml. We will also add spring boot service dependency to create a SOAP web service.
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.betterjavacode</groupId>
<artifactId>Benefits</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>Benefits Maven Webapp</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jersey2-jaxrs</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaDirectory>${project.basedir}/src/main/resources/</schemaDirectory>
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<clearOutputDir>false</clearOutputDir>
<schemaLanguage>WSDL</schemaLanguage>
<generatePackage>com.betterjavacode.benefits.soap</generatePackage>
<forceRegenerate>true</forceRegenerate>
<scehmas>
<schema>
<url>http://localhost:8080/benefits/endpoints/users.wsdl</url>
</schema>
</scehmas>
</configuration>
</plugin>
</plugins>
<finalName>Benefits</finalName>
</build>
</project>

If we run the project with maven build now, the plugin jaxb2-maven-plugin will generate classes

under com.betterjavacode.benefits.soap directory. It will also enable our wsdl SOAP url for users.

This will generate following java objects

 GetUserRequest

 GetUserResponse

 ObjectFactory

 package-info

 User

4. Defining the service

Next, we will define an interface for our service. This will look like below
package com.betterjavacode.benefits.services;

public interface UserAccountService


{
public com.betterjavacode.benefits.soap.User getUserDetails(int id);
}

Implementation of this service will be mapping out entity class User to generated class for soap

service User. Using the id as a key to get user data from repository, we will map to soap service

user. For post purposes, we will not show the implementation of this interface.

5. Creating the Service Endpoint

What is a service endpoint? When a SOAP request for defined URL is handled by Spring servlet,

Spring servlet redirects that request to service endpoint. Service endpoint then processes that

request to create a response. Our spring-boot-starter-web-services dependency will bring all the

necessary classes for annotation purposes.

package com.betterjavacode.benefits.services.endpoints;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

import com.betterjavacode.benefits.services.UserAccountService;
import com.betterjavacode.benefits.soap.GetUserRequest;
import com.betterjavacode.benefits.soap.GetUserResponse;
import com.betterjavacode.benefits.soap.User;

@Endpoint
public class UserAccountServiceEndpoint
{

// private static final String TARGET_NAMESPACE =


"http://com/betterjavacode/benefits/webservices/useraccountservice";
private static final String TARGET_NAMESPACE =
"https://betterjavacode.com/benefits/soap";
@Autowired
private UserAccountService userAccountService;

@PayloadRoot(localPart = "getUserRequest", namespace = TARGET_NAMESPACE)


public @ResponsePayload GetUserResponse getUserRequest(@RequestPayload
GetUserRequest request)
{
GetUserResponse response = new GetUserResponse();
User user = userAccountService.getUserDetails(request.getId());
response.setUser(user);
return response;
}
}

@Endpoint annotation allows the class to be defined as service endpoint and included in

@Component annotation for scanning. Make sure the namespace defined in this class matches

with XSD schema definition. Otherwise, you can run into error for “No Endpoint defined for“.

6. Configuration

Next, we will configure our configuration class to generate wsdl endpoint. This configuration class

will be annotated by @EnableWs to provide web service configuration.

package com.betterjavacode.benefits;

import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;

@Configuration
@EnableWs
@ComponentScan("com.betterjavacode")
public class AppConfig extends WsConfigurerAdapter
{
@Bean
public ServletRegistrationBean
messageDispatcherServlet(ApplicationContext applicationContext)
{
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
return new ServletRegistrationBean(servlet, "/benefits/endpoints/*");
}

@Bean(name = "users")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema
usersSchema)
{
DefaultWsdl11Definition wsdl11definition = new
DefaultWsdl11Definition();
wsdl11definition.setPortTypeName("UserAccountService");
wsdl11definition.setLocationUri("/endpoints");

wsdl11definition.setTargetNamespace("http://com/betterjavacode/benefits/webse
rvices/useraccountservice");
wsdl11definition.setSchema(usersSchema);
return wsdl11definition;
}

@Bean
public XsdSchema usersSchema()
{
return new SimpleXsdSchema(new ClassPathResource("employees.xsd"));
}
}

Few important points about this configuration class are

 MessageDispatcherServlet is a required servlet to dispatch web service messages. We set

this servlet with a bean to handle the URL from which request will be coming.

 DefaultWsdl11Definition creates SOAP for the given XSD schema

 XsdSchema provides abstraction for our users XSD schema

7. Running the SOAP webservice


Now build our project with maven. Run the spring boot application through eclipse to start the

embedded tomcat server. Once the tomcat server starts, if we access

url http://localhost:8080/benefits/endpoints/users.wsdl

Output in the browser will be as below

Here we showed how to create a simple SOAP webservice which we have combined with Spring

Boot REST API service. We can also test this SOAP webservice using Soap UI, as shown in below

screenshot
8. Consuming the SOAP web service

In previous steps, we showed how to produce a SOAP web service, now we will show how to

consume this SOAP web service programmatically.

8.1 Create a client class

Under package com.betterjavacode.benefits.views, define a class UserClient which will extend a

WebServiceGatewaySupport class. WebServiceGatewaySupport class provides web service

methods.

package com.betterjavacode.benefits.views;

import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.ws.soap.client.core.SoapActionCallback;

import com.betterjavacode.benefits.soap.GetUserRequest;
import com.betterjavacode.benefits.soap.GetUserResponse;

public class UserClient extends WebServiceGatewaySupport


{
public GetUserResponse getUserById(int userid)
{
GetUserRequest userrequest = new GetUserRequest();
userrequest.setId(userid);
GetUserResponse response = (GetUserResponse)
getWebServiceTemplate().marshalSendAndReceive(userrequest, new
SoapActionCallback("http://localhost:8080/benefits/endpoints/getUserResponse"
));
return response;
}
}

8.2 Configure the client for Spring Bean support

We will configure Jaxb2Marshaller to support JAXB to set context path. This will help us marshal

and unmarshal our xml request and response through.


package com.betterjavacode.benefits.views;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;

@Configuration
public class ClientAppConfig
{
@Bean
public Jaxb2Marshaller marshaller()
{
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.betterjavacode.benefits.soap");
return marshaller;
}

@Bean
public UserClient userClient(Jaxb2Marshaller marshaller)
{
// WSDL URL - http://localhost:8080/benefits/endpoints/users.wsdl
UserClient uc = new UserClient();

uc.setDefaultUri("http://localhost:8080/benefits/endpoints/users.wsdl");
uc.setMarshaller(marshaller);
uc.setUnmarshaller(marshaller);
return uc;
}
}

8.3 Run the SOAP web service client

We will define a class with main method to pass an argument of user id. Our client will call the

web service with passed argument to return us the data if that user id existed in the database.

public class UserSoapClient


{
public static void main(String[] args)
{
AnnotationConfigApplicationContext ctx = new
AnnotationConfigApplicationContext();
ctx.register(ClientAppConfig.class);
ctx.refresh();
UserClient usc = ctx.getBean(UserClient.class);
System.out.println(" For Employee: ");
GetUserResponse response = usc.getUserById(1);
System.out.println("Name: " + response.getUser()
.getFirstname() + " "
+ response.getUser()
.getLastname());
System.out.println(" Job: " + response.getUser()
.getJobtitle());

}
}

9. Conclusion

In this lesson, we showed how to create a SOAP web service and how to build a client to consume

the same SOAP web service using Spring Boot.


7. Handle uncaught exception in Spring Boot Application
Many times, we have seen exception thrown on your web page and all the stack trace of the

exception. A non-technical user will not be able to understand most of the time. Also stack trace

is not the best option when we can show the same exception error in nice json format with the root

cause. In this lesson, we will show how to handle unhandled exceptions from our previous Spring

REST API we built here. This particular feature will show how to handle most HTTP 500 errors

which happen because of server side issues. Any errors with request or client side, those are HTTP

400 errors and they have been handled in previous post Error Handling in Spring Boot Rest API.

Problem

What happens when there is a database connection issue OR columns in your REST API are

different from specified in database tables? Your API will throw a 500 error and if you don’t have

any mechanism, this will display error in an html format which will not give much information to

user or developer to resolve the issue.

Solution

JAX-RS provides a way to handle uncaught exceptions. This can be done by my implementing an

interface for ExtendedExceptionMapper. Basically this is a contract for a provider that takes java

exceptions and maps them to a response object which can be transformed into a JSON. This can

be implemented as below:
package com.betterjavacode.benefits.utilities;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.glassfish.jersey.spi.ExtendedExceptionMapper;

@Provider
public class UncaughtExceptionMapper implements
ExtendedExceptionMapper<Throwable>
{
private static final Logger LOGGER =
LogManager.getLogger(UncaughtExceptionMapper.class);

@Override
public Response toResponse(Throwable exception)
{
LOGGER.info("Enter >> toResponse ");
LOGGER.debug("Exception Caught: " + exception.getMessage());
LOGGER.info("Exit << toResponse");
return
Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}

@Override
public boolean isMappable(Throwable arg0)
{
return !(arg0 instanceof WebApplicationException);
}
}

Basically this implementation shows a class UncaughtExceptionMapper implementing an

interface ExtendedExceptionMapper which provides a way to map all exceptions which are not of

type WebApplicationException (Most HTTP 400 errors are WebApplicationExceptions).

toResponse method will help to log all the exceptions and convert exceptions into a Response

object.

Conclusion

In this lesson, we showed how to map all uncaught exceptions into json format response.
8. Angular JS User Interface for Spring Boot REST API

In this lesson, we will add a user interface using AngularJS to the REST API we created chapter

2.

Controller for home page

First we will create a controller in Spring Boot rest api to call our home page. All the requests that

will come to web server, will go through this controller and controller will return a home page for

the request based on path.

MainController.java will look like below:

package com.betterjavacode.benefits.controller;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MainController
{
public static final Logger LOGGER =
LogManager.getLogger(MainController.class);

@RequestMapping(value = "/home", method = RequestMethod.GET)


public String homepage()
{
LOGGER.info(" Enter >> homepage() ");
return "index";
}
}

Any request coming to https://localhost:8443/home will return a page from index.html.


Create a Home Page

Now, we will create an index.html page. We will also be using angular JS framework as part of

this home page so that we can build a single page application. If you are not aware of Angular JS

or new to this framework, you can read about it AngularJS. One thing to remember while creating

this page is a directory structure. Lot of issues that arise to create html pages are because of

directory structure. Directory structure will look like below:

The home page index.html page is under main/resources/templates/ directory and it looks like

below
<html ng-app="benefitApp">

<head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"


/><title>Benefit Application</title>

<script>document.write('<base href="' + document.location + '" />');</script>

<link rel="stylesheet" href="/css/bootstrap.css" />

<script src="https://code.angularjs.org/1.6.1/angular.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular-
route.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular-
resource.js"></script>

<script type="text/javascript" src="./js/app.js"></script>

</head>

<body ng-controller="MainCtrl">

Hello {{name}}!
<div>
<ul class="menu">
<li><a href="listUser">user-list</a></li>
<li><a href="listCompany">company-list</a></li>
</ul>
<div ng-view="ng-view"></div>
</div>
</body>
</html>

Home page shows that this is an angular app with name “benefitApp”. This also declares a

controller “MainCtrl” with an angular view. Important to see we are importing angular.js, angular-

route.js and angular-resource.js modules. Click on user-list or company-list, will show list of users

and list of companies respectively.


Create a controller

Now to handle the controller (MainCtrl), we added in index.html, we will add app.js which will

declare the controller. This javascript file also contains config data to handle all the routing of

pages. That’s why we will be importing “ngRoute” and “ngResource” angular modules.

var app = angular.module('benefitApp', ['ngRoute','ngResource']);

var app = angular.module('benefitApp', ['ngRoute','ngResource']);


app.controller('MainCtrl', function($scope, $routeParams)
{
$scope.name = 'World';

$scope.$routeParams = $routeParams;

})

Throughout the interaction on web pages, we will be using different controllers for editing user or

company information and creating user or company. The same file app.js will also handle routing

of these pages which is shown below

app.config(function($routeProvider,$locationProvider) {

$locationProvider.html5Mode(true);

$routeProvider.when('/listUser',

{templateUrl: 'views/listUser.html', controller: 'userController'});

$routeProvider.when('/listCompany',

{templateUrl: 'views/listCompany.html', controller: 'companyController'});

$routeProvider .when('/editUser/:userId',

{ templateUrl : 'views/editUser.html' }) ;

$routeProvider .when('/editCompany/:companyId',
{ templateUrl : 'views/editCompany.html' }) ;

$routeProvider.when('/createUser',

{templateUrl:'views/createUser.html'});

$routeProvider.when('/createCompany',

{templateUrl:'views/createCompany.html'});

});

Rest of the code showing all controllers’ logic has been skipped for post purposes. UserController

or CompanyController are calling rest apis which we have built using Spring boot.

Demo

Now build the code and run our embedded tomcat webserver. Fire up the

url https://localhost:8443/home – it will look like below:

Click on user-list and it will show list of users inside the same page as below:
Click on edit button and we will see a form to update user information:
9. Spring Boot Security in web application
In this lesson, we will show how to use Spring Boot Security to login, authorization based on user

role, logout and error handling.

We will be discussing following use case

1. A user access a home page for application.

2. If user is authenticated, he will be redirected to page

3. If user is not authenticated, he will be challenged to login ( redirected to login page)

4. User enters credentials

5. If correct credentials, a session is created and user is verified for role

6. If incorrect credentials, show an error message

7. If user is of role USER, he is redirected to user profile page. He can only edit or save his

information.

8. If user is of role ADMIN, he is redirected to list of users page.

9. If user clicks on logout, session is deleted and user is redirected to login page.

10. If user (of any role) tries to login after logout, user should be redirected to appropriate page

11. If user is neither USER nor ADMIN, he is redirected to error page

12. Handling of CSRF token

To completely understand this lesson, make sure you have gone through my other posts on Spring

Boot series.

1. Spring Boot REST CRUD API

2. Swagger Documentation
3. User Interface using AngularJS

Database changes

Since this post involves authorization for users, we have to do some database changes. We will

add couple of tables and respective model classes in our REST api modification.

 Table role

 Table user_role

create table role (id int(11) auto_increment primary key not null, role
varchar(255) )
create table user_role (user_id int(11) primary key not null, role_id int(11)
primary key not null))

user_role table helps to maintain many-to-many relationship between user and role table. We will

have only two roles for demo purposes USER and ADMIN.

Another change we have done in table user is that we have added field called password_hash to

store password set by user/administrator for user to login. We will be storing a hash password

value of the original password that user will set.

Dependencies

Since we will be using Spring-security for authentication and authorization purposes, we will add

the dependency for spring security as follows:


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

Controllers and Web Layer

Other than those changes mentioned about, we will demonstrate this post in top-down fashion

rather than bottom-up fashion.

So for web layer, we will define a new controller LoginController and modify our

existing MainController.

package com.betterjavacode.benefits.controller;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.betterjavacode.benefits.entities.User;
import com.betterjavacode.benefits.interfaces.UserManager;

/**
*
* @author Yogesh Mali
*
*/
@Controller
public class LoginController
{
public static final Logger LOGGER =
LogManager.getLogger(LoginController.class);

@Autowired
UserManager userManager;

/**
*
* @param model
* @return
*/
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String userpage(Model model)
{
LOGGER.info(" Enter >> userpage() ");
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
String name = auth.getName();
User user = userManager.findUserByEmail(name);
model.addAttribute("name", user.getFirstname());
model.addAttribute("userid", user.getId());
LOGGER.info(" Exit << userpage() ");
return "user";
}

/**
*
* @return
*/
@RequestMapping(value = { "/login" })
public String login()
{
return "login";
}

/**
*
* @return
*/
@RequestMapping(value = "/403", method = RequestMethod.GET)
public String Error403()
{
return "403";
}
}

As shown in this controller, we have defined a user page, a login page and an error page (403). A

user with role of either USER or ADMIN or both can access a user page which shows that logged

in user’s profile.

Every user irrespective of roles, will be redirected to login page for authentication. If there are any

errors during authentication or authorization, user will be redirected to access denied page (403).

Source code for login page is as below:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head> <title>Benefits Application</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
/> <link rel="stylesheet" type="text/css" th:href="@{/css/login.css}" />
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></scri
pt> <script
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></s
cript></head>
<body>
<div class="container"> <form th:action="@{/login}" method="POST"
class="form-signin">
<h3 class="form-signin-heading" th:text="Welcome"></h3>
<input type="text" id="email" name="username" th:placeholder="Email"
class="form-control" style="width:350px"/>
<input type="password" th:placeholder="Password" id="password"
name="password" class="form-control" style="width:350px"/>
<div align="center" th:if="${param.error}">
<p style="font-size: 20; color: #FF1C19;">Email or Password invalid, please
verify</p>

</div>
<button class="btn btn-lg btn-primary btn-block" name="Submit" value="Login"
type="Submit" th:text="Login" style="width:350px"></button> </form></div>
</body>
</html>

This login page shows a simple form to input username (email) and password and process that

authentication using spring-security database authentication method.

@RequestMapping(value = "/home", method = RequestMethod.GET)


public String homepage(Model model)
{
LOGGER.info(" Enter &gt;&gt; homepage() ");
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
String name = auth.getName();
User user = userManager.findUserByEmail(name);
model.addAttribute("name", user.getFirstname());
LOGGER.info(" Exit &lt;&lt; homepage() ");
return "index";
}
Changes in MainController are about authenticated user and passing that user’s first name to model

to display in html page. UserManager in service layer has been enhanced to return an user based

on username (which is email) . We have also added email to be unique as a constraint in database.

User page for a user with role USER is nothing but a user profile information which he can edit

and update any time.

<html ng-app="benefitApp">
<head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"
/>
<title>Benefit Application</title><script>document.write('<base href="' +
document.location + '" />');</script> <link rel="stylesheet"
href="/css/bootstrap.css" /><script
src="https://code.angularjs.org/1.6.1/angular.js"></script><script
src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular-
route.js"></script><script
src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular-
resource.js"></script><script type="text/javascript"
src="./js/app.js"></script></head><body ng-controller="UserCtrl">Hello
<p th:text="${name}"></p>

<div>
<ul class="menu">
<li><a th:href="@{'userProfile/' + ${userid}}">Profile</a></li>
</ul>
<div ng-view="ng-view"></div>
</div>
<div class="input-group">
<div class="controls"> <a ng-click="logout()" class="btn btn-
small">Logout</a></div>
</div>
</body>
</html>

Authentication

Now we have the application ready with all the required backend details for adding authentication

part. Remember we are using spring-security for authentication and authorization of an

application.
package com.betterjavacode.benefits;

import javax.sql.DataSource;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import
org.springframework.security.config.annotation.authentication.builders.Authen
ticationManagerBuilder;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.builders.WebSecurity;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSec
urity;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityC
onfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@ComponentScan("com.betterjavacode.benefits.services")
@EnableWebSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
public static final Logger LOGGER =
LogManager.getLogger(SecurityConfig.class);

@Autowired
private SimpleAuthenticationSuccessHandler loginSuccess;

@Autowired
private LogoutSuccess logoutSuccess;

@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

@Autowired
private DataSource dataSource;

@Value("${spring.queries.users-query}")
private String usersQuery;

@Value("${spring.queries.roles-query}")
private String rolesQuery;

@Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws
Exception
{
LOGGER.info(" Enter &gt;&gt; configureGlobal() ");
auth.jdbcAuthentication()
.usersByUsernameQuery("select email,password_hash,enabled from user
where email=?")
.authoritiesByUsernameQuery("select u.email,r.role from user u inner
join user_role ur on(u.id=ur.user_id) inner join role r on(r.id=ur.role_id)
where u.email=?")
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder);
LOGGER.info(" Exit &lt;&lt; configureGlobal() ");
}

/**
* Handle Login - Authentication and Redirection
*/
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/home")
.hasAuthority("ADMIN")
.antMatchers("/user")
.hasAnyAuthority("USER", "ADMIN")
.and()
.formLogin()
.loginPage("/login")
.successHandler(loginSuccess)
.permitAll()
.and()
.logout()
.logoutSuccessHandler(logoutSuccess)
.deleteCookies("JSESSIONID")
.invalidateHttpSession(false)
.permitAll()
.and()
.exceptionHandling()
.accessDeniedPage("/403");

/**
* Exclude resources from user-access
*/
@Override
public void configure(WebSecurity web) throws Exception
{
web.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**",
"/images/**");
}
}
What’s happening in this code?

 When a user with role ADMIN or USER calls either /home or /user pages respectively, the

user will be challenged to login.

 Once the user inputs credentials, they are validated against JDBC database authentication

mechanism provided by spring-security.

 If user of role USER tries to access ADMIN home page, user will be redirected to 403

page. Redirection strategy is handled by an authentication success handler.

 If user clicks LOGOUT button on the page he is on, he will be logged out of application

and redirected back to login page. All the cookies will be deleted. Redirection will be

handled by logout success handler.

Changes in AngularJS User Interface Controller

As shown in user.html page, once the user with role USER is logged in, he sees url for his profile

information. If user clicks this url, user sees his or her profile information. This page has a

controller called UserCtrl which basically handles the logout on this initial page. User Profile is

shown on userprofile.html page which has singleusercontroller. This angular js controller

handles updating user profile information or logout. Rest of the code will be updated in github

repository for self-explanation.

Handling CSRF Token

There are two ways Cross-Site Request Forgery token can be handled in Spring application. First

way is by disabling this token generation. This is not a recommended approach as this put your
application to possible CSRF security attacks for hackers. If you are just doing this for demo

purposes, you can disable this in SecurityConfig.java by calling http.csrf().disable().

As spring points out, CSRF Protection should be used for any request that could be processed by

a browser by normal users.

We will be using spring security to handle CSRF token on server side rather than on client side. So

every request that will come to server will be intercepted to add a CSRF token and then verified.

Angular JS verifies the cookie for CSRF token before a user can post any request.

Add a CSRF Filter Class

We will add a filter that will handle setting of CSRF token in a cookie. Angular JS expects a cookie

name to be as XSRF-TOKEN. This class will look like below:

public class CSRFHeaderFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain) throws
ServletException, IOException {
CsrfToken csrf = (CsrfToken)
request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null &&
!token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);

}
Now we will enable csrf token in SecurityConfig as shown below

.and()
.csrf()
.csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(new CSRFHeaderFilter(), CsrfFilter.class);

What is csrfTokenRepository?

We tell spring-security to expect CSRF token in the format that Angular wants to send it back, a

header called X-XSRF-TOKEN instead of default X-CSRF-TOKEN. With these changes, we

don’t have to do anything on client side.

private CsrfTokenRepository csrfTokenRepository()


{
HttpSessionCsrfTokenRepository repository = new
HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}

Demo

In this post, we showed how to use spring security for authentication and authorization. Now we

will show how to run the application. Once the application is built and run from eclipse, access the

page https://localhost:8443/home , we will see below screen:


It will be the same screen if you access https://localhost:8443/user. Now if we enter credentials of

an admin user, we will see below screen:

User screen will be as below:


If you click logout, it will log the user out and show login screen again. In this way, we showed

how we can use spring security for authentication and authorization.


10. Deploying Spring Boot application using a docker
What is docker

Docker is a container platform delivered by a company named “Docker Inc.” Docker can be used

by developers, operators and enterprise users to deliver and use packaged software. Docker has

something called a container. Container can be a virtual machine (VM) in lay man’s terms, but

still little different from VM. Container contains packaged software delivered in a way that it can

be run isolated on a shared operating system. As per official definition – Unlike VMs, container

does not bundle a full operating system – only libraries and settings required to make the software

work are needed.

In this demo, we will use our spring boot application built throughout from Part I to Part VIII.

I am using Windows Platform Toolbox for docker to build my docker containers.

We will build a container with mysql database deployed and another container where we will

deploy spring boot application. This spring boot application container will connect to mysql

database container at runtime. The process is little complicated, but once you get your hands on

docker container, it becomes very straight forward to understand. Also for this post, I will not

explain anything about spring boot application. You should review all the previous posts I have

written explaining how to build and deploy spring boot application on an embedded tomcat.

Building a docker container with mysql

Few things to remember


1. Make sure your spring boot application is working with mysql database before you build a

container.

2. If your application contains user administration and password, make sure you have a super

administrator whose password you can insert in database with password_hash. This is

specifically true for the application we will be deploying in docker container.

For most standard applications (like mysql, java 8, spring-boot), there are number of images

available in docker hub. When we will run our docker container for database, docker shell will

pull the version of that application from the hub to build a container. We will not be creating any

new or blank docker image. To run a docker container with mysql version 5.6, we will use below

command.

docker run --name benefitsmysql -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -e MYSQL_DATABASE=benefitsmysql -p

3308:3306 -d mysql:5.6

 Name of our docker container is benefitsmysql.

 We are not using any password. This is not recommended for production code, I am just

doing this for demo purposes.

 Database name is benefitsmysql.

 Also this database is running at port 3308 to 3306 of local host machine.

 -d to tell Docker to daemonize the container and keep it running.

 mysql:5.6 to download MySQL 5.6 Server image from Docker public repo

Once this is started, there are couple of ways you can verify if we are able to connect to this

database or not.
Get the ip address of this container host with command docker-machine ip . Now in mysql

administrator workbench, access the mysql server with ip address and port 3308 and see if you can

access the database.

Another way on docker shell terminal – use this command docker exec -it benefitsmysql -l , this

will connect you as a root to the shell where mysql is installed. And then you can use mysql as

regular command to access mysql.

To run our Spring boot application successfully, once you access mysql, create following tables:

use benefitsmysql;

create table companyprofile (id int not null auto_increment, establisheddate


date, status varchar(50),corporationtype varchar(50), primary key(id));

create table company(id int not null auto_increment, name varchar(255),


statusid int, type varchar(255), ein varchar(50), companyprofileid int,
primary key(id), foreign key(companyprofileid) references company(id));

create table userprofile(id int not null auto_increment, dob date, doh date,
maritalstatus varchar(50),sex varchar(50),ssn varchar(50),weight varchar(50),
height varchar(50),employmentstatus varchar(50), terminationdate date,
primary key(id));

create table user(id int not null auto_increment, createdate date, email
varchar(255),firstname varchar(255), middlename varchar(255), lastname
varchar(255),username varchar(100),jobtitle varchar(255),password_hash
text,enabled tinyint(4), userprofileid int, primary key(id), foreign
key(userprofileid) references userprofile(id));

create table role(id int not null auto_increment, role varchar(255), primary
key(id));

create table user_role(user_id int not null, role_id int not null, primary
key(user_id, role_id));

Building a docker image for Spring Boot Application along with mysql

To dockerize my spring boot application, we will use a maven plugin to build a docker image.
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<imageName>${docker.image.prefix}/benefits</imageName>
<dockerHost>https://192.168.99.100:2376</dockerHost>
<dockerCertPath>C:\Users\Yogesh
Mali\.docker\machine\machines\default</dockerCertPath>
<dockerDirectory>src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>

I am passing dockerDirectory where Dockerfile will be stored to build our image. Also another

change I have made to my original pom file, is that i have added packaging as jar.

<groupId>com.betterjavacode</groupId>
<artifactId>Benefits</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
.................
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.betterjavacode.benefits.Application</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
I have also changed in my application.properties to point to mysql database container by updating

database url with ipaddress of docker container.

spring.datasource.url=jdbc:mysql://192.168.99.100:3308/benefitsmysql

My Dockerfile to build a docker image is as below:

FROM java:8

VOLUME /tmp

ADD Benefits.jar Benefits.jar

EXPOSE 8443

RUN bash -c 'touch /Benefits.jar'

ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/Benefits.jar"]

Basically this will build a Benefits.jar using java 8 and will expose port 8443 that I can use to

access my application.

Now build a new docker container image by using maven goal as

mvn clean package docker:build

To run the application

docker run -p 8443:8443 --name benefits --link benefitsmysql:mysql -d containerid

This will execute the jar built within that container. Important to note here is --link as it links other

container where we have mysql server deployed. So we are linking two containers and we will call

the database from our spring boot application container. The same command can be used little

differently to see the detail execution log as below

docker run -p 8443:8443 --name benefits --link benefitsmysql:mysql -it containerid


Executing the application

Once the application starts successfully, we will access our application with

url https://192.168.99.100:8443/home , this will look like below:

Another note – Make sure to update ip addess in all angular js references.

In this post, we showed how we can deploy Spring boot application connected to mysql on a docker

container.
11.References

1. https://www.docker.com/
2. https://stackoverflow.com/questions/45952291/connection-
refused-accessing-a-spring-boot-application-running-in-
docker-contai
3. https://github.com/jiwhiz/spring-boot-docker-mysql/wiki
4. https://kielczewski.eu/2014/12/spring-boot-security-
application/
5. https://spring.io/blog/2015/01/12/the-login-page-angular-js-
and-spring-security-part-ii
6. http://microservices.io/patterns/microservices.html
7. https://martinfowler.com/articles/microservices.html
8. https://stackoverflow.com/questions/33041733/microservices
-vs-monolithic-architecture
9. http://briansjavablog.blogspot.ca/2013/01/spring-web-
services-tutorial.html
10. http://www.concretepage.com/spring-4/spring-4-soap-
web-service-producer-consumer-example-with-tomcat
11. https://stackoverflow.com/questions/20210993/consumi
ng-a-web-service-in-java-and-soapui

Вам также может понравиться