How to retrieve all data of the current User in controller? - spring

I need take all data of the current user which is logged in and send it in JSON format into the route "/home". I was searching how to do it, but nothing.. I found that i can take only username and authorities there. Can someone help me to handle it? Thanks all.
There is my AuthController.java
// Getting all user data
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String getUsersDataById(Principal principal) {
return principal.getName();
}
There is my UserRepository
import com.example.demo.Models.Users;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<Users, Integer> {
Optional<Users> findByUsername(String username);
}
There are my services:
MyUserDetails.java
public class MyUserDetails implements UserDetails {
private String username;
private String password;
private String firstname;
private String lastname;
private String email;
private String last_login_date;
private String registration_date;
private String last_login_ip;
private Integer balance;
private Integer status;
private String brith_date;
private List<GrantedAuthority> authorities;
private boolean active;
public MyUserDetails(Users user) {
this.username = user.getUsername();
this.password = user.getPassword();
this.authorities = Arrays.stream(user.getRoles().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
this.active = user.isActive();
}
public MyUserDetails(String username, String firstname, String lastname, String email, String last_login_date, String registration_date, String last_login_ip, Integer balance, Integer status, String brith_date) {
this.username = username;
this.firstname = firstname;
this.lastname = lastname;
this.email = email;
this.last_login_date = last_login_date;
this.registration_date = registration_date;
this.last_login_ip = last_login_ip;
this.balance = balance;
this.status = status;
this.brith_date = brith_date;
}
public MyUserDetails() {
}
// and Override methods by default..
MyUserDetailsService
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Users> user = userRepository.findByUsername(username);
user.orElseThrow(() -> new UsernameNotFoundException("Not found: " + username));
return user.map(MyUserDetails::new).get();
}
}
There is Users.java with columns from table and geters and setters without constructor
#Entity
#Table(name = "users")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String username;
...
/// etc..
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
...
// etc..

Annotate your method with #AuthenticationPrincipal and use UserDetails.
#RequestMapping(value = "/home", method = RequestMethod.GET)
public Userdetails getUsersDataById(#AuthenticationPrincipal UserDetails userDetails) {
return userDetails;
}

Related

incompatible types: no instance(s) of type variable(s) X exist so that (model) conforms to org.springframework.security.core.userdetails.UserDetails

I am using spring's framework security and implementing the UserDetailsService Interface:
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
I override that method as follows:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return ownerRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
and I get the above error for the Owner model which is:
#Entity
#Table
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class Owner {
#Id
private int id;
private String username;
private String email;
private String password;
private String phone_number;
private boolean locked = false;
private boolean enabled = false;
public Owner(String username, String email, String password, String phone_number) {
this.username = username;
this.email = email;
this.password = password;
this.phone_number = phone_number;
}
}
The ownerRepository extends JpaRepository so I don't think there is any use of me importing that here. What is wrong here?

Spring Boot Security 403 "Access Denied"

I am making a RESTFul API (not web-app) and adding Spring Security but unable to do it successfully.
After going through a lot of articles and posts here on stackoverflow, I am finally posting my question. Kindly go through it and let me know what I am missing or configuring wrongly?
Base Entity
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID", nullable = false, updatable = false)
private Long ID;
#CreatedBy
#Column(name = "CreatedBy", nullable = false, updatable = false)
private String createdBy;
#CreatedDate
#Column(name = "CreatedDate", nullable = false, updatable = false)
private LocalDateTime createdDate;
#LastModifiedBy
#Column(name = "ModifiedBy")
private String modifiedBy;
#LastModifiedDate
#Column(name = "ModifiedDate")
private LocalDateTime modifiedDate;
...getters setters
}
Role Entity
#Entity
#Table(name = "ROLE")
public class Role extends BaseEntity {
#Column(name = "Name")
private String name;
...getters setters
}
User Entity
#Entity
#Table(name = "USER")
public class User extends BaseEntity {
#Column(name = "EmiratesID", unique = true, nullable = false, updatable = false)
private String emiratesID;
#Column(name = "FirstName")
private String firstName;
#Column(name = "LastName")
private String lastName;
#Column(name = "StaffID", unique = true, nullable = false, updatable = false)
private String staffID;
#Column(name = "Email", unique = true, nullable = false)
private String email;
#Column(name = "Password", nullable = false)
private String password;
#ManyToOne(optional = false, cascade = CascadeType.MERGE)
#JoinColumn(name = "ROLE_ID")
private Role role;
...getters setters
public UserDetails currentUserDetails() {
return CurrentUserDetails.create(this);
}
}
SecurtiyConfig Class
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final DataSource dataSource;
private final UserDetailsServiceImplementation userDetailsService;
#Autowired
public SecurityConfig(final DataSource dataSource, final UserDetailsServiceImplementation userDetailsService) {
this.dataSource = dataSource;
this.userDetailsService = userDetailsService;
}
#Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.antMatchers("/console/**").permitAll()
.antMatchers("/", "/greetUser", "/register", "/login").permitAll()
.antMatchers("/user/**").hasAnyAuthority(ROLES.USER.getValue(), ROLES.ADMIN.getValue())
.antMatchers("/admin/**").hasAuthority(ROLES.ADMIN.getValue()).anyRequest().authenticated();
httpSecurity.csrf().disable();
// required to make H2 console work with Spring Security
httpSecurity.headers().frameOptions().disable();
}
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity webSecurity) {
webSecurity.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
}
CurrentUserDetails
public class CurrentUserDetails implements UserDetails {
private String ROLE_PREFIX = "ROLE_";
private Long userID;
private String emiratesID;
private String firstName;
private String lastName;
private String staffID;
private String email;
private String password;
private Role role;
public CurrentUserDetails(Long ID, String emiratesID, String firstName,
String lastName, String staffID, String email,
String password, Role role) {
super();
this.userID = ID;
this.emiratesID = emiratesID;
this.firstName = firstName;
this.lastName = lastName;
this.staffID = staffID;
this.email = email;
this.password = password;
this.role = role;
}
public Long getUserID() {
return userID;
}
public String getEmiratesID() {
return emiratesID;
}
public String getEmail() {
return this.email;
}
public Role getRole() {
return this.role;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> grantedAuthority = new ArrayList<>();
grantedAuthority.add(new SimpleGrantedAuthority(ROLE_PREFIX + role.getName()));
return grantedAuthority;
}
#Override
public String getPassword() {
return this.password;
}
#Override
public String getUsername() {
return this.email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
/**
* Helper method to add all details of Current User into Security User Object
* #param user User
* #return UserDetails
*/
public static UserDetails create(User user) {
return new CurrentUserDetails(user.getID(), user.getEmiratesID(),
user.getFirstName(), user.getLastName(),
user.getStaffID(), user.getEmail(),
user.getPassword(), user.getRole());
}
}
UserDetailsService
#Component/#Service
public class UserDetailsServiceImplementation implements UserDetailsService {
private static final Logger userDetailsServiceImplementationLogger = LogManager.getLogger(UserDetailsServiceImplementation.class);
private final UserRepository userRepository;
#Autowired
public UserDetailsServiceImplementation(final UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (StringUtils.isEmpty(username)) {
userDetailsServiceImplementationLogger.error("UserDetailsServiceImplementation.loadUserByUsername() :: FAILED");
throw new UsernameNotFoundException("UserName is not passed");
}
User userFound = userRepository.findByEmail(username);
if (userFound == null) {
userDetailsServiceImplementationLogger.error("No user found with given username = {}", username);
throw new UsernameNotFoundException("No user found with given username");
}
return userFound.currentUserDetails();
}
}
UserController Class
#RestController
#RequestMapping(value = "/user")
public class UserController {
private static Logger userControllerLogger = LogManager.getLogger(UserController.class);
#Autowired
private PropertiesConfig propertiesConfig;
#Autowired
private UserManager userManager;
#RequestMapping(value = "/listAll", method = RequestMethod.GET)
public ResponseEntity<Map<String, Object>> getUsersList() {
userControllerLogger.info("UserController.getUsersList()[/listAll] :: method call ---- STARTS");
LinkedHashMap<String, Object> result = userManager.findAllUsers();
userControllerLogger.info("UserController.getUsersList()[/listAll] :: method call ---- ENDS");
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
AdminContrller Class
#RestController
#RequestMapping(value = "/admin")
public class AdminController {
private static final Logger adminControllerLogger = LogManager.getLogger(AdminController.class);
private final PropertiesConfig propertiesConfig;
private final UserManager userManager;
#Autowired
public AdminController(final PropertiesConfig propertiesConfig, final UserManager userManager) {
this.propertiesConfig = propertiesConfig;
this.userManager = userManager;
}
#RequestMapping(value = "/home", method = {RequestMethod.GET})
public ResponseEntity<String> adminPortal(#RequestBody String adminName) {
adminControllerLogger.info("AdminController.adminPortal()[/home] :: method call ---- STARTS");
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UserDTO adminUser = userManager.findUserByEmail(auth.getName());
if (adminUser == null) {
throw new UsernameNotFoundException(propertiesConfig.getProperty(ApplicationProperties.Messages.NO_USER_FOUND.getValue()));
}
adminControllerLogger.info("AdminController.adminPortal()[/home] :: method call ---- ENDS");
return new ResponseEntity<>(ApplicationConstants.GeneralConstants.WELCOME.getValue() + adminUser.getStaffID(), HttpStatus.OK);
}
}
data.sql
Tried with both values ROLE_USER/ADMIN and USER/ADMIN
INSERT INTO ROLE(ID, CreatedBy, CreatedDate, ModifiedBy, ModifiedDate, Name) VALUES (-100, 'Muhammad Faisal Hyder', now(), '', null, 'ROLE_ADMIN'/'ADMIN')
INSERT INTO ROLE(ID, CreatedBy, CreatedDate, ModifiedBy, ModifiedDate, Name) VALUES (-101, 'Muhammad Faisal Hyder', now(), '', null, 'ROLE_USER'/'USER')
INSERT INTO USER(ID, CreatedBy, CreatedDate, ModifiedBy, ModifiedDate, EmiratesID, FirstName, LastName, Email, StaffID, Password, ROLE_ID) VALUES (-1, 'Muhammad Faisal Hyder', now(), '', null, 'ABCDEF12345', 'Muhammad Faisal', 'Hyder', 'faisal.hyder#gmail.com', 'S776781', '$2a$10$qr.SAgYewyCOh6gFGutaWOQcCYMFqSSpbVZo.oqsc428xpwoliu7C', -100)
INSERT INTO USER(ID, CreatedBy, CreatedDate, ModifiedBy, ModifiedDate, EmiratesID, FirstName, LastName, Email, StaffID, Password, ROLE_ID) VALUES (-2, 'Muhammad Faisal Hyder', now(), '', null, 'BCDEFG12345', 'John', 'Smith', 'John.Smith#gmail.com', 'S776741', '$2a$10$j9IjidIgwDfNGjNi8UhxAeLuoO8qgr/UH9W9.LmWJd/ohynhI7UJO', -101)
I have attached all possible classes I think which are necessary. Kindly let me know what can be the issue.
Articles I went through;
SO-1, SO-2, SO-3, SO-4, Article-1, Article-2
Resolved
#dur thanks to you for pointing it out and others as well for their helpful insights.
1- Use ROLE_ in db entries.
2- Once prefix is added in db then no need to explicitly add this in
#Override
public Collection<? extends GrantedAuthority> getAuthorities(){...}
3- .and().httpBasic(); was missing from SpringSecurity configuration.
4- This is very detailed, might be helpful to others as well.
The problem I'm seeing is that you're granting access for authority ADMIN but you're not adding this authority to the CurrentUserDetails, you're just adding their role. You should add the authority as well, i.e.
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> grantedAuthority = new ArrayList<>();
grantedAuthority.add(new SimpleGrantedAuthority(ROLE_PREFIX + role.getName()));
// add authority in addition to role (no role prefix)
grantedAuthority.add(new SimpleGrantedAuthority(role.getName()));
return grantedAuthority;
}
As #dur pointed out in comments, I am adding answer to my question.
1- Use ROLE_ in db entries.
2- Once prefix is added in db then no need to explicitly add this in
#Override
public Collection<? extends GrantedAuthority> getAuthorities(){...}
3- .and().httpBasic(); was missing from SpringSecurity configuration.
Since this post is very detailed, might be helpful to others as well. For corrected answer kindly refer to my git repo

How can save order detail associated with the user and How can I return order data associated with the user details based the url parameters?

I have created the User and Order entities as bellow. What I want to achieve is that if http://localhost:8080/users/username? is given I want to return only the user detail based on username provided. if http://localhost:8080/users/username?detail=true, I want to return user detail and order details for the username provided. How can I achieve this?
User.java
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String userName;
private String password;
private String firstName;
private String lastName;
private String gender;
private String lastLoggedIn;
#OneToMany
List<Order> listOfOrder;
//constructors
//getter and setter
}
Order.java
#Entity
public class Order
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private float amount;
private String createdAt;
private String deliveredDate;
//constructors
//getter and setter
}
Controller.java
//CREATE CUSTOMER
#RequestMapping(method = POST, value = "/create")
public ResponseEntity createCustomerDetails(#RequestParam String userName, String password, String firstName,
String lastName, String gender) {
String lastLogged = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime());
User user = new User(userName, password, firstName, lastName, gender, lastLogged);
userRepository.save(user);
return ResponseEntity.status(OK).body(user.getId() + " User were successfully saved");
}
//CREATE ORDER
#RequestMapping(method = POST, value = "/order/{userName}")
public ResponseEntity createOrder(#PathVariable ("userName") String userName, #RequestParam float amount)
{
String createdAt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime());
String deliveredDate = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime());
User user = orderService.findUser(userName);
if (!user.equals(null))
{
Order order = new Order(amount,createdAt,deliveredDate);
user.getListOfOrder().add(order);
return ResponseEntity.status(OK).body("order details were saved under "+user.getUserName() + " "+user.getFirstName());
}
return ResponseEntity.status(NOT_FOUND).body(null + " was not found");
}
//GET THE USER DETAILS
#RequestMapping(method = GET, value = "/users/{userName}")
public ResponseEntity getUserDetail(#PathVariable("userName") String userName,
#RequestParam(defaultValue ="none", required = false) String detail) {
if (!detail.equals("none")){
return .....;
}else {
return ........;
}
}
UserRepository
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
User findByUserName(String userName);
}
If you're ok with doing the serialization manually, you can employ JsonView to determine what gets serialized.
https://www.baeldung.com/jackson-json-view-annotation
User.java
import com.fasterxml.jackson.annotation.JsonView;
public class User {
#JsonView(Views.Lite.class)
private String name;
#JsonView(Views.Full.class)
private List<Order> orders;
}
Views.java
public class Views {
public static class Lite {}
public static class Full extends Lite {}
}
UserController.java
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class UserController {
#Autowired
private UserRepository userRepository;
#Autowired
private ObjectMapper mapper;
#GetMapping("/user/{username}")
public ResponseEntity<String> getUserDetail(#PathVariable String username, #RequestParam(required = false) String detail) throws JsonProcessingException {
User user = userRepository.findByUserName(username);
Class viewClass = Views.Lite.class;
if (!StringUtils.isEmpty(detail)) {
viewClass = Views.Full.class;
}
return ResponseEntity.status(HttpStatus.OK)
.body(mapper.writerWithView(viewClass).writeValueAsString(user));
}
}

How to add a new role?

I have a user with admin and user roles, now I need to add ROLE_SUPPORT and restrict this role to only reading the list of users, how can I do this?
public class UserController {
#Autowired
UserService userService;
#RequestMapping(value = "getAll", method = RequestMethod.POST)
public List<User> getUsers() throws IOException {
return userService.getUsers();
}
#PostMapping("save")
#ResponseStatus(HttpStatus.OK)
public void save(#RequestBody User user) {
userService.save(user);
}
#RequestMapping(value = "delete", method = RequestMethod.POST)
public void delete(#RequestBody User user) {
userService.delete(user);
}
#RequestMapping(value = "getUser", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
#ResponseBody
public User getUser(#RequestBody RequestDto requestDto) throws IOException {
return userService.getUser(requestDto.getId());
}
I suppose a new method should be added to this controller, but I could be wrong
public class User extends BaseEntity<Integer> {
public enum Roles {
ADMIN
}
private String firstName;
private String lastName;
#Column(name = "username")
private String username;
#Convert(converter = PurshasedProductConverter.class)
private List<PurshasedProduct> purshasedProducts;
private String email;
private String activationCode;
#Convert(converter = AttachmentConverter.class)
private Attachment userAvatar;
public Attachment getUserAvatar() {
return userAvatar;
}
public void setUserAvatar(Attachment userAvatar) {
this.userAvatar = userAvatar;
}
#JsonProperty(access = Access.WRITE_ONLY)
private String password;
#JsonProperty(access = Access.WRITE_ONLY)
private String temporaryPassword;
#Convert(converter = StringArrayConverter.class)
private String[] roles;
private Date lastPasswordReset;
private Date dateCreated;
private Date dateUpdated;
private Date validatyTime;
private Boolean active;
public User() {
lastPasswordReset = dateCreated = dateUpdated = new Date();
roles = new String[]{"USER"};
}
That is, when requesting with the support role, a list of users should be returned.
Spring-Security provides this support by just adding #PreAuthorize annotation
#RequestMapping(value = "getAll", method = RequestMethod.GET)
**#PreAuthorize("hasRole('ROLE_SUPPORT')")**
public List<User> getUsers() throws IOException {
return userService.getUsers();
}

Spring Boot Data Rest JPA - Entity custom create (User)

I am trying to learn Spring. I created a project with Spring Boot using the following tools:
Spring Data JPA
Spring Data REST
Spring HATEOAS
Spring Security
I am trying to create a User entity. I want the user to have an encrypted password (+ salt).
When i do POST to /api/users i successfully create a new user.
{
"firstname":"John",
"lastname":"Doe",
"email":"johndoe#example.com",
"password":"12345678"
}
But i have 2 problems:
the password is saved in clear-text
the salt is null
+----+---------------------+-----------+----------+----------+------+
| id | email | firstname | lastname | password | salt |
+----+---------------------+-----------+----------+----------+------+
| 1 | johndoe#example.com | John | Doe | 12345678 | NULL |
+----+---------------------+-----------+----------+----------+------+
The problem i think is that the default constructor is used and not the other one i have created. I am new to Spring and JPA so i must be missing something. Here is my code.
User.java
#Entity
#Table(name = "users")
public class User{
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
public String firstname;
#Column(nullable = false)
public String lastname;
#Column(nullable = false, unique = true)
public String email;
#JsonIgnore
#Column(nullable = false)
public String password;
#JsonIgnore
#Column
private String salt;
public User() {}
public User(String email, String firstname, String lastname, String password) {
this.email = email;
this.firstname = firstname;
this.lastname = lastname;
this.salt = UUID.randomUUID().toString();
this.password = new BCryptPasswordEncoder().encode(password + this.salt);
}
#JsonIgnore
public String getSalt() {
return salt;
}
#JsonProperty
public void setSalt(String salt) {
this.salt = salt;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
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;
}
#JsonIgnore
public String getPassword() {
return password;
}
#JsonProperty
public void setPassword(String password) {
this.password = password;
}
}
UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
public User findByEmail(String email);
public User findByEmailAndPassword(String email, String password);
}
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application .class, args);
}
}
Also if someone finds what i did wrong, i would like to point me where/how i should put the user login code (decryption).
Thanks.
So, here is how i solved my problem: i created a Controller as my custom endpoint and then i created a service in which i placed the logic i wanted for the creation of the user. Here is the code:
UserController.java
#Controller
public class UserController {
#Autowired
private UserService userService;
#RequestMapping("/api/register")
#ResponseBody
public Long register(#RequestBody User user) {
return userService.registerUser(user);
}
...
}
UserService .java
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public Long registerUser(User user) {
user.setPassword(new BCryptPasswordEncoder().encode(password));
userRepository.save(user);
return user.getId();
}
...
}
so by doing a POST with
{
"firstname":"John",
"lastname":"Doe",
"email":"johndoe#example.com",
"password":"12345678"
}
in /api/register, i can now create a user with a hashed password.
If you want Spring to use your constructor, you need to
remove the no-argument constructor
annotate every parameter in the other constructor with #JsonProperty like this
public User(#JsonProperty("email") String email,
#JsonProperty("firstname") String firstname,
#JsonProperty("lastname") String lastname,
#JsonProperty("password") String password) {
this.email = email;
this.firstname = firstname;
this.lastname = lastname;
this.password = new BCryptPasswordEncoder().encode(password);
}
You don't need to provide a salt value to the BCryptPasswordEncoder because it already salts passwords by itself.

Resources