Java EE 8 и выше
Если вы используете Java EE 8 или новее, поставьте @RememberMe
на пользовательский HttpAuthenticationMechanism
вместе сRememberMeIdentityStore
.
@ApplicationScoped
@AutoApplySession
@RememberMe
public class CustomAuthenticationMechanism implements HttpAuthenticationMechanism {
@Inject
private IdentityStore identityStore;
@Override
public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext context) {
Credential credential = context.getAuthParameters().getCredential();
if (credential != null) {
return context.notifyContainerAboutLogin(identityStore.validate(credential));
}
else {
return context.doNothing();
}
}
}
public class CustomIdentityStore implements RememberMeIdentityStore {
@Inject
private UserService userService; // This is your own EJB.
@Inject
private LoginTokenService loginTokenService; // This is your own EJB.
@Override
public CredentialValidationResult validate(RememberMeCredential credential) {
Optional<User> user = userService.findByLoginToken(credential.getToken());
if (user.isPresent()) {
return new CredentialValidationResult(new CallerPrincipal(user.getEmail()));
}
else {
return CredentialValidationResult.INVALID_RESULT;
}
}
@Override
public String generateLoginToken(CallerPrincipal callerPrincipal, Set<String> groups) {
return loginTokenService.generateLoginToken(callerPrincipal.getName());
}
@Override
public void removeLoginToken(String token) {
loginTokenService.removeLoginToken(token);
}
}
Вы можете найти пример из реальной жизни в Java EE Kickoff Application.
Java EE 6/7
Если вы используете Java EE 6 или 7, создайте долговременный файл cookie для отслеживания уникального клиента и использования сервлета.3.0 API обеспечивает программный вход в систему HttpServletRequest#login()
, когда пользователь не вошел в систему, но файл cookie присутствует.
Этого проще всего достичь, если создать другую таблицу БД с java.util.UUID
значение в качестве PK и идентификатор соответствующего пользователя в качестве FK.
Примите следующую форму входа в систему:
<form action="login" method="post">
<input type="text" name="username" />
<input type="password" name="password" />
<input type="checkbox" name="remember" value="true" />
<input type="submit" />
</form>
И следующее в doPost()
методе Servlet
, которыйотображается на /login
:
String username = request.getParameter("username");
String password = hash(request.getParameter("password"));
boolean remember = "true".equals(request.getParameter("remember"));
User user = userService.find(username, password);
if (user != null) {
request.login(user.getUsername(), user.getPassword()); // Password should already be the hashed variant.
request.getSession().setAttribute("user", user);
if (remember) {
String uuid = UUID.randomUUID().toString();
rememberMeService.save(uuid, user);
addCookie(response, COOKIE_NAME, uuid, COOKIE_AGE);
} else {
rememberMeService.delete(user);
removeCookie(response, COOKIE_NAME);
}
}
(COOKIE_NAME
должно быть уникальным именем cookie, например, "remember"
, а COOKIE_AGE
должно быть возрастом в секундах, например, 2592000
на 30 дней)
ЕёВот как может выглядеть doFilter()
метод Filter
, который отображается на ограниченных страницах:
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
User user = request.getSession().getAttribute("user");
if (user == null) {
String uuid = getCookieValue(request, COOKIE_NAME);
if (uuid != null) {
user = rememberMeService.find(uuid);
if (user != null) {
request.login(user.getUsername(), user.getPassword());
request.getSession().setAttribute("user", user); // Login.
addCookie(response, COOKIE_NAME, uuid, COOKIE_AGE); // Extends age.
} else {
removeCookie(response, COOKIE_NAME);
}
}
}
if (user == null) {
response.sendRedirect("login");
} else {
chain.doFilter(req, res);
}
В сочетании с этими вспомогательными методами cookie (слишком плохо, что они отсутствуют в Servlet API):
public static String getCookieValue(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (name.equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
Cookie cookie = new Cookie(name, value);
cookie.setPath("/");
cookie.setMaxAge(maxAge);
response.addCookie(cookie);
}
public static void removeCookie(HttpServletResponse response, String name) {
addCookie(response, name, null, 0);
}
Несмотря на то, что UUID
чрезвычайно трудно переборить, вы можете предоставить пользователю возможность привязать опцию «запомнить» к IP-адресу пользователя (request.getRemoteAddr()
) и сохранить / сравнить его вбаза данных также.Это делает его чуть более надежным.Кроме того, было бы полезно иметь «дату истечения», сохраненную в базе данных.
Полезно также заменять значение UUID
всякий раз, когда пользователь изменяет свой пароль.
Java EE 5 или ниже
Пожалуйста, обновите.