luchob/softuni-sep-2023

Проблем със секюрити и опцията за запаметяване на потребителя.

Closed this issue · 7 comments

Връзка към проекта:

връзка

Кратко описание:
Здравей Лъчо, сблъсквам се със следното нещо, след като имплементирах security се случва следното, искам да актуализирам потребителя си, като му презапиша данните в базата на определен линк. След което го пренасочвам към страницата с информация за него.
Обаче понеже в сесията не се е актуализирал тоест UserDetails, от спринг и ми хвърля ексепшуни.
Към момента го реших като Логоутна потребителя и го предупредя за това предварително, но искам да го направя да се актуализира без да има нужда от логоут и логване след това.

Второто ишу е с ремембер ме функционалността. Не можах да я подкарам, след като я избера ме праща на едн-пойнт на еррор пейджа.

Възможно най-накратко опишете:

  1. Какво очаквате да се случи --> Ако може да ме насочиш какво и как да направя.
  2. Какво всъщност се случва --> По-горе описаното.
  3. Какво опитахте --> Търсих в нета, опитах да актуализирам Security Contenta, но безуспешно. А за ремембер ми преписах всичко от лекцията и пак не ще да стане ;(

Стъпки за репродуциране: --> За да репродуцираш проблема ми с секюритито, трябва от UserService да се изтрие ред 134 (httpSession.invalidate();). И след това ще започне да хвърля ексепшъни.
За ремембер ми, просто го избираш като опция и то си е бъгаво в момента :)

Опишете възможно най-лесните стъпки, с помощта на които може да се репродуцира проблемът.

Здравей! Може би ще е удобно да сложиш css-ите в репозиторито :-)

Относно проблемът с remember me... Token base remember me сървиса ще се опита да изрови UserDetails от твоя юзър сървис, тъй като за алгоритъма за криптиране на remember me cookie-то му трябва паролата на юзъра.

Ето как го прави в TokenBasedRememberMeServices:

String username = retrieveUserName(successfulAuthentication);
....
UserDetails user = getUserDetailsService().loadUserByUsername(username);
password = user.getPassword();
if (!StringUtils.hasLength(password)) {
	this.logger.debug("Unable to obtain password for user: " + username);
	return;
}

Ето и retrieveUserName:

protected String retrieveUserName(Authentication authentication) {
		if (isInstanceOfUserDetails(authentication)) {
			return ((UserDetails) authentication.getPrincipal()).getUsername();// обърни внимание, user-name!!!
		}
		return authentication.getPrincipal().toString();
	}

Обаче, в твоя юзър сървис неправилно си сетнал user-name-a да не е равен на имейла :-)

    private static UserDetails map(UserEntity user) {
        return User.withUsername(user.getUsername()) //<--- проблем!
                .password(user.getPassword())
                .authorities(user.getRoles().stream().map(PlannerUserDetailsService::mapRoles).toList()) //// TODO: 14.11.23 add roles
                .build();
    }

Затова и ремембър ми не може да намери UserDetails от твоя сървис, тъй като вместо имейл ще му подаде user name. Оттам не може да генерира cookie и т.н.

Здравей Лъчо. Ще го погледна по късно. Но умишлено съм сложил да е username, а не мейла. Задължително ли трябва да е Майл?

Това, което се връща от UserDetails#getUsername(), трябва да е същото, по което търси твоя UserDetailsService#loadUserByUsername. И трябва да е уникално за всеки юзър. Независимо дали е username, имейл, телефон, бабата на user-a :-) или нещо друго. Вътрешно спринг ползва тази функционалност (например в remember me service-a) и вградени неща престават да работят.

Поздрави,
Л.

За другото не мога да разбера - искаш да смениш паролата на юзъра и презаписваш ентитито, нали? Защо трябва да го логаутваш?

Здравей Лъчо, сега стигам до проекта отново :D. Та добавих CSS-ите в репото и оправих ремембер ме функционалноста. Сега продължава с това което си написал по-долу :).

За другото не мога да разбера - искаш да смениш паролата на юзъра и презаписваш ентитито, нали? Защо трябва да го логаутваш?

На това, искам да сменя или потребителското име или паролата или мейла. Затова го логоутвам. До сега беше потребителското име, като го сменя и не го логоутна в сесията се пази старото име и всичко останало търсеше по име и гърмеше. Същото е за мейла и паролата.

Здравей!

Опасявам се, че начина по който реализираш логаута е малко наивен :-) Първо да надникнем в механизма, по който Spring Security прави логаут. Те използват т.нар. LogoutHandler-и, като основния от тях е SecurityContextLogoutHandler. Какво прави той, може да надникнеш тук.

Освен този хендлър има и други описани тук.

В документацията понатам се описва как да постигнеш програмен логаут в секция "Creating a Custom Logout Endpoint". А именно:

Something like the following is needed at a minimum:

SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();

@PostMapping("/my/logout")
public String performLogout(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
    // .. perform logout
    this.logoutHandler.doLogout(request, response, authentication);
    return "redirect:/home";
}

Надявам се, че това ще разреши logout проблемите :-)
Поздрави,
Л.