How to config Spring Security with JPA? - java

I need to add Spring Security into my project. What is the right way to do it? I have to entities User and UserRole and DAO and Services for them. I use EntityManager to access data. I read, that I just need to write implementation for UserDetails, but I don't know how to do it correctly. Here my code:
User.java
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty
private Integer id;
#Column(name = "username", length = 20, nullable = false)
#JsonProperty
private String username;
#Column(name = "password", nullable = false)
#JsonProperty
private String password;
#Column(name = "enabled", nullable = false)
#JsonProperty
private boolean enabled;
#Column(name = "email", nullable = false)
#JsonProperty
private String email;
#OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
private Set<UserRole> userRoles;
//getters and setters
UserRole.java
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty
private Integer id;
#ManyToOne(optional = false)
#JoinColumn(name = "user_ID", referencedColumnName = "id", foreignKey = #ForeignKey(ConstraintMode.NO_CONSTRAINT))
private User user;
#Column(name="role")
#JsonProperty
private String role;
//getters and setters
What should I do?

I had wrote a blog post about exactly what you are looking for. see this post and I am pretty sure it will answer your question:
https://giannisapi.wordpress.com/2011/09/21/spring-3-spring-security-implementing-custom-userdetails-with-hibernate/
In the Service layer of UserDetails below, pay attention that it implements UserDetailsService from org.springframework.security.core.userdetails.UserDetailsService.
and also :
he loadUserByUsername methods return the result of the assembler.buildUserFromUserEntity . Simply put, what this method of the assembler does is to to construct a org.springframework.security.core.userdetails.User object from the given UserEntity DTO. The code of the Assembler class is given below:

Related

Cascade type in Spring boot

I'm a new to Spring boot.
I'd like to know which cascade type I have to use in this case.
I have an Employee class and a Departement.
Many Employees can work in a Departement (So i guess it's a #ManyToOne Relation) and a Department can have one or more Employees (so it's a **#OneToMany).
I want to perform some edits on the Employee class which have to be propagated to the other dependency.
BUT if I delete one employee, I can't delete an entire Departement, so I have to just delete the Employee.
#Entity
public class Department
{
#Column(nullable = false)
private String name;
#Id
private String code;
#Column(nullable = false)
private String address;
#Column(unique = true, nullable = false)
private String website;
#Column(unique = true, nullable = false)
private String cellNumber;
#JsonIgnore
#OneToMany(mappedBy = "department")
private Set<Employee> emplpoyee;
//constructors, getters and setters
}
Here is the Employee Class
public class Employee
{
#Id
private String SSN;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String lastname;
#Column(nullable = false)
private String username;
#Column(nullable = false)
private String gender;
#Column(unique = true, nullable = false)
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String role;
#ManyToOne(cascade = CascadeType.???, fetch = FetchType.LAZY)
#JoinTable(
name = "employee_works_in_dept",
joinColumns = {#JoinColumn(name = "employee_SSN")},
inverseJoinColumns = {#JoinColumn(name = "dept_code")}
)
private Department department;
}
What is that I have to replace with ??
I tried with CascadeType.ALL but it also deletes the Entire Department
First of all I don't think you need a separate join table if your Employee can only be a part of one Department.
If your employee can work on multiple departments then it is Many to Many relation.
When considering your question, you can simply remove your CascadeType. you don't need it in the Employee class.
If you need to delete all the employees under a Department when Department is deleted , then you can simply add your CascadeType.ALL on Department side.
There is a previous thread with better answer here.
What is the meaning of the CascadeType.ALL for a #ManyToOne JPA association

Spring Data JPA/Hibernate LazyInitializationException

I am new to hibernate/spring data JPA and occuring some problems when dealing with Foreign Keys.
I have the two table:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String role;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "job_id", referencedColumnName = "id")
private Job job;
}
#Entity
public class Job{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String name;
#OneToMany(
mappedBy = "job",
cascade = CascadeType.PERSIST,
fetch = FetchType.EAGER
)
private Set<User> users = new HashSet<>();
}
I am trying to save one Job (job.Id) at the Users table. But if I am trying to peform a get(repository.getOne(id)) (via ...extends JPARepository<User,Long> I am getting the following error:
org.hibernate.LazyInitializationException: could not initialize proxy [backend.entity.User#193] - no Session
Does anyone has a Tip how to fix it? I have read a lot on stackoverflow, but could not find out how to fix that.

One to many relationship in springboot

I have a file named data.sql which contains SQL queries 'INSERT INTO'.
I have table User which model is:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// other fields
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "user")
#Column(name = "vacations")
private Set<Vacation> vacations = new HashSet<>();
And I have model Vacation where is:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Column(name = "begin_date")
private LocalDateTime beginDateOfVacation;
#NotNull
#Column(name = "end_date")
private LocalDateTime endDateOfVacation;
#NotEmpty
#Column(name = "type")
private String typeOfVacation;
#NotEmpty
#Column(name = "reason")
private String reasonOfVacation;
#ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private User user;
And in my data.sql I am trying to insert into vacation User with existing ID.
It "passing" threw compiler, but on localhost I can see only this:
Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.springproject27.springproject.user.User["vacations"])
It is H2 Database Engine, the query which I try to pass is:
INSERT INTO VACATION(ID,BEGIN_DATE,END_DATE,TYPE,REASON,USER_ID) VALUES
(22,'2012-09-17 18:47:52.69','2012-09-20 18:47:52.69','Unpaid leave','Sick',10);
As you see its happening because of JSON recursion when you are trying to serialize your data because of bi-directional relationship between User entity & Vacation entity. Preferred method to use #JsonIgnoreProperties (if you are using Jackson 2.0+ version) to break the recursion during serialization.
NOTE: Other way to break the JSON recursion is using JsonBackReference and JsonManagedReference in combiation. But I prefer #JsonIgnoreProperties due to no data loss during serialization.
If you are using Lombok, only use #Getter & #Setter annotation not #ToString (or #Data which has #ToString included), as it will cause the same recursion issue.
class User{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// other fields
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "user")
#Column(name = "vacations")
#JsonIgnoreProperties("user")
private Set<Vacation> vacations = new HashSet<>();
}
class Vacation{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Column(name = "begin_date")
private LocalDateTime beginDateOfVacation;
#NotNull
#Column(name = "end_date")
private LocalDateTime endDateOfVacation;
#NotEmpty
#Column(name = "type")
private String typeOfVacation;
#NotEmpty
#Column(name = "reason")
private String reasonOfVacation;
#ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
#JsonIgnoreProperties("vacations")
private User user;
}

Hibernate "ManyToOne ... references an unknown entity" exception

I just cannot get the relationship working between my two classes mapped to SQL tables with Hibernate.
The Role class:
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#OneToMany(mappedBy="memberinfo")
private Set<Memberinfo> members;
...
}
And the Memberinfo class:
#Entity
#Table(name = "memberinfo")
public class Memberinfo {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id", nullable = false)
private int id;
#Column(name = "userid", nullable = false)
private String userid;
#Column(name = "email", nullable = false)
private String email;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "salt", nullable = false)
private String salt;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "address")
private String address;
#Column(name = "phonenum")
private String phonenum;
#ManyToOne(targetEntity=Role.class)
#JoinColumn(name="role_id")
private Role role;
...
}
When i try to fetch data from the DB, it connects, but throws an exception:
"HTTP Status 500 - #OneToOne or #ManyToOne on model.Memberinfo.role references an unknown entity: model.Role".
If i delete the variable "Role", then it works, and i can fetch the membership data, but i need the connection between the two tables, but in this case, the previously mentioned exception appears every time.
No other solutions on stackoverflow worked for me so far.
Any idea what am i doing wrong?
The "unknown entity error" can be thrown if the class is not actually an Entity (not annotated whith javax.persistence #Entity) or if the persitence provider doesn't "know" the class (package not scanned).
Is the Role class imported in Memberinfo the correct one ? Maybe you are importing another Role class from another library.

Best practices for identifying user roles and permissions?

What are some best practices when defining users with different roles/permissions, such as normal user with restricted access and administrator with full access?
My user class looks something like this:
#Entity
#Table(name = "users")
public class User {
#Id
#Column(name = "userID")
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
private Long userID;
public Long getUserID() {
return userID;
}
#ManyToOne(fetch = FetchType.LAZY)
#Column(nullable = false)
private Role role;
#Column(nullable = false)
private boolean isActive;
#Column(nullable = false, unique = true)
private String email;
#Column(nullable = false)
private String password;
#Column
#Temporal(TemporalType.TIMESTAMP)
private Calendar lastLoggedIn;
#Column(nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Calendar createdDate;
#Version
private Integer version;
}
My Role class is:
#Entity
#Table(name = "roles")
public class Role {
#Id
#Column(name = "roleID")
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
private Long roleID;
#Column(nullable = false)
private String roleName;
#Column(nullable = false)
private String rolePermissions;
}
Say my application needed to retrieve a list of user and their roles, should an instance of User hold a reference to an instance (or proxy) of Role? What if my application had to find all Users for a particular Role, does Role have to have a List of Users? What are some tried and true ways of designing and implementing this relationship?
Also, am I doing the mapping correctly with JPA?
You should use,
#CollectionOfElements
#JoinTable(name = "ROLE_PERMISSIONS", joinColumns = #JoinColumn(name = "roleID"))
#Column(name = "Permission")
private List<String> rolePermissions;
instead of,
#Column(nullable = false)
private String rolePermissions;
I would suggest using
#role=Admin
for all the administrator level functions
and use
#role=User
for normal user and more if annotations if necesary

Categories