Tomcat 7 JSP页面:使用Shibboleth身份验证会话

时间:2015-10-11 05:58:23

标签: jsp tomcat authentication shibboleth i2b2

Tomcat 7异常:

  

Apache Tomcat / 7.0.63 - HTTP状态500 - 发生异常   处理JSP页面

参考:

我将此代码与i2b2应用程序集成,因此我们可以对我们的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 IdP:

http://shibboleth.net/

问题:

在设置&#34; id&#34;的行上,页面在状态代码为500的JSP页面上失败。因为它上面的一行有一个&#34; auth&#34;变量(Java中的List<string>类型),为null。请注意,此页面包含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页面&gt;自动重定向到Shibboleth IdP&gt;使用IdP凭证登录&gt;重定向回i2b2页面&gt;此代码将用户和会话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. 如果我们想要更多地破解此页面,我们如何简单地避免条件错误,但仍然有页面进程,然后重定向?

  4. 还有其他想法吗?

    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();
            }
    
        }
    }
    

1 个答案:

答案 0 :(得分:0)

事实上,Tomcat确实有办法捕获未处理的应用程序异常。它等同于ASP.NET Application_Error事件。这可能不是一个优雅的解决方案,但它现在有效。每当我们得到任何JSP异常时,我们都应该重定向到这个error.jsp页面,然后通过在error.jsp页面代码中强制重定向来重定向到Shibboleth IdP登录页面。

在部署web.xml文件中设置此项。这与i2b2部署文件夹有关。

<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