Endpointy do sign in i sign out w Spring Security

0

Mam 2 endpointy, jeden /sgin-in do logowania i drugi /sign-out to wylogowywania. Zastanawiam się, jak to sensownie zaimplementować w Spring Security od strony serwisów. Wiadomo, muszę mieć jakiś AuthService z tymi metodami, ale co powinno się w nich znajdować? Nie pytam o oczywiste rzeczy takie jak sprawdzanie czy dane z dto zgadzają się z tymi z bazy tylko o to co potem. Wiem, że tworzy się UsernamePasswordAuthenticationToken, tylko gdzie go potem przekazać, żeby user został zalogowany a potem wylogowany?

Pseudo kod do momentu gdzie wiem co robić mniej więcej:

void signIn(LoginDto dto) {
var user = getUserOrThrow(dto.username())
if (!user.password().equal(dto.password() ) ) {
throw new AuthException(dto.password())
}
new UsernamePasswordAuthenticationToken(dto.username(), dto.password())
...
//Nie wiem co dalej.
}


void signOut() {
//Skądś trzeba wziąć zalogowane usera, tylko skąd (SecurityContextHolder?) i co dalej?
}
0

Znalazłem takie rozwiązanie na podstawie filmiku Hindusa na YT:

package com.example.taskmanager.user;

import com.example.taskmanager.user.dto.SignInDTO;
import com.example.taskmanager.user.dto.UserDTO;
import com.example.taskmanager.user.exception.NotUniqueUserNameException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;

@RequiredArgsConstructor
class AuthService {

    private final PasswordEncoder passwordEncoder;
    private final UserRepository userRepository;

    private final AuthenticationManager authenticationManager;

    UserDTO signUp(String username, String password, UserRole role) {
        var correctUsername= Username.of(username);
        if (userRepository.existsByUsername(correctUsername)){
            throw new NotUniqueUserNameException(username);
        }
        var user = new User(
                correctUsername,
                Password.of(password).encoded(passwordEncoder),
                role,
                UserStatus.OPEN
        );
        return userRepository
                .save(user)
                .toDTO();
    }

    public void signIn(SignInDTO dto) {
        var token = new UsernamePasswordAuthenticationToken(dto.username(), dto.password());
        var correctToken= authenticationManager.authenticate(token);
        SecurityContextHolder.getContext().setAuthentication(correctToken);
    }

    public void signOut() {
        SecurityContextHolder.clearContext();
    }
}

Jest ok czy ktoś ma lepszy pomysł?

1

Jeżeli używasz JWT, to wylogowanie się zarządzane przez docelowy serwer staje się trochę mission impossible.
screenshot-20221004085414.jpg
Jak klient raz dostanie token, to ani AuthService, ani SecuredService nie mają nad nim kontroli.
Najprostszym sposobem wylogowania, jest "zapomnienie" tego tokena przez klienta. Jeżeli trzeba go unieważnić z poziomu zabezpieczanych usług, to nie ma możliwości zrobienia tego "prosto", bo jak widać SecuredService nie komunikuje się z AuthService w celu potwierdzenia ważności przesłanego tokena.
To co można zrobić, to:

  1. Nadawać krótki czas życia dla tokena i go odświeżać, wtedy np. po 5 minutach sam wygaśnie.
  2. Dodatkowo po stronie SecuredService można sobie zrobić (np. w pamięci) listę unieważnionych tokenów, i podczas sprawdzania weryfikować, czy dany token nie jest unieważniony.

Najprościej, tak jak pisałem, unieważnić token po stronie klienta, zwyczajnie wpisać token=null

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