Spring Security - kiedy zapisać informacje w bazie o logowaniu użytkownika

0

Cześć.
Jestem w trakcie nauki springa i stanąłem w momencie gdy chciałem logować w bazie danych moment zalogowania użytkownika (kolumna last_login typu datetime) poprzez form do panelu na stronie.
Mógłbym to robić np podczas wejścia użytkownika do strefy zabezpieczonej - ponieważ wiem że jest po autoryzacji (chociaż nie do końca bo mógł się nie logować tylko wszedł dzięki sesji), ale doszedłem do wniosku że to powielanie zapytań SQL.
Jedno (a dokładnie dwa) już było wykonywane podczas wpisania danych i kliknięcia ZALOGUJ - wtedy to chciałbym przeprowadzić owy proces.
Jednak za chiny nie mogę się dobrać do momentu kiedy to się dzieje...
Czy mógłby mi ktoś pomóc?

Np w logach mam:

Hibernate: select cliententi0_.id as id1_1_, cliententi0_.activated as activate2_1_, cliententi0_.activated_date as activate3_1_, cliententi0_.address1 as address4_1_, cliententi0_.address2 as address5_1_, cliententi0_.blocked as blocked6_1_, cliententi0_.blocked_reason as blocked_7_1_, cliententi0_.email as email8_1_, cliententi0_.first_name as first_na9_1_, cliententi0_.last_access as last_ac10_1_, cliententi0_.last_login as last_lo11_1_, cliententi0_.last_name as last_na12_1_, cliententi0_.password as passwor13_1_, cliententi0_.phone as phone14_1_, cliententi0_.registration_date as registr15_1_, cliententi0_.zip_code as zip_cod16_1_ from clients cliententi0_ where cliententi0_.email=?
Hibernate: select roles0_.client_entity_id as client_e1_2_0_, roles0_.roles_id as roles_id2_2_0_, clientrole1_.id as id1_3_1_, clientrole1_.description as descript2_3_1_, clientrole1_.role as role3_3_1_ from clients_roles roles0_ inner join roles clientrole1_ on roles0_.roles_id=clientrole1_.id where roles0_.client_entity_id=?

Ale nie mogę odnaleźć gdzie to się dzieje.
Badałem temat AuthProvidera ale do niczego działającego nie doszedłem :(

0

Zbadam te eventy.

Robiłem coś takiego dotychczas:


@Service
public class CustomClientDetailsService implements UserDetailsService {
	
	private ClientRepository clientRepository;
	private SessionData session;
	
	@Autowired
	public void setUserRepository(ClientRepository clientRepository, SessionData session) {
		this.clientRepository = clientRepository;
		this.session = session;
	}


	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		ClientEntity client = clientRepository.findByEmail(username);
		if(client == null) throw new UsernameNotFoundException("client not found");
		org.springframework.security.core.userdetails.User userDetails = 
					new org.springframework.security.core.userdetails.User(
						client.getEmail(), 
						client.getPassword(), 
						convertAuthorities(client.getRoles())
					);
		client.setLastLogin(new Date());
		clientRepository.save(client);
		session.put("client", client);
		return userDetails;
	}

	private Set<GrantedAuthority> convertAuthorities(Set<ClientRoleEntity> userRoles) {
		Set<GrantedAuthority> authorities = new HashSet<>();
		for(ClientRoleEntity ur: userRoles) {
			authorities.add(new SimpleGrantedAuthority(ur.getRole()));
		}
		return authorities;
	}
[...]

Ale nie wiem w jakim momencie wywoływana jest ta metoda i czy wywołanie jej oznacza poprawne zalogowanie.
Poza tym dzieje się to czego nie chciałem:

**Hibernate**: select cliententi0_.id as id1_1_, cliententi0_.activated as activate2_1_, cliententi0_.activated_date as activate3_1_, cliententi0_.address1 as address4_1_, cliententi0_.address2 as address5_1_, cliententi0_.blocked as blocked6_1_, cliententi0_.blocked_reason as blocked_7_1_, cliententi0_.email as email8_1_, cliententi0_.first_name as first_na9_1_, cliententi0_.last_access as last_ac10_1_, cliententi0_.last_login as last_lo11_1_, cliententi0_.last_name as last_na12_1_, cliententi0_.password as passwor13_1_, cliententi0_.phone as phone14_1_, cliententi0_.registration_date as registr15_1_, cliententi0_.zip_code as zip_cod16_1_ from clients cliententi0_ where cliententi0_.email=?
**Hibernate**: select roles0_.client_entity_id as client_e1_2_0_, roles0_.roles_id as roles_id2_2_0_, clientrole1_.id as id1_3_1_, clientrole1_.description as descript2_3_1_, clientrole1_.role as role3_3_1_ from clients_roles roles0_ inner join roles clientrole1_ on roles0_.roles_id=clientrole1_.id where roles0_.client_entity_id=?
pl.test.component.SessionData@62d34de9 created
**Hibernate**: select cliententi0_.id as id1_1_0_, cliententi0_.activated as activate2_1_0_, cliententi0_.activated_date as activate3_1_0_, cliententi0_.address1 as address4_1_0_, cliententi0_.address2 as address5_1_0_, cliententi0_.blocked as blocked6_1_0_, cliententi0_.blocked_reason as blocked_7_1_0_, cliententi0_.email as email8_1_0_, cliententi0_.first_name as first_na9_1_0_, cliententi0_.last_access as last_ac10_1_0_, cliententi0_.last_login as last_lo11_1_0_, cliententi0_.last_name as last_na12_1_0_, cliententi0_.password as passwor13_1_0_, cliententi0_.phone as phone14_1_0_, cliententi0_.registration_date as registr15_1_0_, cliententi0_.zip_code as zip_cod16_1_0_ from clients cliententi0_ where cliententi0_.id=?
**Hibernate**: select roles0_.client_entity_id as client_e1_2_0_, roles0_.roles_id as roles_id2_2_0_, clientrole1_.id as id1_3_1_, clientrole1_.description as descript2_3_1_, clientrole1_.role as role3_3_1_ from clients_roles roles0_ inner join roles clientrole1_ on roles0_.roles_id=clientrole1_.id where roles0_.client_entity_id=?
**Hibernate**: update clients set activated=?, activated_date=?, address1=?, address2=?, blocked=?, blocked_reason=?, email=?, first_name=?, last_access=?, last_login=?, last_name=?, password=?, phone=?, registration_date=?, zip_code=? where id=?

2 dodatkowe zapytania SQL..

A ja chce zrobić

client.setLastLogin(new Date());
clientRepository.save(client);

w momencie gdy jestem pewny że użytkownik się zalogował i korzystając z clientEntity którego pobrał spring (z bazy danych) podczas autoryzacji w celu sprawdzenia hasła.

0

Właśnie coś mi nie pasował ten Hibernate w logach, zastanawiałem się czy to nie Twoje. A jednak. Nie widzę w jaki sposób tutaj rozpinasz transakcję, może dlatego Hibernate dociąga dane w momencie save(). Zapnij się na event.

Btw. Nie da się tak po prostu „skorzystać z selecta”, aby zmodyfikować i zapisać encję - poczytaj o cache pierwszego poziomu w JPA (to samo co sesja Hibernate).

0

Przesiedziałem kilka godzinek i znalazłem rozwiązanie.
Zaimplementowałem własny AuthenticationProvider.

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
	private ClientRepository clientRepository;
	private SessionData session;
	private PasswordEncoder passwordEncoder;
	
	@Autowired
    public CustomAuthenticationProvider(ClientRepository clientRepository,SessionData session,PasswordEncoder passwordEncoder) {
		super();
		this.clientRepository = clientRepository;
		this.session = session;
		this.passwordEncoder = passwordEncoder;
	}

    @Override
    public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
        final String email = authentication.getName();
        final String password = authentication.getCredentials().toString();
        ClientEntity client = clientRepository.findByEmail(email);
        if(client == null) return null;
        
        if (email.equals(client.getEmail()) && passwordEncoder.matches(password, client.getPassword())) {
            ClientPrincipal clientPrincipal = new ClientPrincipal(client);
            Authentication auth = new UsernamePasswordAuthenticationToken(clientPrincipal,client.getPassword(),clientPrincipal.getAuthorities());
            
            session.put("client", client);
            return auth;
        } else {
            return null;
        }
    }

    @Override
    public boolean supports(final Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}
1
  1. Nie widzę ustawiania żadnej daty
  2. Jesteś podatny na kradzież haseł: https://www.baeldung.com/java-storing-passwords
0
@Override
    public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
        final String email = authentication.getName();
        final String password = authentication.getCredentials().toString();
        ClientEntity client = clientRepository.findByEmail(email);
        if(client == null) return null;
        
        if (email.equals(client.getEmail()) && passwordEncoder.matches(password, client.getPassword())) {
            ClientPrincipal clientPrincipal = new ClientPrincipal(client);
            Authentication auth = new UsernamePasswordAuthenticationToken(clientPrincipal,client.getPassword(),clientPrincipal.getAuthorities());
            
            client.setLastLogin(new Date());
            //clientRepository.save(client);
            clientRepository.updateLastLogin(client.getLastLogin(),client.getEmail());
            
            session.put("client", client);
            return auth;
        } else {
            return null;
        }
    }

W celu wyeliminowania dodatkowych selectów zrobiłem @Query updateLastLogin.

  1. Dzięki za informacje :)

Pozdrawiam

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