Как реализовать "оставаться в системе" при входе пользователя в веб-приложение
на большинстве веб-сайтов, когда пользователь собирается предоставить имя пользователя и пароль для входа в систему, есть флажок "оставаться в системе". Если вы установите флажок, он будет поддерживать вход во все сеансы из одного и того же веб-браузера. Как я могу реализовать то же самое в Java EE?
Я использую управляемую аутентификацию контейнера на основе формы со страницей входа JSF.
<security-constraint>
<display-name>Student</display-name>
<web-resource-collection>
<web-resource-name>CentralFeed</web-resource-name>
<description/>
<url-pattern>/CentralFeed.jsf</url-pattern>
</web-resource-collection>
<auth-constraint>
<description/>
<role-name>STUDENT</role-name>
<role-name>ADMINISTRATOR</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>jdbc-realm-scholar</realm-name>
<form-login-config>
<form-login-page>/index.jsf</form-login-page>
<form-error-page>/LoginError.jsf</form-error-page>
</form-login-config>
</login-config>
<security-role>
<description>Admin who has ultimate power over everything</description>
<role-name>ADMINISTRATOR</role-name>
</security-role>
<security-role>
<description>Participants of the social networking Bridgeye.com</description>
<role-name>STUDENT</role-name>
</security-role>
3 ответов
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 приложение.
Java EE 6/7
если вы находитесь на Java EE 6 или 7, homegrow долгоживущий 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()
метод a 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);
}
}
(the COOKIE_NAME
должно быть уникальным именем cookie, например "remember"
и COOKIE_AGE
должен быть возраст в секундах, например,2592000
в течение 30 дней)
вот как doFilter()
метод a 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 (жаль, что они отсутствуют в 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
is чрезвычайно трудно грубой силы, вы можете предоставить пользователю возможность заблокировать опцию" запомнить " на IP-адрес пользователя (request.getRemoteAddr()
) и хранить/сравнивать его в базе данных. Это делает его немного более надежным. Кроме того, было бы полезно иметь" срок годности", хранящийся в базе данных.
это также хорошая практика, чтобы заменить UUID
значение, когда пользователь изменил свой пароль.
Java EE 5 или ниже
пожалуйста, обновите.
обычно это делается так:
когда вы входите в систему пользователя, вы также устанавливаете cookie на клиенте (и сохраняете значение cookie в базе данных), истекающее через определенное время (обычно 1-2 недели).
когда приходит новый запрос, вы проверяете, существует ли определенный файл cookie, и если да, загляните в базу данных, чтобы узнать, соответствует ли он определенной учетной записи. Если он соответствует, вы будете" свободно " войти в эту учетную запись. Когда я говорю свободно, я имею в виду, что вы только позволяете этой сессии читать некоторые информация и не писать информацию. Вам нужно будет запросить пароль, чтобы разрешить параметры записи.
Это все, что есть. Хитрость заключается в том, чтобы убедиться, что "свободно" войти не в состоянии сделать много вреда для клиента. Это несколько защитит пользователя от того, кто захватывает его файл cookie remember me и пытается войти в систему как он.
вы не можете войти в систему пользователя полностью через HttpServletRequest.логин (имя пользователя, пароль), так как вы не должны хранить как имя пользователя, так и простой текстовый пароль в базе данных. Также вы не можете выполнить этот вход с хэшем пароля, который сохраняется в базе данных. Однако вам нужно идентифицировать пользователя с помощью маркера cookie/DB, но войти в систему без ввода пароля с помощью пользовательского модуля входа (Java class) на основе Glassfish server API.
см. следующие ссылки для получения дополнительной информации подробности:
http://www.lucubratory.eu/custom-jaas-realm-for-glassfish-3/
пользовательский механизм безопасности в приложении Java EE 6/7