【问题标题】:How do I programmatically call authenticate from within a servlet like j_security_check would do我如何以编程方式从像 j_security_check 这样的 servlet 中调用身份验证
【发布时间】:2013-12-28 14:49:42
【问题描述】:

我们有j_securtiy_check 工作的基于Web 的表单登录身份验证。我们想通过编程登录身份验证来改变它。让 servlet 验证传递给它的用户名和密码的正确方法是什么? servlet 显然没有受到保护。

我们一直在试验这个 server.xml 领域:

<Realm  className="org.apache.catalina.realm.DataSourceRealm"
    dataSourceName="UserDatabase"
    userTable="app_user" userNameCol="login_name" userCredCol="password_value"
    userRoleTable="user_perm" roleNameCol="permission_name"
    allRolesMode="authOnly" digest="MD5"
/>

原因是我们有一个 java webstart 客户端,它将登录信息发送到一个未受保护的 loginServlet。此 servlet 当前针对 JOSSO 单点登录服务进行身份验证,但我希望删除它并为初学者使用简单的 tomcat7 身份验证。然后最终迁移到 OpenAM。如果我能以编程方式生成 JSSESSIONIDSSO 值并将其填充到 cookie 中。

这是我找到的一些代码。这是调用身份验证的正确方法吗?

ApplicationContextFacade acf = (ApplicationContextFacade) this.getServletContext();

Field privateField = ApplicationContextFacade.class.getDeclaredField("context");  
privateField.setAccessible(true);  
ApplicationContext appContext = (ApplicationContext) privateField.get(acf);  
Field privateField2 = ApplicationContext.class.getDeclaredField("context");  
privateField2.setAccessible(true);  
StandardContext stdContext = (StandardContext) privateField2.get(appContext);  
Realm realm = stdContext.getRealm();  

Principal principal = realm.authenticate(loginBean.getUsername(), loginBean.getPassword());  
if (principal == null)
{
   return 0;
}
GenericPrincipal genericPrincipal = (GenericPrincipal) principal;

System.out.println ("genericPrincipal=" + genericPrincipal.toString());

【问题讨论】:

    标签: tomcat authentication servlets programmatically-created j-security-check


    【解决方案1】:

    如果您已经在使用 Servlet 3.0 或更高版本,则对于编程身份验证,请使用 login()HttpServletRequest 方法。

    if (request.getUserPrincipal() == null) {
        request.getSession(); // create session before logging in
        request.login(username, password);
    }
    

    Servlet API 为您提供 login()logout() 方法,用于以编程方式访问容器管理的安全性。

    【讨论】:

      【解决方案2】:

      我认为在 Java webstart 客户端应用程序中,当您需要进行身份验证时,您只需使用任何 HTTP 客户端使用 POST 方法将用户名、密码发送到您的 LoginServer。在 loginServlet 中,您使用 request.login ( userName, password ) 然后以任何格式(XML、JSON)返回身份验证结果。在客户端,您也必须从响应标头中解析身份验证结果( POST 结果)和 JSESSIONID cookie。对于后续请求,您可能需要发送之前解析的 JSESSIONID。

      【讨论】:

      • 原来我的 Eclipse 仍然是为服务器 2.X 设置的。我无权访问“登录(用户,密码)”可用的 HttpServletRequest 3.0。现在我已经尝试过了,它失败了,并出现了不太有用的错误:原因:javax.servlet.ServletException:在 org.apache 的 org.apache.catalina.authenticator.AuthenticatorBase.doLogin(AuthenticatorBase.java:820) 处登录失败。 catalina.authenticator.AuthenticatorBase.login(AuthenticatorBase.java:800) 在 org.apache.catalina.connector.Request.login(Request.java:2621)。我不认为 OpenAm 支持这一点。
      • 您可以使用另一种方式(不使用 Servlet 3.0+ 的 request.login):将您的身份验证请求提交到 /j_security_check 操作,用户名参数为“j_username”,密码参数名称为“j_password”。容器将为您处理身份验证(这类似于 request.login )。注意这是Container登录的解决方案。
      【解决方案3】:

      我想跟进此事。

      真的没有一个简单的答案。

      最后的代码使用纯反射来尝试调用领域内的身份验证方法。问题是这实际上取决于附加的领域。

      JOSSO (org.josso.tc55.agent.jaas.CatalinaJAASRealm) 例如没有这个方法。相反,它有一个叫做 createPrincipal(String username, Subject subject) 的东西。他们建议这样做的过程(至少对于 josso 1.5)是使用这样的代码:

                  impl = getIdentityProvider(endpoint);
                  String assertion =   impl.assertIdentityWithSimpleAuthentication(username,password);
                  sessionID = impl.resolveAuthenticationAssertion(assertion);
      

      如果您使用 OpenAM(这是我试图迁移到的)作为您的单点登录提供程序而不是 JOSSO,则完全不同。我目前的想法是使用他们直接从 webstart 客户端提供的 RESTful 服务。

      我对这个想法的第一个问题 - 试图从 webstart java 客户端找到一个我可以使用的 API,它 1)没有很大的 jar 文件大小,2)适用于 tomee+ CXF 版本 2.6.4。 (我对此知之甚少,“是的,只需使用 CXF 3.0 客户端 jar,因为它们可以很好地与 tomee+ 的 CXF 版本配合使用......”)

      无论如何,如果您使用 Tomcat7 的预设数据源机制来设置领域,这里的代码“应该”工作。

                  Class c = Class.forName("org.apache.catalina.core.ApplicationContextFacade");
                  Object o = this.getServletContext();
                  System.out.println ("servletContext is really:" + o.getClass().getCanonicalName());
      
                  Field privateField = o.getClass().getDeclaredField("context");  
                  privateField.setAccessible(true);  
                  Object appContext =  privateField.get(o);  
                  Field privateField2 = appContext.getClass().getDeclaredField("context");  
                  privateField2.setAccessible(true);  
                  Object stdContext =  privateField2.get(appContext);
                  Method getRealm = stdContext.getClass().getMethod("getRealm");
                  Object realm = getRealm.invoke(stdContext);
      
                  Principal principal = null;
                  try
                  {
                      Method authenticate = realm.getClass().getMethod("authenticate");  
                      principal = (Principal)authenticate.invoke(realm, loginBean.getUsername(), loginBean.getPassword());
                      if (principal == null)
                      {
                          return 0;
                      }
                  }
                  catch (Exception e2)
                  {
                      // The authenticate method doesn't exist within the configured server.xml realm
                      e2.printStackTrace();
                  }
      

      同样,如果您发现自己试图在未受保护的 servlet 中对用户进行身份验证。

      -丹尼斯

      【讨论】:

        【解决方案4】:

        我注意到这不再是最新的。最终的解决方案是使用 OpenAM 提供的 Java SDK。

        这是起点:http://openam.forgerock.org/openam-documentation/openam-doc-source/doc/dev-guide/index/chap-jdk.html

        1) 将此 SDK 附带的所有 jar 文件添加到您的 Web 应用程序。 2) 将您的 servlet(或重客户端)更改为具有以下代码:

            private void addLoginCallbackMessage(LoginCredentialsBean loginBean, Callback [] callbacks)
                throws UnsupportedCallbackException
        {
            int i = 0;
            try
            {
                for (i = 0; i < callbacks.length; i++)
                {
                    if (callbacks[i] instanceof TextOutputCallback)
                    {
                        handleTextOutputCallback((TextOutputCallback) callbacks[i]);
                    }
                    else if (callbacks[i] instanceof NameCallback)
                    {
                        handleNameCallback(loginBean.getUsername(), (NameCallback) callbacks[i]);
                    }
                    else if (callbacks[i] instanceof PasswordCallback)
                    {
                        handlePasswordCallback(loginBean.getPassword(), (PasswordCallback) callbacks[i]);
                    }
                    else if (callbacks[i] instanceof TextInputCallback)
                    {
                        handleTextInputCallback((TextInputCallback) callbacks[i]);
                    }
                    else if (callbacks[i] instanceof ChoiceCallback)
                    {
                        handleChoiceCallback((ChoiceCallback) callbacks[i]);
                    }
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
                throw new UnsupportedCallbackException(callbacks[i], e.getMessage());
            }
        }
        
        private void handleTextOutputCallback(TextOutputCallback toc)
        {
            System.out.println("Got TextOutputCallback");
            // display the message according to the specified type
        
            switch (toc.getMessageType())
            {
            case TextOutputCallback.INFORMATION:
                System.out.println(toc.getMessage());
                break;
            case TextOutputCallback.ERROR:
                System.out.println("ERROR: " + toc.getMessage());
                break;
            case TextOutputCallback.WARNING:
                System.out.println("WARNING: " + toc.getMessage());
                break;
            default:
                System.out.println("Unsupported message type: " +
                        toc.getMessageType());
            }
        }
        
        private void handleNameCallback(String name, NameCallback nc)
                throws IOException
        {
            nc.setName(name);
        }
        
        private void handleTextInputCallback(TextInputCallback tic)
                throws IOException
        {
            // not supported for server side
            // prompt for text input
        }
        
        private void handlePasswordCallback(String password, PasswordCallback pc)
                throws IOException
        {
            // prompt the user for sensitive information
        
            pc.setPassword(password.toCharArray());
        }
        
        private void handleChoiceCallback(ChoiceCallback cc)
                throws IOException
        {
            // not supported for server side
        
            // ignore the provided defaultValue
            /*        
            System.out.print(cc.getPrompt());
        
            String [] strChoices = cc.getChoices();
            for (int j = 0; j < strChoices.length; j++)
            {
                System.out.print("choice[" + j + "] : " + strChoices[j]);
            }
            System.out.flush();
            cc.setSelectedIndex(Integer.parseInt((new BufferedReader
                    (new InputStreamReader(System.in))).readLine()));
            */
        }
        
        
        private void doLogin ()
        {
            // ... lots of other logic here
        
            // TODO: Make this into modules with this one being for OpenAM
            if (_useOpenAM)
            {
                String orgName = "/";
                String moduleName = "DataStore";
                String locale = "en_US";
        
                AuthContext lc = new AuthContext(orgName);
                AuthContext.IndexType indexType = AuthContext.IndexType.MODULE_INSTANCE;
                lc.login(indexType, moduleName, locale);
        
                boolean succeed = false;
                Callback [] callbacks = null;
        
                // get information requested from module
                while (lc.hasMoreRequirements())
                {
                    callbacks = lc.getRequirements();
                    if (callbacks != null)
                    {
                        addLoginCallbackMessage(loginBean, callbacks);
                        lc.submitRequirements(callbacks);
                    }
                }
        
                if (lc.getStatus() == AuthContext.Status.SUCCESS)
                {
                    try
                    {
                        System.out.println("Login succeeded.");
                        openAMSessionId = lc.getAuthIdentifier();
                        System.out.println("lc.getAuthIdentifier()=" + openAMSessionId);
                        System.out.println("lc.getSuccessURL()=" + lc.getSuccessURL());
                        System.out.println("lc.getSSOToken().getAuthLevel()=" + lc.getSSOToken().getAuthLevel());
                        System.out.println("lc.getSSOToken().getAuthType()=" + lc.getSSOToken().getAuthType());
                        System.out.println("lc.getSSOToken().getHostName()=" + lc.getSSOToken().getHostName());
                        System.out.println("lc.getSSOToken().getIdleTime()=" + lc.getSSOToken().getIdleTime());
                        System.out.println("lc.getSSOToken().getMaxIdleTime()=" + lc.getSSOToken().getMaxIdleTime());
                        System.out.println("lc.getSSOToken().getMaxSessionTime()=" + lc.getSSOToken().getMaxSessionTime());
                        System.out.println("lc.getSSOToken().getTimeLeft()=" + lc.getSSOToken().getTimeLeft());
                        System.out.println("lc.getSSOToken().getIPAddress()=" + lc.getSSOToken().getIPAddress());
                        System.out.println("lc.getSSOToken().getTokenID()=" + lc.getSSOToken().getTokenID().toString());
                        System.out.println("lc.getSSOToken().getPrincipal()=" + lc.getSSOToken().getPrincipal().toString());
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
        
                    succeed = true;
                }
                else if (lc.getStatus() == AuthContext.Status.FAILED)
                {
                    System.out.println("Login failed.");
                }
                else
                {
                    System.out.println("Unknown status: " + lc.getStatus());
                }
        
                System.out.println( "OpenAM login success=" + succeed);
            }
        }
        

        上面代码中重要的一点是变量 openAMSessionId 。最终获得了新的 OpenAM 单点登录会话 ID,您可以将其传递给所有受保护的客户端应用程序,这样用户就不会在登录时受到挑战。

        我希望这会有所帮助。

        -dklotz

        【讨论】:

          猜你喜欢
          • 2010-11-18
          • 1970-01-01
          • 2013-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-02
          相关资源
          最近更新 更多