Skip to content

Commit 0e4cd59

Browse files
authored
Merge pull request #3 from manirDev/spring-security
Spring security
2 parents ef48c93 + 425ced4 commit 0e4cd59

17 files changed

+229
-30
lines changed

pom.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@
4848
<artifactId>modelmapper</artifactId>
4949
<version>3.1.0</version>
5050
</dependency>
51-
51+
<dependency>
52+
<groupId>org.springframework.boot</groupId>
53+
<artifactId>spring-boot-starter-security</artifactId>
54+
</dependency>
5255
<dependency>
5356
<groupId>org.springframework.boot</groupId>
5457
<artifactId>spring-boot-starter-test</artifactId>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.manir.springbootecommercerestapi.config;
2+
3+
import com.manir.springbootecommercerestapi.security.CustomUserDetailsService;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.http.HttpMethod;
8+
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
9+
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
10+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
11+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
12+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
13+
import org.springframework.security.core.userdetails.User;
14+
import org.springframework.security.core.userdetails.UserDetails;
15+
import org.springframework.security.core.userdetails.UserDetailsService;
16+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
17+
import org.springframework.security.crypto.password.PasswordEncoder;
18+
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
19+
20+
import javax.annotation.Resource;
21+
22+
@Configuration
23+
@EnableWebSecurity
24+
/***
25+
global security is used for enable security at method level for example permitting get methods
26+
Ex: PreAuthorize("hasRole('ADMIN')")
27+
***/
28+
@EnableGlobalMethodSecurity(prePostEnabled = true)
29+
public class SecurityConfig extends WebSecurityConfigurerAdapter {
30+
31+
@Autowired
32+
private CustomUserDetailsService customUserDetailsService;
33+
34+
@Override
35+
protected void configure(HttpSecurity http) throws Exception {
36+
http
37+
.csrf().disable()
38+
.authorizeRequests()
39+
//to permit all get request and secure post put and delete methods
40+
.antMatchers(HttpMethod.GET, "/api/**").permitAll()
41+
.anyRequest()
42+
.authenticated()
43+
.and()
44+
.httpBasic();
45+
46+
}
47+
48+
//In memory Auth
49+
/**
50+
@Override
51+
@Bean
52+
protected UserDetailsService userDetailsService() {
53+
UserDetails user = User.builder().username("customer").password(passwordEncoder().encode("customer")).roles("USER").build();
54+
UserDetails admin = User.builder().username("admin").password(passwordEncoder().encode("admin")).roles("ADMIN").build();
55+
56+
return new InMemoryUserDetailsManager(user, admin);
57+
}
58+
**/
59+
60+
//DB Auth
61+
@Override
62+
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
63+
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
64+
}
65+
66+
@Bean
67+
PasswordEncoder passwordEncoder(){
68+
return new BCryptPasswordEncoder();
69+
}
70+
}

src/main/java/com/manir/springbootecommercerestapi/controller/CategoryController.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.springframework.beans.factory.annotation.Autowired;
88
import org.springframework.http.HttpStatus;
99
import org.springframework.http.ResponseEntity;
10+
import org.springframework.security.access.prepost.PreAuthorize;
1011
import org.springframework.web.bind.annotation.*;
1112

1213

@@ -18,6 +19,7 @@ public class CategoryController {
1819
private CategoryService categoryService;
1920

2021
//create category api
22+
@PreAuthorize("hasRole('ADMIN')")
2123
@PostMapping("/createCategory")
2224
public ResponseEntity<CategoryDto> createCategory(@RequestBody CategoryDto categoryDto){
2325
CategoryDto responseCategory = categoryService.createCategory(categoryDto);
@@ -42,6 +44,7 @@ public ResponseEntity<CategoryDto> getCatecoryById(@PathVariable Long categoryId
4244
}
4345

4446
//update category api
47+
@PreAuthorize("hasRole('ADMIN')")
4548
@PutMapping("/updateCategory/{categoryId}")
4649
public ResponseEntity<CategoryDto> updateCategory(@RequestBody CategoryDto categoryDto,
4750
@PathVariable Long categoryId){
@@ -50,6 +53,7 @@ public ResponseEntity<CategoryDto> updateCategory(@RequestBody CategoryDto categ
5053
}
5154

5255
//delete category api
56+
@PreAuthorize("hasRole('ADMIN')")
5357
@DeleteMapping("/deleteCategory/{categoryId}")
5458
public ResponseEntity<String> deleteCategory(@PathVariable Long categoryId){
5559
categoryService.deleteCategory(categoryId);

src/main/java/com/manir/springbootecommercerestapi/controller/ProductController.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.springframework.http.HttpStatus;
1010
import org.springframework.http.MediaType;
1111
import org.springframework.http.ResponseEntity;
12+
import org.springframework.security.access.prepost.PreAuthorize;
1213
import org.springframework.web.bind.annotation.*;
1314
import org.springframework.web.multipart.MultipartFile;
1415

@@ -22,6 +23,7 @@ public class ProductController {
2223
private ProductService productService;
2324

2425
//product create api
26+
@PreAuthorize("hasRole('ADMIN')")
2527
@RequestMapping(value = "/createProduct", method = RequestMethod.POST, consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE})
2628
public ResponseEntity<ProductDto> createProduct(@RequestPart("productDto") ProductDto productDto,
2729
@RequestPart("file") MultipartFile file){
@@ -30,6 +32,7 @@ public ResponseEntity<ProductDto> createProduct(@RequestPart("productDto") Produ
3032
}
3133

3234
//create product with category
35+
@PreAuthorize("hasRole('ADMIN')")
3336
@PostMapping("/{categoryId}/saveProductByCategoryId")
3437
public ResponseEntity<ProductDto> saveProductByCategoryId(@PathVariable Long categoryId,
3538
@RequestBody ProductDto productDto){
@@ -57,6 +60,7 @@ public ResponseEntity<ProductDto> getProductById(@PathVariable Long productId){
5760
}
5861

5962
//update product api
63+
@PreAuthorize("hasRole('ADMIN')")
6064
@PutMapping("/{categoryId}/updateProduct/{productId}")
6165
public ResponseEntity<ProductDto> updateProduct(@PathVariable Long categoryId,
6266
@RequestBody ProductDto productDto,
@@ -66,6 +70,7 @@ public ResponseEntity<ProductDto> updateProduct(@PathVariable Long categoryId,
6670
}
6771

6872
//delete product api
73+
@PreAuthorize("hasRole('ADMIN')")
6974
@DeleteMapping("/deleteProduct/{productId}")
7075
public ResponseEntity<String> deleteProduct(@PathVariable Long productId){
7176
productService.deleteProduct(productId);

src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import com.manir.springbootecommercerestapi.dto.CartItemDto;
44
import com.manir.springbootecommercerestapi.response.CartItemResponse;
55
import com.manir.springbootecommercerestapi.service.ShoppingCartService;
6+
import com.manir.springbootecommercerestapi.utils.isAuthenticatedAsAdminOrUser;
67
import org.springframework.http.HttpStatus;
78
import org.springframework.http.ResponseEntity;
9+
import org.springframework.security.access.prepost.PreAuthorize;
810
import org.springframework.web.bind.annotation.*;
911

1012
import javax.annotation.Resource;
@@ -18,6 +20,7 @@ public class ShoppingCartController {
1820
private ShoppingCartService shoppingCartService;
1921

2022
//find by customer api
23+
@isAuthenticatedAsAdminOrUser
2124
@GetMapping("/findByCustomer/{customerId}")
2225
public CartItemResponse findByCustomerId(@PathVariable Long customerId){
2326
CartItemResponse responseCartItems = shoppingCartService.findByCustomerId(customerId);
@@ -26,6 +29,7 @@ public CartItemResponse findByCustomerId(@PathVariable Long customerId){
2629
}
2730

2831
//add item to the cart api
32+
@isAuthenticatedAsAdminOrUser
2933
@PostMapping("/addItem/{customerId}/{productId}/{quantity}")
3034
public ResponseEntity<CartItemResponse> addCartItem(@PathVariable Long customerId,
3135
@PathVariable Long productId,
@@ -36,6 +40,7 @@ public ResponseEntity<CartItemResponse> addCartItem(@PathVariable Long customerI
3640
}
3741

3842
//update item quantity api
43+
@isAuthenticatedAsAdminOrUser
3944
@PutMapping("/updateItemQuantity/{customerId}/{productId}/{quantity}")
4045
public ResponseEntity<CartItemResponse> updateItemQuantity(@PathVariable Long customerId,
4146
@PathVariable Long productId,
@@ -47,6 +52,7 @@ public ResponseEntity<CartItemResponse> updateItemQuantity(@PathVariable Long cu
4752
}
4853

4954
//delete item product api
55+
@isAuthenticatedAsAdminOrUser
5056
@DeleteMapping("/deleteItemProduct/{customerId}/{productId}")
5157
public ResponseEntity<String> deleteItemProduct(@PathVariable Long customerId, @PathVariable Long productId){
5258
shoppingCartService.deleteItemProduct(customerId, productId);

src/main/java/com/manir/springbootecommercerestapi/model/CartItem.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package com.manir.springbootecommercerestapi.model;
22

3-
import com.fasterxml.jackson.annotation.JsonIgnore;
43
import lombok.AllArgsConstructor;
54
import lombok.Data;
65
import lombok.NoArgsConstructor;
76

87
import javax.persistence.*;
9-
import java.util.Set;
108

119
@AllArgsConstructor
1210
@NoArgsConstructor
@@ -27,9 +25,9 @@ public class CartItem {
2725
@JoinColumn(name = "product_id")
2826
private Product product;
2927

30-
//relation with user
28+
//relation with customer
3129
@ManyToOne()
3230
@JoinColumn(name = "customer_id")
33-
private Customer customer;
31+
private User customer;
3432

3533
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.manir.springbootecommercerestapi.model;
2+
3+
import lombok.Data;
4+
5+
import javax.persistence.*;
6+
7+
@Data
8+
@Entity
9+
@Table(name = "roles")
10+
public class Role {
11+
12+
@Id
13+
@GeneratedValue(strategy = GenerationType.IDENTITY)
14+
private Long id;
15+
@Column(length = 60)
16+
private String name;
17+
}

src/main/java/com/manir/springbootecommercerestapi/model/Customer.java renamed to src/main/java/com/manir/springbootecommercerestapi/model/User.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
package com.manir.springbootecommercerestapi.model;
22

3-
import com.fasterxml.jackson.annotation.JsonIgnore;
43
import lombok.Data;
54

65
import javax.persistence.*;
76
import java.util.Set;
87

98
@Data
109
@Entity
11-
@Table(name = "customers", uniqueConstraints = {@UniqueConstraint(columnNames = {"userName"}),
10+
@Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"userName"}),
1211
@UniqueConstraint(columnNames = {"email"})
1312
})
14-
public class Customer {
13+
public class User {
1514
@Id
1615
@GeneratedValue(strategy = GenerationType.IDENTITY)
1716
private Long id;
@@ -25,4 +24,11 @@ public class Customer {
2524
fetch = FetchType.LAZY, orphanRemoval = true,
2625
mappedBy = "customer")
2726
private Set<CartItem> cartItems;
28-
}
27+
28+
//relation with role
29+
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
30+
@JoinTable(name = "user_roles",
31+
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
32+
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
33+
private Set<Role> roles;
34+
}

src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public interface CartItemRepository extends JpaRepository<CartItem, Long> {
1111

1212
List<CartItem> findByCustomerId(Long customerId);
1313

14-
//CartItem findByCustomerAndProduct(Customer customer, Product product);
14+
//CartItem findByCustomerAndProduct(User customer, Product product);
1515
CartItem findByCustomerIdAndProductId(Long customerId, Long productId);
1616

1717
@Query("UPDATE CartItem c SET c.quantity = ?3 WHERE c.product.id = ?2 AND c.customer.id = ?1")

src/main/java/com/manir/springbootecommercerestapi/repository/CustomerRepository.java

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.manir.springbootecommercerestapi.repository;
2+
3+
import com.manir.springbootecommercerestapi.model.Role;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
import java.util.Optional;
7+
8+
public interface RoleRepository extends JpaRepository<Role, Long> {
9+
Optional<Role> findByName(String name);
10+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.manir.springbootecommercerestapi.repository;
2+
3+
import com.manir.springbootecommercerestapi.model.User;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
import java.util.Optional;
7+
8+
public interface UserRepository extends JpaRepository<User, Long>{
9+
10+
Optional<User> findByEmail(String email);
11+
Optional<User> findByUserNameOrEmail(String username, String email);
12+
Optional<User> findByUserName(String username);
13+
Boolean existsByUserName(String username);
14+
Boolean existsByEmail(String email);
15+
16+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.manir.springbootecommercerestapi.security;
2+
3+
import com.manir.springbootecommercerestapi.model.Role;
4+
import com.manir.springbootecommercerestapi.model.User;
5+
import com.manir.springbootecommercerestapi.repository.UserRepository;
6+
import lombok.AllArgsConstructor;
7+
import org.springframework.beans.factory.annotation.Autowired;
8+
import org.springframework.security.core.GrantedAuthority;
9+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
10+
import org.springframework.security.core.userdetails.UserDetails;
11+
import org.springframework.security.core.userdetails.UserDetailsService;
12+
import org.springframework.security.core.userdetails.UsernameNotFoundException;
13+
import org.springframework.stereotype.Service;
14+
15+
import javax.annotation.Resource;
16+
import java.util.Collection;
17+
import java.util.Set;
18+
import java.util.stream.Collectors;
19+
20+
@Service
21+
@AllArgsConstructor
22+
public class CustomUserDetailsService implements UserDetailsService {
23+
private final UserRepository userRepository;
24+
25+
@Override
26+
public UserDetails loadUserByUsername(String userNameOrEmail) throws UsernameNotFoundException {
27+
User user = userRepository.findByUserNameOrEmail(userNameOrEmail, userNameOrEmail)
28+
.orElseThrow(
29+
() -> new UsernameNotFoundException("User not found with username or email: " + userNameOrEmail)
30+
);
31+
return new org.springframework.security.core.userdetails.User(
32+
user.getEmail(),
33+
user.getPassword(),
34+
mapRolesToAuthorities(user.getRoles())
35+
);
36+
}
37+
38+
private Collection<? extends GrantedAuthority> mapRolesToAuthorities(Set<Role> roles){
39+
return roles.stream()
40+
.map(role -> new SimpleGrantedAuthority(role.getName()))
41+
.collect(Collectors.toList());
42+
}
43+
}

0 commit comments

Comments
 (0)