2

我們有基於web的表單登錄認證,j_securtiy_check工作。我們希望通過程序化登錄身份驗證來更改它。讓servlet驗證傳遞給它的用戶名和密碼的正確方法是什麼?這個servlet顯然是不受保護的。如何以編程方式從servlet內調用認證,如j_security_check會做

我們一直在嘗試這樣的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" 
/> 

這樣做的原因是,我們有將登錄信息發送到未受保護的loginServlet一個Java Webstart的客戶端。這個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()); 

回答

0

我注意到,這已不再是最新的。最終的解決方案是使用OpenAM提供的Java SDK。

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

1)添加的所有附帶此SDK到你的Web應用程序的jar文件。 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

1

我想用Java Webstart的客戶端應用程序,當你需要問的認證,你只使用POST方法使用任何HTTP客戶端發送的用戶名,密碼登錄服務器的。在loginServlet中,使用request.login(userName,password),然後以任何格式(XML,JSON)返回認證結果。在客戶端,你必須從響應頭解析認證結果(POST結果)和JSESSIONID cookie。對於後續請求,您可能需要發送之前解析過的JSESSIONID。

+0

事實證明,我的eclipse仍然爲服務器2.X設置。我無法訪問HttpServletRequest 3.0,其中「login(user,pass)」可用。現在我已經嘗試了這個,它失敗的時候出現的錯誤不是很有幫助:由於: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支持這一點。 –

+0

您可以使用其他方式(不使用Servlet 3.0+的request.login):將您的身份驗證請求提交至/ j_security_check操作,其中username參數爲「j_username」,密碼參數名稱爲「j_password」。 Container將爲您處理身份驗證(這與request.login類似)。請注意,這是Container登錄的解決方案。 – Loc

+0

http://docs.oracle.com/javaee/6/tutorial/doc/glxce.html - j_security_check的鏈接 – Loc

1

我想跟進此事。

真的不是一個簡單的答案。

最後的代碼使用純反射嘗試調用領域內的身份驗證方法。問題是這真的取決於所附的領域。例如

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服務。

我的這個想法的第一個問題 - 試圖找到一個API,我可以使用從webstart java客戶端1)沒有巨大的jar文件大小,2)與tomee + CXF 2.6.4版一起使用。 (我對此沒有足夠的瞭解,「是的,只是使用CXF 3.0客戶端的罐子,因爲他們可以使用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中進行驗證。

丹尼斯

5

如果你已經上的Servlet 3.0或更新版本,爲編程認證使用的HttpServletRequestlogin()方法。

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

Servlet API中爲您提供login()logout()方法編程訪問容器管理的安全性。

相關問題