Skip to content

Commit

Permalink
draft
Browse files Browse the repository at this point in the history
  • Loading branch information
mkyong committed Feb 22, 2019
1 parent 97b26ad commit 5147569
Show file tree
Hide file tree
Showing 13 changed files with 583 additions and 0 deletions.
102 changes: 102 additions & 0 deletions spring-rest-security/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-rest-security</artifactId>
<packaging>jar</packaging>
<name>Spring Boot REST API Example</name>
<version>1.0</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
</parent>

<!-- Java 8 -->
<properties>
<java.version>1.8</java.version>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</properties>

<dependencies>

<!-- spring mvc, rest -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

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

<!-- spring security test -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>

<!-- jpa, crud repository -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- in-memory database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

<!-- unit test rest -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- test patch operation need this -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.7</version>
<scope>test</scope>
</dependency>

<!-- hot swapping, disable cache for template, enable live reload -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>

</dependencies>

<build>
<plugins>

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<addResources>true</addResources>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>

</plugins>

</build>
</project>
82 changes: 82 additions & 0 deletions spring-rest-security/src/main/java/com/mkyong/Book.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.mkyong;

import com.mkyong.error.validator.Author;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;

@Entity
public class Book {

@Id
@GeneratedValue
private Long id;

@NotEmpty(message = "Please provide a name")
private String name;

@Author
@NotEmpty(message = "Please provide a author")
private String author;

@NotNull(message = "Please provide a price")
@DecimalMin("1.00")
private BigDecimal price;

// avoid this "No default constructor for entity"
public Book() {
}

public Book(String name, String author, BigDecimal price) {
this.name = name;
this.author = author;
this.price = price;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
87 changes: 87 additions & 0 deletions spring-rest-security/src/main/java/com/mkyong/BookController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.mkyong;

import com.mkyong.error.BookNotFoundException;
import com.mkyong.error.BookUnSupportedFieldPatchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.util.List;
import java.util.Map;

@RestController
@Validated
public class BookController {

@Autowired
private BookRepository repository;

// Find
@GetMapping("/books")
List<Book> findAll() {
return repository.findAll();
}

// Save
@PostMapping("/books")
Book newBook(@Valid @RequestBody Book newBook) {
return repository.save(newBook);
}

// Find
@GetMapping("/books/{id}")
Book findOne(@PathVariable @Min(1) Long id) {
return repository.findById(id)
.orElseThrow(() -> new BookNotFoundException(id));
}

// Save or update
@PutMapping("/books/{id}")
Book saveOrUpdate(@RequestBody Book newBook, @PathVariable Long id) {

return repository.findById(id)
.map(x -> {
x.setName(newBook.getName());
x.setAuthor(newBook.getAuthor());
x.setPrice(newBook.getPrice());
return repository.save(x);
})
.orElseGet(() -> {
newBook.setId(id);
return repository.save(newBook);
});
}

// update author only
@PatchMapping("/books/{id}")
Book patch(@RequestBody Map<String, String> update, @PathVariable Long id) {

return repository.findById(id)
.map(x -> {

String author = update.get("author");
if (!StringUtils.isEmpty(author)) {
x.setAuthor(author);

// better create a custom method to update a value = :newValue where id = :id
return repository.save(x);
} else {
throw new BookUnSupportedFieldPatchException(update.keySet());
}

})
.orElseGet(() -> {
throw new BookNotFoundException(id);
});

}

@DeleteMapping("/books/{id}")
void deleteBook(@PathVariable Long id) {
repository.deleteById(id);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.mkyong;

import org.springframework.data.jpa.repository.JpaRepository;

public interface BookRepository extends JpaRepository<Book, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.mkyong;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.math.BigDecimal;

@SpringBootApplication
public class StartBookApplication {

// start everything
public static void main(String[] args) {
SpringApplication.run(StartBookApplication.class, args);
}

@Bean
CommandLineRunner initDatabase(BookRepository repository) {
return args -> {
repository.save(new Book("A Guide to the Bodhisattva Way of Life", "Santideva", new BigDecimal("15.41")));
repository.save(new Book("The Life-Changing Magic of Tidying Up", "Marie Kondo", new BigDecimal("9.69")));
repository.save(new Book("Refactoring: Improving the Design of Existing Code", "Martin Fowler", new BigDecimal("47.99")));
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.mkyong.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

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

/*auth.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER")
.and()
.withUser("admin").password("{noop}password").roles("ADMIN");*/

}

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

http.httpBasic().and().authorizeRequests().
antMatchers(HttpMethod.GET, "/books/**").hasRole("USER").
antMatchers(HttpMethod.POST, "/books").hasRole("ADMIN"). //save
antMatchers(HttpMethod.PUT, "/books/**").hasRole("ADMIN"). //update
antMatchers(HttpMethod.PATCH, "/books/**").hasRole("ADMIN").//update
antMatchers(HttpMethod.DELETE, "/books/**").hasRole("ADMIN"). //delete*/
and().
csrf().disable();

/*http
.authorizeRequests()
.antMatchers("/books").hasRole("USERS")
.antMatchers("/books/**").hasRole("ADMIN")
.and()
.httpBasic()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);*/

}

@Bean
public UserDetailsService userDetailsService() {
// for testing only
User.UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("user").password("password").roles("USER").build());
manager.createUser(users.username("admin").password("password").roles("USER", "ADMIN").build());
return manager;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mkyong.error;

public class BookNotFoundException extends RuntimeException {

public BookNotFoundException(Long id) {
super("Book id not found : " + id);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.mkyong.error;

import java.util.Set;

public class BookUnSupportedFieldPatchException extends RuntimeException {

public BookUnSupportedFieldPatchException(Set<String> keys) {
super("Field " + keys.toString() + " update is not allow.");
}

}
Loading

0 comments on commit 5147569

Please sign in to comment.