0% found this document useful (0 votes)
71 views

Expense Tracker API

The document provides a roadmap for the Expense Tracker API course. It outlines topics that will be covered across two editions, including setting up the development environment, creating REST APIs for expenses and users, connecting to a MySQL database, adding features like pagination and sorting, and deploying the application to platforms like Heroku and AWS. Future topics that may be covered include password reset functionality, testing, API versioning, and integrating additional services.

Uploaded by

m.pusiewicz
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
71 views

Expense Tracker API

The document provides a roadmap for the Expense Tracker API course. It outlines topics that will be covered across two editions, including setting up the development environment, creating REST APIs for expenses and users, connecting to a MySQL database, adding features like pagination and sorting, and deploying the application to platforms like Heroku and AWS. Future topics that may be covered include password reset functionality, testing, API versioning, and integrating additional services.

Uploaded by

m.pusiewicz
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 209

Course Road Map

Expense Tracker API


Edition - 1

What is REST API? CRUD oprations using Data JPA Con gure the Spring Security

Setup the Dev Environment Add Pagination and Sorting Create Mapping between entities

Different ways of creating project Handle the Exceptions Adding JWT to the application

Basics of Spring Boot Validate the REST APIs Advance features in Postman

Develop REST APIs for Expense Filter the records Deploy application to Heroku

Connecting to the MySQL DB Develop REST APIs for User What’s next ?
fi
Edition - 2 (Updating Soon)

Deploy the application to AWS Password Reset Feature

Con gure the Swagger Testing the Service layer

Versioning the REST APIs Writing Native SQL queries

Add AWS Email Service Writing JPQL queries

Logging functionality Many more…

Role based authentication


fi
Understanding REST API/RESTful Web Service

Expense Tracker API


What is REST API?

• It is a collection/group of URLs which is available on the server

www.myapp.com/readnotes Fetch the list of


notes from the
www.myapp.com/createnotes server

www.myapp.com/deletenotes Create new note


on the server
• Each URL is call it as REST end point

• Each REST end point performs a certain operation

• Each REST end point also contains a HTTP method


HTTP Methods

• There are several HTTP methods are available

• There are 4 HTTP methods we will use most commonly

GET - Fetch the data from the server

POST - Create a data on the server

PUT - Update a data on the server

Delete - Delete a data on the server

• For HTTP POST and PUT methods supports HTTP Request Body
What is our Goal?

• Create these kind of URLs and make them available on the internet

• Anyone in the world can call to our REST end point from their application

• REST APIs are language and platform independent


What is the data format to exchange the information?

• There are different data formats are available to exchange the information

• JSON is the most commonly and used data format to exchange information

• We send the data in JSON format to server

• Server will respond to the request in JSON format


What is JSON?

• JSON stands for Javascript Object Notation

• It contains key and value pairs

• The key will always be in String but value could be anything

• It could be String, Number, Boolean, Object etc..

"name": "Bushan"
"age": 29,
"address":
"location": "India"
{

}
}

Application Architecture

Expense Tracker API


Application Architecture

Presentation Layer Service Layer Data Layer

Custome Custome Custome


Controller Service Repository
r

Different ways to create Spring Boot project

Expense Tracker API


Different ways to create Spring Boot project

There are 2 different ways to create Spring Boot project

1. Create a Spring Boot project in ST

2. Create a Spring Boot project using Spring Initializr


S

1. Create a Spring Boot project in STS

• STS is a development IDE created by Spring tea

• It a avour of Eclipse IDE but it has a great support for spring boot applications
fl
m

2. Create a Spring Boot using Spring initializr

• Spring initializr is a online web tool created by Spring team


https://start.spring.io
Summary
• There are 2 different approaches to create Spring boot starter projec

• First approach using ST

• Second approach using Spring initializr


S

Challenge #1

Expense Tracker API


Understand the Spring boot project structure

Expense Tracker API


Spring boot starter project structure

Files/Folder Description

src/main/java Contains the java source les

src/main/resources Contains the properties les /UI templates/static les

src/test/java Contains the java source les for unit testing

JRE System Library Contains the jdk and system related jars

Maven dependencies Contains the dependency jars for our application

target Contains the les jar/war le after the build

HELP.md Contains the reference links

mvnw It allows to run maven project for Linux/Mac

mvnw.cmd It allows to run maven project for Windows

pom.xml Contains the info about project and dependencies


fi
fi
fi
fi
fi
fi
Understand the Spring Boot Starters

Expense Tracker API


What are starter dependencies?
• Starter dependencies are created by Spring tea

• It is a group of dependencies which will help us to create speci c kind of application

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

Web application, RESTful web services


spring-web, spring-webmvc, jackson-databin
tomcat-embed-core
d

fi
Why we need starter dependencies?
* denotes a particular type of application
• Spring boot starters follows a naming conventio

spring-boot-starter-

• With one single dependency we can download all other dependencie

• We don’t need to worry about the version mismatc

• Spring boot starters makes development easier and rapi

• The pom.xml le looks clean


fi
*

Summary
• Spring boot starter dependencies are group of maven dependencie

• It downloads all the maven dependencies with correct version

• It helps us to create speci c kind of applicatio

• These dependencies make development faster and easier


fi
n

Deep dive into @SpringBootApplication

Expense Tracker API


Understanding @SpringBootApplication annotation

• @SpringBootApplication internally uses multiple annotation

• It uses 3 most important annotations

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

Understanding @SpringBootCon guration


• @SpringBootCon guration also uses @Con guration annotation internall

• @Con guration is alternate for xml l

• These classes contains beans by de ning the methods with @Bean annotation

@SpringBootApplication
public class SpringbootdemoApplication {

public static void main(String[] args) {


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

}
fi
fi
fi
fi
e

fi
fi
y

Understanding @ComponentScan
• @ComponentScan will scan for all the components in our applicatio

• Spring will scan for all the components under the base packag

• If the components are present outside of base packag

• We need to specify the package names explicitly

package in.bushansirgur.springbootdemo

@SpringBootApplication
public class SpringbootdemoApplication {

public static void main(String[] args) {


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

}
;

Understanding @EnableAutoCon guration

• Spring will automatically con gure the app based on the classpath dependencie

• Spring will con gure DispatcherServle

• Spring will convert Java object to JSON and vice vers

• Spring will con gure logging framework

• Spring will start tomcat server on port localhost:8080


fi
fi
fi
t

fi
a

Summary

• @SpringBootApplication annotation is composed of several annotation

• @SpringBootCon guration, @EnableAutoCon guration and @ComponentScan


fi
fi
s

Create a rst REST end point for expenses

Expense Tracker API


fi
1. Create a class ExpenseController

URL: localhost:8080/expenses

File: ExpenseController.java

public class ExpenseController {

}
2. De ne a method getAllExpenses()

URL: localhost:8080/expenses

File: ExpenseController.java
public class ExpenseController {

public String getAllExpenses() {

return "List of expenses”;

}
fi
3. Map the URL to a Java method

URL: localhost:8080/expenses

URI or REST end point

File: ExpenseController.java
public class ExpenseController {

@GetMapping("/expenses")
public String getAllExpenses() {
return "List of expenses";
}

@RequestMapping(method = RequestMethod.GET)
4. Make the class as Rest controller

URL: localhost:8080/expenses

@Controller @Component

File: ExpenseController.java
@RestController
public class ExpenseController {

@GetMapping("/expenses")
public String getAllExpenses() {
return "List of expenses";
}

@ResponseBody
Add the dependencies to the application

Expense Tracker API


Add the dependencies to the application
File: pom.xml
<dependencies>
...

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

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
Create a database and table for expenses

Expense Tracker API


Create a database and table for expenses
Con gure the datasource

Expense Tracker API


fi
Create a database and table for expenses
File: application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/expensetracker
spring.datasource.username=root
spring.datasource.password=scbushan05
Retrieve the list of expenses from database

Expense Tracker API


1. Create an Expense entity
File: Expense.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "tbl_expenses")
public class Expense {

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

@Column(name = "expense_name")
private String name;

private String description;

@Column(name = "expense_amount")
private BigDecimal amount;

private String category;

private Date date;


}
2. Create a JPA Repository for Expense entity
File: ExpenseRepository.java
@Repository
public interface ExpenseRepository extends JpaRepository<Expense, Long> {

}
3. Create an Expense service
File: ExpenseService.java
public interface ExpenseService {

List<Expense> getAllExpenses();
}
4. Create an Expense service implementation
File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private ExpenseRepository expenseRepo;

@Override
public List<Expense> getAllExpenses() {
return expenseRepo.findAll();
}

}
5. Update the Expense controller
File: ExpenseController.java
@RestController
public class ExpenseController {

@Autowired
private ExpenseService expenseService;

@GetMapping("/expenses")
public List<Expense> getAllExpenses() {
return expenseService.getAllExpenses();
}

}
Creating database tables using JPA

Expense Tracker API


Update the properties le to add JPA properties
File: application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/expensetracker
spring.datasource.username=root
spring.datasource.password=scbushan05

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

fi
Add base URL to the REST end points

Expense Tracker API


Update the properties le to add context path
File: application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/expensetracker
spring.datasource.username=root
spring.datasource.password=scbushan05

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

server.servlet.context-path=/api/v1

fi
Understand passing a parameter in the URL

Expense Tracker API


How to pass parameter in the URL?

There are 2 different ways to pass parameter in the URL

1. Using the path variabl

2. Using the query string


e

Using the Path Variable

URL: localhost:8080/expenses/45

@GetMapping("/expenses/{id}")
public Expense getExpenseById(@PathVariable("id") Long id) {
...
}

@PathVarible is used to bind URL variable to method parameter


Using the Path Variable

URL: localhost:8080/users/12/expenses/45

@GetMapping("/users/{userId}/expenses/{id}")
public List<Expense> getExpensesByUserId(@PathVariable("userId") Long user,
@PathVariable Long id) {
//TODO:
}
Passing a parameter using query strings

Expense Tracker API


Using the query strings

URL: localhost:8080/expenses?id=45

@GetMapping("/expenses")
public Expense getExpenseById(@RequestParam(“id") Long id) {
...
}

@RequestParam is used to get the query strings from URL


Using the query strings

URL: localhost:8080/users/expenses?userId=23&id=34

@GetMapping("/users/expenses")
public List<Expense> getExpensesByUserId(@RequestParam("userId") Long user,
@RequestParam Long id) {
//TODO:
}
Create a REST end point to get the expense by id

Expense Tracker API


1. De ne a method in expense service interface
File: ExpenseService.java

public interface ExpenseService {

...

Expense getExpenseById(Long id);

}
fi
2. Call a repository method to retrieve the expense
File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private ExpenseRepository expenseRepo;

...

@Override
public Expense getExpenseById(Long id) {
Optional<Expense> expense = expenseRepo.findById(id);
if (expense.isPresent()) {
return expense.get();
}
throw new RuntimeException("Expense is not found for the id "+id);
}

}
3. Create a REST end point to retrieve the expense

URL: localhost:8080/expenses/45

File: ExpenseController.java
@RestController
public class ExpenseController {

@Autowired
private ExpenseService expenseService;

...

@GetMapping("/expenses/{id}")
public Expense getExpenseById(@PathVariable("id") Long id) {
return expenseService.getExpenseById(id);
}

}
Create a REST end point to delete expense by id

Expense Tracker API


1. De ne a method in expense service interface
File: ExpenseService.java
public interface ExpenseService {

...

void deleteExpenseById(Long id);


}
fi
2. Call a repository method to delete the expense by id
File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private ExpenseRepository expenseRepo;

...

@Override
public void deleteExpenseById(Long id) {
expenseRepo.deleteById(id);
}

}
3. Create a REST end point to delete the expense by id

URL: localhost:8080/expenses?id=45

File: ExpenseController.java
@RestController
public class ExpenseController {

@Autowired
private ExpenseService expenseService;

...

@DeleteMapping("/expenses")
public void deleteExpenseById(@RequestParam("id") Long id) {
expenseService.deleteExpenseById(id);
}

}
Map the HTTP Request to a Java Object

Expense Tracker API


Map the HTTP Request to a Java Object

URL: localhost:8080/expenses
public class Expense {

private Long id;


{
"name": "Water bill", private String name;
"description": "water bill", Jackson API
private String description;
"amount": 700.00,
"category": "Bills", private BigDecimal amount;
"date": "2021-10-24"
} private String category;

private Date date;

}
Map the HTTP Request to a Java Object
@RequestBody annotation Map the

HTTP Request Body to a Java Object


URL: localhost:8080/expenses

@PostMapping("/expenses")
public void saveExpenseDetails(@RequestBody Expense expense) {

System.out.println("Printing the expense details "+expense);

}
Create a REST end point to save the expense data

Expense Tracker API


1. De ne a method in expense service interface
File: ExpenseService.java
public interface ExpenseService {

...

Expense saveExpenseDetails(Expense expense);

}
fi
2. Call a repository method to save the expense data
File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private ExpenseRepository expenseRepo;

...

@Override
public Expense saveExpenseDetails(Expense expense) {
return expenseRepo.save(expense);
}

}
3. Create a REST end point to save the expense data

URL: localhost:8080/expenses

File: ExpenseController.java
@RestController
public class ExpenseController {

@Autowired
private ExpenseService expenseService;

...

@PostMapping("/expenses")
public Expense saveExpenseDetails(@RequestBody Expense expense) {
return expenseService.saveExpenseDetails(expense);
}

}
Create a REST end point to update the expense data

Expense Tracker API


1. De ne a method in expense service interface
File: ExpenseService.java
public interface ExpenseService {

...

Expense updateExpenseDetails(Long id, Expense expense);

}
fi
2. Call a repository method to update the expense data
File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private ExpenseRepository expenseRepo;

...

@Override
public Expense updateExpenseDetails(Long id, Expense expense) {
Expense existingExpense = getExpenseById(id);
existingExpense.setName(expense.getName() != null ? expense.getName() : existingExpense.getName());
existingExpense.setDescription(expense.getDescription() != null ? expense.getDescription() : existingExpense.getDescription());
existingExpense.setCategory(expense.getCategory() != null ? expense.getCategory() : existingExpense.getCategory());
existingExpense.setAmount(expense.getAmount() != null ? expense.getAmount() : existingExpense.getAmount());
existingExpense.setDate(expense.getDate() != null ? expense.getDate() : existingExpense.getDate());
return expenseRepo.save(existingExpense);
}

}
3. Create a REST end point to update the expense data

URL: localhost:8080/expenses/34

File: ExpenseController.java
@RestController
public class ExpenseController {

@Autowired
private ExpenseService expenseService;

...

@PutMapping("/expenses/{id}")
public Expense updateExpenseDetails(@RequestBody Expense expense, @PathVariable Long id) {
return expenseService.updateExpenseDetails(id, expense);
}

}
Save the Timestamps to the Expense table

Expense Tracker API


Modify the expense table to add timestamps

created_at will store the current date and time


when a new record has been saved.

updated_at will store the last modified date and time


when a record has been updated.
Update the Entity class to add timestamps
File: Expense.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "tbl_expenses")
public class Expense {

...

@Column(name = "created_at", nullable = false, updatable = false)


@CreationTimestamp
private Timestamp createdAt;

@Column(name = "updated_at")
@UpdateTimestamp
private Timestamp updatedAt;
}
Understand the HTTP Response Status codes

Expense Tracker API


Understand the HTTP Response Status codes

• HTTP Response Status codes plays an important rol

• It indicates that whether a speci c HTTP request has been successfully complete

• By default every HTTP request contains HTTP status code 200 (OK

• We can customise the HTTP status codes for different operations


fi
e

Different HTTP Status codes

• HTTP Status codes are divided into multiple groups

Informational Responses 100 - 199

Successful Responses 200 - 299

Redirection Messages 300 - 399

Client Error Responses 400 - 499

Sever Error Responses 500 - 599


Most commonly used HTTP Status codes

• HTTP Status codes are divided into multiple groups

200 (OK) The request is successfully completed.

201 (CREATED) A resource has been created successfully.

There is no content to send back as HTTP


204 (NO CONTENT)
response.

400 (BAD REQUEST) Due to the invalid syntax.

403 (FORBIDDEN) Client does not have right access for the content.

404 (NOT FOUND) Server cannot find the request resource.

500 (INTERNAL SERVER ERROR) Server does not know how to handle the request.
Understanding Pagination and Sorting

Expense Tracker API


Implementing Pagination and Sorting

• Implementing Pagination and Sorting in Spring boot application is very eas

• Data JPA provides support for Pagination and Sorting out of the bo

• Data JPA provides a repository speci cally for Pagination and Sorting

fi
x

JPA Repository for Pagination and Sorting

• JPA provides a repository for Pagination and Sorting: PagingAndSortingRepository

File: JPARepository.java
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

}
Add the pagination support

File: PagingAndSortingRepository.java
Page<T> findAll(Pageable pageable);

Pageable is an interface which provides pagination

Page is an interface which is sublist of a list of objects


Add the pagination support

• Data JPA provides 2 important parameters: Page and Siz

• Page indicates that the Page number

• Size indicates that the number of elements

localhost:8080/expenses?page=0&size=5

JSON response for the Pagination


{
"content": [
...
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"pageNumber": 0,
"pageSize": 20,
"offset": 0,
"paged": true,
"unpaged": false
},
"totalPages": 1,
"totalElements": 6,
"last": true,
"size": 20,
"numberOfElements": 6,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"first": true,
"number": 0,
"empty": false
}
Add Pagination to Expenses

Expense Tracker API


1. Modify the REST end point to accept the Pageable

URL: localhost:8080/expenses

File: ExpenseController.java
@RestController
public class ExpenseController {

@Autowired
private ExpenseService expenseService;

...

@GetMapping("/expenses")
public Page<Expense> getAllExpenses(Pageable page) {
return expenseService.getAllExpenses(page);
}

}
2. Modify the method in Expense Service interface
File: ExpenseService.java
public interface ExpenseService {

...

Page<Expense> getAllExpenses(Pageable page);


}
3. Modify the method in Expense Service Implementation
File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private ExpenseRepository expenseRepo;

...

@Override
public Page<Expense> getAllExpenses(Pageable page) {
return expenseRepo.findAll(page);
}

}
Create a Custom Exception for Expense

Expense Tracker API


1. Create a Model class to hold the exception information

File: ErrorObject.java
@Data
public class ErrorObject {

private Integer statusCode;

private String message;

private long timestamp;


}
2. Create a Custom exception that extends Exception class

File: ExpenseNotFoundException.java
public class ExpenseNotFoundException extends RuntimeException {

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

public ExpenseNotFoundException(String message) {


super(message);
}
}
3. Create a Global exception class

File: GlobalExceptionHandler.java
@ControllerAdvice
public class GlobalExceptionHandler{

@ExceptionHandler(ExpenseNotFoundException.class)
public ResponseEntity<ErrorObject> handleExpenseNotFoundException(ExpenseNotFoundException ex, WebRequest request) {

ErrorObject eObject = new ErrorObject();

eObject.setStatusCode(HttpStatus.NOT_FOUND.value());

eObject.setMessage(ex.getMessage());

eObject.setTimestamp(System.currentTimeMillis());

return new ResponseEntity<ErrorObject>(eObject, HttpStatus.NOT_FOUND);


}
}
4. Update service method to handle the custom exception
File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private ExpenseRepository expenseRepo;

...

@Override
public Expense getExpenseById(Long id) throws ExpenseNotFoundException {
Optional<Expense> expense = expenseRepo.findById(id);
if (expense.isPresent()) {
return expense.get();
}
throw new ExpenseNotFoundException("Expense is not found for the id "+id);
}

}
Handling Invalid method argument Exception

Expense Tracker API


Create a method to handle type mismatch Exception

File: GlobalExceptionHandler.java
public class GlobalExceptionHandler{

...

@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<Object> handleException(MethodArgumentTypeMismatchException ex, WebRequest request) {
ErrorObject eObject = new ErrorObject();
eObject.setStatusCode(HttpStatus.BAD_REQUEST.value());
eObject.setMessage(ex.getMessage());
eObject.setTimestamp(System.currentTimeMillis());
return new ResponseEntity<Object>(eObject, HttpStatus.BAD_REQUEST);
}
}
Handling the General Exception

Expense Tracker API


Create a general exception

File: GlobalExceptionHandler.java
public class GlobalExceptionHandler{

...

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorObject> handleGeneralException(Exception ex, WebRequest request) {
ErrorObject eObject = new ErrorObject();
eObject.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
eObject.setMessage(ex.getMessage());
eObject.setTimestamp(System.currentTimeMillis());
return new ResponseEntity<ErrorObject>(eObject, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Understanding about Hibernate Validator

Expense Tracker API


Hibernate Validator

• Hibernate validator is the reference implementation of Bean validation

• Hibernate validator allows us to validate the Java beans with its annotations

• Spring version 4 and higher supports hibernate validator

Important hibernate validator annotations

Annotation Purpose
@NotNull Checks that the element is not null
@NotEmpty Checks that the element is not null nor empty
@NotBlank Checks that the element is not null and trimmed length is > 0
@Email Checks that the element is valid email
@Size Checks that the element size is between min and max
@Future Checks that the annotated date is in the future
@Pattern Checks that the string matches the regular expression
Add validations to the Expenses

Expense Tracker API


Add the validation rules to Expense entity

File: Expense.java
public class Expense {

...

@Column(name = "expense_name")
@NotNull
private String name;

...
}
Update ExpenseController to validate the Expense bean

File: ExpenseController.java
@RestController
public class ExpenseController {

@Autowired
private ExpenseService expenseService;

...

@PostMapping("/expenses")
public Expense saveExpenseDetails(@Valid @RequestBody Expense expense) {
return expenseService.saveExpenseDetails(expense);
}

}
Customise the error response
File: GlobalExceptionHandler.java
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler{

...

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {

Map<String, Object> body = new LinkedHashMap<>();


body.put("timestamp", System.currentTimeMillis());
body.put("statusCode", status.value());

//Get all errors


List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(x -> x.getDefaultMessage())
.collect(Collectors.toList());

body.put("errors", errors);

return new ResponseEntity<Object>(body, status);


}
}
Filter the Expenses by Name using keyword

Expense Tracker API


1. Create a nder method in ExpenseRepository

File: ExpenseRepository.java
@Repository
public interface ExpenseRepository extends JpaRepository<Expense, Long> {

Page<Expense> findByNameContaining(String keyword, Pageable page);

>SELECT * FROM tbl_expenses WHERE name LIKE ‘%keyword%’


fi
2. De ne a method in ExpenseService interface

File: ExpenseService.java
public interface ExpenseService {

...

List<Expense> readByName(String name, Pageable page);


}
fi
3. Call the repository method from expense service implementation

File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private ExpenseRepository expenseRepo;

...

@Override
public List<Expense> readByName(String name, Pageable page) {
return expenseRepo.findByNameContaining(name, page).toList();
}

}
4. Create a REST end point to lter expenses by Category

URL: localhost:8080/api/v1/expenses/name?name=bills

File: ExpenseFilterController.java
@RestController
public class ExpenseController {

@Autowired
private ExpenseService expenseService;

@GetMapping("/expenses/name")
public List<Expense> getAllExpensesByName(@RequestParam String name, Pageable page) {
return expenseService.readByName(name, page);
}
}

fi
Filter the expenses by Category

Expense Tracker API


1. Create a nder method in ExpenseRepository

File: ExpenseRepository.java
@Repository
public interface ExpenseRepository extends JpaRepository<Expense, Long> {

Page<Expense> findByCategory(String category, Pageable page);

>SELECT * FROM tbl_expenses WHERE category=?


fi
2. De ne a method in ExpenseService interface

File: ExpenseService.java
public interface ExpenseService {

...

List<Expense> readByCategory(String category, Pageable page);


}
fi
3. Call the repository method from expense service implementation

File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private ExpenseRepository expenseRepo;

...

@Override
public List<Expense> readByCategory(String category, Pageable page) {
return expenseRepo.findByCategory(category, page).toList();
}

}
4. Create a REST end point to lter expenses by Category

URL: localhost:8080/api/v1/expenses/category?category=vegetables

File: ExpenseFilterController.java
@RestController
public class ExpenseController {

@Autowired
private ExpenseService expenseService;

@GetMapping("/expenses/category")
public List<Expense> getAllExpensesByCategory(@RequestParam String category, Pageable page) {
return expenseService.readByCategory(category, page);
}
}

fi
Filter the expenses by Dates

Expense Tracker API


1. Create a nder method in ExpenseRepository

File: ExpenseRepository.java
@Repository
public interface ExpenseRepository extends JpaRepository<Expense, Long> {

Page<Expense> findByDateBetween(Date startDate, Date endDate, Pageable page);

>SELECT * FROM tbl_expenses WHERE date BETWEEN ‘startDate’ AND ‘endDate’


fi
2. De ne a method in ExpenseService interface

File: ExpenseService.java
public interface ExpenseService {

...

List<Expense> readByDate(Date startDate, Date endDate, Pageable page);


}
fi
3. Call the repository method from expense service implementation

File: ExpenseServiceImpl.java

@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private ExpenseRepository expenseRepo;

...

@Override
public List<Expense> readByDate(Date startDate, Date endDate, Pageable page) {
if (startDate == null) {
startDate = new Date(0);
}
if (endDate == null) {
endDate = new Date(System.currentTimeMillis());
}
Page<Expense> pages = eRepo.findByDateBetween(startDate, endDate, page);

return pages.toList();

}
4. Create a REST end point to lter expenses by Dates

URL: localhost:8080/api/v1/expenses/date?startDate=2021-01-01&endDate=2021-03-31

File: ExpenseFilterController.java
@RestController
public class ExpenseController {

@Autowired
private ExpenseService expenseService;

@GetMapping("/expenses/date")
public List<Expense> getAllExpensesByDate(
@RequestParam(required = false) Date startDate,
@RequestParam(required = false) Date endDate,
Pageable page) {
return expenseService.readByDate(startDate, endDate, page);
}
} fi
Create REST end point for create user

Expense Tracker API


1. Create User entity to save the user details to database
File: User.java
@Entity
@Table(name = "tbl_users")
@Data
public class User {

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

private String name;

@Column(unique = true)
private String email;

private String password;

private Long age;

@Column(name="created_at", nullable = false, updatable = false)


@CreationTimestamp
private Timestamp created_at;

@Column(name="updated_at")
@UpdateTimestamp
private Timestamp updated_at;
}
2. Create a User model class
File: UserModel.java
@Data
public class UserModel {

private String name;

private String email;

private String password;

private Long age = 0L;

}
3. Create a User repository

File: UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

}
4. Create a User service and declare a method

File: UserService.java
public interface UserService {

...

User createUser(UserModel user);


}
5. Create implementation class and override the method for create user

File: UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepo;

...

@Override
public User create(UserModel uModel) {
User user = new User();
BeanUtils.copyProperties(uModel, user);
return userRepo.save(user);
}

}
6. Create a REST end point to create new user

URL: localhost:8080/api/v1/users

File: UserController.java
@RestController
public class UserController {

@Autowired
private UserService userService;

@PostMapping("/register")
public ResponseEntity<User> save(@RequestBody UserModel user) {
return new ResponseEntity<User>(userService.create(user), HttpStatus.CREATED);
}
}
Add validations to the create user REST API

Expense Tracker API


1. Create a User model class
File: UserModel.java
@Data
public class UserModel {

@NotBlank(message = "Please enter name")


private String name;

@NotNull(message = "Please enter email")


@Email(message = "Please enter valid email")
private String email;

@NotNull(message = "Please enter password")


@Size(min = 5, message = "Password should be atleast 5 characters")
private String password;

private Long age = 0L;

}
2. Create a REST end point to create new user

URL: localhost:8080/api/v1/users

File: UserController.java
@RestController
public class UserController {

@Autowired
private UserService userService;

@PostMapping("/register")
public ResponseEntity<User> save(@Valid @RequestBody UserModel user) {
return new ResponseEntity<User>(userService.create(user), HttpStatus.CREATED);
}
}
Handle the Exception Existing email

Expense Tracker API


1. De ne a JPA method to check the existing email

File: UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

Boolean existsByEmail(String email);

}
fi
2. Create a Custom Exception for Existing items

File: ItemAlreadyExistsException.java
public class ItemAlreadyExistsException extends RuntimeException {

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

public ItemAlreadyExistsException(String message) {


super(message);
}
}
3. Create a exception handler for ItemAlreadyExistsException

File: GlobalExceptionHandler.java
public class GlobalExceptionHandler{

...

@ExceptionHandler(ItemAlreadyExistsException.class)
public ResponseEntity<Object> handleItemExistsException(ItemAlreadyExistsException ex, WebRequest request) {
ErrorObject eObject = new ErrorObject();
eObject.setStatusCode(HttpStatus.CONFLICT.value());
eObject.setMessage(ex.getMessage());
eObject.setTimestamp(System.currentTimeMillis());
return new ResponseEntity<Object>(eObject, HttpStatus.CONFLICT);
}
}
4. Check for email exists before save the details to database

File: UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepo;

...

@Override
public User create(UserModel uModel) {
if (uRepo.existsByEmail(uModel.getEmail())) {
throw new ItemAlreadyExistsException("User is already registered with email:"+uModel.getEmail());
}
User user = new User();
BeanUtils.copyProperties(uModel, user);
return userRepo.save(user);
}

}
Create REST end point to read user info

Expense Tracker API


1. Declare a method inside user service

File: UserService.java
public interface UserService {

...

User read(Long id);


}
2. Call repository method to read the user info

File: UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepo;

...

@Override
public User read(Long id) throws ResourceNotFoundException {
return userRepo.findById(id).orElseThrow(() -> new ResourceNotFoundException("User not found for the id:"+id));
}

}
3. Create a REST end point to read user info

URL: localhost:8080/api/v1/users/3

File: UserController.java
@RestController
public class UserController {

@Autowired
private UserService userService;

@GetMapping("/users/{id}")
public ResponseEntity<User> get(@PathVariable Long id){
return new ResponseEntity<User>(userService.read(id), HttpStatus.OK);
}
}
Create REST end point to update user info

Expense Tracker API


1. Declare a method inside user service

File: UserService.java
public interface UserService {

...

User update(User user, Long id);


}
2. Call repository method to update the user info

File: UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepo;

...

@Override
public User update(User user, Long id) throws ResourceNotFoundException {
User oUser = read(id);
oUser.setName(user.getName() != null ? user.getName() : oUser.getName());
oUser.setEmail(user.getEmail() != null ? user.getEmail() : oUser.getEmail());
oUser.setPassword(user.getPassword() != null ? user.getPassword() : oUser.getPassword());
oUser.setAge(user.getAge() != null ? user.getAge() : oUser.getAge());
return userRepo.save(oUser);
}

}
3. Create a REST end point to update user info

URL: localhost:8080/api/v1/users/3

File: UserController.java
@RestController
public class UserController {

@Autowired
private UserService userService;

@PutMapping("/users/{id}")
public ResponseEntity<User> update(@RequestBody User user, @PathVariable Long id){
User mUser = userService.update(user, id);
return new ResponseEntity<User>(mUser, HttpStatus.OK);
}
}
Create REST end point to delete user info

Expense Tracker API


1. Declare a method inside user service

File: UserService.java
public interface UserService {

...

void delete(Long id);


}
2. Call repository method to delete the user info

File: UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepo;

...

@Override
public void delete(Long id){
User user = read(id);
userRepo.delete(user);
}

}
3. Create a REST end point to delete user info

URL: localhost:8080/api/v1/users/3

File: UserController.java
@RestController
public class UserController {

@Autowired
private UserService userService;

@DeleteMapping("/users/{id}")
public ResponseEntity<HttpStatus> delete(@PathVariable Long id) throws ResourceNotFoundException {
userService.delete(id);
return new ResponseEntity<HttpStatus>(HttpStatus.NO_CONTENT);
}
}
Understand the Spring Security Default
Con guration

Expense Tracker API


fi
1. Add Spring security dependency to the application

File: pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Spring Security Control Flow

Authentication Authentication Authentication


Filter Manager Provider

Securit UserDetail Passwor


Context Service Encoder
y

Customize the Spring security for HTTP requests

Expense Tracker API


Customise the Spring Security

• To customise the Spring Security use the class: WebSecurityConfigurerAdapter

WebSecurity
MySecurityConfig ConfigurerAdapter

• It provides several methods to customise the Spring Securit

• We can Customise the HTTP requests, User details etc..,

• We need to override the methods and provide our own implementation


Customise the Spring Security

WebSecurityCon gurerAdpater

Available Methods

void configure(AuthenticationManagerBuilder auth)

void configure(HttpSecurity http)


fi
Customise the Spring Security
Default implementation: configure(HttpSecurity)

File: WebSecurityCon gurerAdpater.java


public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {

...

protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests((requests) -> requests.anyRequest().authenticated());

http.formLogin();

http.httpBasic();
}

...

}
fi
Override the con gure() to customise the HTTP requests

File: AppSecurityCon g.java

@Configuration
public class AppSecurityConfig extends WebSecurityConfigurerAdapter{

@Override
protected void configure(HttpSecurity http) throws Exception {

http
.authorizeRequests()
.antMatchers("/login", "/register").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
fi
fi
Customize the Spring security to con gure
multiple users

Expense Tracker API

fi
Customise the Spring Security

WebSecurityCon gurerAdpater

Available Methods

void configure(AuthenticationManagerBuilder auth)

void configure(HttpSecurity http)


fi
Override the con gure() to customise the HTTP requests

File: WebSecurityCon g.java

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

...

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.inMemoryAuthentication()
.withUser("bushan").password("12345").authorities("admin")
.and()
.withUser("bharath").password("12345").authorities("user")
.and()
.passwordEncoder(NoOpPasswordEncoder.getInstance());

}
}
fi
fi
Customize the Spring security to con gure
multiple users

Expense Tracker API

fi
Con gure multiple users
File: WebSecurityCon g.java
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
UserDetails user1 = User.withUsername("bushan").password("12345").authorities("admin").build();
UserDetails user2 = User.withUsername(“bharath").password("12345").authorities("user").build();
userDetailsService.createUser(user1);
userDetailsService.createUser(user2);
auth.userDetailsService(userDetailsService);
}

@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
fi
fi
Creating a custom user details service

Expense Tracker API


Spring Security Control Flow

Authentication Authentication Authentication


Filter Manager Provider

Securit UserDetail Passwor


Context Service Encoder
y

1. Create a query method to nd user by email

File: UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long>{

...

Optional<User> findByEmail(String email);


}

fi
2. Create a custom user details service class

File: CustomUserDetailsService.java
@Service
public class CustomUserDetailsService implements UserDetailsService{

@Autowired
private UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User existingUser = userRepository.findByEmail(email).orElseThrow(() ->
new UsernameNotFoundException("User not found the for the email:"+email));
return new org.springframework.security.core.userdetails.User(
existingUser.getEmail(), existingUser.getPassword(), new ArrayList<>());
}

}
3. Con gure authentication manager to use custom user details service

File: AppSecurityCon g.java


@Configuration
public class AppSecurityConfig extends WebSecurityConfigurerAdapter{

@Autowired
private CustomUserDetailsService userDetailsService;

...

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

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

}
fi
fi
Encode the user password and save to the database

Expense Tracker API


1. Use BCryptPassword encoder to encrypt the password

File: WebSecurityCon g.java


@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

...

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

}
fi
2. Encode the user password before saving to the database

File: UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {

@Autowired
private PasswordEncoder bcryptEncoder;

...

@Override
public User create(UserModel uModel) {
if (uRepo.existsByEmail(uModel.getEmail())) {
throw new ItemAlreadyExistsException("User is already registered with email:"+uModel.getEmail());
}
User user = new User();
BeanUtils.copyProperties(uModel, user);
user.setPassword(bcryptEncoder.encode(user.getPassword()));
return userRepo.save(user);
}

}
3. Encode the password before updating it to the database

File: UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {

@Autowired
private PasswordEncoder bcryptEncoder;

...

@Override
public User update(User user, Long id) throws ResourceNotFoundException {
User oUser = read(id);
oUser.setName(user.getName() != null ? user.getName() : oUser.getName());
oUser.setEmail(user.getEmail() != null ? user.getEmail() : oUser.getEmail());
oUser.setPassword(user.getPassword() != null ? bcryptEncoder.encode(user.getPassword()) : oUser.getPassword());
oUser.setAge(user.getAge() != null ? user.getAge() : oUser.getAge());
return userRepo.save(oUser);
}

}
Create REST API for login

Expense Tracker API


1. Create a model class for Login API

File: LoginModel.java
@Data
public class LoginModel {

private String email;

private String password;


}
2. Update the login REST API to validate the user

File: AuthController.java
@RestController
public class AuthController {

@Autowired
private AuthenticationManager authenticationManager;

@PostMapping("/login")
public ResponseEntity<HttpStatus> login(@RequestBody LoginModel login) {
Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken
(login.getEmail(), login.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authenticate);
return new ResponseEntity<HttpStatus>(HttpStatus.OK);
}

}
Create Mapping between Expense and User

Expense Tracker API


Idea

• User is responsible for creating the expense

• Each expense is associated with speci c user

One Many

User
fi
s

First Approach: Bi-directional

File: User.java File: Expense.java

public class Expense {


public class User {
...
...
@ManyToOne(fetch = FetchType.LAZY)
@OneToMany(mappedBy = "user")
@JoinColumn(name = "user_id", nullable = false)
private List<Expense> expenses;
private User user;
}
}
Second Approach: Uni-directional

File: User.java File: Expense.java

public class Expense {


public class User {
...
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
} private User user;
}
Get the logged in user details

Expense Tracker API


Recap

File: AuthController.java
@RestController
public class AuthController {

@Autowired
private AuthenticationManager authenticationManager;

@PostMapping("/login")
public ResponseEntity<HttpStatus> login(@RequestBody LoginModel login) {
Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken
(login.getEmail(), login.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authenticate);
return new ResponseEntity<HttpStatus>(HttpStatus.OK);
}

}
Declare a method inside User service

File: UserService.java
public interface UserService {

...

User getLoggedInUser();
}
Get the logged in user from Spring security context

File: UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepo;

...

@Override
public User getLoggedInUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

String email = authentication.getName();

return userRepository.findByEmail(email).orElseThrow(() ->


new UsernameNotFoundException("User not found for the email"+email));
}

}
Add user to the expense entity before saving to db

Expense Tracker API


Get the logged in user from Spring security context
File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private UserService userService;

...

@Override
public Expense saveExpenseDetails(Expense expense) {
expense.setUser(userService.getLoggedInUser());
return expenseRepo.save(expense);
}

}
Read all expense by user id

Expense Tracker API


Create a JPA nder method
File: ExpenseRepository.java
@Repository
public interface ExpenseRepository extends JpaRepository<Expense, Long> {

Page<Expense> findByUserId(Long userId, Pageable page);


}

>SELECT * FROM tbl_expenses WHERE user_id=?


fi
Call the repository method and pass the user id
File: ExpenseServiceImpl.java

@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private UserService userService;

...

@Override
public Page<Expense> getAllExpenses(Pageable page) {
return expenseRepo.findByUserId(userService.getLoggedInUser().getId(), page);
}

}
Read single expense by user id

Expense Tracker API


Create a JPA nder method
File: ExpenseRepository.java
@Repository
public interface ExpenseRepository extends JpaRepository<Expense, Long> {

Optional<Expense> findByUserIdAndId(Long userId, Long expenseId);


}

>SELECT * FROM tbl_expenses WHERE user_id=? AND id=?


fi
Call the repository method to pass the user id and expense id
File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Autowired
private UserService userService;

...

@Override
public Expense getExpenseById(Long id){
Optional<Expense> expense = expenseRepo.findByUserIdAndId(userService.getLoggedInUser().getId(), id);
if (expense.isPresent()) {
return expense.get();
}
throw new ResourceNotFoundException("Expense is not found for the id "+id);
}

}
Updating the JPA nder methods

Expense Tracker API

fi
Update the JPA nder methods
File: ExpenseRepository.java
@Repository
public interface ExpenseRepository extends JpaRepository<Expense, Long> {

Page<Expense> findByUserIdAndCategory(Long userId, String category, Pageable page);

Page<Expense> findByUserIdAndNameContaining(Long userId, String keyword, Pageable page);

Page<Expense> findByUserIdAndDateBetween(Long userId, Date startDate, Date endDate, Pageable page);


}

>SELECT * FROM tbl_expenses WHERE user_id=? AND category=?

>SELECT * FROM tbl_expenses WHERE user_id=? AND name LIKE ‘%keyword%’

>SELECT * FROM tbl_expenses WHERE user_id = ? AND date BETWEEN ‘startDate’ AND ‘endDate’
fi
Call the updated nder methods
File: ExpenseServiceImpl.java
@Service
public class ExpenseServiceImpl implements ExpenseService {

@Override
public List<Expense> readByCategory(String category, Pageable page) {
return expenseRepo.findByUserIdAndCategory(userService.getLoggedInUser().getId(), category, page).toList();
}

@Override
public List<Expense> readByName(String keyword, Pageable page) {
return expenseRepo.findByUserIdAndNameContaining(userService.getLoggedInUser().getId(), keyword, page).toList();
}

@Override
public List<Expense> readByDate(Date startDate, Date endDate, Pageable page) {

if (startDate == null) {
startDate = new Date(0);
}

if (endDate == null) {
endDate = new Date(System.currentTimeMillis());
}

return expenseRepo.findByUserIdAndDateBetween(userService.getLoggedInUser().getId(),
startDate, endDate, page).toList();
}
fi
Update the User REST end points

Expense Tracker API


1. Modify the service methods

File: UserService.java
public interface UserService {

...

User readUser();

User updateUser(UserModel user);

void deleteUser();
}
2. Modify the user service implementation class
File: UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {

@Override
public User readUser() {
Long userId = getLoggedInUser().getId();
return userRepository.findById(userId).orElseThrow(() ->
new ResourceNotFoundException("User not found for the id:”+userId));
}

@Override
public User updateUser(UserModel user) {
User existingUser = readUser();
existingUser.setName(user.getName() != null ? user.getName() : existingUser.getName());
existingUser.setEmail(user.getEmail() != null ? user.getEmail() : existingUser.getEmail());
existingUser.setPassword(user.getPassword() != null ?
bcryptEncoder.encode(user.getPassword()) : existingUser.getPassword());
existingUser.setAge(user.getAge() != null ? user.getAge() : existingUser.getAge());
return userRepository.save(existingUser);
}

@Override
public void deleteUser() {
User existingUser = readUser();
userRepository.delete(existingUser);
}

}
3. Update the REST end points
File: UserController.java
@RestController
public class UserController {

@Autowired
private UserService userService;

@GetMapping("/profile")
public ResponseEntity<User> readUser() {
return new ResponseEntity<User>(userService.readUser(), HttpStatus.OK);
}

@PutMapping("/profile")
public ResponseEntity<User> updateUser(@RequestBody UserModel user) {
return new ResponseEntity<User>(userService.updateUser(user), HttpStatus.OK);
}

@DeleteMapping("/deactivate")
public ResponseEntity<HttpStatus> deleteUser() {
userService.deleteUser();
return new ResponseEntity<HttpStatus>(HttpStatus.NO_CONTENT);
}
}
Understand - What is JWT?

Expense Tracker API


Problem with current system?

• It uses session based authentication mechanism to secure the REST APIs

• We need to pass username and password for every HTTP request

JWT
• JWT stands for Json Web Toke

• It is one of the most widely used form of toke

• It is not a language dependen

• The best part of JWT is, it follows stateless authentication mechanism


t

Structure of JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd
WIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4g
RG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.S KxwRJ
SMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Header:
Alogirithm and Token Payload: Data Signature
Type
fl
JWT Control ow Server
1 Email/Password
2 Authenticate

4 Return JWT to client


3 JWT Token
.
.
.

5 Pass JWT token in Header


/expenses
Authorization : Bearer <Token>

6 Pass JWT token in Header


/expenses/6
Authorization : Bearer <Token>
fl
Add JWT to our application

Expense Tracker API


Add JWT dependency to our application
File: pom.xml
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
Create JWT Util class
File: JwtTokenUtil.java

@Component
public class JwtTokenUtil {

private static final long JWT_TOKEN_VALIDITY = 5*60*60;

@Value("${jwt.secret}")
private String secret;

public String generateToken(UserDetails userDetails) {


Map<String, Object> claims = new HashMap<>();
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
...

public boolean validateToken(String jwtToken, UserDetails userDetails) {

final String username = getUsernameFromToken(jwtToken);

return username.equals(userDetails.getUsername()) && !isTokenExpired(jwtToken);

}
}
Create a custom JWT lter
File: JwtRequestFilter.java
public class JwtRequestFilter extends OncePerRequestFilter {

...

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

final String requestTokenHeader = request.getHeader("Authorization");

String jwtToken = null;

String username = null;


// JWT token is in the form of "Bearer <token>". Remove bearer word and get only the token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {

jwtToken = requestTokenHeader.substring(7);

try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Unable to get JWT Token");
//System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
throw new RuntimeException("Jwt Token has expired");
//System.out.println("Jwt Token has expired");
}

...
}
fi
Add the lter to the con g le
File: WebSecurityCon g.java

@Override
protected void configure(HttpSecurity http) throws Exception {

http.csrf().disable()
.authorizeRequests().antMatchers("/login", "/register").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.httpBasic();
}
fi
fi
fi
fi

You might also like