Skip to content

Commit bc409cc

Browse files
Added JWT security and Swagger for documentation
1 parent 455717a commit bc409cc

16 files changed

+385
-12
lines changed

build.gradle

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
plugins {
2-
id 'org.springframework.boot' version '2.6.5'
2+
id 'org.springframework.boot' version '2.5.5'
33
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
44
id 'java'
55
}
66

7-
group = 'com.example.library'
8-
version = '0.0.1-SNAPSHOT'
7+
group = 'com.example'
8+
version = '1.0'
99
sourceCompatibility = '11'
1010

1111
repositories {
@@ -15,8 +15,12 @@ repositories {
1515
dependencies {
1616
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
1717
implementation 'org.springframework.boot:spring-boot-starter-web'
18+
implementation 'io.springfox:springfox-swagger2:2.9.2'
19+
implementation 'io.springfox:springfox-swagger-ui:2.9.2'
20+
implementation 'org.springframework.boot:spring-boot-starter-security'
1821
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
1922

23+
implementation 'io.jsonwebtoken:jjwt:0.9.1'
2024
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
2125

2226
runtimeOnly'com.microsoft.sqlserver:mssql-jdbc'
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.example.library.LibraryAPI.domain.dto;
2+
3+
public class AuthenticationRequest {
4+
private String username;
5+
private String password;
6+
7+
public String getUsername() {
8+
return username;
9+
}
10+
11+
public void setUsername(String username) {
12+
this.username = username;
13+
}
14+
15+
public String getPassword() {
16+
return password;
17+
}
18+
19+
public void setPassword(String password) {
20+
this.password = password;
21+
}
22+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.example.library.LibraryAPI.domain.dto;
2+
3+
public class AuthenticationResponse {
4+
5+
private String jwt;
6+
7+
public AuthenticationResponse(String jwt){
8+
this.jwt = jwt;
9+
}
10+
11+
public String getJwt() {
12+
return jwt;
13+
}
14+
15+
public void setJwt(String jwt) {
16+
this.jwt = jwt;
17+
}
18+
}

src/main/java/com/example/library/LibraryAPI/domain/repository/BookRepository.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ public interface BookRepository {
1010
List<Book>getAll();
1111
Optional<Book> getBook(int bookId);
1212
Book save (Book book);
13-
void delete(int bookId);
13+
Optional<List<Book > >findBookByAuthorName(String name);
14+
Optional<List<Book>> findBookByAuthorSurname(String surname);
15+
void delete(int bookId);
1416
}

src/main/java/com/example/library/LibraryAPI/domain/service/BookService.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,10 @@ public boolean delete(int bookId){
3131
return true;
3232
}).orElse(false);
3333
}
34-
34+
public Optional<List<Book>> findBookByAuthorName(String name){
35+
return bookRepository.findBookByAuthorName(name);
36+
}
37+
public Optional<List<Book>> findBookByAuthorSurname(String surname){
38+
return bookRepository.findBookByAuthorSurname(surname);
39+
}
3540
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.example.library.LibraryAPI.domain.service;
2+
3+
import org.springframework.security.core.userdetails.*;
4+
import org.springframework.stereotype.*;
5+
6+
import java.util.*;
7+
@Service
8+
@Component
9+
public class LibraryUserDetailService implements UserDetailsService {
10+
11+
@Override
12+
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
13+
return new User("Test", "{noop}Test", new ArrayList<>());
14+
}
15+
}

src/main/java/com/example/library/LibraryAPI/persistence/BookRepository.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ public Book save(Book book) {
3131
return bookCrudRepository.save(book);
3232
}
3333

34+
@Override
35+
public Optional<List<Book>> findBookByAuthorName(String name) {
36+
List<Book> books = bookCrudRepository.findBookByAuthorName(name);
37+
return Optional.of(books);
38+
}
39+
40+
41+
@Override
42+
public Optional<List<Book>> findBookByAuthorSurname(String surname) {
43+
List<Book> books = bookCrudRepository.findBookByAuthorSurname(surname);
44+
return Optional.of(books);
45+
}
46+
3447
@Override
3548
public void delete(int bookId) {
3649
bookCrudRepository.deleteById(bookId);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.example.library.LibraryAPI.persistence.crud;
2+
3+
import com.example.library.LibraryAPI.persistence.entity.*;
4+
import org.springframework.data.repository.*;
5+
6+
public interface AuthorCrudRepository extends CrudRepository<Author, Integer> {
7+
8+
}
Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
package com.example.library.LibraryAPI.persistence.crud;
22

33
import com.example.library.LibraryAPI.persistence.entity.*;
4+
import org.springframework.data.jpa.repository.*;
45
import org.springframework.data.repository.*;
56

7+
import java.util.*;
8+
69

710
public interface BookCrudRepository extends CrudRepository<Book, Integer> {
8-
}
11+
@Query(value = "SELECT dbo.books.book_id, dbo.books.name" +
12+
", dbo.authors.name AS 'Author', dbo.authors.surname as 'Surname' FROM dbo.books INNER JOIN dbo.authors ON dbo.books.author_id = dbo.authors.author_id where authors.name = '?'", nativeQuery = true)
13+
List<Book> findBookByAuthorName(String name);
14+
15+
16+
17+
@Query(value = "SELECT dbo.books.book_id, dbo.books.name, dbo.authors.name AS 'Author', dbo.authors.surname as 'Surname' FROM dbo.books INNER JOIN dbo.authors ON dbo.books.author_id = dbo.authors.author_id WHERE authors.surname = '?'", nativeQuery = true)
18+
List<Book> findBookByAuthorSurname(String surname);
19+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.example.library.LibraryAPI.web.config;
2+
3+
import org.springframework.context.annotation.*;
4+
import springfox.documentation.builders.*;
5+
import springfox.documentation.service.*;
6+
import springfox.documentation.spi.*;
7+
import springfox.documentation.spring.web.plugins.*;
8+
import springfox.documentation.swagger2.annotations.*;
9+
10+
import java.util.*;
11+
12+
@Configuration //Para habilitar swagger
13+
@EnableSwagger2
14+
public class SwaggerConfig {
15+
private ApiInfo apiInfo() {
16+
return new ApiInfo(
17+
"My REST API for a library",
18+
"Rest API for a library using Spring Boot",
19+
"API 1",
20+
"Terms of service",
21+
new Contact("BrunoMazzocchi", "www.github.com/BrunoMazzocchi", "none"),
22+
"License of API",
23+
"API license URL",
24+
Collections.emptyList());
25+
}
26+
27+
@Bean //
28+
public Docket api(){
29+
return new Docket(DocumentationType.SWAGGER_2)
30+
.select()
31+
.apis(RequestHandlerSelectors.basePackage("com.example.library.LibraryAPI.web.config.controller"))
32+
.build();
33+
}
34+
35+
36+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.example.library.LibraryAPI.web.config.controller;
2+
3+
4+
import com.example.library.LibraryAPI.domain.dto.*;
5+
import com.example.library.LibraryAPI.domain.service.*;
6+
import com.example.library.LibraryAPI.web.security.*;
7+
import org.springframework.beans.factory.annotation.*;
8+
import org.springframework.http.*;
9+
import org.springframework.security.authentication.*;
10+
import org.springframework.security.core.userdetails.*;
11+
import org.springframework.web.bind.annotation.*;
12+
import springfox.documentation.annotations.*;
13+
14+
@RestController
15+
@RequestMapping("/auth")
16+
@ApiIgnore
17+
public class AuthController {
18+
19+
@Autowired
20+
private AuthenticationManager authenticationmanager;
21+
22+
@Autowired
23+
LibraryUserDetailService libraryUserDetailService;
24+
25+
@Autowired
26+
private JWTUtil jwtUtil;
27+
@PostMapping("/authenticate")
28+
public ResponseEntity<AuthenticationResponse> createToken(@RequestBody AuthenticationRequest request){
29+
try {
30+
authenticationmanager.authenticate(new UsernamePasswordAuthenticationToken(request.getPassword(), request.getUsername()));
31+
32+
//Envia los datos al JWT generator
33+
UserDetails userDetails = libraryUserDetailService.loadUserByUsername(request.getUsername());
34+
String jwt = jwtUtil.generateToken(userDetails);
35+
36+
return new ResponseEntity<>(new AuthenticationResponse(jwt), HttpStatus.OK);
37+
} catch (BadCredentialsException e) {
38+
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
39+
}
40+
}
41+
}

src/main/java/com/example/library/LibraryAPI/web/config/controller/BookController.java

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.example.library.LibraryAPI.domain.service.*;
44
import com.example.library.LibraryAPI.persistence.entity.*;
5+
import io.swagger.annotations.*;
56
import org.springframework.beans.factory.annotation.*;
67
import org.springframework.http.*;
78
import org.springframework.web.bind.annotation.*;
@@ -16,24 +17,62 @@ public class BookController {
1617
private BookService bookService;
1718

1819
@GetMapping("/all")
20+
@ApiOperation("Gets all books")
1921
public ResponseEntity<List<Book>> getAll(){
2022
return new ResponseEntity<>(bookService.getAll(), HttpStatus.OK);
2123
}
2224

25+
26+
@ApiOperation("Search a specific book with an ID")
2327
@GetMapping("/{id}")
24-
public ResponseEntity<Book>getBook(@PathVariable("id") int bookId){
28+
@ApiResponses({
29+
@ApiResponse(code = 200, message = "OK"),
30+
@ApiResponse(code = 404, message = "Not found")
31+
})
32+
public ResponseEntity<Book>getBook(@ApiParam(value = "Book's ID", required = true, example = "1")@PathVariable("id") int bookId){
2533
return bookService.getBook(bookId)
2634
.map(book -> new ResponseEntity<>(book, HttpStatus.OK))
2735
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
2836
}
2937

38+
@ApiOperation("Search a book with the author's name or surname (not working right now)")
39+
@GetMapping("/author/{nameOrSurname}")
40+
@ApiResponses({
41+
@ApiResponse(code = 200, message = "OK"),
42+
@ApiResponse(code = 404, message = "Not found")
43+
})
44+
public ResponseEntity<List<Book>> findBookByAuthorNameOrSurname(@ApiParam(value = "Author's name/surname (String)", required = true, example = "1")@PathVariable String nameOrSurname){
45+
return bookService.findBookByAuthorName(nameOrSurname).map(
46+
books -> new ResponseEntity<>(books, HttpStatus.OK)
47+
48+
).orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
49+
}
50+
51+
@ApiOperation("Saves a book with the information required")
52+
@ApiResponses({
53+
@ApiResponse(code = 200, message = "OK"),
54+
@ApiResponse(code = 404, message = "Not found")
55+
})
3056
@PostMapping("/save")
31-
public ResponseEntity<Book> save(@RequestBody Book book){
57+
public ResponseEntity<Book> save(@ApiParam(value = "The book entity", required = true, example = "" +
58+
"{\n" +
59+
" \"name\": 0,\n" +
60+
" \"pageCount\": {\n" +
61+
" \"point\": 0,\n" +
62+
" \"authorId\": \"string\",\n" +
63+
" \"typeId\": 0\n" +
64+
" }" +
65+
"") @RequestBody Book book){
3266
return new ResponseEntity<>(bookService.save(book), HttpStatus.CREATED);
3367
}
3468

69+
@ApiOperation("Deletes a product with the information required")
70+
@ApiResponses({
71+
@ApiResponse(code = 200, message = "OK"),
72+
@ApiResponse(code = 404, message = "Not found")
73+
})
3574
@DeleteMapping("/delete/{id}")
36-
public ResponseEntity delete(@PathVariable("id") int bookId){
75+
public ResponseEntity delete(@ApiParam(value = "The book's ID")@PathVariable("id") int bookId){
3776
if(bookService.delete(bookId)) {
3877
return new ResponseEntity(HttpStatus.OK);
3978
} else {

src/main/java/com/example/library/LibraryAPI/web/config/controller/BorrowController.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.example.library.LibraryAPI.domain.service.*;
44
import com.example.library.LibraryAPI.persistence.entity.*;
5+
import io.swagger.annotations.*;
56
import org.springframework.beans.factory.annotation.*;
67
import org.springframework.http.*;
78
import org.springframework.web.bind.annotation.*;
@@ -16,26 +17,53 @@ public class BorrowController {
1617
private BorrowService borrowService;
1718

1819
@GetMapping("/all")
20+
@ApiOperation("Gets all the borrows registered")
1921
public ResponseEntity<List<Borrow>> getAll(){
2022
return new ResponseEntity<>(borrowService.getAll(), HttpStatus.OK);
2123
}
2224

25+
@ApiOperation("Search a specifics borrow with a student ID")
2326
@GetMapping("/student/{studentId}")
24-
public ResponseEntity<List<Borrow>> getBorrowByStudentId(@PathVariable int studentId){
27+
@ApiResponses({
28+
@ApiResponse(code = 200, message = "OK"),
29+
@ApiResponse(code = 404, message = "Not found")
30+
})
31+
public ResponseEntity<List<Borrow>> getBorrowByStudentId(@ApiParam(value = "Student's ID", required = true, example = "1")@PathVariable int studentId){
2532
return borrowService.getBorrowByStudentId(studentId).map(
2633
borrows -> new ResponseEntity<>(borrows, HttpStatus.OK)
2734

2835
).orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
2936
}
37+
38+
39+
@ApiOperation("Search a specifics borrow with a book ID")
3040
@GetMapping("/book/{bookId}")
31-
public ResponseEntity<List<Borrow>> getBorrowByBookId(@PathVariable int bookId){
41+
@ApiResponses({
42+
@ApiResponse(code = 200, message = "OK"),
43+
@ApiResponse(code = 404, message = "Not found")
44+
})
45+
public ResponseEntity<List<Borrow>> getBorrowByBookId(@ApiParam(value = "Book's ID", required = true, example = "1")@PathVariable int bookId){
3246
return borrowService.getBorrowByBookId(bookId).map(
3347
borrows -> new ResponseEntity<>(borrows, HttpStatus.OK)
3448

3549
).orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
3650
}
51+
52+
@ApiOperation("Saves a borrow with the information required")
53+
@ApiResponses({
54+
@ApiResponse(code = 200, message = "OK"),
55+
@ApiResponse(code = 404, message = "Not found")
56+
})
3757
@PostMapping("/save")
38-
public ResponseEntity<Borrow> save(@RequestBody Borrow borrow){
58+
public ResponseEntity<Borrow> save(@ApiParam(value = "The book entity", required = true, example = "" +
59+
"{\n" +
60+
" \"borrowId\": 0,\n" +
61+
" \"studentId\": {\n" +
62+
" \"bookId\": 0,\n" +
63+
" \"takenDate\": \"string\",\n" +
64+
" \"broughtDate\": 0\n" +
65+
" }" +
66+
"")@RequestBody Borrow borrow){
3967
return new ResponseEntity<>(borrowService.save(borrow), HttpStatus.CREATED);
4068
}
4169
}

0 commit comments

Comments
 (0)