如何实现“保持登录状态”当用户登录Web应用程序时 [英] How to implement "Stay Logged In" when user login in to the web application
问题描述
在大多数网站上,当用户即将提供登录系统的用户名和密码时,会出现一个保持登录状态的复选框。如果选中该复选框,它将使您在同一Web浏览器的所有会话中登录。如何在Java EE中实现相同的功能?
On most websites, when the user is about to provide the username and password to log into the system, there's a checkbox like "Stay logged in". If you check the box, it will keep you logged in across all sessions from the same web browser. How can I implement the same in Java EE?
我正在使用基于FORM的容器管理身份验证和JSF登录页面。
I'm using FORM based container managed authentication with a JSF login page.
<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>
推荐答案
Java EE 8及以上
如果您使用的是Java EE 8或更高版本,请输入 @RememberMe
/javaee-spec/javadocs/javax/security/enterprise/authentication/mechanism/http/HttpAuthenticationMechanism.html\"rel =noreferrer> 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应用程序中的/ a>。
You can find a real world example in the Java EE Kickoff Application.
如果您使用的是Java EE 6或7, homegrow一个长期存在的cookie来跟踪唯一的客户端并使用Servlet 3.0 API提供的程序化登录 HttpServletRequest#login()
-in但是cookie存在。
If you're on Java EE 6 or 7, homegrow a long-living cookie to track the unique client and use the Servlet 3.0 API provided programmatic login HttpServletRequest#login()
when the user is not logged-in but the cookie is present.
如果你创建另一个带有 java.util.UUID的数据库表,这是最容易实现的。 code>作为PK的值和有问题的用户的ID作为FK。
This is the easiest to achieve if you create another DB table with a java.util.UUID
value as PK and the ID of the user in question as 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
:
And the following in doPost()
method of a Servlet
which is mapped on /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天)
(the COOKIE_NAME
should be the unique cookie name, e.g. "remember"
and the COOKIE_AGE
should be the age in seconds, e.g. 2592000
for 30 days)
以下是<$ c $的方式c> doFilter()在受限制的页面上映射的过滤器
的方法可能如下所示:
Here's how the doFilter()
method of a Filter
which is mapped on restricted pages could look like:
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中缺失):
In combination with those cookie helper methods (too bad they are missing in 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()
)并存储/比较它也在数据库中。这使得它更加强大。此外,在数据库中存储过期日期将非常有用。
Although the UUID
is extremely hard to brute-force, you could provide the user an option to lock the "remember" option to user's IP address (request.getRemoteAddr()
) and store/compare it in the database as well. This makes it a tad more robust. Also, having an "expiration date" stored in the database would be useful.
替换 UUID $ c也是一种好习惯。每当用户更改密码时,$ c>值。
请升级。
这篇关于如何实现“保持登录状态”当用户登录Web应用程序时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!