ฉันจะเรียกการรับรองความถูกต้องโดยทางโปรแกรมจากภายในเซิร์ฟเล็ตอย่าง j_security_check ได้อย่างไร

เรามีการตรวจสอบสิทธิ์การเข้าสู่ระบบด้วยแบบฟอร์มบนเว็บโดยที่ j_securtiy_check ทำงาน เราต้องการเปลี่ยนแปลงโดยการตรวจสอบการเข้าสู่ระบบแบบเป็นโปรแกรม วิธีที่เหมาะสมในการให้เซิร์ฟเล็ตตรวจสอบชื่อผู้ใช้และรหัสผ่านที่ส่งผ่านไปคืออะไร? เห็นได้ชัดว่าเซิร์ฟเล็ตไม่มีการป้องกัน

เราได้ทดลองกับ server.xml Realm นี้:

<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 ที่ไม่มีการป้องกัน ปัจจุบันเซิร์ฟเล็ตนี้รับรองความถูกต้องกับบริการลงชื่อเพียงครั้งเดียวของ JOSSO แต่ฉันต้องการลบสิ่งนี้ออกและใช้การรับรองความถูกต้อง tomcat7 อย่างง่ายสำหรับผู้เริ่มต้น จากนั้นจึงย้ายไปยัง OpenAM ในที่สุด หากฉันสามารถสร้างค่า JSSESSIONIDSSO โดยทางโปรแกรมและบรรจุสิ่งนี้ลงในคุกกี้

นี่คือรหัสบางส่วนที่ฉันพบ นี่เป็นวิธีที่ถูกต้องในการเรียกใช้การตรวจสอบสิทธิ์หรือไม่

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



คำตอบ (4)


หากคุณใช้ Servlet 3.0 หรือใหม่กว่าอยู่แล้ว สำหรับการตรวจสอบสิทธิ์แบบเป็นโปรแกรม ให้ใช้วิธี login() ของ HttpServletRequest

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

Servlet API มอบ login() และ logout() วิธีการสำหรับการเข้าถึงทางโปรแกรมไปยังการรักษาความปลอดภัยที่จัดการโดยคอนเทนเนอร์

person Rustam    schedule 26.02.2014

ฉันคิดว่าในแอปไคลเอนต์ Java webstart เมื่อคุณต้องการถามการรับรองความถูกต้อง คุณเพียงแค่ใช้ไคลเอนต์ HTTP ใด ๆ เพื่อส่งชื่อผู้ใช้และรหัสผ่านไปยัง LoginServer ของคุณโดยใช้วิธี POST ใน LoginServlet คุณใช้ request.login ( ชื่อผู้ใช้ รหัสผ่าน ) จากนั้นส่งคืนผลลัพธ์การตรวจสอบสิทธิ์ในรูปแบบใด ๆ ( XML, JSON ) ที่ฝั่งไคลเอ็นต์ คุณต้องแยกวิเคราะห์ผลลัพธ์การตรวจสอบสิทธิ์ ( POST result ) และคุกกี้ JSESSIONID จากส่วนหัวการตอบกลับด้วย สำหรับคำขอครั้งต่อๆ ไป คุณอาจต้องส่ง JSESSIONID ที่คุณแยกวิเคราะห์ไว้ก่อนหน้านี้

person Loc    schedule 10.12.2013
comment
ปรากฎว่า eclipse ของฉันยังคงถูกตั้งค่าสำหรับเซิร์ฟเวอร์ 2.X ฉันไม่สามารถเข้าถึง HttpServletRequest 3.0 ที่มีการเข้าสู่ระบบ (ผู้ใช้, ผ่าน) ตอนนี้ฉันได้ลองสิ่งนี้แล้ว มันล้มเหลวโดยมีข้อผิดพลาดน้อยกว่าที่เป็นประโยชน์: เกิดจาก: javax.servlet.ServletException: การเข้าสู่ระบบล้มเหลวที่ org.apache.catalina.authenticator.AuthenticatorBase.doLogin(AuthenticatorBase.java:820) ที่ org.apache catalina.authenticator.AuthenticatorBase.login(AuthenticatorBase.java:800) ที่ org.apache.catalina.connector.Request.login(Request.java:2621) ฉันไม่คิดว่า OpenAm รองรับสิ่งนี้ - person D-Klotz; 17.12.2013
comment
คุณสามารถใช้วิธีอื่น ( ไม่ใช้ request.login ของ Servlet 3.0+): ส่งคำขอการรับรองความถูกต้องของคุณไปที่การดำเนินการ /j_security_check ด้วยพารามิเตอร์ชื่อผู้ใช้คือ j_username และชื่อพารามิเตอร์รหัสผ่านคือ j_password คอนเทนเนอร์จะจัดการการรับรองความถูกต้องให้กับคุณ (ซึ่งคล้ายกับ request.login) โปรดทราบว่านี่คือวิธีแก้ปัญหาการเข้าสู่ระบบคอนเทนเนอร์ - person Loc; 17.12.2013
comment
docs.oracle.com/javaee/6/tutorial/doc/glxce html - ลิงก์ของ j_security_check - person Loc; 17.12.2013

ฉันอยากจะติดตามเรื่องนี้

ไม่มีคำตอบง่ายๆ

รหัสในตอนท้ายใช้การสะท้อนกลับที่แท้จริงเพื่อพยายามเรียกใช้วิธีการรับรองความถูกต้องภายในขอบเขต ปัญหาก็คือว่าเรื่องนี้ขึ้นอยู่กับอาณาจักรที่แนบมาจริงๆ

ตัวอย่างเช่น JOSSO (org.josso.tc55.agent.jaas.CatalinaJAASRealm) ไม่มีวิธีนี้ แต่มันมีสิ่งที่เรียกว่า createPrincipal(ชื่อผู้ใช้สตริง, หัวเรื่อง) แทน กระบวนการที่แนะนำสำหรับการทำเช่นนี้ (อย่างน้อย josso 1.5) คือการใช้โค้ดดังนี้:

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

หากคุณใช้ OpenAM (ซึ่งเป็นสิ่งที่ฉันพยายามจะเปลี่ยนไปใช้) เป็นผู้ให้บริการการลงชื่อเพียงครั้งเดียวของคุณแทนที่จะเป็น JOSSO มันจะแตกต่างไปจากเดิมอย่างสิ้นเชิง แนวคิดปัจจุบันที่ฉันกำลังดำเนินการคือการใช้บริการ RESTful ที่พวกเขามอบให้โดยตรงจากไคลเอนต์ webstart

ปัญหาแรกของฉันกับแนวคิดนั้น - กำลังพยายามค้นหา API ที่ฉันสามารถใช้จากไคลเอนต์ webstart java ที่ 1) ไม่มีขนาดไฟล์ jar ขนาดใหญ่ 2) ใช้งานได้กับ tomee+ CXF เวอร์ชัน 2.6.4 (ฉันไม่รู้เรื่องนี้มากพอที่จะพูดว่า "ใช่ แค่ใช้ CXF 3.0 client jars เพราะมันจะทำงานได้ดีกับ CXF เวอร์ชัน tomee+...")

อย่างไรก็ตาม นี่คือโค้ดที่ 'ควร' ใช้งานได้หากคุณใช้กลไกแหล่งข้อมูลกระป๋องของ 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();
            }

นี่เป็นกรณีที่คุณพบว่าตัวเองกำลังพยายามตรวจสอบสิทธิ์ผู้ใช้จากภายในเซิร์ฟเล็ตที่ไม่มีการป้องกัน

-เดนนิส

person D-Klotz    schedule 13.12.2013

ฉันสังเกตเห็นว่าสิ่งนี้ไม่ทันสมัยอีกต่อไป ทางออกสุดท้ายคือการใช้ Java SDK ที่ OpenAM จัดเตรียมไว้ให้

นี่คือจุดเริ่มต้น: http://openam.forgerock.org/openam-documentation/openam-doc-source/doc/dev-guide/index/chap-jdk.html

1) เพิ่มไฟล์ jar ทั้งหมดที่มาพร้อมกับ SDK นี้ไปยังเว็บแอปพลิเคชันของคุณ 2) เปลี่ยนเซิร์ฟเล็ตของคุณ (หรือไคลเอนต์หนัก) ให้มีรหัสต่อไปนี้:

    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 ใหม่ที่คุณสามารถส่งต่อไปยังแอปพลิเคชันไคลเอนต์ที่ได้รับการป้องกันทั้งหมดของคุณ เพื่อที่ผู้ใช้จะไม่ถูกท้าทายให้เข้าสู่ระบบ

ฉันหวังว่านี่จะช่วยได้.

-dklotz

person D-Klotz    schedule 14.01.2014