Issue
I am trying to map owner(User) with an offer(car) whenever user is adding an offer. With setup like below user_id is always null. What am i missing ? I think it's something with controller. Mappings seems ok to me but I may be wrong. Owner is the person who is logged in and adds an offer, so it should be mapped by his id.
Edit I have found out that my @AuthenticationPrincipal Authentication auth is always null anyways, even tho only authenticated user can acces POST /api/cars endpoint. It seems counter-intuitive
Car Controller
@Controller
public class CarController {
@Autowired
private CarService carService;
@Autowired
UserRepository userRepository;
@GetMapping("/api/cars")
public String getCars(Model model) {
model.addAttribute("cars", carService.getAllCars());
return "cars";
}
@GetMapping("/api/cars/new")
public String createNewCarOfferForm(Model model) {
Car car = new Car();
model.addAttribute("car", car);
return "createNewOfferForm";
}
@PostMapping("/api/cars")
public String saveCar(@ModelAttribute("car") Car car, @AuthenticationPrincipal Authentication auth) {
User customUser = (User)auth;
car.setOwner(customUser);
carService.saveCar(car);
return "redirect:/api/cars";
}
@GetMapping("/api/cars/view/{id}")
public String viewOffer(@PathVariable Long id, Model model) {
model.addAttribute("car", carService.getCarById(id));
return "viewOffer";
}
}
User Entity
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String roles = "ROLE_USER";
@OneToMany(mappedBy = "owner")
private Set<Car> cars = new HashSet<>();
public User() {
super();
}
public User(String username, String password, String roles) {
super();
this.username = username;
this.password = password;
this.roles = roles;
//GETTERS SETTERS
Car entity
@Entity
public class Car {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String carPhotoUrl;
private String carTitle;
private double price;
private int yearModel;
private int mileage;
private String engineType;
private float engineCapacity;
private int enginePower;
private String gearboxType;
private String driveType;
private String colour;
private Boolean isDamaged;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="user_id", referencedColumnName = "id")
private User owner;
public Car() {
super();
}
public Car(String carPhotoUrl, String carTitle, double price, int yearModel, int mileage, String engineType,
float engineCapacity, int enginePower, String gearboxType, String driveType, String colour,
Boolean isDamaged) {
super();
this.carPhotoUrl = carPhotoUrl;
this.carTitle = carTitle;
this.price = price;
this.yearModel = yearModel;
this.mileage = mileage;
this.engineType = engineType;
this.engineCapacity = engineCapacity;
this.enginePower = enginePower;
this.gearboxType = gearboxType;
this.driveType = driveType;
this.colour = colour;
this.isDamaged = isDamaged;
}
//GETTERS SETTERS
USER REPOSITORY
public interface UserRepository extends JpaRepository<User, Long>
{
Optional<User> findByUsername(String username);
Optional<User> findById(Long id);
}
User Details Service
public class UserDetailsService implements UserDetails{
private String username;
private String password;
private List<GrantedAuthority> authorities;
public UserDetailsService() {
super();
}
public UserDetailsService(User user){
this.username=user.getUsername();
this.password=user.getPassword();
this.authorities = Arrays.stream(user.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 username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
Solution
I fixed it but it took me some time. This is now endpoint to add a Car (so no @AuthenticationPrincipal)
@PostMapping("/api/cars/addCar")
public String saveCar(@ModelAttribute("car") Car car, Principal
principal) {
User user = userService.getUserByName(principal.getName());
car.setOwner(user);
carService.saveCar(car);
return "redirect:/api/cars";
}
I bet it's not a clean solution. I've changed return type of UserRepository from Optional to User. I think it will bring more problems in the future. But for now it works.
Answered By - Robert
Answer Checked By - Mildred Charles (JavaFixing Admin)