I have a authentication issue with mysql via spring boot rest api.
I want to create a user from the service myself using 'saveUser' method and access this user information and rest api. Without using any mvc structure (html,js,css ex.), i want to create a user using the service and log in with that user to rest api.I mentioned all the units of the project below.
I'd appreciate it if you helped.
User
#Data
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#Column(name = "email")
#Email
#NotEmpty
private String email;
#Column(name = "password")
#Length(min = 5)
#NotEmpty
private String password;
#Column(name = "name")
#NotEmpty
private String name;
#Column(name = "last_name")
#NotEmpty
private String lastName;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
//G&S
public int getId() {
return id;
}
public void setId(int 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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Role
#Data
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private int id;
#Column(name = "role")
private String role;
//G&S
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
UserRepository
public interface UserRepository extends JpaRepository<User, Integer>{
User findByEmail(String email);
List<User> findByName(String name);
List<User> findByLastName(String lastName);
}
RoleRepository
public interface RoleRepository extends JpaRepository<Role, Integer>{
Role findByRole(String role);
}
Service
#org.springframework.stereotype.Service
public class UserService {
#Autowired
UserRepository userRepository;
#Autowired
RoleRepository roleRepository;
#Autowired
public UserService(UserRepository userRepository,
RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
//CREATE USER
public User saveUser(User user) {
user.setPassword("mutlu1234");
Role userRole = roleRepository.findByRole("ADMIN");
user.setRoles(new HashSet<Role>(Arrays.asList(userRole)));
return userRepository.save(user);
}
}
MainController
#Controller
public class MainController {
#Autowired
UserService userService;
#RequestMapping(value="/", method = RequestMethod.GET)
public String createUser() {
User mutlu = new User();
userService.saveUser(mutlu);
return "Created";
}
#RequestMapping(value = "/private/{accountNumber}")
public String getPrivateAccountData(#PathVariable final int accountNumber){
return "Private account lined:"+accountNumber;
}
}
Config
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private DataSource dataSource;
#Override
public void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity.authorizeRequests().antMatchers("/private/*").hasRole("ADMIN").and().formLogin();
}
#Autowired
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.
jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select email, password from users where email=?")
.authoritiesByUsernameQuery("select u.email, r.role from users u inner join user_role ur on(u.user_id=ur.user_id) inner join role r on(ur.role_id=r.role_id) where u.email=?")
.dataSource(dataSource);
}
}
data.sql in the resources
REPLACE INTO `role` VALUES (1,'ADMIN');
REPLACE INTO `role` VALUES (2,'USER');
ApplicationProperties
server.port=8090
spring.datasource.url=jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=Turkey
spring.datasource.username=root
spring.datasource.password=
spring.datasource.initialization-mode=always
spring.jpa.hibernate.ddl-auto=none
I think the get method on the "/" is the issue. It should be a post method for data to be persisted to the database. Try changing it to a post method.
Related
I am using Spring boot-I have 3 classes User,Role and UserRole.I have pesisted both role object and user object but i get error that role object is not persisted.The mappings- between User and UserRole is OneToMany ,between Role and UserRole OneToMany.In the UserServiceImpl class i have persisted Role object roleRepository.save(userRole.getRole());
Error is-
Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.bookstore.domain.security.UserRole.role -> com.bookstore.domain.security.Role
#Entity
public class User implements UserDetails,Serializable {
private static final long serialVersionUID=157954L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Id",nullable = false,updatable = false)
private Long id;
private String userName;
private String password;
private String firstName;
private String lastName;
private String email;
private String phone;
private boolean enabled;
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JsonIgnore
private Set<UserRole> userRoles=new HashSet<UserRole>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Set<UserRole> getUserRoles() {
return userRoles;
}
public void setUserRoles(Set<UserRole> userRoles) {
this.userRoles = userRoles;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authorities=new HashSet<GrantedAuthority>();
userRoles.forEach(userRole->{
authorities.add(new Authority(userRole.getRole().getRoleName()));
});
return authorities;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return enabled;
}
#Override
public String getUsername() {
return userName;
}
}
#Entity
public class Role implements Serializable{
private static final long serialVersionUID=68678L;
#Id
private Long roleId;
private String roleName;
#OneToMany(mappedBy = "role",cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JsonIgnore
private Set<UserRole> userRoles=new HashSet<UserRole>();
public Role() {
}
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public Set<UserRole> getUserRoles() {
return userRoles;
}
public void setUserRoles(Set<UserRole> userRoles) {
this.userRoles = userRoles;
}
}
#Entity
public class UserRole implements Serializable {
private static final long serialVersionUID=456874L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userRoleId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "roleId")
private Role role;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "userId")
private User user;
public UserRole(User user,Role role) {
this.role = role;
this.user = user;
}
public UserRole() {
super();
}
public Long getUserRoleId() {
return userRoleId;
}
public void setUserRoleId(Long userRoleId) {
this.userRoleId = userRoleId;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
#Service
public class UserServiceImpl implements UserService {
private static final Logger LOG=LoggerFactory.getLogger(UserServiceImpl.class);
#Autowired
UserRepository userRepository;
#Autowired
RoleRepository roleRepository;
#Transactional
#Override
public User CreateUser(User user, Set<UserRole> userRoles) {
User localUser=userRepository.findByUserName(user.getUserName());
if(localUser!=null) {
LOG.warn("Username {} already exists",user.getUserName());
}
else {
for(UserRole userRole:userRoles) {
roleRepository.save(userRole.getRole());
LOG.error("inside for {}",userRole.getRole().getRoleName());
}
user.getUserRoles().addAll(userRoles);
localUser=userRepository.save(user);
}
return localUser;
}
}
#SpringBootApplication
public class BookStoreApplication implements CommandLineRunner {
#Autowired
UserService userService;
public static void main(String[] args) {
SpringApplication.run(BookStoreApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
User user1=new User();
user1.setUserName("test");
user1.setPassword(SecurityUtility.passwordEncoder().encode("test"));
user1.setEmail("test#test.com");
user1.setEnabled(true);
user1.setFirstName("testFirstName");
user1.setLastName("testLastName");
user1.setPhone("123456789");
Role role1=new Role();
role1.setRoleId((long)1);
role1.setRoleName("ROLE_USER");
UserRole userRole1=new
UserRole(user1,role1);
Set<UserRole> userRoles1=new HashSet<UserRole>();
userRoles1.add(userRole1);
userService.CreateUser(user1, userRoles1);
User user2=new User();
user2.setUserName("admin");
user2.setPassword(SecurityUtility.passwordEncoder().encode("admin"));
user2.setEmail("admin#admin.com");
user2.setEnabled(true);
user2.setFirstName("adminFirstName");
user2.setLastName("adminLastName");
user2.setPhone("223456789");
Role role2=new Role();
role2.setRoleId((long) 2);
role2.setRoleName("ROLE_ADMIN");
UserRole userRole2=new UserRole(user2,role2);
Set<UserRole> userRoles2=new HashSet<UserRole>();
userRoles2.add(userRole2);
userService.CreateUser(user2, userRoles2);
}
}
Couple of issues here.
The first (and the question) issue and the reason why you are getting a "Transient state error" is because you are trying to save an entity with entities attached to it that are NOT yet managed by hibernate.
Have a read of: Entity Lifecycle Model in Hibernate
Caused by: org.hibernate.TransientPropertyValueException:
object references an unsaved transient instance -
save the transient instance before flushing :
com.bookstore.domain.security.UserRole.role
->
com.bookstore.domain.security.Role
So you are somewhere trying to save a UserRole with a Role that is not yet managed.
When you call new on an entity, it is in a Transient state. Hibernate doesn't know how to handle it. It doesn't have a database ID and is not part of the context for hibernate to manage (make the relevent queries etc).
To make an entity managed you need to save it via the repo.
I.e. roleRepo.save(role)
You will then notice it then has an Id and is now managed by hibernate.
#Service
public class UserServiceImpl implements UserService {
#Transactional
#Override
public User CreateUser(User user, Set<UserRole> userRoles) {
User localUser = userRepository.findByUserName(user.getUserName());
if (localUser != null) {
LOG.warn("Username {} already exists", user.getUserName());
} else {
// For each transient UserRole passed in, save the Role.
// Role is now managed.
for (UserRole userRole : userRoles) {
roleRepository.save(userRole.getRole());
LOG.error("inside for {}", userRole.getRole().getRoleName());
}
user.getUserRoles().addAll(userRoles);
localUser = userRepository.save(user);
}
return localUser;
}
}
This service above doesn't maybe do what you expect.
You are getting the Roles and saving them.
You then don't replace the Role in UserRole with the managed one back from the repo.
Maybe this would work?
for(UserRole userRole:userRoles) {
//The role is now managed.
Role managedRole = roleRepository.save(userRole.getRole());
//Replace the transient role in the UserRole with a managed Role.
userRole.setRole(managedRole);
}
So when it goes on to save the User:
user.getUserRoles().addAll(userRoles);
localUser = userRepository.save(user);
The UserRoles (which are still transient) have a managed Role at least.
The Cascade.ALL should do what you expect (I am unsure mind!) and save the transient UserRoles because Cascade.ALL will save the children UserRoles.
https://www.baeldung.com/jpa-cascade-types
=============================
The second issue, not causing the problem in question, but you may want to go have a think about:
At the moment you have:
User 1 : M UserRole
UserRole M : 1 Role
1 User has many UserRoles.
1 Role has many UserRoles.
The modelling here just smells off.
Usually you'd have some Role entities/database entries that can be related to a user via ManyToMany relationship.
User signs up, is given the "USER" role in their Set<Role> userRoles rather than creating a new Role for each user with "USER" as a field.
So a user has a relationship to a role via a "join table" UserRole.
Spring can already create a join table for you. You do not really need the UserRole entity in your code as it stands as it just holds a FK_User and FK_Role.
Basically, you want:
User M:M Role
I.e. a user can have many roles.
Simply use the #ManyToMany annotation for a Many:Many relationship between user and roles.
To add a role you search the database for
Role findByRoleName
And add that managed entity to the user's roles and then persist the user.
ManyToMany Baeldung
I tried to insert an entity that contains foreign key from Spring boot JPA to MYSQL. But get error:
java.sql.SQLIntegrityConstraintViolationException: Column 'role_role_id' cannot be null.
I know it is an error when I insert null into a column that does not allow null. I know the fix(please see myMainController below) is get object Role (foreignkey) to set into my User object then insert User Object to DB (new row in DB) (the necessary data of Role is already in the DB). But I want when I insert User, it will auto insert Object Role because I have set the realtion clearly(more info please see my pictures in User Entity and Role Entity). Please see my Code.
This is ERR:
java.sql.SQLIntegrityConstraintViolationException: Column 'role_role_id' cannot be null.
This is myMainRestController
#RestController
public class MyMainController {
#Autowired
private UserRepository service;
#RequestMapping("/i")
#ResponseBody
public String Welcome() {
return "Welcome!";
}
#JsonIgnore
#RequestMapping(value = "/", //
method = RequestMethod.GET, //
produces = { MediaType.APPLICATION_JSON_VALUE })
#ResponseBody
public User initData() {
User user = new User();
user.setUserId(4);
user.setPassword("1234");
user.setFullname("Nguyễn Văn D");
user.setPhonenumber("012345678");
user.setAddress("2 đường Số 7 Bình Trị Dông");
user.setFontIdCardImg("p2fontid.jpg");
user.setBackIdCardImg("p2backid.jpg");
user.setRoleId(2);
user.setAreaId(2);
// Role role = new Role();
// role.setRoleId(user.getRoleId()); / My fix here
// user.setRole(role);
service.save(user);
return user;
}
}
This is my User Entity:
#Entity
#Table(name="user")
#NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="user_id", unique=true, nullable=false)
private Integer userId;
#Column(length=1000)
private String address;
#Column(name="area_id")
private Integer areaId;
#Column(name="back_id_card_img", length=200)
private String backIdCardImg;
#Column(name="font_id_card_img", length=200)
private String fontIdCardImg;
#Column(length=45)
private String fullname;
#Column(length=45)
private String password;
#Column(length=45)
private String phonenumber;
#Column(name="role_id")
private Integer roleId;
//bi-directional many-to-one association to Accident
#OneToMany(mappedBy="user")
private List<Accident> accidents;
//bi-directional many-to-one association to Area
#ManyToOne
#JoinColumn(name="area_area_id")
private Area area;
//bi-directional many-to-one association to Policeworktime
#ManyToOne
#JoinColumn(name="policeworktime_police_user_id")
private Policeworktime policeworktime;
//bi-directional many-to-one association to Role
#ManyToOne(cascade={CascadeType.ALL})
#JoinColumn(name="role_role_id", nullable=false)
private Role role;
public User() {
}
public Integer getUserId() {
return this.userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getAreaId() {
return this.areaId;
}
public void setAreaId(Integer areaId) {
this.areaId = areaId;
}
public String getBackIdCardImg() {
return this.backIdCardImg;
}
public void setBackIdCardImg(String backIdCardImg) {
this.backIdCardImg = backIdCardImg;
}
public String getFontIdCardImg() {
return this.fontIdCardImg;
}
public void setFontIdCardImg(String fontIdCardImg) {
this.fontIdCardImg = fontIdCardImg;
}
public String getFullname() {
return this.fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhonenumber() {
return this.phonenumber;
}
public void setPhonenumber(String phonenumber) {
this.phonenumber = phonenumber;
}
public Integer getRoleId() {
return this.roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public List<Accident> getAccidents() {
return this.accidents;
}
public void setAccidents(List<Accident> accidents) {
this.accidents = accidents;
}
public Accident addAccident(Accident accident) {
getAccidents().add(accident);
accident.setUser(this);
return accident;
}
public Accident removeAccident(Accident accident) {
getAccidents().remove(accident);
accident.setUser(null);
return accident;
}
public Area getArea() {
return this.area;
}
public void setArea(Area area) {
this.area = area;
}
public Policeworktime getPoliceworktime() {
return this.policeworktime;
}
public void setPoliceworktime(Policeworktime policeworktime) {
this.policeworktime = policeworktime;
}
public Role getRole() {
return this.role;
}
public void setRole(Role role) {
this.role = role;
}
}
This is my Role Entity:
#Entity
#Table(name="role")
#NamedQuery(name="Role.findAll", query="SELECT r FROM Role r")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="role_id", unique=true, nullable=false)
private Integer roleId;
#Column(name="role_name", length=45)
private String roleName;
//bi-directional many-to-one association to User
#OneToMany(mappedBy="role", cascade={CascadeType.ALL})
private List<User> users;
public Role() {
}
public Integer getRoleId() {
return this.roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return this.roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public List<User> getUsers() {
return this.users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public User addUser(User user) {
getUsers().add(user);
user.setRole(this);
return user;
}
public User removeUser(User user) {
getUsers().remove(user);
user.setRole(null);
return user;
}
}
You don't need to create Colunm roleId. You just put relational with tables. For example in my situation, I have OneToMany User with Role. I only established relational in my MySql DB. And then, you can create your own entities or use the JPA tool at your disposal. User entity has a object Role, Role entity has a List.
#Entity
#Table(name="role")
#NamedQuery(name="Role.findAll", query="SELECT r FROM Role r")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="role_id", unique=true, nullable=false)
private Integer roleId;
#Column(name="role_name", length=45)
private String roleName;
//bi-directional many-to-one association to User
#OneToMany(mappedBy="role")
private List<User> users;
public Role() {
}
public Integer getRoleId() {
return this.roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return this.roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public List<User> getUsers() {
return this.users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public User addUser(User user) {
getUsers().add(user);
user.setRole(this);
return user;
}
public User removeUser(User user) {
getUsers().remove(user);
user.setRole(null);
return user;
}
}
#Entity
#Table(name = "user")
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
// bi-directional many-to-one association to Role
#ManyToOne
#JoinColumn(name = "role_role_id", nullable = false)
private Role role;
public Role getRole() {
return this.role;
}
public void setRole(Role role) {
this.role = role;
}
}
I'm trying to add a functionality to my webapp with user registration. Webapp is based on spring boot, hibernate and mysql database, frontend is in angular. Generally, the user creation procedure is working correctly, user data is correctly send from frontend to backend via json and saved to the database in shop_user table (with all the user data, such as name, surname, address etc.), but it DOESN'T have role column.
I also have table 'role', which should be:
id name
1 USER
2 ADMIN
and joined table user_role, which consists of user_id from table shop_user and role id from table role, so it should look like this:
id_user id_role
1 2
2 1
3 1
When user is being created on the website, it is hard-coded to set the role by default to USER. This seems to work quite well as it adds a new row in shop_user, and it adds a row to user_role, but... it also creates a new row in 'role' table.
so in the end 'role' table looks like this:
id name
1 ADMIN
2 USER
3 USER
4 USER
5 USER
99 USER
`
while this is not a blocking bug that stops application from working, it is not 'as it should work' unfortunately... as the table should only consist of two role rows (and possibly additional ones, in the future), but not multiplicated for each user!
here's the flawed code of user:
User
#Entity
#Table(name = "shop_user")
public class User extends AbstractEntity {
#Column
private String firstName;
#Column
private String lastName;
#Column
private String addressLine;
#Column
private String city;
#Column
private String country;
#Column
private String zipCode;
#Column
private String phoneNumber;
#Column
private String email;
#Column
private String password;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "id_user", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "id_role", referencedColumnName = "id"),
uniqueConstraints = {#UniqueConstraint(columnNames = {"id_user", "id_role"})})
private List<Role> roles;
public User() {
}
public User(User user) {
setId(user.getId());
this.firstName = user.getFirstName();
this.lastName = user.getLastName();
this.addressLine = user.getAddressLine();
this.city = user.getCity();
this.country = user.getCountry();
this.zipCode = user.getZipCode();
this.phoneNumber = user.getPhoneNumber();
this.email = user.getEmail();
this.password = user.getPassword();
this.roles= user.getRoles();
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getAddressLine() {
return addressLine;
}
public void setAddressLine(String addressLine) {
this.addressLine = addressLine;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
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;
}
}
Role implementation:
Role
#Entity
#Table(name = "role")
public class Role extends AbstractEntity {
#Column
private String name;
#ManyToMany(mappedBy = "roles", cascade = CascadeType.PERSIST)
private List<User> users;
public Role(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
Abstract entity:
AbstractEntity
#MappedSuperclass
public abstract class AbstractEntity implements Persistable<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public void setId(Long id) {
this.id = id;
}
#Override
public Long getId() {
return id;
}
#Override
public boolean isNew() {
return id == null;
}
}
User service:
UserServiceImpl
#Service
public class UserServiceImpl extends AbstractServiceImpl<User, UserDTO> implements UserService {
private final UserRepository userRepository;
private final UserConverter userConverter;
public UserServiceImpl(UserRepository userRepository, UserConverter
userConverter) {
this.userRepository = userRepository;
this.userConverter = userConverter;
}
#Override
protected JpaRepository<User, Long> getRepository() {
return userRepository;
}
#Override
protected Converter<User, UserDTO> getConverter() {
return userConverter;
}
#Override
#Transactional
public User registerUser(User user) {
List<Role> roles = new LinkedList<>();
roles.add(new Role("USER"));
user.setRoles(roles);
return userRepository.save(user);
}}
I am nearly sure that this comes to the relations mapping in Hibernate and object creation, but can't quite figure it out...
Any help will be appreciated, thank you!
The issue is here:
#Override
#Transactional
public User registerUser(User user) {
List<Role> roles = new LinkedList<>();
roles.add(new Role("USER"));
user.setRoles(roles);
return userRepository.save(user);
}}
Since the relationship User -> Role is cascade persist, the (new) role new Role("USER") is also persisted and you ended up with a new Role for each user instead of reusing the existing one.
The solution is to check the existence of a Role with name = USER. If doesn't exist, insert it. Otherwise add the existent one to the roles collection.
I have integrated the role and want to manage the access of the specific service on the basis of the role.
An admin can create an AGENT and that agent came under the group of ADMIN user.
What I basically did have create a 1 to Many relation ship because my user could have only 1 role.
#Entity
#Table(name = "role")
public class Role {
private Long id;
private String name;
private Collection<User> users;
public Role() {
}
public Role(String name) {
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
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;
}
#OneToMany(mappedBy = "role", cascade = CascadeType.ALL)
public Collection<User> getUsers() {
return users;
}
public void setUsers(Collection<User> users) {
this.users = users;
}
}
And here I have the user which the group relation as well the group is also 1 TO MANY because a user_admin can have multiple agents but agent could not have multiple admins.
#Entity
#Table(name = "user")
public class User {
private long id;
private String username;
private String password;
private boolean enabled = false;
private Role role;
private UserGroup userGroup;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User(String username, String password, Role role) {
this.username = username;
this.password = password;
this.role = role;
}
public User(String username, String password, boolean enabled, Role role, UserGroup userGroup) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.role = role;
this.userGroup = userGroup;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#NotBlank
#Column(nullable = false, updatable = false, unique = true)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#NotBlank
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(nullable = false, updatable = true)
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
#ManyToOne
#JoinColumn(name = "role_id", referencedColumnName = "id")
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
#ManyToOne
#JoinColumn(name = "user_group_id", referencedColumnName = "id")
public UserGroup getUserGroup() {
return userGroup;
}
public void setUserGroup(UserGroup userGroup) {
this.userGroup = userGroup;
}
}
At the time of creation of the user I specify the role and the group as well.
And in the SecurityConfig I have configure like this.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(Constants.BASE_URL_FILE_UPLOADER + Constants.URL_UPLOAD_FILE).hasRole("ADMIN") .anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.exceptionHandling().authenticationEntryPoint(new ContentSearcherAuthenticationEntryPoint());
}
But If I am accessing this end-point with the admin user I throws forbidden and also in the function when I access authentication.getAuthorities()it return emptyList
ResponseEntity<JsonNode> uploadFile(#RequestParam("file") MultipartFile file, Authentication authentication) {
logger.info("Authentication is [{}] and user is [{}]", authentication.getAuthorities(), authentication.getName()); // []
}
I am confused in UserDetailsService I have also added the GrantedAuthority as well like this.
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository userRepository;
private RoleRepository roleRepository;
public UserDetailsServiceImpl(UserRepository userRepository, RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username + " Not Exists");
}
user.setEnabled(true);
userRepository.save(user);
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.isEnabled(),
true, true, true, getAuthorities(user.getRole()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Role role) {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(Constants.ROLE_PREFIX + role.getName()));
return authorities;
}
}
What I am missing is there any more configuration I have to add ?
I am using JWT for the authentication purpose some thing should not to be added in this as well ?
Because when I get the Authentication in the JWT successfulAuthentication it shows the Authorities.
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
logger.info("Auth in the successful is [{}]", authResult.getAuthorities()); // [ROLE_ADMIN]
}
Using Spring-Security core 5.0.9.
I did it after getting the hint from #Reza Nasiri question basically I was not adding the Authorities when authenticating the JWT token now what I did is,
return new UsernamePasswordAuthenticationToken(user, null, getAuthorities("ADMIN");
In my getAuthentication function in JWTAuthorizationFilter class.
I have a problem with Rest response when I use ManyToMany annotation.
Problem is this answer:
Problem accessing /json2/1. Reason:
Server Error
Caused by:
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.Tomek.entity.User.roles, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.Tomek.entity.Role["users"]->org.hibernate.collection.internal.PersistentBag[0]->com.Tomek.entity.User["roles"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.Tomek.entity.User.roles, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.Tomek.entity.Role["users"]->org.hibernate.collection.internal.PersistentBag[0]->com.Tomek.entity.User["roles"])
Without ManyToMany annotation(like in the Model class Role) I response JSON format
[{"id":1,"name":"ROLE_USER"},{"id":2,"name":"ROLE_ADMIN"}]
RestController
#Controller
public class RestController {
#Autowired
private UserService userService;
#Autowired
private BlogService blogService;
#Autowired
private RoleService roleService;
#RequestMapping("/json")
public String JsonLink(Model model){
model.addAttribute("result", blogService.findAll());
return "json";
}
#RequestMapping(value = "/json2/{id}", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody List<Role> ShowJson(#PathVariable int id) {
Hibernate.initialize(roleService.findAll());
List<Role> role = roleService.findAll();
System.out.println(role.toString());
return role;
}
Model Role (comment #ManyToMany)
#Entity
#JsonAutoDetect
public class Role {
#Id
#GeneratedValue
private Integer id;
private String name;
/*#ManyToMany(fetch = FetchType.EAGER,mappedBy = "roles")
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}*/
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Role(String name) {
this.name = name;
}
public Role() {
}
#Override
public String toString() {
return "Role [id=" + id + ", name=" + name + "]";
}
}
Model User
#Entity
#JsonAutoDetect
public class User {
#Id
#GeneratedValue
private Integer id;
private String name;
private String email;
private String password;
private boolean enabled;
#ManyToMany
#JoinTable
private List<Role> roles;
#OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE)
private List<Blog> blogs;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public List<Blog> getBlogs() {
return blogs;
}
public void setBlogs(List<Blog> blogs) {
this.blogs = blogs;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
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 getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Service
#Service
public class RoleService {
#Autowired
private RoleRepository roleRepository;
public List<Role> findAll(){
return roleRepository.findAll();
}
JSP
<c:forEach items="${result}" var="item">
json
</c:forEach>
The problem is with serializing property roles of User entity. When you load your entity in RoleService and then return result to controller the hibernate session is ended. You are not able to load it outside hibernate session.
You cannot load these property eagerly too. Then there would be a chance to load large tree of objects.
In my opinion to solve your proplem you have to create 3 REST services and 3 normal service method which would load flat data structures:
/roles/{id} - it loads single role (without users property)
/roles/{id}/users - it loads all users that have role with given id (without roles property)
/users/{id}/roles - it loads roles for user with given id
Additionaly you have to annotate your collection properties (roles, users) with annotation #JsonIgnore to ignore them during serialization to json.