6

对Okta和Android了解不多。有谁知道一个很好的教程,它展示了如何将一个 android 应用程序连接到 Okta 框架中。或者我是否实现了 SAML SSO 实现,然后 Okta 与之相关联?任何代码示例都值得赞赏 - 特别是显示通用 SSO 的 Android 实现的代码示例,如果存在这样的事情。

4

1 回答 1

12

好的,这里有很多内容和一些我没有做的工作。但基本思想是在服务器端(我们使用 .Net),我们使用“kentor”创建了一个 SAML 通信层。我没有使用这个,但想法是软件为 SSO(例如 Okta)与客户端的身份提供程序 (IDP) 通信。IDP 客户端通常必须提供包含安全信息和最终 URL 的 XML 元数据,并且您向他们提供您的 SSO xml 元数据(抱歉,我没有在这部分工作!)。

基本上从那里开始,它在 Android 方面非常简单。最重要的是,上述交互产生了一个 SSO 客户端提供的 url,您将在 Android 端使用它来创建一个 webview,这将允许他们输入他们的登录信息以进行验证。

我们对 URL 进行了硬编码,因为我们专门为客户创建了白标产品(您将看到Constants.SINGLE_SIGNON_URL下面),但没有什么可以阻止您在客户传递 SSO 的组织代码后将 URL 传回(我们现在正在努力)。换句话说,您存储 URL 或根据哪个客户生成 URL,然后在设备向您传递组织代码时返回该 URL。该 URL 实际上指向您的服务器,该服务器重定向到 SSO 的 IDP (Okta) 登录页面。这是因为来自 OKTA 的响应需要发送到您的服务器,最终它将通过重定向发送回您的 web 视图。我们使用 cookie 来存储生成的用户名,以允许正常的登录过程。可能有很多不同的方法可以做到这一点,Okta 甚至提供了原生移动设备功能,但客户必须支持它。

这是一个图表,希望能阐明其中的一些高级部分:

在此处输入图像描述

代码仅涵盖上图中的 1)、2) 和 5)。1) 对 WebView 的调用非常明显。2) 确实是对Constants.SINGLE_SIGNON_URL服务器的调用,它应该重定向到 IDP 页面。当用户在那里登录时,它会被发送回您的服务 (SP) 并被重定向回您的 WebView。再一次,我们在 cookie 中存储了一些东西,以便提取以继续我们的正常登录。

一个关键是要意识到 WebViewshouldOverrideUrlLoading()被多次调用。忽略所有这些,除了发回服务器 URL 的那个,此时您提取所需的数据(在我们的例子中是服务器已验证的登录信息)。这可以在通话中看到GlobalState.getInstance().currentUserName = getCookieValue("_username" ,cookies);

可能没有很好地解释这一点(已经一个月左右了!)。这是完成大部分工作的 SSOActivity 示例:

public class SSOActivity extends Activity {
    WebView webView;
    private Button mCancel;
    private Button mReset;

    /**
     * Grabs the specified variables out of the list of cookies
     * 
     * @param fieldName
     * @param cookies
     * @return
     */
    public String getCookieValue(String fieldName, final String cookies){     
        String CookieValue = null;

        String[] cookiessplit = cookies.split(";");
        for (String str : cookiessplit ) {
            if(str.contains(fieldName)) {
                String[] value=str.split("=");
                CookieValue = value[1];
                break;
            }
        }              
        return CookieValue; 
    }

    public void clearCookies() {
        try {
            android.webkit.CookieManager cookieManager = CookieManager.getInstance();
            cookieManager.removeAllCookie();
        }
        catch (Exception ex) 
        {
            Utilities.logException(ex);
            Utilities.logError("SSOActivity", "clearCookies() : " + ex.getMessage() );
        }
    }

    /**
     * Cancels the SSO request in Webview
     * 
     * @param view
     */
    public void cancelSSOClick (View view) {
        Utilities.logInfo("cancelSSOClick", "Cancel SSO click");
        setResult(Activity.RESULT_CANCELED, null);
        SSOActivity.this.finish();
    }

    /**
     * Resets and deletes cookies and SSOUrl if one exists
     * 
     * @param view
     */
    public void resetSSOClick (View view) {
        Utilities.logInfo("resetSSOClick", "Cancel SSO click");
        setResult(Activity.RESULT_CANCELED, null);
        clearCookies();
        SSOActivity.this.finish();
    }



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setResult(Activity.RESULT_OK, null);

        // Setup the web view. It will redirect to SSO site for login
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_sso);

        mCancel = (Button)findViewById(R.id.cancelSSO);
        mCancel.setTextColor(Color.WHITE);

        mReset = (Button)findViewById(R.id.resetSSO);
        mReset.setTextColor(Color.WHITE);

        webView = (WebView) findViewById(R.id.ssoViewer);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setSupportZoom(false);
        webView.setWebViewClient(new WebViewClient() {
           @Override
            public boolean shouldOverrideUrlLoading (WebView view, String url) {
                try {
                    // If coming from our system, then we need to check the cookie for username password, for
                    // some SSO this might be different than the base url. Check for both
                    if (url.equals(Constants.getBaseUrl()) || url.equals(Constants.SSO_RETURN_URL)) {

                        CookieManager cookieManager = CookieManager.getInstance();
                        final String cookies = cookieManager.getCookie(url);
                        GlobalState.getInstance().currentUserName = getCookieValue("_username" ,cookies);
                        SSOActivity.this.finish();
                        return true;
                    }
                } 
                catch (Exception ex) {
                    GlobalState.getInstance().currentUserName = "";
                    GlobalState.getInstance().currentPassword = "";
                    setResult(Activity.RESULT_CANCELED, null);
                    SSOActivity.this.finish();
                }

                return false;
           }

        });

        try {
            webView.loadUrl(Constants.SINGLE_SIGNON_URL);
        }
        catch (Exception ex) {
            Utilities.logException(ex);
            Utilities.logError("SSOActivity", "onCreate(), webView.loadUrl(ssoUrl) : " + ex.getMessage() );

        }

    }

}

以下是支持 Activity 的 XML 示例:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:id="@+id/ssoViewerLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:id="@+id/button_layout"
                android:layout_width="match_parent"
                android:orientation="horizontal"
                android:layout_height="wrap_content"
                android:gravity="center|bottom"
                android:layout_alignParentBottom="true">      

                <Button
                    android:id="@+id/cancelSSO"
                    android:layout_marginTop="16dp"
                    android:layout_width="125dp"
                    android:layout_height="55dp"
                    android:layout_margin="5dp"
                    android:onClick="cancelSSOClick"
                    android:text="Cancel Login"
                    android:background="@drawable/button_login" />
                <Button
                    android:id="@+id/resetSSO"
                    android:layout_marginTop="16dp"
                    android:layout_width="125dp"
                    android:layout_height="55dp"
                    android:layout_margin="5dp"
                    android:onClick="resetSSOClick"
                    android:text="Reset SSO"
                    android:background="@drawable/button_login"/>
        </LinearLayout>
     <WebView
               android:id="@+id/ssoViewer"
               android:layout_width="fill_parent"
               android:layout_height="fill_parent"
               android:layout_above="@id/button_layout" />  
    </RelativeLayout>

在代码中的其他地方调用它,如下所示:

                        Intent viewIntent = new Intent(getActivity(), SSOActivity.class);
                        (getActivity()).startActivityForResult(viewIntent, Constants.SINGLE_SIGN_ON);

最后你应该看到什么:

在此处输入图像描述

希望这可以帮助!

于 2016-06-16T18:47:11.643 回答