spring JPA users, roles authentication - how do I avoid duplicate role entries? - spring

Hi I'm new to spring JPA and spring security, I've spent a while trying to get authentication working based on users and roles.
A user should be able to have many roles, and a role can belong to many users.
There should only be about 4 roles.
I've tried to achieve this with a #Manytomany mapping and a join table.
It more or less works, but when I try to persist a new user with a set of Roles it will add duplicate entries to the roles table (or if I put a unique key on the roles table hits a UK violation).
here's the code:
user
import java.util.Set;
import javax.persistence.*;
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String email;
private String password;
public long getId() {
return id;
}
public void setId(long id) {
this.id = 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 Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
#ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
#JoinTable(joinColumns = #JoinColumn(name = "id"),inverseJoinColumns = #JoinColumn(name = "roleId"))
private Set<Role> roles;
public User(String email, String password, Set<Role> roles) {
super();
this.email = email;
this.password = password;
this.roles = roles;
}
}
role
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String role;
#ManyToMany(mappedBy = "roles",fetch = FetchType.LAZY)
private Set<User> users;
public Role(String role) {
super();
this.role = role;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public Set<User> getUsers() {
return users;
}
public void setUser(Set<User> users) {
this.users = users;
}
}

ManyToMany is the correct logical relationship type. The reason you are getting too many roles is probably that you create new instances of Role, but if the role already exist in the database, you must load it into the persistence context, and only create a new role if it does not exist.
It would look something like this if you managed the transactions in code, if you're using container managed persistence with #Transactional you should remove the transaction code, and the try-finally block.
try {
List<String> roleNames = Lists.newArrayList("user, admin, superuser");
User user = new User("Peter", "Pan");
List<Role> roles = new ArrayList<>();
entityManager.getTransaction().begin();
for (String roleName : roleNames) {
List<Role> found = entityManager.createQuery("select r from Roles r where r.name = :roleName", Role.class)
.setParameter("roleName", roleName).getResultList();
if (found.isEmpty()) {
Role role = new Role(roleName);
entityManager.persist(role);
roles.add(role);
} else {
roles.addAll(found);
}
}
user.setRoles(roles);
entityManager.persist(user);
entityManager.getTransaction().commit();
} finally {
entityManager.close();
}
Personally I almost never use JPA's ManyToMany, I typically break it down to 2 OneToManyrelations, that way I have an Entity for the mapping table which is otherwise magically created by JPA. This also gives you additional control, for instance if you need to modify or delete a role from a user, you can delete the UserRoleMapping entity directly, instead of having to manipulate list on Role and User. It also giver you more control over cascading.

Related

mapped entity saved again - issue

whenever i am using cartRepository.save(cart) user is again inserted in user table ?(Cart is having a field User).
how not to save the user again?
Users class:
#Entity
#Table(name="users")
public class Users {
#Id
#GeneratedValue
private int id;
#Column(name="nameofuser")
//#UniqueElements
private String nameofuser;
#Column(name="password")
private String password;
#OneToOne(mappedBy="user",cascade = {CascadeType.ALL})
Cart cart;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNameofuser() {
return nameofuser;
}
public void setNameofuser(String nameofuser) {
this.nameofuser = nameofuser;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Cart Entity:
#Entity
#Table(name="cart")
public class Cart {
#Id
#GeneratedValue
#Column(name="id")
int id;
#OneToOne(cascade = CascadeType.ALL)
Users user;
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
CartService class:
package com.praful.lastTry.Services;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.praful.lastTry.MenuRepository.CartRepository;
import com.praful.lastTry.Models.Cart;
import com.praful.lastTry.Models.Users;
#Service
public class CartService {
#Autowired CartRepository cartRepository;
public void bindingWithUser(Users user) {
Cart cart=new Cart();
Users user1=new Users();
cart.setUser(user1);
System.out.println(" from here==================================");
cartRepository.save(cart);
System.out.println("till here ==================================");
}
}
whenever i am using cartRepository.save(cart) user is again inserted in user table
Because of this code :
Users user1=new Users();
Every time you are sending a new user.
Change it to something:
public void bindingWithUser(Users user) {
Cart cart=new Cart();
Users user1= getUser();
cart.setUser(user1);
System.out.println(" from here==================================");
cartRepository.save(cart);
System.out.println("till here ==================================");
}
private Users getUser(){
//return logged in user or your logic to get user which is already there in Database
}
Seems like in your bindingWithUser function you are passing a User object user but inside the function, you are initializing a new User object user1 and setting it to the cart object before saving it

I've a field which is not primary key id. how can i fetch data using JPA repository through that non primary key?

I've a model User. There's a field which is contact and it's a non primary key. How can i fetch data using this key? it's an unique key.
This is my model.
#Entity
#Table(name = "tbl_user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
long userId;
#Column(name = "name")
String name;
#Column(name = "email")
String email;
#Column(name = "contact")
String contact;
#Column(name = "category")
String category;
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getContact() {
return contact;
}
public void setContact(String contact) {
this.contact = contact;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public User() {
}
}
This is the method inside Service layer.
public User getUserByContact(String contact) {
Optional<User> result = userRepository.findByContact(contact);
User user = result.get();
return user;
}
This is the repository.
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select u from User u where u.contact = ?1")
User findByContact(String contact);
}
I'm getting an error on "select u from User u where u.contact = ?1" this portion under User and it's saying "cant resolve symbol User". Would appreciate any sort of help.
Leave out the #Query part, that part is not needed if you extend a JpaRepository. As stated in the documentation JPA derives the query from the method name.
I'm not sure whether this is an issue, but in your entity class you use a long for id and in your repository definition (JpaRepository<User, Long>) you use a Long. Correct me if this is not problematic.
If you want to use #Query, then the right approach would be to use #Param to define the variable
#Query("select u from User u where u.contact = :contactVar ", nativeQuery = true)
User findByContact(#Param("contactVar")String contactVar);

Many to one relation with inherited objects - Postgres

I'm building an application in which I need a single login page. However, there are two 'sorts' of users: Clients and Trainers. My domain looks like this:
User
|
+-----+-----+
| |
Person Trainer
My user will contain email, password etc. so I can use this table to verify my login requests.
A Trainer should store a list of persons, and a person should have 1 trainer.
The one to many side works, the trainer can store a list of Person class objects, but I can't link back to a trainer via a client.
This is my User class:
package com.example.demo.model;
import javax.persistence.*;
#Entity
#Inheritance
#Table(name="users")
public abstract class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
public User(){}
public User(String email) {
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
The Person class:
package com.example.demo.model;
import javax.persistence.*;
#Entity
public class Person extends User {
#Column
private int age;
#ManyToOne
#JoinColumn
private Trainer trainer;
public Person(){}
public Person(String email, int age) {
super(email);
this.age = age;
}
}
The Trainer class:
package com.example.demo.model;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
public class Trainer extends User {
#Column
private String description;
#OneToMany(mappedBy = "trainer", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Person> clients;
public Trainer(){}
public Trainer(String email, String description) {
super(email);
this.description = description;
}
public void addClient(Person person){
this.clients = new ArrayList<>();
this.clients.add(person);
}
}
How should I access a client's trainer via the ManyToOne annotation? Or are there other options?
Thanks in advance!
Dont initialize list in addClient(). You are overwriting/deleting existing clients. Do it in field declaration.
#OneToMany(...)
private List<Person> clients = new ArrayList<>();
Set both sides of relation:
public void addClient(Person person){
person.setTrainer(this);
this.clients.add(person);
}

Spring Security Set Role On Registration

I'm new to Spring security, so I've followed some tutorials but I'm having trouble understanding how the structure of roles really works under the hood. I have two tables, one for the User:
#Entity
#Table(name = "UserProfile", schema = "dbo", catalog = "DevTestTeam")
public class UserProfileEntity implements UserDetails{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private long id;
#Column(name = "enabled", nullable = false)
private boolean enabled;
#NotEmpty(message = "Enter a password.")
#Size(min = 6, max = 15, message = "Password must be between 6 and 15 characters.")
#Column(name = "password", nullable = true, length = 100)
private String password;
#NotEmpty(message = "Enter a username.")
#Size(min = 6, max = 20, message = "Username must be between 6 and 20 characters.")
#Column(name = "username", nullable = true, length = 20, unique = true)
private String username;
#OneToOne
#JoinColumn(name = "role_id")
private RoleEntity role;
public RoleEntity getRole() {
return role;
}
public void setRole(RoleEntity role) {
this.role = role;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return authorities;
}
and one for the role:
#Entity
#Table(name = "Role", schema = "dbo", catalog = "DevTestTeam")
public class RoleEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private long id;
#Column(name = "name", nullable = true, length = 255)
private String name;
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;
}
My confusion comes when creating a new user. I have a registration form backed by a UserProfileEntity object, and that populates the username and password. Then obviously it's easy to setEnabled()=true (I left some of the getters/setters out of this code for clarity).
My question is how to set the role when instantiating a UserProfileEntity to be saved in the database. My role_id foreign key should just take an integer and return the role from the Role table, but I'm not sure how to express this when instantiating. I have a ROLE_USER in the roles table with an id of 1, and I feel like this is pretty simple to instantiate but I can't find the answer I'm looking for.
UserImpl:
#Service
public class UserProfileServiceImpl implements UserProfileService{
#Autowired
private UserProfileDao userDao;
#Override
public UserProfileEntity findByUser(String username) {
return userDao.findByUsername(username);
}
#Override
public List<UserProfileEntity> findAll() {
List<UserProfileEntity> list = userDao.findAll();
return list;
}
#Override
public UserProfileEntity save(UserProfileEntity persisted) {
userDao.save(persisted);
return null;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserProfileEntity user = userDao.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found.");
}
return user;
}
}
You'll need some repository method to obtain user role by name:
RoleEntity roleEntity = roleEntityRepository.findByName("ROLE_USER");
Then set that RoleEntity to UserProfileEntity before persisting it:
UserProfileEntity userProfileEntity = new UserProfileEntity();
userProfileEntity.setRoleEntity(roleEntity);
userService.save(userProfileEntity);
What you also want is to leave your UserProfileEntity unextended. For Spring Security, you'll need UserDetailsService implementation:
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserProfileEntity userProfileEntity = userRepository.findByUsername(username);
if (userProfileEntity == null) {
throw new UsernameNotFoundException("Non existing user!");
}
return new org.springframework.security.core.userdetails.User(userProfileEntity.getUsername(),
userProfileEntity.getPassword(),
Arrays.asList(new SimpleGrantedAuthority(userByUsername.getRoleEntity().getName())));
}
}
However, I see that your requirements are quite simple - one role per user. Therefore, your RoleEntity could simply be an enum with predefined roles:
public enum RoleEntity {
ROLE_USER
}
And in UserProfileEntity you'd use it like this:
public class UserProfileEntity {
#Enumerated(EnumType.STRING)
private RoleEntity roleEntity;
}
To persist user with role:
UserProfileEntity userProfileEntity = new UserProfileEntity();
userProfileEntity.setRoleEntity(RoleEntity.USER);
userService.save(userProfileEntity);

SpringSecurity with database in vaadin maven project

I have a vaadin maven project witch I integrate in spring security so I am able to see the default login form of spring security .
Now I want to get users from postgres database can you help me please? have you any tuto or books?
As example (respecting Roland Krügers example) for Spring Boot Application + SpringSecurity + Vaadin4Spring # Hibernate, you have to define the following properties in application.properties:
# DATASOURCE
spring.datasource.url=jdbc:postgresql://localhost/myDB
spring.datasource.username=myuser
spring.datasource.password=mypwd
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.xa.data-source-class-name=org.postgresql.xa.PGXADataSource
spring.datasource.pinGlobalTxToPhysicalConnection="true"
# one transaction manager via view
spring.jpa.open-in-view=true
# for mapping models and attributes to table names and column names
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy
spring.jta.default-transaction-timeout=2000
Using application.properties in your app add the following annotation to your main class:
#PropertySources({
#PropertySource({"classpath:application.properties"})
})
Now an entity of a user can look like:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
#Entity
#Table(name = "USER")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false, updatable = false)
private Long id;
#Column(name = "LAST_NAME", nullable = false)
private String lastName;
#Column(name = "FIRST_NAME", nullable = false)
private String firstName;
#Column(name = "LOGGED_IN")
private boolean loggedIn;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public boolean isLoggedIn() {
return loggedIn;
}
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
}
}
After that you have to define a repository interface for Hibernate:
/**
*Spring auto repository for database access of User objects.
**/
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findOneByName(String userName);
}
Now you can do something like that in your login method:
...
vaadinSecurity.login(userName.getValue(), passwordField.getValue());
Optional<User> ouser = userRepository.findOneByName(userName);
if (ouser.isPresent()) {
User user = ouser.get();
user.setLoggedIn(true);
}
...

Resources