Design patterns 如何同时对一个用户进行身份验证(登录)?他可以同时在不同的计算机上登录两次

Design patterns 如何同时对一个用户进行身份验证(登录)?他可以同时在不同的计算机上登录两次,design-patterns,jakarta-ee,authentication,Design Patterns,Jakarta Ee,Authentication,我的意思是用户ID不能同时在两台不同的机器上进行身份验证。我怎样才能控制这一切?我正在使用Java EE平台。每次用户登录时,您都会给他一些令牌(例如)。稍后,当用户执行某些操作时,您将从他那里接收该令牌,并根据您的内部数据存储(例如,数据库)对其进行验证。如果验证成功,则允许用户进入 为了禁用来自不同计算机的多个登录,您的令牌应该包括机器的ID以及其他数据。在a)登录和b)验证期间,您将比较来自令牌的ID和当前使用机器的真实ID。Java EE没有为这种情况提供直接的解决方案 您可以引入ser

我的意思是用户ID不能同时在两台不同的机器上进行身份验证。我怎样才能控制这一切?我正在使用Java EE平台。

每次用户登录时,您都会给他一些令牌(例如)。稍后,当用户执行某些操作时,您将从他那里接收该令牌,并根据您的内部数据存储(例如,数据库)对其进行验证。如果验证成功,则允许用户进入


为了禁用来自不同计算机的多个登录,您的令牌应该包括机器的ID以及其他数据。在a)登录和b)验证期间,您将比较来自令牌的ID和当前使用机器的真实ID。

Java EE没有为这种情况提供直接的解决方案

您可以引入servlet
过滤器来强制执行您的限制:

  • 跟踪用户是否登录,并拒绝重复用户的请求。如果您想扩大规模,那么在数据库中跟踪它,而不是在应用程序范围内(尽管应用程序范围的哈希表是一个快速而肮脏的原型解决方案)

  • 您可以使用
    HttpServletRequest.getRemoteAddr
    来区分两台传入的计算机,但是这无助于区分同一路由器后面的两台计算机

  • 将EJB注入过滤器以处理JPA请求;使用EJB将消除其他问题

  • 在所有servlet上部署筛选器(否则会出现安全漏洞):

  • 下面定义的AbstractSecurityFilter增强了标准JavaEE安全检查。它将减少检查每个传入servlet请求的开销,只对“新登录”进行完整检查。它还将处理拒绝逻辑。它不实现正在讨论的身份验证,但可以为此类验证提供基础

子类,并为
身份验证
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);
    }
}

Spring security在会话中具有并发控制。看

/***
 * 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);
    }
}