I have just started studying Springboot. Everything worked fine until I run into this problem. I've searched every StackOverFlow topic and internet overall and none resolved my problems. I tried to set Content-Type and Accepts the right way but it still didn't work.
UserController:
package com.example.carnet.api;
import com.example.carnet.model.User;
import com.example.carnet.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
#RequestMapping("/carnet/user")
public class UserController {
private final UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping("/all")
public List<User> getUsers() {
return userService.getUsers();
}
#GetMapping(path = "/{email}")
public User getUserByEmail(#PathVariable("email") String email) {
return userService.getUserByEmail(email);
}
#GetMapping("/validate")
public boolean validateUser(#RequestParam("email") String email, #RequestParam("password") String password) {
return userService.validateUser(email, password);
}
#PostMapping("/add")
public void addUser(#RequestBody User user) {
userService.addUser(user);
}
#DeleteMapping(path = "/{id}")
public void deleteUserById(#PathVariable("id") int id) {
userService.deleteUserById(id);
}
#PutMapping
public void updatePassword(#RequestBody User user) {
userService.updatePassword(user);
}
}
User Model:
package com.example.carnet.model;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.sql.Date;
import java.util.List;
#Table(name = "users")
#Entity
public class User {
#Id
private int user_id;
private String email;
private String password;
private String name;
private String surname;
private Date birth_date;
#OneToMany(mappedBy = "user")
#JsonManagedReference
private List<Rental> rentals;
public User() {
}
public int getUser_id() {
return user_id;
}
public void setUser_id(int user_id) {
this.user_id = user_id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public Date getBirth_date() {
return birth_date;
}
public void setBirth_date(Date birth_date) {
this.birth_date = birth_date;
}
}
Error after doing POST request with Postman:
{
"timestamp": "2020-05-06T19:01:16.498+0000",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/json;charset=UTF-8' not supported",
"path": "/carnet/user/add"
}
SOLVED: Removing #JsonManagedReference from User Model solved the problem!
This problem is because of the #JsonManagedReference in your model class,
Try interchanging #JsonBackReference and #JsonManagedReference. It should work.
Just explore the documentation for more info.
This is one similar issue reported for the same case.
Related
Description
I have created a database with three tables
users (user_id, name, password),
the Role table with columns
role_id, role and the third column Role_user with user_id,role_id.
error while running the program,
I have stored the username, passwords with the help of hashing with SHA2, for security purposes.
While running the program it is throwing the error as mentioned above. How should I resolve this error?
package com.techprimers.security.securitydbexample.model;
import javax.persistence.*;
import java.util.Set;
#Entity
#Table(name = "user")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#Column(name = "password")
private String password;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
public Users() {
}
public Users(Users users) {
this.roles = users.getRoles();
this.name = users.getName();
this.id = users.getId();
this.password = users.getPassword();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Role
package com.MonitoringDashboardNew.Model;
import javax.persistence.*;
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private int roleId;
#Column(name = "role")
private String role;
public Role() {
}
public int getRoleId() {
return roleId;
}
public void setRoleId(int roleId) {
this.roleId = roleId;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
CustomUserDetails
package com.MonitoringDashboardNew.Model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.stream.Collectors;
public class CustomUserDetails extends Users implements UserDetails {
public CustomUserDetails(final Users users) {
super(users);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles()
.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRole()))
.collect(Collectors.toList());
}
#Override
public String getPassword() {
return super.getPassword();
}
#Override
public String getUsername() {
return super.getName();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
Repository
package com.MonitoringDashboardNew.Repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.MonitoringDashboardNew.Model.Users;
#Repository
public interface UsersRepository extends JpaRepository<Users, Integer> {
Optional<Users> findByName(String username);
}
Service
package com.MonitoringDashboardNew.Services;
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 org.springframework.stereotype.Service;
import com.MonitoringDashboardNew.Model.CustomUserDetails;
import com.MonitoringDashboardNew.Model.Users;
import com.MonitoringDashboardNew.Repository.UsersRepository;
import java.util.Optional;
#Service
public abstract class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UsersRepository usersRepository;
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
Optional<Users> optionalUsers = usersRepository.findByName(name);
// Optional<Users> optionalUsers = usersRepository.findById(id);
optionalUsers
.orElseThrow(() -> new UsernameNotFoundException("Username not found"));
return optionalUsers
.map(CustomUserDetails::new).get();
}
}
SecurityConfuguration
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import com.MonitoringDashboardNew.Services.CustomUserDetailsService;
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#EnableJpaRepositories()
#Configuration
#Component
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("**/monitoring/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin().permitAll();
}
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
}
Error
Description:
Field userDetailsService in com.example.demo.SecurityConfiguration required a bean of type 'com.MonitoringDashboardNew.Services.CustomUserDetailsService' that could not be found.
The injection point has the following annotations:
#org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.MonitoringDashboardNew.Services.CustomUserDetailsService' in your configuration.
Your CustomUserDetailsService is abstract and cannot be created.
#Service
public abstract class CustomUserDetailsService implements UserDetailsService {
Make it non abstract:
#Service
public class CustomUserDetailsService implements UserDetailsService {
I have converted the JSON into POJO using GSON.
I am looking to store Employee entity object into mysql using JPA's save() method. But I am getting an error saying "cannot determine the type for Address". So how should I go with this?
Here is the error:
Could not determine type for : Address
Employee.java
package com.example.demo;
import java.math.BigDecimal;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import com.google.gson.annotations.Expose;
#Entity
public class Employee {
#Id
private int id;
private String name;
private int age;
private BigDecimal salary;
private String designation;
private Address address;
private long[] phoneNumbers;
/*Getter and Setter Methods*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public BigDecimal getSalary() {
return salary;
}
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
public String getDesignation() {
return designation;
}
public void setDesignation(String designation) {
this.designation = designation;
}
public long[] getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(long[] phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Address.java
package com.example.demo;
import javax.persistence.Entity;
import javax.persistence.Id;
//#Entity
public class Address {
#Id
private String street;
private String city;
private int zipCode;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getZipCode() {
return zipCode;
}
public void setZipcode(int zipcode) {
this.zipCode = zipcode;
}
#Override
public String toString(){
return getStreet() + ", "+getCity()+", "+getZipCode();
}
}
Controller Class
package com.example.demo;
import net.sf.json.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import net.sf.json.JSONObject;
#Controller // This means that this class is a Controller
#RequestMapping(path="/demo") // This means URL's start with /demo
(after Application path)
public class MainController {
#Autowired // This means to get the bean called userRepository
// Which is auto-generated by Spring, we will use it
to handle the data
private UserRepository userRepository;
#GetMapping(path="/add") // Map ONLY GET Requests
public #ResponseBody String addNewUser (#RequestBody String json) {
// #ResponseBody means the returned String is the response, not a view name
// #RequestParam means it is a parameter from the GET or POST request
//JSONObject jsonObject = JSONObject.fromObject(json);
Gson gson=new GsonBuilder().create();
Employee employee =gson.fromJson(json,Employee.class);
userRepository.save(employee);
return "Successfully added to database using JPA!";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<Employee> getAllUsers() {
// This returns a JSON or XML with the users
return userRepository.findAll();
}
}
Try adding implements Serializable for your Entity classes.
ie.
#Entity
public class Employee implements Serializable{
}
Whenever you use #Entity annotation in your class and trying to save its instance in database. At very intially a create table command is formed in hibernate which creates table in the database with the given specifications. As in employee table you specify the datatypes of all data-members. When cursor come to 'Address', it gave error because hibernate not able to find any datatype related this or any table related this.
As you commented #Entity annoatation in Address class. So no table regarding that will be created in database.
Address is class reference for this hibernate need the class(table). As class(table) is not there ,so the error comes.
I'm trying to build a small Spring Boot CRUD app with some basic e-commerce functionality (i.e. add to cart, etc.). My Basic entities are customer, cheese, roles and orders.
Customer's have a many-to-many relationship with Cheese (the fictional object I'm selling) objects. In addition, Orders have a many-to-many relationship with Cheese objects. When my customer checks out, I am intending to transfer the cart contents (i.e. the list of Cheeses) to the Order object, along with customer id, total price, etc. I want the "Orders" to be able to be logged by myself, as well as to provide an order history for the customer. The instantiating of the order object with customer.getCheeses() is what is giving me the shared collection error.
I can somewhat get around this by creating new Cheese items, however, that messes up my database, creating duplicates upon every new order.
The processing of orders is done in the completeOrder() function in UserController. All of the html/thymeleaf seems to be working - I can post it if it will help.
Cheese
package com.example.demo.models;
import javax.persistence.*;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
#Entity
public class Cheese {
#NotNull
#Size(min=2, max=20)
private String name;
#NotNull
#Size(min=2, max=20)
private String description;
#NotNull
#DecimalMax("10000.0") #DecimalMin("0.0")
private BigDecimal price;
#Id
#GeneratedValue
private int id;
#ManyToMany(mappedBy = "cheeses")
private List<Customer> customers = new ArrayList<>();
#ManyToMany(mappedBy = "cheeses")
private List<Orders> orders = new ArrayList<>();
public Cheese() {}
public Cheese(String name, String description, BigDecimal price) {
this.name = name;
this.description = description;
this.price = price;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<Orders> getOrders() {
return orders;
}
public void setOrders(List<Orders> orders) {
this.orders = orders;
}
}
Customer
package com.example.demo.models;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
#Entity
public class Customer implements Serializable {
#NotNull
#Size(min = 2, max = 25)
private String name;
#GeneratedValue
#Id
private int accountNumber;
private BigDecimal accountFunds;
#NotNull
#Size(min = 2)
private String password;
#NotNull
#Size(min = 2, max = 25)
#Email
private String email;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name="user_roles",
joinColumns={#JoinColumn(name="CUSTOMER_EMAIL", referencedColumnName = "email")},
inverseJoinColumns={#JoinColumn(name="ROLE_ID", referencedColumnName="id")})
private List<Role> roles;
//#ElementCollection
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name="cheese_customers",
joinColumns={#JoinColumn(name="CUSTOMER_ID", referencedColumnName = "accountNumber")},
inverseJoinColumns={#JoinColumn(name="PRODUCT_ID", referencedColumnName="id")})
private List<Cheese> cheeses = new ArrayList<>();
public Customer(String name, String password, String email) {
this.name = name;
this.password = password;
this.email = email;
this.accountFunds = new BigDecimal(225.00);
}
public Customer() {}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAccountNumber() {
return accountNumber;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public BigDecimal getAccountFunds() {
return accountFunds;
}
public void setAccountFunds(BigDecimal accountFunds) {
this.accountFunds = accountFunds;
}
public List<Cheese> getCheeses() {
return cheeses;
}
public void setCheeses(List<Cheese> cheeses) {
this.cheeses = cheeses;
}
}
Orders
package com.example.demo.models;
import javax.persistence.*;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
#Entity
public class Orders {
#GeneratedValue
#Id
private int orderId;
#ManyToMany(cascade= CascadeType.ALL)
#JoinTable(name="customer_orders",
joinColumns={#JoinColumn(name="ORDER_ID", referencedColumnName = "orderId")},
inverseJoinColumns={#JoinColumn(name="PRODUCT_ID", referencedColumnName="id")})
private List<Cheese> cheeses = new ArrayList<>();
private int customerId;
private BigDecimal totalPrice;
private Date date;
public Orders() {}
public Orders(List<Cheese> cheeses, int customerId, BigDecimal totalPrice) {
this.cheeses = cheeses;
this.customerId = customerId;
this.totalPrice = totalPrice;
this.date = new Date();
}
private String getFormattedDate() {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
return format.format(this.date);
}
public int getOrderId() {
return orderId;
}
public List<Cheese> getCheeses() {
return cheeses;
}
public void setCheeses(List<Cheese> cheeses) {
this.cheeses = cheeses;
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public BigDecimal getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(BigDecimal totalPrice) {
this.totalPrice = totalPrice;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
UserController
package com.example.demo.controllers;
import com.example.demo.models.Customer;
import com.example.demo.models.Orders;
import com.example.demo.models.data.CheeseDao;
import com.example.demo.models.data.CustomerDao;
import com.example.demo.models.data.OrdersDAO;
import com.example.demo.models.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
#Controller
#RequestMapping("cheese")
public class UserController {
#Autowired
private CustomerDao customerDao;
#Autowired
UserService userService;
#Autowired
CheeseDao cheeseDao;
#Autowired
OrdersDAO ordersDAO;
#RequestMapping(value = "login")
public String loginPage(Model model) {
model.addAttribute("title", "Login Page");
model.addAttribute("customer", new Customer());
return "cheese/login";
}
#RequestMapping(value = "account")
public String accountInfo(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Customer customer = customerDao.findByEmail(authentication.getName());
model.addAttribute("name", customer.getName());
model.addAttribute("funds", customer.getAccountFunds());
model.addAttribute("customer", customer);
model.addAttribute("cheeses", customer.getCheeses());
model.addAttribute("total", userService.getCartTotal(customer));
return "cheese/account";
}
#PostMapping(value = "account")
public String removeItem(#RequestParam int cheeseId) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Customer customer = customerDao.findByEmail(authentication.getName());
if (customer.getCheeses().contains(cheeseDao.getCheeseById(cheeseId))) {
customer.getCheeses().remove(cheeseDao.getCheeseById(cheeseId));
}
customerDao.save(customer);
return "redirect:/cheese/account";
}
#RequestMapping(value = "checkout")
public String orderCheckout(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Customer customer = customerDao.findByEmail(authentication.getName());
model.addAttribute("cheeses", customer.getCheeses());
model.addAttribute("total", userService.getCartTotal(customer));
return "cheese/checkout";
}
#GetMapping("signup")
public String displaySignUpForm(Model model) {
model.addAttribute("title", "Sign Up");
model.addAttribute("customer", new Customer());
return "cheese/signup";
}
#PostMapping(value = "signup")
public String processSignUp(Model model, #ModelAttribute Customer customer, Errors errors) {
if (errors.hasErrors()) {
return "cheese/signup";
}
userService.createUser(customer);
return "cheese/success";
}
#GetMapping("ordersuccess")
public String showForm() {
return "cheese/ordersuccess";
}
#PostMapping("checkout")
public String completeOrder() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Customer customer = customerDao.findByEmail(authentication.getName());
double accountFunds = customer.getAccountFunds().doubleValue();
double cartTotal = userService.getCartTotal(customer).doubleValue();
if (accountFunds >= cartTotal) {
accountFunds = accountFunds - cartTotal;
customer.setAccountFunds(new BigDecimal(accountFunds));
Orders order = new Orders(customer.getCheeses(), customer.getAccountNumber(), new BigDecimal(cartTotal));
customer.getCheeses().clear();
customerDao.save(customer);
ordersDAO.save(order);
return "redirect:/cheese/ordersuccess";
}
return "redirect:cheese/checkout";
}
#GetMapping("orders")
public String viewOrderHistory(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Customer customer = customerDao.findByEmail(authentication.getName());
List<Orders> orders = ordersDAO.findOrdersByCustomerId(customer.getAccountNumber());
model.addAttribute("orders", orders);
return "cheese/orders";
}
}
So what you are trying to do is fetch and fill the cheese collection when you get a customer? Normally, in order to do that, you must set lazy loading to false, otherwise the session closes before you can fetch the collection.
To be able to load the customer with it's cheese collection, you must got to your Hibernate query and use a "join fetch" command. Something like this.
sessionFactory.getCurrentSession().createQuery("from Customer C join fetch C.cheeses").list();
This will force the query to fetch the cheese collection before the session closes. Also, one more thing, normally I would use a Set to avoid duplicates in the collection. I hope this helps.
I am trying to register and log-in users with Spring Security, as well as give them roles, which is required by spring security. I am currently receiving the following error when registering more than one user.
The error is:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'USER' for key 'UK_8sewwnpamngi6b1dwaa88askk'
The role_name can be the same for many users, so I made an 'id' for roles that is the primary key, and is autogenerated. My "USER_ROLES" table in my MySQL database only has one entry, which is the first email, and role_name, "USER". The Customer table has all the entries, regardless of errors. I'll keep working on this.
Thanks for looking.
Customer.java
package com.example.demo.models;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
#Entity
public class Customer implements Serializable {
#NotNull
#Size(min=2, max=25)
private String name;
#GeneratedValue
#Id
private int accountNumber;
private BigDecimal accountFunds;
#NotNull
#Size(min=2)
private String password;
#NotNull
#Size(min=2, max=25)
#Email
private String email;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "USER_ROLES", joinColumns = {
#JoinColumn(name = "CUSTOMER_EMAIL", referencedColumnName = "email")
}, inverseJoinColumns = {
#JoinColumn(name = "ROLE_NAME", referencedColumnName = "name")
})
private List<Role> roles;
public Customer(String name, String password, String email) {
this.name = name;
this.password = password;
this.email = email;
}
public Customer() {}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAccountNumber() {
return accountNumber;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public BigDecimal getAccountFunds() {
return accountFunds;
}
public void setAccountFunds(BigDecimal accountFunds) {
this.accountFunds = accountFunds;
}
}
Role.java
package com.example.demo.models;
import javax.persistence.*;
import java.util.List;
#Entity
public class Role {
#Id
#GeneratedValue
private int id;
private String name;
#ManyToMany(mappedBy = "roles")
private List<Customer> customers;
public Role(String name, List<Customer> customers) {
this.name = name;
this.customers = customers;
}
public Role(String name) {
this.name = name;
}
public Role() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Customer> getCustomers() {
return customers;
}
public void setCustomers(List<Customer> customers) {
this.customers = customers;
}
}
UserService.java
package com.example.demo.models.services;
import com.example.demo.models.Customer;
import com.example.demo.models.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public void createUser(Customer customer) {
Role userRole = new Role("USER");
List<Role> roles = new ArrayList<>();
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
customer.setPassword(encoder.encode(customer.getPassword()));
roles.add(userRole);
customer.setRoles(roles);
userRepository.save(customer);
}
public void createAdmin(Customer customer) {
Role userRole = new Role("ADMIN");
List<Role> roles = new ArrayList<>();
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
customer.setPassword(encoder.encode(customer.getPassword()));
roles.add(userRole);
customer.setRoles(roles);
userRepository.save(customer);
}
}
UserRepository
package com.example.demo.models.services;
import com.example.demo.models.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<Customer, String> {
}
SecurityConfig
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.sql.DataSource;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select email as principal, password as credentials, true from customer where email=?")
.authoritiesByUsernameQuery("select customer_email as principal, role_name as role from user_roles where customer_email=?")
.passwordEncoder(passwordEncoder()).rolePrefix("ROLE_");
}
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.authorizeRequests()
.antMatchers(
"/**/webjars/**",
"/cheese/signup",
"/cheese/login",
"/cheese/success").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/cheese/login")
.defaultSuccessUrl("/cheese/account")
.permitAll();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Ok, so I got it to work. SecurityConfig is the SAME, except you need to change 'role_name' to 'role_id'.
Just to give a little bit of explanation, I did the following:
Add id to roles, make it Primary Key/autogenerated.
For the Customer many-to-many, I joined the customer email to (inverse join) the role 'id'
For the Role, I did a one-to-many mapping. joining the role 'id' with (inverse join) customer email.
This way I ended up with a 'user_roles' table where the role_id was mapped to the customer email, and a 'role' table where the role_id was mapped to the role (i.e. "USER", "ADMIN", etc.), allowing for duplicates. I am still a beginner, so maybe even this is not the best way to do it, but it worked for me.
The code...
Customer.java
package com.example.demo.models;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
#Entity
public class Customer implements Serializable {
#NotNull
#Size(min = 2, max = 25)
private String name;
#GeneratedValue
#Id
private int accountNumber;
private BigDecimal accountFunds;
#NotNull
#Size(min = 2)
private String password;
#NotNull
#Size(min = 2, max = 25)
#Email
private String email;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name="user_roles",
joinColumns={#JoinColumn(name="CUSTOMER_EMAIL", referencedColumnName = "email")},
inverseJoinColumns={#JoinColumn(name="ROLE_ID", referencedColumnName="id")})
private List<Role> roles;
public Customer(String name, String password, String email) {
this.name = name;
this.password = password;
this.email = email;
}
public Customer() {}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAccountNumber() {
return accountNumber;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public BigDecimal getAccountFunds() {
return accountFunds;
}
public void setAccountFunds(BigDecimal accountFunds) {
this.accountFunds = accountFunds;
}
}
Role.java
package com.example.demo.models;
import javax.persistence.*;
import java.util.List;
#Entity
public class Role {
#Id
#GeneratedValue
private int id;
private String name;
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name="user_roles",
joinColumns={#JoinColumn(name="ROLE_ID", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name = "CUSTOMER_EMAIL", referencedColumnName = "email")
})
private List<Customer> customers;
public Role(String name, List<Customer> customers) {
this.name = name;
this.customers = customers;
}
public Role(String name) {
this.name = name;
}
public Role() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Customer> getCustomers() {
return customers;
}
public void setCustomers(List<Customer> customers) {
this.customers = customers;
}
}
If I want to create Json payload as below using fasterxml.jackson, what do I have to do?
{
"email":
}
And this is how my model class look like:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.io.Serializable;
#JsonInclude(JsonInclude.Include.ALWAYS)
#JsonPropertyOrder({ "email" })
public class ApiRequest implements Serializable {
private static final long serialVersionUID = 1L;
#JsonProperty("email")
private String email;
public ApiRequest() {
}
/**
* #param email
*/
public ApiRequest(String email) {
this.email = email.equalsIgnoreCase("null") ? null : email;
}
#JsonProperty("email")
public String getEmail() {
return email;
}
#JsonProperty("email")
public void setEmail(String email) {
this.email = email.equalsIgnoreCase("null") ? null : email;
}
}
Thanks!