我的意思是用户 ID 不能同时在两台不同的机器上进行身份验证。我该如何控制这个?我正在使用 Java EE 平台。
3 回答
Java EE 没有为这种情况提供直接的解决方案。
您可能会引入一个 servletFilter
来强制执行您的限制:
跟踪用户是否登录,并拒绝重复用户的请求。如果你想扩大规模,那么在数据库中跟踪它,而不是在应用程序范围内[尽管应用程序范围的哈希表将是一个快速而肮脏的原型解决方案]。
您可以
HttpServletRequest.getRemoteAddr
用来区分两台传入的计算机,但是这无助于区分同一路由器后面的两台计算机。将 EJB 注入过滤器以处理您的 JPA 请求;使用 EJB 将消除其他问题。
在所有servlet上部署过滤器(否则会有安全漏洞):
@WebFilter({"/*"}) public class SecurityFilter extends AbstractSecurityFilter { ... }
AbstractSecurityFilter,定义如下,增强了标准的 Java EE 安全检查。它将减少检查每个传入 servlet 请求的开销,只对“新登录”进行全面检查。它还将处理拒绝逻辑。它不实现正在讨论的身份验证,但可以提供这样的基础。
子类并为authenticate
和getFailedLoginPage()
方法提供定义:
/***
* Augment the Java EE security check(s).
*
* Java EE Security is the primary check, so this filter doesn't take any action
* until after the user successfully logs in through the standard Java EE security
* mechanisms. Once logged in, the method authenticate is called to perform
* the custom checks. If these checks fail the user is logged out and forwarded
* to the page returned by getFailedLoginPage().
*/
public abstract class AbstractSecurityFilter implements Filter
{
protected static final String ATTR__IS_AUTHENTICATED = AbstractSecurityFilter.class.getName() + ".authenticated";
/**
* @return true if request meets our authentication requirements.
*/
protected abstract boolean authenticate(HttpServletRequest request, HttpServletResponse response);
protected abstract String getFailedLoginPage();
@Override
final public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException
{
/**
*
* Order of events (and order is somewhat important): <code>
*
* - ignore if user has not logged in (if Java EE Security hasn't required login, neither do we here).
*
* - check for this filter's ATTR__IS_AUTHENTICATED flag to be set on the session attributes:
* if set we have nothing else to do.
*
* - call authenticate...
* - on failure: logout, forward to failedLoginPage, and return (cancel remainder of this request).
* - on success: set ATTR__IS_AUTHENTICATED flag in the session attributes.
*
* </code>
*/
if (request instanceof HttpServletRequest) {
final HttpServletRequest httpRequest = (HttpServletRequest)request;
final HttpSession session = httpRequest.getSession(false);
if (session != null) {
/*
* Ignore if user has not (yet) logged in; let JEE Security filter(s) do their job(s) first.
*/
final Principal userPrincipal = httpRequest.getUserPrincipal();
if (userPrincipal != null) {
// Check that user has not been fully "verified".
final Object attribute = session.getAttribute(ATTR__IS_AUTHENTICATED);
if (attribute == null) {
if ( !authenticate(httpRequest, (HttpServletResponse)response) {
httpRequest.logout();
final ServletContext servletContext = httpRequest.getServletContext();
servletContext.getRequestDispatcher(getFailedLoginPage()).forward(request, response);
return;
}
// User has passed local checks; record so we don't have to do this again.
final String name = userPrincipal.getName();
session.setAttribute(ATTR__IS_AUTHENTICATED, name);
}
}
}
}
chain.doFilter(request, response);
}
}
每次用户登录时,您都会给他一些令牌(例如Session ID)。稍后,当用户执行某些操作时,您会从他那里收到此令牌,并根据您的内部数据存储(例如数据库)对其进行验证。如果验证成功,则让用户进入。
为了禁用来自不同计算机的多次登录,您的令牌应该包括机器的 ID 和其他数据。在 a) 登录和 b) 验证期间,您将比较令牌中的 ID 和当前使用的机器的真实 ID。
Spring-security 在会话中具有并发控制。请参阅http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#concurrent-sessions。