hi i have a problem with my project (small blog site) that i can't handle for a while
that's my LoginController
'''
#Controller
#SessionAttributes("currentUser")
public class LoginController {
#Autowired
UserService userService;
#ModelAttribute("currentUser")
public User setUpUserForm() {
return new User();
}
#RequestMapping(value="/login",method = RequestMethod.GET)
public String showLoginPage() {
return "loginsite";
}
#RequestMapping(value="/login",method=RequestMethod.POST)
public String userLogin(#ModelAttribute("currentUser") User user,ModelMap model,#RequestParam String login,#RequestParam String password) {
if(userService.checkUserLoginData(login, password)==true) {
user=userService.getUserByLogin(login);
System.out.println("Logged in user");
System.out.println("ID:"+user.getUserId());
System.out.println("Login:"+user.getLogin());
System.out.println("Password:"+user.getPassword());
System.out.println("Email:"+user.getEmail());
return "welcomesite";
}else {
model.put("message", "Check your login and password ! ");
return "loginsite";
}
}
}
'''
that's my UserController
'''
#Controller
public class UserController {
#Autowired
UserService userService;
#RequestMapping(value="/",method = RequestMethod.GET)
public String posts() {
return "redirect:/posts";
}
#RequestMapping(value="/createaccount",method = RequestMethod.GET)
public String showRegisterPage() {
return "registrationpage";
}
#RequestMapping(value="/createaccount",method = RequestMethod.POST)
public String createAccount(ModelMap model,#RequestParam String login,#RequestParam String password,#RequestParam String email) {
userService.addUser(new User(login,password,email));
return "redirect:/login";
}
#RequestMapping(value="/login/accountsettings",method = RequestMethod.GET)
public String showAccountSettings(#SessionAttribute("currentUser") User user) {
System.out.println("amil"+user.getEmail());
return "userSettings";
}
#RequestMapping(params="update",value="/login/accountsettings",method = RequestMethod.POST)
public String updateAccountSettings(#SessionAttribute("currentUser") User user,#RequestParam String login,#RequestParam String password,#RequestParam String email) {
user=userService.updateUser(new User(login,password,email));
return "redirect:/login";
}
#RequestMapping(params="delete",value="/login/accountsettings",method = RequestMethod.POST)
public String deleteAccount(#RequestParam String login) {
userService.deleteUser(login);
return "redirect:/posts";
}
'''
i would like to pass user data after logging in to next pages for ex /login/accountsettings or even to have user id to add post with his id as a creator of this post. After logging in everythink is okay i have complete user data but when im tring to get them in /login/accountsettings User is empty(full of null values).what am I doing wrong?
output:
enter image description here
or I have a question, is there another better way to transfer user data until logging out?
Thanks for help!
Related
I have this code, where I send an object to the API:
validateLogin(user:User):Observable<string>{
console.log(JSON.stringify(user));
return this.http.post<string>(`http://localhost:8080/login/login`, user).pipe(
map((resp)=>{
return this.repareString(JSON.stringify(resp));
})
)
}
I don't see anything wrong, but Spring Boot says "required request parameter 'user' for method parameter type String is not present". I've also tried sending it as a JSON object but it says the same. I believe that this is caused by the code in angular, that's why I post it here.
#RestController
#RequestMapping("/login")
public class LoginController {
#PostMapping("/login")
public static String login(#RequestParam String user) throws Exception{
System.out.println(user);
Login l = new ObjectMapper().readValue(user, Login.class);
if(l.checkUser().equals("ok")){
if(l.checkPassword().equals("ok")){
return "logged";
} else {
return l.checkPassword();
}
}
return l.checkUser();
}
}
And the Login class:
public class Login extends Database{
public String email;
public String pass;
public Statement stm;
public Login(String email, String pass) throws Exception{
this.email = email;
this.pass = pass;
this.stm = (Statement) this.con.createStatement();
}
I have tried sending it as a JSON string and I've also tried sending the object properties individually as various params.
Change your controller like this:
#RestController
#RequestMapping("/login")
public class LoginController {
#PostMapping("/login")
public static String login(#RequestBody Login login) throws Exception{
System.out.println(login);
if(login.checkUser().equals("ok")){
if(login.checkPassword().equals("ok")){
return "logged";
} else {
return login.checkPassword();
}
}
return login.checkUser();
}
}
Task:
Make a single wrapper for server responses to the client.
My decision:
Create an ApiResponse object and use it in the controller and services
Am I doing the right thing? Sorry I don't know much about this...
ApiResponce
#Data
public class ApiResponse {
private Date timestamp;
private int status;
private String message;
private Object data;
public ApiResponse(int status, String message, Object data) {
this.timestamp = new Date();
this.status = status;
this.message = message;
this.data = data;
}
Controller
#RestController
#RequestMapping("/api/v1/admin")
#RequiredArgsConstructor
#PreAuthorize("hasAuthority('ADMIN')")
public class Admin {
private final UserService userService;
#PostMapping(value = "/users/add", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<ApiResponse> addUser(#RequestBody User user) {
return ResponseEntity.ok(userService.addUser(user));
}
}
Service
public interface UserService {
ApiResponse addUser(User user);
ApiResponse updateUser(User user);
ApiResponse getUserByEmail(String email);
ApiResponse getUserById(Long id);
}
Implementation method example
#Override
public ApiResponse addUser(User user) {
log.info("Saving new user to the database. Email: {}", user.getEmail());
// Check if the user is already in the database
User useDB = userRepo.findByEmail(user.getEmail());
if (useDB != null) {
return new ApiResponse(200, "A user with this e-mail already exists in the system!", user);
}
try {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRepo.save(user);
return new ApiResponse(200, "User added successfully", null);
} catch (Exception ex) {
log.error("New user added error! " + ex.getMessage());
return new ApiResponse(403, "New user registration error! " + ex.getMessage(), null);
}
}
Am I moving in the right direction? Or is it a sign of bad code?
Actually what I tried above)
As per my knowledge, you're doing good. Make sure to use BCryptPasswordEncoder in Configuration file in order to make password encrypted.
I have a UserController that receives a UserDTO and creates/updates the user in the DB. The problem I'm getting is that I also have a login, and when I insert the username and password on the login form, I always get the 'Wrong Password.' exception, despite the credentials being correctly inserted.
One thing I suspect is that BCrypt is to blame, since due to the fact that it generates random salt while encoding, maybe, just maybe, the cipher text ends up being different and stuff, which is weird, since I assume that it should work. I want to know how can I fix this problem of the hashing being different & not being able to validate the userCredentials
I have tried for example encoding the received password and using the matches method via my autowired passwordEncoder, and I'm using my own authProvider.
Here's the code, let me know if you need anything else.
CustomAuthProvider.java
#Service
public class CustomAuthProvider implements AuthenticationProvider {
private final UserServiceImpl userServiceImpl;
private final BCryptPasswordEncoder passwordEncoder;
#Autowired
public CustomAuthProvider(UserServiceImpl userServiceImpl, BCryptPasswordEncoder passwordEncoder) {
this.userServiceImpl = userServiceImpl;
this.passwordEncoder = passwordEncoder;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails userDetails = userServiceImpl.loadUserByUsername(username);
if (!passwordEncoder.matches(password, userDetails.getPassword())) { //The problem is here evidently.
throw new BadCredentialsException("Wrong password.");
}
return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Also, here's the loadUserByUsername method:
UserServiceImpl.java
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDTO user = this.getUserByUsername(username);
User anUser = convertToUser(user);
ModelMapper modelMapper = new ModelMapper();
return modelMapper.map(anUser,UserPrincipal.class);
}
}
And here is the save method I use to save and update users, as well as the LoginController;
#Override
public void save(UserDTO user) {
User aUser = this.convertToUser(user);
aUser.setPassword(passwordEncoder.encode(aUser.getPassword()));
this.userRepository.save(aUser); }
LoginController.java:
#RestController
public class LoginController{
private final CustomAuthProvider providerManager;
#Autowired
public LoginController(CustomAuthProvider providerManager) {
this.providerManager = providerManager;
}
#GetMapping("/login")
public String login() {
return "login";
}
#PostMapping("/login")
public String login(#RequestParam("username") #NotBlank String username,
#RequestParam("password") #NotBlank String password, Model model) {
if(username == null || password == null) { //This is probably not necessary
model.addAttribute("error", "Invalid credentials");
return "login";
}
try {
Authentication auth = providerManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password)
);
SecurityContextHolder.getContext().setAuthentication(auth);
return "redirect:/notes";
} catch (AuthenticationException e) {
model.addAttribute("error", "Invalid credentials");
return "login";
}
}
}
UserPrincipal.java
#Data
public class UserPrincipal implements Serializable , UserDetails {
int id;
private String username;
private String password;
private Date accountCreationDate = new Date();
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
}
UserDTO.java
#Data
public class UserDTO implements Serializable {
int id;
private String username;
private String password;
private List<Note> notes = new ArrayList<>();
}
I read several issues related to this topic, like
Spring Boot PasswordEncoder.matches always false
Spring Security - BcryptPasswordEncoder
Inconsistent hash with Spring Boot BCryptPasswordEncoder matches() method
How can bcrypt have built-in salts?
Decode the Bcrypt encoded password in Spring Security to deactivate user account
but none of those helped me solve my issue and there was no real solution to the problem since most of them don't even have an accepted answer.
EDIT: Found out that the 'matches' method only works if I insert the hashed password, not the raw password.
Found out my mistake:
The setPassword method in the User class was re-hashing the hashed password which was already being hashed on the save method, thus the modelMapper.map() method used that setPassword method, therefore the passwords never matched and the password I got from the user class never matched the actual password I could see on my database.
I have a problem with my ControllerTest. I'm not sure how to test for the Optional - does someone know how? The other test gives me a NullPointerException for the stubbing : when(couponService.getCouponById(id)).thenReturn(expectedCoupon);
Would be awesome if someone could help me.
public class CouponControllerTest {
#MockBean
private CouponService couponService;
#MockBean
private UserService userService;
#Autowired
MockMvc mockMvc;
#Test
public void checkAndUpdateCoupon() throws Exception {
int id = 1;
int userId = 1;
Coupon expectedCoupon = new Coupon(1, 1);
when(couponService.getCouponById(id)).thenReturn(expectedCoupon);
List<User> userList = new ArrayList<User>();
when(userService.getAllUser()).thenReturn(userList);
List<Coupon> couponList = new ArrayList<Coupon>();
when(couponService.getAllCoupons()).thenReturn(couponList);
mockMvc.perform(get("/checkCoupon")
.param("id", "1")
.param("userId", "1"))
.andExpect(status().isOk())
.andExpect(view().name("couponPage"))
.andExpect(model().attribute("error", "Not correct user id or coupon id."))
.andExpect(model().attribute("users", userList))
.andExpect(model().attribute("coupons", couponList));
verify(couponService).updateCoupons(id, userId);
}
}
#Controller
public class CouponController {
#Autowired
CouponService couponService;
#Autowired
UserService userService;
#GetMapping("/checkCoupon")
public String checkCoupon(ModelMap model, #RequestParam Integer id, #RequestParam Integer userId, Coupon coupon) {
Optional<Coupon> couponFromDatabase = couponService.byUserIdAndId(coupon.getUserId(), coupon.getId());
if(couponFromDatabase.isEmpty()) {
String error = "Not correct user id or coupon id.";
model.addAttribute("error", error);
} else {
String message = couponService.updateCoupons(id, userId);
model.addAttribute("message", message);
}
List<User> userList = userService.getAllUser();
model.addAttribute("users", userList);
List<Coupon> couponList = couponService.getAllCoupons();
model.addAttribute("coupons", couponList);
return "couponPage";
}
}
I think you need to do some changes in mocking the first service.
when( couponService.byUserIdAndId(anyLong(), anyLong()) ).thenReturn( Optional.of(expectedCoupon) );
Here the anyLong() refer to any Incoming long data type number.
Override your existing code with this above line.
I have this controller
#Controller
#RequestMapping("/")
public class UserController {
#Autowired
private UserServices userServices;
public String em;
#GetMapping("/settings")
public String showProfilePage(Model model, #ModelAttribute User user){
em=user.getEmail();
model.addAttribute("user",user);
return "editprofile";
}
#PostMapping("/settings")
public String updateProfile(Model model,#ModelAttribute User user,BindingResult bindingResult,Principal principal){
if(bindingResult.hasErrors()){
return "/editprofile";
}
if(userServices.isUserPresentUpdate(user.getEmail(),em)){
model.addAttribute("exist",true);
return "/editprofile";
}
userServices.update(user);
return "redirect:/settings";
}
#GetMapping("/settings/password")
public String passwordEdit(Model model, #ModelAttribute User user){
model.addAttribute("user",user);
return "editprofilepassword";
}
#PostMapping("/settings/password")
public String passwordUpdate(#ModelAttribute User user, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "editprofilepassword";
}
userServices.updateHeslo(user);
return "redirect:/settings";
}
#ModelAttribute("user")
public User getUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UserDetails userDetail = (UserDetails) auth.getPrincipal();
User u = userServices.findByEmail(userDetail.getUsername());
return u.getId() !=null ? userServices.findOne(u.getId()) : new User();
}
}
I'm looking for a method that changes principal name or any other solutions.
Every time I change the email in /settings, the email gets changed, but the principal name is still the same and it breaks.