Access User Data in Spring, Hibernate - java

I did this tutorial: http://www.mkyong.com/spring-security/spring-security-hibernate-annotation-example/
And it works perfectly. But I don't understand something. I add a new column to my database. It is named FullName. I completed the User class with the setter and getter method. But for example, how can I access this data in the admin page? How can I print out the actual session user's name. Like: Hi , your full name is .
Edit: So how can I acces the logged user's other details, like fullname, address, sex and other stuff like that, not just the username and password?
Edit2: Thanks, it is more clear now. My only question. He converts his own User class to the org.springframework.security.core.userdetails.User. And this class hasn't got a contructor with fullname. So Do I need to create an own UserDetails class override the orginial. Or what have I need to do?
Edit3: Sorry, but i don't understand this. Which service method? Can you show this for me with this example. Thank you very much.

You have add the code in the controller class MainController which is given in the example mentioned in the mkyong site.
#RequestMapping(value = "/admin**", method = RequestMethod.GET)
public ModelAndView adminPage() {
ModelAndView model = new ModelAndView();
model.addObject("title", "Spring Security + Hibernate Example");
model.addObject("message", "This page is for ROLE_ADMIN only!");
model.setViewName("admin");
String fullName = ...; // Add the required logic to get the full name.
model.addObject("FullName", fullName); // Then add the fullName to your model object
return model;
}
Then finally edit the admin.jsp file to display the Full Name.
Update:
The required code is also available in mkyong link. Here is the snippet taken from the same page:
UserDaoImpl.java class has all the code that is needed for your functionality.
List<User> users = new ArrayList<User>();
users = sessionFactory.getCurrentSession()
.createQuery("from User where username=?")
.setParameter(0, username)
.list();
if (users.size() > 0) {
return users.get(0);
} else {
return null;
}
When you get the User object from hibernate it loads all the properties of user, as you have added new field - FullName then this field gets loaded in User object by hibernate if you have proper mapping in hbm files.
This logic is called from the service class MyUserDetailsService using method loadUserByUsername. So take help of this method from service class to get the User details by providing the username input parameter.
Code from service class:
#Transactional(readOnly=true)
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
com.mkyong.users.model.User user = userDao.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());
return buildUserForAuthentication(user, authorities);
}
// Converts com.mkyong.users.model.User user to
// org.springframework.security.core.userdetails.User
private User buildUserForAuthentication(com.mkyong.users.model.User user, List<GrantedAuthority> authorities) {
return new User(user.getUsername(), user.getPassword(), user.isEnabled(), true, true, true, authorities);
}
As you can see here from above code, a new User object is created at line:
new User(user.getUsername(), user.getPassword(), user.isEnabled(), true, true, true, authorities);
jut update this line to include the Full name and also add the required constructor in User class.

Related

Spring Boot GET method with soft delete how to add another exception in service impl

I'm a beginner in Java and spring boot, and I implemented a User class with CRUD features. However now I need to add the feature that if exclusionFlag is True, the user can't be shown or modified, so I need to add the test if the flag is true before these CRUD actions.
The Code in the moment
UserServiceImpl.java
#Override
public UserResponse getAllUsers(int pageNo, int pageSize, String sortBy, String sortDir) {
Sort sort = sortDir.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortBy).ascending()
: Sort.by(sortBy).descending();
Pageable pageable = PageRequest.of(pageNo, pageSize, sort);
Page<User> users = userRepository.findAll(pageable);
List<User> listOfUsers = users.getContent();
List<UserDto> content= listOfUsers.stream().map(user -> mapToDTO(user)).collect(Collectors.toList());
UserResponse userResponse = new UserResponse();
userResponse.setContent(content);
userResponse.setPageNo(users.getNumber());
userResponse.setPageSize(users.getSize());
userResponse.setTotalElements(users.getTotalElements());
userResponse.setTotalPages(users.getTotalPages());
userResponse.setLast(users.isLast());
return userResponse;
}
#Override
public UserDto getUserById(long id) {
User user = userRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("User not found"));
//any other addition like:
//User user = userRepository.filterByExclusionFlag(true).orElseThrow(() -> new ResourceNotFoundException("User not found"));
//results in errors like the function can't throw exception and I added the filterByExclusionFlag in UserRepository. Have no idea how to add this feature on UserService
return mapToDTO(user);
}
The best approach is to create a new column in the user's table as exclusion_flag and using constructor DTO projection instead of inbuilt findById with #Query having exlusionFlag as value false.
Save each user with default value for Flag as false. Your sample query will be like this : select needed columns from User where exclusion_flag =false .
And for your modification API (PUT) , ask the client to pass the value fetched from the above (GET) API to pass in the request.The flag passed in the request param helps you to identify if user can be modified or not.

Email or username validation

I want to check the email from database through controller and check if the email already exists or not!!! (email = column in db), please suggest me some ideas i'm new here
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public String saveUser(#ModelAttribute User user, Model model) {
if (!user.getName().isEmpty() || !user.getEmail().isEmpty() || !user.getPassword().isEmpty()) {
user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));
udao.addUser(user);
return "login";
}
else {
model.addAttribute("error", "fill the form completely!!!");
return "signup";
}
}
Assuming you have the udao as your data access, you just need to create a method that gets the user object given an email id like
#Query(value="select * from users where email = :emailId and isactive=1", nativeQuery=true)
User getUserByEmailId(#Param("emailId")String emailId);
OR
#Query(value="select count(*) from users where email = :emailId and isactive=1", nativeQuery=true)
Integer getUserByEmailId(#Param("emailId")String emailId);
In the option2, you just get the count of the users having the email, a value of 0 indicates that the email is Unique and otherwise.
Once this DAO is completed, you can verify by calling this method and if your response is null, it means that there is no user in the db, else user is present, you can show the end user that the user already exists with the provided email id.
Couple of points for improving the code,
Please do not write your DAO accessing logic in the controller, it is best advised to use a service like UserDetailsService so that all the data access and business rules are applied in the service facilitating SOC, UnitTestability and lot more.
Also, consider using a highly secure algorithm for handling passwords like PBKDF2 which is very secure.
HTH
Assuming that you're using Spring Data JPA you can query the DB in order to check if there is an user with that email, by Spring Data JPA Docs (check the Example 60. Query method declaration in UserRepository):
public interface UserRepository extends Repository<User, Long> {
User findByEmail(String emailAddress);
}
So, in your controller you can do something like:
#Autowired
UserRepository userRepository;
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public String saveUser(#ModelAttribute User user, Model model) {
if (!user.getName().isEmpty() || !user.getEmail().isEmpty() || !user.getPassword().isEmpty()) {
User userFromDb = userRepository.findByEmail(user.getEmail());
// Please note that I'm not sure if it will return null or empty User object, you need to check that
if (userFromDb == null) {
// Do the logic if no email exists in the DB
} else {
// Do the logic if an email exists in the DB
}
}
}

How to pass extra parameter in Spring login

I am using Spring framework. At the login page you have to enter Username and password.
Now the user should also enter his role when logging in. So I put a combo box there where you can select role: Admin, User or Manager. So I extended the available function to be able to also process post. But somehow the combobox value is never received by this method. It seems spring filters it before.
#RequestMapping(value = { "/", "/login" }, method = {RequestMethod.GET,RequestMethod.POST})
public String loginPage(#RequestParam(required=false) String role) {
if (isCurrentAuthenticationAnonymous()) {
return "login";
} else {
return "redirect:/user/workingtimeNow" ;
}
}
As you can see I added RequestMethod.POST and #RequestParam(required=false) String role . But role is always null.

Getting the current logged in user name when using Spring Security

Its a coupon system app using Spring security, spring MVC,
now.
when the app starts, I need to somehow initialize the current logged in user into the controller.
Issue is:
If I try to get the current user via SecurityContextHolder it is impossible because it seems like spring is initializing the controllers before the security so I cannot get it in the controller.
Is there anything I'm missing? a different approach of getting the current logged in user after he logs in?
What you need is called #AuthenticationPrincipal.
You can inject it in controller method like this:
#GetMapping("/")
public void get(#AuthinticationPrincipal User user){ ... }
Here is documentation
Alternatively, you can create your own annotation and custom argument resolver, and inject whatever you want.
Solution 1: Principal principal
#RequestMapping(value = {"/", ""})
public String start(Principal principal, Model model) {
String currentUser = principal.getName();
return currentUser;
}
Solution 2: Authentication authentication
#RequestMapping(value = {"/", ""})
public String currentUserName(Authentication authentication) {
return authentication.getName();
}
Solution 3: SecurityContextHolder
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
More Here

Spring security - Limiting access to my update profile page

I am using spring security in my application and ran into a problem. My application has an Update Profile page. I have added preAuthorized() with request mapping as
#PreAuthorize("isAuthenticated()")
#RequestMapping (value="/user/{uid}/profile/update", method = GET)
public String updateProfileView(#ModelAttribute("form") UserProfileForm form, #PathVariable ("uid") Integer userId, Model model){
It works fine, and unauthenticated user can not access this page.
But the issue is that every Authenticated User can access this page.
For example : User A logged in into application, he/she will be able to update every one's profile.
My CustomUserDetailService class is
#Service
#Transactional
public class CustomUserDetailsService implements UserDetailsService {
#Resource
UserService userService;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
com.analyst.future.domain.User user = userService.getUser(email);
SimpleGrantedAuthority auth = new SimpleGrantedAuthority("ROLE_USER");
Collection<SimpleGrantedAuthority> authorities = new HashSet<SimpleGrantedAuthority>();
authorities.add(auth);
User userDeatails = new User(user.getEmail(), user.getPassword(), authorities);
return userDeatails;
}
}
I don't think i can restrict it with roles as every authenticated user will have same roles.
Is there any way i can restrict Authenticated user to access only self update profile page.
I am no Spring Security expert, but try reading up on using Expression-Based Access - Link here
There is one tiny little line that matches what you want -
For example, if you wanted a particular method to only allow access to a user whose username matched that of the contact, you could write
#PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);
I think in your case it would be something like
#PreAuthorize("email == authentication.email")
This is method level though, so maybe not what you are looking for? Good news is that there is a way to use the logged in user and match it against the request user. :)
Since all previous answers talk about matching username (which is included in the principal object) but you need to match the userID, this needs a little more work. We first need to return a custom User object that extends UserDetails. Here is how you can do that:
Step 1: Make your 'User' model implement UserDetails
#Entity
public class UserAccount implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty("id")
private int userId;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
private String email;
private String password;
// other fields and methods
}
You will also have to override some methods from UserDetails, which is easy to figure out.
Step 2: Make you User Service implement UserDetailsService
#Component
public class UserAccountService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) {
// todo
}
}
Step 3: Return YOUR user model from the loadUserByUsername method
#Override
public UserDetails loadUserByUsername(String username) {
Optional<UserAccount> userAccount = userAccountDao.findByEmail(username);
if (!userAccount.isPresent()) {
throw new MyException("User account not found for the given Username.", HttpStatus.NOT_FOUND);
}
return userAccount.get();
}
Step 4: Add the #PreAuthorize expression to your resource
#PutMapping(value = "/{userId}")
#PreAuthorize("#userId == authentication.principal.userId or hasAuthority('ADMIN')")
public UserAccount updateUserAccount(#PathVariable("userId") int userId,
#RequestBody UserAccount userAccount) {
return userAccountService.updateUserAccount(userId, userAccount);
}
Important things to notice above are:
We can check if the userId is equal above, only because our custom UserAccount model has a userId. If we had returned a raw UserDetail (like org.springframework.security.core.userdetails.User) object instead, it will not have any 'id' property to match with, so above will fail.
Above #PreAuthorize annotation checks for 2 conditions. It allows the request if the user is the owner of the Account or if the user has ADMIN authority.

Categories

Resources