Вопрос немного длиннее, чем ожидалось, поэтому вот ссылка на похожий вопрос (3-ий пост), где я не нашел ответ удовлетворительным.
TL; DR
Я пытаюсь выйти из системы с помощью модуля входа в систему JAAS. Вот краткая структура проекта: LoginService отвечает за создание экземпляра LoginContext, когда пользователь хочет войти:
@Service
public class LoginService {
public UserDTO getUserDTOFrom(Credentials credentials) {
try {
LoginContext loginContext = new LoginContext("Login", new JAASCallbackHandler(credentials));
loginContext.login();
// construct UserDTO object.
} catch (LoginException e) {
LOGGER.error("Login Exception: {}", e.getMessage());
// construct UserDTO object.
}
// return UserDTO object.
}
Этот метод вызывается LoginController:
@RestController
@RequestMapping("/login")
public class LoginController {
private final LoginService loginService;
@Autowired
public LoginController(LoginService loginService) {
this.loginService = loginService;
}
@PostMapping
public ResponseEntity<UserDTO> getUserDTOFrom(@Valid @RequestBody Credentials credentials) {
UserDTO userDTO = loginService.getUserDTOFrom(userForm);
// return response that depends on outcome in the login service
}
}
Проблема возникает, когда я хочу выйти из ранее вошедшего в систему пользователя. LoginContext отвечает за вызов метода выхода из системы в JAAS Login Module. Например:
loginContext.logout();
Метод в модуле входа в систему JAAS:
public class JAASLoginModule implements LoginModule {
@Override
public boolean logout() {
subject.getPrincipals().remove(usernamePrincipal);
subject.getPrincipals().remove(passwordPrincipal);
return true;
}
}
У меня нет LoginContext в LogoutService и я не могу полностью очистить ранее аутентифицированный субъект.
Я попытался создать одноэлементный компонент, поэтому я смог бы получить тот же экземпляр LoginContext:
@Configuration
public class LoginContextBean {
@Lazy
@Bean
public LoginContext getLoginContext(Credentials credentials) throws LoginException {
System.setProperty("java.security.auth.login.config", "resources/configuration/jaas.config");
return new LoginContext("Login", new JAASCallbackHandler(credentials));
}
}
@Service
public class LoginService {
private final ObjectProvider<LoginContext> loginContextProvider;
@Autowired
public LoginService(ObjectProvider<LoginContext> loginContextProvider) {
this.loginContextProvider = loginContextProvider;
}
public UserDTO getUserDTOFrom(Credentials credentials) {
try {
LoginContext loginContext = loginContextProvider.getObject(credentials);
loginContext.login();
// construct UserDTO object.
} catch (LoginException e) {
LOGGER.error("Login Exception: {}", e.getMessage());
// construct UserDTO object.
}
// return UserDTO object.
}
}
@Service
public class LogoutService {
private final ObjectProvider<LoginContext> loginContextProvider;
@Autowired
public LogoutService(ObjectProvider<LoginContext> loginContextProvider) {
this.loginContextProvider = loginContextProvider;
}
public void performLogout() {
LoginContext loginContext = loginContextProvider.getObject();
try {
loginContext.logout();
} catch (LoginException e) {
LOGGER.error("Failed to logout: {}.", e.getMessage());
}
}
}
Это не особенно полезно, поскольку следующий / тот же пользователь регистрируется Вы получите NPE на LoginContext. Я прочитал, что HttpServletRequest getSession (). Invalidate (); Предположим, что для вызова logout () JAAS или что выход из системы HttpServletRequest () будет делать эту работу. Но оба метода не имеют никакого эффекта. Например:
@RestController
@RequestMapping("/logout")
public class LogoutController {
private final LogoutService logoutService;
@Autowired
public LogoutController(LogoutService logoutService) {
this.logoutService = logoutService;
}
@DeleteMapping
public ResponseEntity<Void> deleteJwt(@CookieValue("jwt_cookie") String jwtToken, HttpServletRequest request) throws ServletException {
request.getSession().invalidate(); // logout() is not called.
request.logout(); // logout() is not called.
return getResponse();
}
}
Я хочу получить руку на ранее созданный LoginContext, когда пользователь хочет выйти, но создать новый, когда другой пользователь пытается войти. Обратите внимание, что я не использую Spring Security.
EDIT :
Одна из идей состояла в том, чтобы использовать синглтон, который будет содержать набор контекстов входа в систему, связанных с конкретным пользователем. А затем позвоните и уничтожьте их, когда пользователь выйдет из системы. Ключом для такого набора может быть токен jwt или userId. После дальнейших размышлений мне показалось, что пользователь может иметь несколько сеансов, и в этом случае userId в качестве ключа не сможет служить своей цели. Второй вариант - токен jwt. Но есть сценарий, когда будущее промежуточное ПО выдаст новый токен jwt по истечении срока действия, тогда мой набор не сможет вернуть действительный контекст входа в систему.