2015-10-11 13 views
0

的Tomcat 7例外:的Tomcat 7的JSP頁面:使用Shibboleth的認證會話在Tomcat中身份驗證時,應避免500錯誤

的Apache Tomcat/7.0.63 - HTTP狀態500 - 發生異常 處理JSP頁面

參考:

我綜合這段代碼與我們i2b2的應用程序,所以我們可以AU證明我們的Shibboleth IdP。

https://github.com/HSSC/i2b2-web-integration/blob/master/doc/INSTALL.md

應用(i2b2):

http://www.i2b2.org

testshib:

(我們遵循了這一打造出來的Shibboleth服務提供商)

http://www.testshib.org/

Shibboleth的國際開發署

http://shibboleth.net/

問題:

的頁面是一個JSP頁面上沒有用的500狀態碼,對設置「ID」,因爲它上面的行有行爲空的「auth」變量(類型爲List<string>的Java)。請注意,此頁面包含JSP標籤和Javascript的組合。

的代碼片段沒有login.jsp頁面上:

function initI2B2() 
{ 

    // alert('initI2B2'); 
    <% 
     shibboleth.ConnectDatabase auth = (shibboleth.ConnectDatabase) session.getAttribute("connectDatabase"); 
     String id = org.apache.commons.lang.StringEscapeUtils.escapeHtml(auth.getUser().identifier); // fails here because the List<string> object is null 
     String sessionId = org.apache.commons.lang.StringEscapeUtils.escapeHtml(auth.getUser().session); 
     Calendar cal = Calendar.getInstance(); 
     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
     String dateTime = sdf.format(cal.getTime()); 
     String token = DigestUtils.sha512Hex(dateTime + sessionId); 
    %> 

    var passedInUserKey = "<%= org.apache.commons.lang.StringEscapeUtils.escapeHtml(auth.getUser().identifier)%>"; 
    var passedInPassKey = "<%= org.apache.commons.lang.StringEscapeUtils.escapeHtml(auth.getUser().session)%>"; 
    var passedInDomainK = "demo"; 
    var passedInInstitutionid = "1"; 
    var tokenV = "<%= token%>"; 

工作流程造成的?

如果我們有一個良好的會話,代碼通常很好。我們現在的解決方法是在24小時內使會話超時。

這裏是工作流程導致錯誤......

命中i2b2頁>自動重定向到的Shibboleth的IdP>登錄時使用的IdP證書>重定向回i2b2頁>這段代碼走的是用戶和會話ID(密碼)退出Shibboleth會話並使用它自動授權到i2b2應用程序。

login.jsp頁面被攻破以從Shibboleth會話中獲取數據,因此用戶不必輸入憑證。

問題是,當shibboleth會話超時,或Tomcat會話過期或緩存在瀏覽器中被清除時,會發生此錯誤,因爲整個頁面需要來自會話(id和密碼)的這兩個變量, 。

清除緩存是獲取此錯誤的最快方法。另一種方法是將Tomcat默認會話超時從30分鐘更改爲1分鐘。

<session-config> 
    <session-timeout>1</session-timeout> 
</session-config> 

想法可能給可能對此問題的回答:

  1. 在ASP.NET中,如果會話不存在,你可以告訴它自動重定向到另一頁(使用組態)。 Tomcat是否通過提供某些條件(使用代碼)來提供該功能?

  2. 我們該如何強制殺死Tomcat會話(不處理此頁面),並重定向,就好像我們對i2b2有一個全新的請求?我知道在ASP.NET中,如果您收到應用程序錯誤,您可以調用代碼。 Tomcat能提供什麼嗎?

  3. 如果我們想要更多地破解這個頁面,我們如何簡單地避免一個條件的錯誤,但仍然有頁面進程,然後重定向?

有沒有其他想法?

package edu.tmc.uth.i2b2shibboleth; 

import java.security.*; 
import java.sql.*; 
import java.util.*; 
import javax.faces.bean.*; 
import javax.faces.context.*; 
import javax.servlet.ServletContext; 
import javax.servlet.http.*; 

/**Manages the retrieval of Shibboleth attributes and the connection to the 
* i2b2 hive database. 
* 
* @author JRussell 
*/ 
@ManagedBean(name = "connectDatabase") 
@SessionScoped 
public class ConnectDatabase implements HttpSessionBindingListener { 

    private User user; 
    private Connection connection; 
    private List<String> test = new ArrayList<String>(); 
    String propertyPath = null; 
    private ResourceBundle properties; 

    public User getUser() { 
     return user; 
    } 

    /*Main function for retrieving Shibboleth information and updating 
    * database records. This is called from the Facelets page (index.xhtml). 
    */ 
    public List<String> getUserInfo() { 
     try { 
      connection = connectToDatabase(); 
      setShibbolethAttributes(); 
      updateI2b2UserDatabase(); 

      connection.close(); 
     } catch (SQLException e) { 
      e.printStackTrace(); 
     } 

     return test; 
    } 

    /*Returns a connection to the i2b2 hive database. 
    * Uses the properties in the database.properties file. 
    */ 
    public Connection connectToDatabase() { 
     try {    
      properties = ResourceBundle.getBundle("edu/tmc/uth/i2b2shibboleth/database"); 
      String url = properties.getString("I2B2_PM.connectionURL"); 
      String userName = properties.getString("I2B2_PM.userName"); 
      String password = properties.getString("I2B2_PM.password"); 

      Class.forName(properties.getString("I2B2_PM.databaseClass")); 
      connection = DriverManager.getConnection(url, userName, password); 
      if (connection != null) { 
       System.out.println("connected to DB"); 
       test.add("Connected to i2b2 hive database."); 
      } 
     } catch (SQLException e) { 
      e.printStackTrace(); 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 
     return connection; 
    } 

    /*Populates the Shibboleth attributes into the User object. 
    * The request header values are based on the attribute-map.xml configuration 
    * file on the Shibboleth Service Provider. The attribute identifiers and 
    * the attributes released are configured on an institutional basis. 
    */ 
    public void setShibbolethAttributes() { 
     HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();   
     String firstName = request.getHeader("Shib-InetOrgPerson-givenName"); 
     String lastName = request.getHeader("Shib-Person-surname"); 
     String email = request.getHeader("Shib-InetOrgPerson-mail"); 
     String identifier = request.getHeader("Shib-iamPerson-subjectUniqueId"); 
     String session = request.getHeader("Shib-Session-ID"); 
     user = new User(firstName, lastName, email, identifier, session); 
     test.add("Basic User attributes"); 
     test.add(user.toString()); 
     test.add("All Shibboleth headers"); 
     //Outputs all of the headers from the Shibboleth request 
     Enumeration enumer = request.getHeaderNames(); 
     while(enumer.hasMoreElements()){ 
      String headerName = enumer.nextElement().toString(); 
      if(headerName.startsWith("Shib")){ 
      test.add(headerName+" - "+request.getHeader(headerName)); 
      } 
     } 
    } 

    /*Add a new user to the i2b2 hive database if they don't have an existing record. 
    * Update the password and enable the user if the i2b2 user already exists. 
    */ 
    private void updateI2b2UserDatabase() throws SQLException { 
     //Try to find current user in database 
     String stmt = "SELECT user_id FROM pm_user_data WHERE user_id=?"; 
     PreparedStatement pst = connection.prepareStatement(stmt); 
     pst.setString(1, user.identifier); 
     System.out.println(stmt+" "+user.identifier); 
     ResultSet result = pst.executeQuery(); 
     //user record found in database so update password 
     if (result.next()) { 
      System.out.println("user record found"); 
      test.add("Subject identifier already in database - " + result.getString("user_id")); 
      stmt = "UPDATE pm_user_data SET password=?, status_cd=? WHERE user_id=?"; 
      pst = connection.prepareStatement(stmt); 
      pst.setString(1, encryptMD5(user.session)); 
      pst.setString(2, "A"); 
      pst.setString(3, user.identifier); 
      pst.executeUpdate(); 
      connection.commit(); 
     } 
     else { //new user so add record to database 
      test.add("New user - " + user.identifier + " " + user.first + " " + user.last); 
      stmt = "INSERT INTO pm_user_data (user_id, full_name, password, email, status_cd) \n" 
        + "VALUES (?,?,?,?,?)"; 
      pst = connection.prepareStatement(stmt); 
      pst.setString(1, user.identifier); 
      pst.setString(2, user.first + " " + user.last); 
      pst.setString(3, encryptMD5(user.session)); 
      pst.setString(4, user.email); 
      pst.setString(5, "A"); 
      pst.executeUpdate(); 

      //assign user roles to i2b2 project 
      String project = properties.getString("I2B2_PM.projectName"); 
      pst = connection.prepareStatement("INSERT INTO pm_project_user_roles (project_id, user_id, user_role_cd, status_cd) \n" 
        + "VALUES (?,?,?,?)"); 
      pst.setString(1, project); 
      pst.setString(2, user.identifier); 
      pst.setString(3, "DATA_OBFSC"); 
      pst.setString(4, "A"); 
      pst.executeUpdate(); 
      pst.setString(3, "DATA_AGG"); 
      pst.executeUpdate(); 
      pst.setString(3, "USER"); 
      pst.executeUpdate(); 
      connection.commit(); 
     } 
    } 

    /* The i2b2 applications expect passwords to be encrypted. 
    *This function encrypts passwords with MD5 before inserting them 
    *into the database. 
    */ 
    private String encryptMD5(String text) { 
     String encrypted = ""; 

     byte[] defaultBytes = text.getBytes(); 
     try { 
      MessageDigest algorithm = MessageDigest.getInstance("MD5"); 
      algorithm.reset(); 
      algorithm.update(defaultBytes); 
      byte messageDigest[] = algorithm.digest(); 

      StringBuilder hexString = new StringBuilder(); 
      for (int i = 0; i < messageDigest.length; i++) { 
       hexString.append(Integer.toHexString(0xFF & messageDigest[i])); 
      } 
      encrypted = hexString.toString(); 
     } catch (NoSuchAlgorithmException e) { 
      e.printStackTrace(); 
     } 
     return encrypted; 
    } 

    /*Function needed to implement HttpSessionBindingListener. 
    * Don't need to modify the function itself. 
    */ 
    public void valueBound(HttpSessionBindingEvent event) { 
     //do nothing 
    } 

    //This function deactivates a user in the i2b2 hive database when 
    //the user session times out. Session timeout is set in web.xml. 
    public void valueUnbound(HttpSessionBindingEvent event) { 
     connection = connectToDatabase(); 
     try { 
      //Try to find current user in database 
      String stmt = "SELECT user_id FROM pm_user_data WHERE user_id=?"; 
      PreparedStatement pst = connection.prepareStatement(stmt); 
      pst.setString(1, user.identifier); 
      ResultSet result = pst.executeQuery(); 
      //user record found in database so deactivate them since they have logged off 
      if (result.next()) { 
       System.out.println("in results.next"); 
       stmt = "UPDATE pm_user_data SET status_cd=? WHERE user_id=?"; 
       pst = connection.prepareStatement(stmt); 
       pst.setString(1, "D"); 
       pst.setString(2, user.identifier); 
       pst.executeUpdate(); 
       connection.commit(); 
      } 
      connection.close(); 
      ServletContext context = event.getSession().getServletContext(); 
      FacesContext.getCurrentInstance().getExternalContext().redirect("logout.xhtml"); 
     } catch (Exception ex) { 
      ex.printStackTrace(); 
     } 

    } 
} 

回答

0

Tomcat確實有辦法捕獲未處理的應用程序異常。這有點像ASP.NET Application_Error事件。這可能不是一個優雅的解決方案,但它現在可行。每當我們得到任何JSP異常時,我們都應該重定向到這個error.jsp頁面,然後通過在error.jsp頁面代碼中強制重定向,然後到達Shibboleth IdP登錄頁面。

在部署web.xml文件中進行設置。這與i2b2 deploy文件夾相關。

<error-page> 
     <exception-type>java.lang.Throwable</exception-type> 
     <location>/error/error.jsp</location> 
</error-page> 

然後我把這個到頁面的error.jsp:

<%@ page isErrorPage="true" import="java.io.*" contentType="text/plain"%> 

Message: 
<%=exception.getMessage()%> 

StackTrace: 
<% 
    // redirect to Shibboleth IdP login page 
    String redirectURL = "https://redirecturl/"; 
    response.sendRedirect(redirectURL); 
%> 

然後重新啓動Linux服務。必須按照這個順序完成,因爲shibd和tomcat服務存在依賴關係。

service httpd stop 
service tomcat stop 
service jboss stop 
service shibd stop 
service shibd start 
service jboss start 
service tomcat start 
service httpd start