brak możliwości zalogowania użytkownika Java Spring

0

Mam problem, otóż nie mogę sie zalogować na użytkownika, którego wcześniej dodalem. Dostaje komunikat, o błednym haśle lub loginie, który sam zaimplementowalem w klasie konfiguracyjnej. Dodam, że nie ma problemu z zalogowaniem się bezpośrednie po rejstracji przy pomocy zwracania headers oraz na użytkownika domyślnego, którego autoryzuje w kodzie.

Proszę o pomoc, bo mocno mnie to blokuje i już mase czasu poswięcilem na znalezieniu błędu :(


package doctorsoffice.Patient;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping("/registers")
public class UserController {
    UserService userService;
 
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }
 
    @PostMapping
    public ResponseEntity<UserDto> registerUser(@RequestParam String email, @RequestParam String password, @RequestParam long pesel,
                                                @RequestParam int age, @RequestParam String name, @RequestParam String surname) {
        UserDto newUser = userService.registerUser(email, password, pesel, name, surname, age);
//        HttpHeaders headers = new HttpHeaders();
//        headers.add("Location", "/hello");
//        //return new ResponseEntity<UserDto>(newUser, HttpStatus.CREATED);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Location", "/hello");
        return new ResponseEntity<>(headers, HttpStatus.FOUND);
 
    }
}
 
package doctorsoffice.Security;
 
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
 
public class LoginController {
 
    @GetMapping("/hello")
    public String helllo() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        return auth.getName();
    }
}
package doctorsoffice.Security;
 
import doctorsoffice.Patient.UserDetailServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
 
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
 
        http.authorizeRequests()
                .antMatchers("/*.html").permitAll()
                .anyRequest().permitAll();
 
         //http.httpBasic();
 
        http
                .formLogin()
                .loginPage("/users/login")
                .failureHandler((request, response, exception) -> response.sendError(HttpStatus.BAD_REQUEST.value(),
                        "Username or password invalid"))
                .usernameParameter("email")
                .passwordParameter("password")
                .defaultSuccessUrl("/hello").permitAll();
        http
                .logout()
                .logoutUrl("/user/logout")
                .invalidateHttpSession(true)
                .logoutSuccessUrl("/hello")
                .permitAll();
    }
 
 
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
 
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin").password("pass").roles("ADMIN");
    }
 
    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        return new UserDetailServiceImpl();
    }
 
//    @Override
//    public void configure(WebSecurity web) throws Exception {
//        web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources",
//                "/configuration/security", "/swagger-ui.html", "/webjars/**");
//
//    }
}
 
 
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
<form action="/users/login" method="post">
    <div>Email : <input type="text" name="email"/></div>
    <div>Password: <input type="password" name="password"/></div>
    <div><input type="submit" value="Sign in"/></div>
</form>
</body>
</html>
 
 
package doctorsoffice.Patient;
 
import doctorsoffice.common.LoginAlreadyInUseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
 
import javax.transaction.Transactional;
import java.util.Collections;
 
@Service
@Transactional
public class UserServiceImpl implements UserService {
    private UserRepository userRepository;
    private UserMapper userMapper;
    private PasswordEncoder passwordEncoder;
 
    @Autowired
    public UserServiceImpl(UserRepository userRepository, UserMapper userMapper, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.userMapper = userMapper;
        this.passwordEncoder = passwordEncoder;
    }
 
    @Override
    public UserDto registerUser(String email, String password, Long pesel, String name, String surname, int age) {
        if(userRepository.existsByEmail(email)){
            throw new LoginAlreadyInUseException(email);
        }
        User user = new User();
        user.setAge(age);
        user.setEmail(email);
        user.setName(name);
        user.setSurname(surname);
        user.setPassword(passwordEncoder.encode(password));
        user.setPesel(pesel);
        user.setUserRole(UserRoles.ROLE_USER);
        userRepository.save(user);
        SecurityContext ctx = SecurityContextHolder.getContext();
        Authentication auth =
                new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList());
        ctx.setAuthentication(auth);
 
        return userMapper.toUserDto(user);
 
    }
}
 


0

Strzelałbym, że dlatego, że nie kodujesz hasła i Spring przy próbie logowania próbuje odkodować "pass". Daj tam:

auth.inMemoryAuthentication()
                .withUser("user")
                .password(passwordEncoder()
                        .encode("pass"))
                .roles("ADMIN");
0

@slayer9:

package doctorsoffice.Patient;

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;

public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findUserByEmail(username);
    }
}

tak

0

@slayer9: a jak mam wtedy wyciągnąć usera z repozytorium?

0

Tutaj na szybko znalazłem w jakiej postaci powinieneś zwrócic Usera z tej metody:

https://javapointers.com/tutorial/spring-custom-userdetailsservice-example/

0
slayer9 napisał(a):

Tutaj na szybko znalazłem w jakiej postaci powinieneś zwrócic Usera z tej metody:

https://javapointers.com/tutorial/spring-custom-userdetailsservice-example/

Jestem pewien, że moje rozwiązanie jest prawidłowe. W poprzednim treningowym projekcie zrobiłem dokladnie tak samo. Jezeli bym chciał zrobić jak w linku musiałbym zmienić całą moją koncepcję.

1

Nie mam za bardzo teraz czasu na szukanie błędu, ale jeśli chciałbyś sobie podejrzeć mój działający kod (nie pracuję w Javie, więc nie wiem na ile to powinno wyglądać tak jak u mnie), to podsyłam gita ;)

https://github.com/ToTomki/getinpoland/blob/master/src/main/java/pl/getinpoland/auth/UserAuth.java

0

@kucia2129, błąd polega na tym, że w metodzie loadUserByUsername zwracasz gołego użytkownika pobranego z bazy danych, a powinieneś zwrócić obiekt User, tak jak Ci pisałem, który przechowuje informacje o loginie, haśle i atrybutach, przeczytaj
https://docs.spring.io/autorepo/docs/spring-security/4.2.x-SNAPSHOT/apidocs/org/springframework/security/core/userdetails/User.html
i
https://docs.spring.io/autorepo/docs/spring-security/4.2.x-SNAPSHOT/apidocs/org/springframework/security/core/userdetails/UserDetails.html

I wcale nie musisz zmieniać całej swojej koncepcji.

0

@slayer9:
Jestem chyba trochę oporny, bo: w dokumentacji UserDetailService znajduje, że metoda loadUserByUsername(java.lang.String username) zwraca : a fully populated user record (never null). Ja to odbieram jako kompletnego Usera. Mógłbyś mnie jakoś nakierować jak zwrócic takiego User z hasłem?

Zresztą w repozytorium mam:

package doctorsoffice.Patient;

import org.springframework.data.jpa.repository.JpaRepository;


public interface UserRepository extends JpaRepository<User, Long> {
    User findUserByEmail(String email);
}

a w UserDetailService zwracam return userRepository.findUserByEmail(username);

1

Bardzo prosto:

  1. W metodzie loadUserByUsername pobierz sobie normalnie użytkownika przez userRepository.findUserByEmail(username) ze Spring Data.
  2. Stwórz metodę, która zwróci atrybuty posiadane przez Usera w postaci najprostszej kolekcji, a jako argument przyjmuje kolekcje uprawnień z klasy użytkownika DAO. Chodzi tu o to, że w bazie trzymasz użytkownika i w jakieś kolekcji, która do niego należy trzymasz ROLE_ADMIN czy ROLE_USER (w postaci zwykłych Stringów) itd w zależności kim ten użytkownik jest. I na podstawie tego powiedzmy ROLE_USER musisz stworzyć obiekt klasy implementującej interfejs GrantedAuthority, czyli w najprostszym przypadku SimpleGrantedAuthority. Więc piszesz sobie za pomocą strumieni zgrabną instrukcję, która pobiera z listy uprawnień użytkownika ROLE_USER w postaci Stringa, tworzy New SimpleGrantedAuthority("tutaj finalnie ma być ROLE_USER w postaci Stringa"), całość zwracasz w postaci listy.
  3. W metodzie loadUserByUsername dajesz return("tutaj nazwa loginu, jak chcesz żeby logował się po mailu umieszczasz tu email, czyli pewnie coś w stylu user.getEmail()", "tutaj hasło, pewnie user.getPassword()", "a tutaj nazwa metody z punktu drugiego przyjmująca jako argument na przykład user.getRoles"); Wszystko zależy oczywiście jak tam sobie zbudowałeś klasę DAO.

I to jest jeden sposób, bo możesz jeszcze inaczej: Stworzyć metodą User createUser(Użytkownik użytkownik), napisać tam to co w punkcje drugim i zwrócić to samo co w punkcie trzecim, a w loadUserByUsername pobierasz tylko użytkownika przez Spring Data z bazy i albo przekazujesz do stworzonej przez siebie metody createUser albo generujesz new UsernameNotFoundException. Może brzmi to trochę skomplikowanie, ale to dlatego, że napisałem w skrócie co tam się pod spodem dzieje, w rzeczywistości implementacja jest dziecinnie prosta. Dobrym pomysłem jest też znaleźć gotowca, przeanalizować z dokumentacją dokładnie jak działa i wtedy napisanie czegoś swojego staje się o wiele łatwiejsze. Aha, jak piszę User to chodzi o org.springframework.security.core.userdetails.User, a użytkownik to ten prosto z bazy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1