This test project becouse of I don't know how I inject #AuthenticationPrincipal annotation in method ,I have some problem,#AuthenticationPrincipal UserDetails throws exception
UserDetails userDetails = SecurityContextHolder.getContext().getAuthentication().getPrincipal()
is working not problem but I want to inject this annotation in method how can I do ?
When I added annotation in method this exception is thrown
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: No primary or default constructor found for interface org.springframework.security.core.userdetails.UserDetails
How can I inject #AuthenticationPrincipal to index method in MainControll class ?
How can I solve this situation ?
MainControll.class
package com.sencerseven.springsecurityauth.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class MainController {
#RequestMapping(value = {"/"})
public ModelAndView index(#AuthenticationPrincipal UserDetails userDetails) {
ModelAndView mv = new ModelAndView("index");
mv.addObject("user", userDetails);
return mv;
}
#RequestMapping(value = {"/login"})
public ModelAndView loginPage(#RequestParam(name="error",required = false)String error,
#RequestParam(name="logout",required = false)String logout) {
ModelAndView mv = new ModelAndView("login");
mv.addObject("userClickLoginPage",true);
return mv;
}
#RequestMapping("/perform-logout")
public String logout(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null) {
new SecurityContextLogoutHandler().logout(httpServletRequest, httpServletResponse, authentication);
}
return "redirect:/";
}
}
This class is my main controller
WebSecurityConfig.class
package com.sencerseven.springsecurityauth.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
public static BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Configuration
public static class HomeConfigLogin extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").permitAll().and()
.formLogin().loginPage("/login").usernameParameter("username").passwordParameter("password")
.defaultSuccessUrl("/", true).loginProcessingUrl("/login").and().logout().and().exceptionHandling()
.accessDeniedPage("/").and().csrf();
}
}
}
MyUserDetailService
package com.sencerseven.springsecurityauth.security;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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.sencerseven.springsecurityauth.dao.UserDAO;
import com.sencerseven.springsecurityauth.dto.Role;
#Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserDAO userDAO;
#Transactional
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
com.sencerseven.springsecurityauth.dto.User user = userDAO.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getRole());
return buildUserForAuthentication(user, authorities);
}
private User buildUserForAuthentication(com.sencerseven.springsecurityauth.dto.User user,
List<GrantedAuthority> authorities) {
return new User(user.getUsername(), user.getPassword(), user.isEnabled(), true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<Role> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
for (Role userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
}
User.class
package com.sencerseven.springsecurityauth.dto;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
#Entity
#Table(name = "User")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private boolean enabled;
private String email;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> role;
public User() {
}
public User(String username, String password, boolean enabled, String email, Set<Role> role) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.email = email;
this.role = role;
}
public int getId() {
return id;
}
public void setId(int 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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Set<Role> getRole() {
return role;
}
public void setRole(Set<Role> role) {
this.role = role;
}
#Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + ", enabled=" + enabled
+ ", email=" + email + ", role=" + role + "]";
}
}
Role.class
package com.sencerseven.springsecurityauth.dto;
import java.io.Serializable;
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.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
#Entity
#Table(name = "Role")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String role;
#ManyToMany(mappedBy = "role", fetch = FetchType.LAZY)
private Set<User> user;
public Role() {
}
public Role(String role, Set<User> user) {
this.role = role;
this.user = user;
}
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;
}
public Set<User> getUser() {
return user;
}
public void setUser(Set<User> user) {
this.user = user;
}
#Override
public String toString() {
return "Role [id=" + id + ", role=" + role + ", user=" + user + "]";
}
}
if you use WebMvcConfigurationSupport and #AuthenticationPrincipal than you must add a AuthenticationPrincipalArgumentResolver:
public class WebMvcConfig extends WebMvcConfigurationSupport {
...
#Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthenticationPrincipalArgumentResolver());
}
Related
I am new to spring boot as after completing a crud function rest API in spring-boot I'm badly stuck in login rest API for android application where I just want to use email and password in POSTMAPPING. Please anyone could help me here?
Controller class
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import demo.loginapi.main.model.UserModel;
import demo.loginapi.main.repository.UserRepository;
import demo.loginapi.main.repository.UserService;
#RestController
#RequestMapping("/api/user")
public class UserController {
#Autowired
UserRepository userRepository;
#Autowired
UserService userService;
#GetMapping("/getallusers")
public List<UserModel> getAllCustomer(){
List<UserModel> alluserlist = userRepository.findAll();
return alluserlist;
}
#PostMapping("/login/{email}")
public List<UserModel> getemailpassword(#PathVariable(value = "email") String email){
List<UserModel> alluseremailpassword = userRepository.findUserByEmail(email);
return alluseremailpassword;
}
#PostMapping("/login2/{email}")
public UserModel getuserbyemail(#PathVariable(value = "email") String email)
{
UserModel userByEmailForLogin = userRepository.findByEmail(email);
return userByEmailForLogin;
}
#PostMapping("/loginemailpass/{email}/{password}")
public ResponseEntity<Map<String, String>> loginUser(#RequestBody Map<String, Object> userMap){
String email = (String) userMap.get("email");
String password = (String) userMap.get("password");
UserModel userModel = userService.validate(email,password);
return new ResponseEntity<>(generateJWTToken(userModel),HttpStatus.OK);
}
private Map<String, String> generateJWTToken(UserModel userModel) {
// TODO Auto-generated method stub
return null;
}
}
** Entity Class **
package demo.loginapi.main.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "user_login")
public class UserModel {
#Id
#Column(name = "userId", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
#Column(name = "username", nullable = false)
private String username;
#Column(name = "email", nullable = false)
private String email;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "role", nullable = false)
private String role;
public UserModel() {
//default constructor
}
public UserModel(Long userId, String username, String email, String password, String role) {
//super();
this.userId = userId;
this.username = username;
this.email = email;
this.password = password;
this.role = role;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
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 getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
#Override
public String toString() {
return "UserModel [userId=" + userId + ", username=" + username + ", email=" + email + ", password=" + password
+ ", role=" + role + "]";
}
}
** Repository Class **
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import demo.loginapi.main.model.UserModel;
#Repository
public interface UserRepository extends JpaRepository<UserModel, Long>{
List<UserModel> findUserByEmail(String email);
UserModel findByEmail(String email);
//UserModel validate(String email, String password);
UserModel findByEmailAndPaswword(String email, String password);
//UserModel findByEmailPassword(String email, String password);
}
** Service Class **
import demo.loginapi.main.model.UserModel;
public interface UserService {
UserModel validate(String email, String password);
}
** Service Implementation **
package demo.loginapi.main.repository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import demo.loginapi.main.model.UserModel;
#Service
public class UserRepoImpl implements UserService{
#Autowired
UserRepository userRepository;
#Override
public UserModel validate(String email, String password) {
if(email != null) email = email.toLowerCase();
return userRepository.findByEmailAndPaswword(email, password);
}
}
In your repository class , is findByEmailAndPassword is available in jparepository, if not use through #query annotation like #query("select usename from userlogin us where us.username =:email") Something like that in your method.
Also if pre defined method is available please check the spelling, seems incorrect. It has double w in it. findByEmailAndPaswword
The method getUserAuthority(java.util.Set<com.djamware.springsecuritymongodb.domain.Role>) in the type CustomUserDetailsService is not applicable for the arguments (java.util.Set<javax.management.relation.Role>)
Why giving this error?
This Java file is under package com.djamware.springsecuritymongodb.services and User.java is under package com.djamware.springsecuritymongodb.domain
package com.djamware.springsecuritymongodb.services;
import com.djamware.springsecuritymongodb.domain.Role;
import com.djamware.springsecuritymongodb.domain.User;
import com.djamware.springsecuritymongodb.repositories.RoleRepository;
import com.djamware.springsecuritymongodb.repositories.UserRepository;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
#Service
public class CustomUserDetailsService implements UserDetailsService{
#Autowired
private UserRepository userRepository;
#Autowired
private RoleRepository roleRepository;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
public User findUserByEmail(String email) {
return userRepository.findByEmail(email);
}
public void saveUser(User user) {
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
user.setEnabled(true);
Role userRole = roleRepository.findByRole("ADMIN");
// user.setRoles(new HashSet<>(Arrays.asList(userRole)));
user.setRoles(new HashSet<Role>(Arrays.asList(userRole)));
userRepository.save(user);
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if(user != null) {
List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
return buildUserForAuthentication(user, authorities);
} else {
throw new UsernameNotFoundException("username not found");
}
}
private List<GrantedAuthority> getUserAuthority(Set<Role> userRoles) {
Set<GrantedAuthority> roles = new HashSet<GrantedAuthority>();
for (Role role : userRoles) {
roles.add(new SimpleGrantedAuthority(role.getRole()));
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>(roles);
return grantedAuthorities;
}
private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}
}
I have created User Class attached here.
User.java
package com.djamware.springsecuritymongodb.domain;
import java.util.HashSet;
import java.util.Set;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import javax.management.relation.Role;
#Document(collection = "user")
public class User {
#Id
private String id;
#Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)
private String email;
private String password;
private String fullname;
private boolean enabled;
#DBRef
private Set<Role> roles;
public String getId() {
return id;
}
public void setId(String 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 getFullname() {
return fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public void setRoles(HashSet<com.djamware.springsecuritymongodb.domain.Role> hashSet) {
// TODO Auto-generated method stub
}
}
The reason is that the Role that CustomUserDetailsServices getUserAuthority(Set<Role> userRoles) is expecting com.djamware.springsecuritymongodb.domain.Role (see its imports). However, User is returning javax.management.relation.Role (see its imports). To fix it, remove import javax.management.relation.Role; from the User class. Since User and Role are in the same package you don't need to explicitly import anything.
I tried to add One To Many Annotation to my spring boot project, but when I run my project I get "Failed to execute CommandLineRunner " error.
I wanted users in the user's table to have more than one city. So, I tried to add OneToMany Annotation.
You can see the error at the attachment.
User Class
package io.javabrains.springsecurity.jpa.models;
import com.spring.weather.*;
import java.util.*;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
#Entity
#Table(name="app_user")
public class User {
#Id
#GeneratedValue(strategy =GenerationType.AUTO)
private int id;
private String userName;
private String password;
private boolean active;
private String role;
private String city;
#OneToMany(targetEntity = UserCity.class,cascade = CascadeType.ALL)
#JoinTable(name="USER_CITY",joinColumns=#JoinColumn(name="m_user_id"),
inverseJoinColumns=#JoinColumn(name="cityId"))
private List<UserCity> usercity;
public User() {
super();
// TODO Auto-generated constructor stub
}
public List<UserCity> getUsercity() {
return usercity;
}
public void setUsercity(List<UserCity> usercity) {
this.usercity = usercity;
}
public int getId() {
return id;
}
public void setId(int 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 boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
User City Class
package io.javabrains.springsecurity.jpa.models;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name="user_city")
public class UserCity {
#Id
#GeneratedValue(strategy =GenerationType.AUTO)
private int cityId;
private String cityName;
public UserCity() {
super();
// TODO Auto-generated constructor stub
}
public UserCity(int cityId, String cityName, User mUser) {
super();
this.cityId = cityId;
this.cityName = cityName;
this.mUser = mUser;
}
#ManyToOne
private User mUser;
public int getCityId() {
return cityId;
}
public void setCityId(int cityId) {
this.cityId = cityId;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public User getmUser() {
return mUser;
}
public void setmUser(User mUser) {
this.mUser = mUser;
}
}
User Repository
package io.javabrains.springsecurity.jpa;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import io.javabrains.springsecurity.jpa.models.User;
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByUserName(String userName);
}
User City Repository
package io.javabrains.springsecurity.jpa;
import org.springframework.data.jpa.repository.JpaRepository;
import io.javabrains.springsecurity.jpa.models.User;
import io.javabrains.springsecurity.jpa.models.UserCity;
public interface CityRepository extends JpaRepository<UserCity,id>{
}
Spring Application Class
package io.javabrains.springsecurity.jpa;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.spring.weather.WeatherService;
import io.javabrains.springsecurity.jpa.models.User;
import io.javabrains.springsecurity.jpa.models.UserCity;
#SpringBootApplication
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
public class SpringsecurityApplication implements CommandLineRunner{
#Bean
public WeatherService ws() {
return new WeatherService ();
}
#Autowired
UserRepository userRepository;
CityRepository cityRepository;
public static void main(String[] args) {
SpringApplication.run(SpringsecurityApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
System.out.println("Application Running.");
User adminUser= new User();
UserCity ucity=new UserCity();
UserCity ucity2=new UserCity();
ucity.setCityName("amsterdam");
adminUser.setUserName("Admin");
adminUser.setPassword(new BCryptPasswordEncoder().encode("pass"));
adminUser.setRole("ROLE_ADMIN");
adminUser.setActive(true);
adminUser.setCity("bologna");
ucity.setmUser(adminUser);
userRepository.save(adminUser);
cityRepository.save(ucity);
User newUser= new User();
newUser.setUserName("User");
newUser.setPassword(new BCryptPasswordEncoder().encode("pass"));
newUser.setRole("ROLE_USER");
newUser.setActive(true);
newUser.setCity("maribor");
ucity2.setmUser(newUser);
userRepository.save(newUser);
cityRepository.save(ucity2);
}
}
The problem you are encountering, more specifically the NullPointerException at line 54 of your main application, is caused by the fact that the cityRepository is not
instantiated.
Looking through your configuration, I see that you only register the UserRepository with the #EnableJpaRepositories annotation.
Try adding also the CityRepository to the #EnableJpaRepositories, and also specify this bean as a candidate for autowiring( also add #Autowired to this bean, as you did for UserRepository)
For a good practice, following the MVC structure, it would be nice is all your spring repositories, the beans responsible for all CRUD operations with the database to be under the same package
Im learning Spring Security with REST, and I have 2 MySQL tables, one for users and one for authorities. The user_id from authorities table is foreign key for id from user table. I want to use these roles from authorities table with Spring Security for role-based authorisation. But I have a problem at the begining.
When I try to do a POST request to insert some data in these 2 tables the user_id from authorities table is NULL.
This is the code:
User entity:
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
#Entity
public class ApplicationUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String username;
private String password;
#OneToMany(mappedBy="applicationUser", cascade=CascadeType.ALL)
private List<Authorities> authorities;
public ApplicationUser() {
}
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 List<Authorities> getAuthorities() {
return authorities;
}
public void setAuthorities(List<Authorities> authorities) {
this.authorities = authorities;
}
}
Authorities entity:
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import com.fasterxml.jackson.annotation.JsonIgnore;
#Entity
public class Authorities {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String role;
#JsonIgnore
#ManyToOne(cascade= {CascadeType.DETACH, CascadeType.MERGE,
CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="user_id")
private ApplicationUser applicationUser;
public Authorities() {
}
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 ApplicationUser getApplicationUser() {
return applicationUser;
}
public void setUser(ApplicationUser applicationUser) {
this.applicationUser = applicationUser;
}
}
REST Controller:
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class UserController {
#Autowired
private ApplicationUserRepository applicationUserRepository;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#GetMapping("/sign-up")
public List<ApplicationUser> getUsers() {
return applicationUserRepository.findAll();
}
#PostMapping("/sign-up")
public void signUp(#RequestBody ApplicationUser user) {
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
applicationUserRepository.save(user);
}
}
And when I try to do a POST request with this data:
{
"username": "ion",
"password": "1234",
"authorities": [
{
"role": "ROLE_USER"
}
]
}
the response is 200 but the MySQL tables look like this:
So, the user table looks fine, but the authorities table have the user_id cell NULL, and it should be 82.
I want to know how to resolve this problem and any feedback will be appreciated.
This question already has answers here:
Springboot Security hasRole not working
(3 answers)
Closed 4 years ago.
I am using spring security in my spring boot app.
My app works well with in-memory authentication.
But when users are loaded from the database it does not authenticate. It returns 403 access denied error code. UserDetailsService is able to fetch the user information from the database but I dont know where it is going wrong. I am new to spring.
here is my complete code related to security
User.java
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import lombok.Data;
#Entity
#Table(name = "user")
#Data
public class User {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
#Column(name = "USERNAME", unique = true)
#NotNull
private String username;
#Column(name = "PASSWORD")
#NotNull
private String password;
#Column(name = "DISPLAY_NAME")
private String displayName;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "id")
private Set<Role> userRoles;
private String profilePicturePath;
}
Role.java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
#Entity
#Table(name = "roles")
#Data
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String role;
}
UserRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.cloudsofts.cloudschool.people.users.pojos.User;
#Repository("userRepository")
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
RoleRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.cloudsofts.cloudschool.people.users.pojos.Role;
#Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
}
UserService.java
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.cloudsofts.cloudschool.people.users.pojos.User;
import com.cloudsofts.cloudschool.people.users.repositories.UserRepository;
#Service
public class UserService {
#Autowired
UserRepository userRep;
#Autowired
private PasswordEncoder passwordEncoder;
public List<User> getAllUsers() {
List<User> users = userRep.findAll();
return users;
}
public void addUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRep.save(user);
}
public void updateUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRep.save(user);
}
public void deleteUser(Long id) {
userRep.delete(id);
}
public User getUser(Long id) {
return userRep.findOne(id);
}
}
RoleService.java
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cloudsofts.cloudschool.people.users.pojos.Role;
import com.cloudsofts.cloudschool.people.users.repositories.RoleRepository;
#Service
public class RoleService {
#Autowired
RoleRepository userRoleRep;
public void addUserRole(Role role) {
userRoleRep.save(role);
}
public void updateUserRole(Role role) {
userRoleRep.save(role);
}
public void deleteUserRole(Long id) {
userRoleRep.delete(id);
}
public Role getUserRole(Long id) {
return userRoleRep.findOne(id);
}
public List<Role> getAllUserRoles() {
return userRoleRep.findAll();
}
}
CustomUserDetails.java
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import lombok.Data;
#Data
public class CustomUserDetails implements UserDetails {
private static final long serialVersionUID = 1L;
private User user;
public CustomUserDetails(final User user) {
this.user = user;
}
public CustomUserDetails() {
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
final Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
Set<Role> roles = null;
if (user != null) {
roles = user.getUserRoles();
}
if (roles != null) {
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getRole()));
}
}
return authorities;
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
CustomUserDetailsService.java
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.cloudsofts.cloudschool.people.users.pojos.CustomUserDetails;
import com.cloudsofts.cloudschool.people.users.pojos.Role;
import com.cloudsofts.cloudschool.people.users.pojos.User;
import com.cloudsofts.cloudschool.people.users.repositories.UserRepository;
#Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
} else {
System.out.println("______________________________________________________________");
System.out.println("username: " + user.getUsername());
System.out.println("password: " + user.getPassword());
System.out.println("Roles: ");
for (Role role : user.getUserRoles()) {
System.out.println(role.getRole());
}
System.out.println("______________________________________________________________");
return new CustomUserDetails(user);
}
}
}
SecurityConfig.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.cloudsofts.cloudschool.security.CustomUserDetailsService;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Qualifier("userDetailsService")
#Autowired
CustomUserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
try {
auth.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder());
System.out.println("_________________________________________________");
String username = SecurityContextHolder.getContext().getAuthentication().getName();
System.out.println("_________________________________________________");
System.out.println("You have logged in as " + username);
System.out.println("_________________________________________________");
} catch (Exception e) {
System.out.println("_________________________________________________");
System.out.println(e.getMessage());
System.out.println("_________________________________________________");
}
}
#Bean(name = "passwordEncoder")
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/*
* #Autowired public void configureGlobal(AuthenticationManagerBuilder auth)
* throws Exception {
* auth.inMemoryAuthentication().withUser("student").password("student").roles(
* "student").and().withUser("admin") .password("admin").roles("admin"); }
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// http.authorizeRequests().anyRequest().permitAll();
// http.authorizeRequests().antMatchers("/api/**").permitAll();
http.authorizeRequests().antMatchers("/student/**").hasAnyRole("student", "admin");
http.authorizeRequests().antMatchers("/api/admin/**").hasRole("admin");
http.authorizeRequests().antMatchers("/library/**").hasAnyRole("librarian", "admin");
http.httpBasic();
// http.formLogin().and().logout().logoutSuccessUrl("/login?logout").permitAll();
}
}
Screenshots
Postman Screenshot
Browser Screenshot
Users in the db
Roles in the db
User-Role mapping
Console output after giving the credentials
You seems to using the BCryptPasswordEncoder to encrypt and decrypt password. But 'Users' table screenshot shows the password in plain text. Can you validate the place where you are saving or updating the user, it is actually encoding the password and also the password encoder bean is of type 'BCryptPasswordEncoder'
I got the problem solved.
I had to add ROLE_ as a prefix to the roles.
Now everything works fine