Android SSO Okta 集成示例

Android SSO Okta Integration Example

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

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

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

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

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

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

一个关键是要意识到WebView的shouldOverrideUrlLoading()被调用了几次。忽略所有那些,除了发回服务器 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() );

        }

    }

}

这里是 XML 支持 Activity 的例子:

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

最后你应该看到的是:

希望对您有所帮助!