Monday, June 26, 2023

Spring Security - Step4 - Sample Spring Security application - Take User Info from database

Before we have hard coded user details inside the "SecurityConfig" and loaded using "InMemoryUserDetailsManager"
Now let's take those details from database
To do that we need
1. create a entity/model class to map the database table --> UserInfo
2. Repository class --> UserInfoRepository
3. UserController class to save the details of new user
4. UserService class to connect Repository class
5.  UserInfoUserDetails config class which implements UserDetails class for configurations

Below is the code

SecurityConfig

package com.example.springsecurityjavaTechie.config;

import com.example.springsecurityjavaTechie.service.UserInfoUserDetailsService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;


@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

@Bean
UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
// authentication
// UserDetails admin = User.withUsername("Alex")
// .password(passwordEncoder.encode("Pwd1"))
// .roles("ADMIN")
// .build();
// UserDetails user = User.withUsername("John")
// .password(passwordEncoder().encode("Pwd2"))
// .roles("USER")
// .build();
// return new InMemoryUserDetailsManager(admin, user);
return new UserInfoUserDetailsService();
}

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/products/welcome", "/users/new").permitAll()//just let any one access
.and()
.authorizeHttpRequests().requestMatchers("/products/**")
.authenticated().and().formLogin().and().build();

}

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

package com.example.springsecurityjavaTechie.config;

import com.example.springsecurityjavaTechie.model.UserInfo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class UserInfoUserDetails implements UserDetails {

private String name;
private String password;
private List<GrantedAuthority> authorities;

public UserInfoUserDetails(UserInfo userInfo){
this.name = userInfo.getName();
this.password = userInfo.getPassword();
this.authorities = Arrays.stream(userInfo.getRoles().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}

@Override
public String getPassword() {
return password;
}

@Override
public String getUsername() {
return name;
}

@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
}
}

Controllers

package com.example.springsecurityjavaTechie.controller;

import com.example.springsecurityjavaTechie.model.Product;
import com.example.springsecurityjavaTechie.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/products")
public class ProductController {

@Autowired
private ProductService service;

@GetMapping("/welcome")
public String welcome() {
return "Welcome this endpoint is not secure";
}


@GetMapping("/all")
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public List<Product> getAllTheProducts() {
return service.getProducts();
}

@GetMapping("/{id}")
@PreAuthorize("hasAuthority('ROLE_USER')")
public Product getProductById(@PathVariable int id) {
return service.getProduct(id);
}
}
package com.example.springsecurityjavaTechie.controller;

import com.example.springsecurityjavaTechie.model.UserInfo;
import com.example.springsecurityjavaTechie.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/users")
public class UserController {

@Autowired
UserService userService;

@PostMapping("/new")
public String addNewUser(@RequestBody UserInfo userInfo) {
return userService.addUser(userInfo);
}
}

Model

package com.example.springsecurityjavaTechie.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Product {

private int productId;
private String name;
private int qty;
private double price;
}

package com.example.springsecurityjavaTechie.model;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String email;
private String password;
private String roles;
}


Repository

package com.example.springsecurityjavaTechie.repository;


import com.example.springsecurityjavaTechie.model.UserInfo;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserInfoRepository extends JpaRepository<UserInfo, Integer> {
Optional<UserInfo> findByName(String username);

}
Service classes

package com.example.springsecurityjavaTechie.service;

import com.example.springsecurityjavaTechie.model.Product;
import com.example.springsecurityjavaTechie.model.UserInfo;
import com.example.springsecurityjavaTechie.repository.UserInfoRepository;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class ProductService {

List<Product> productList = null;

// @Autowired
// private UserInfoRepository repository;
//
// @Autowired
// private PasswordEncoder passwordEncoder;

@PostConstruct
public void loadProductsFromDB() {
productList = IntStream.rangeClosed(1, 100)
.mapToObj(i -> Product.builder()
.productId(i)
.name("product " + i)
.qty(new Random().nextInt(10))
.price(new Random().nextInt(5000)).build()
).collect(Collectors.toList());
}


public List<Product> getProducts() {
return productList;
}

public Product getProduct(int id) {
return productList.stream()
.filter(product -> product.getProductId() == id)
.findAny()
.orElseThrow(() -> new RuntimeException("product " + id + " not found"));
}



}

package com.example.springsecurityjavaTechie.service;

import com.example.springsecurityjavaTechie.config.UserInfoUserDetails;
import com.example.springsecurityjavaTechie.model.UserInfo;
import com.example.springsecurityjavaTechie.repository.UserInfoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.Optional;

public class UserInfoUserDetailsService implements UserDetailsService {

@Autowired
UserInfoRepository userInfoRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<UserInfo> userInfo = userInfoRepository.findByName(username);
return userInfo.map(UserInfoUserDetails::new)
.orElseThrow(()-> new UsernameNotFoundException("user not found "+username));
}
}

package com.example.springsecurityjavaTechie.service;

import com.example.springsecurityjavaTechie.model.UserInfo;
import com.example.springsecurityjavaTechie.repository.UserInfoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class UserService {

@Autowired
PasswordEncoder passwordEncoder;

@Autowired
UserInfoRepository userInfoRepository;

public String addUser(UserInfo userInfo) {
userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword()));
userInfoRepository.save(userInfo);
return "user added to system ";
}
}

Main class

package com.example.springsecurityjavaTechie;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringSecurityJavaTechieApplication {

public static void main(String[] args) {
SpringApplication.run(SpringSecurityJavaTechieApplication.class, args);
}

}

References

No comments:

Post a Comment