Java 基于Android的webcas认证

Java 基于Android的webcas认证,java,android,authentication,ssl,cas,Java,Android,Authentication,Ssl,Cas,我正试图通过安卓系统登录CAS系统,我不知道该怎么做 stackoverflow link谈到了类似的内容,但我无法理解问题的解决方案。我以前没有身份验证协议和HTTP方面的经验。我将感谢任何和所有的帮助 编辑:我在GitHub上找到了一个适用于Android的CAS客户端,我试着使用它来查看我是否能够正确地进行身份验证。不幸的是,我仍然有问题。在执行login()命令时,出现以下错误: 01-20 16:47:19.322: D/CASCLIENT(22682): Ready to get L

我正试图通过安卓系统登录CAS系统,我不知道该怎么做

stackoverflow link谈到了类似的内容,但我无法理解问题的解决方案。我以前没有身份验证协议和HTTP方面的经验。我将感谢任何和所有的帮助

编辑:我在GitHub上找到了一个适用于Android的CAS客户端,我试着使用它来查看我是否能够正确地进行身份验证。不幸的是,我仍然有问题。在执行login()命令时,出现以下错误:

01-20 16:47:19.322: D/CASCLIENT(22682): Ready to get LT from https://www.purdue.edu/apps/account/cas/login?service=http://watcher.rcac.purdue.edu/nagios
01-20 16:47:21.825: D/CASCLIENT(22682): Response = HTTP/1.1 200 OK
01-20 16:47:21.875: D/CASCLIENT(22682): LT=LT-137794-1UkrL1jXJGPMZfuuVDn4RXbcQ3kfCQ
01-20 16:47:21.875: D/CASCLIENT(22682): POST https://www.purdue.edu/apps/account/cas/login?service=http://watcher.rcac.purdue.edu/nagios
01-20 16:47:23.186: D/CASCLIENT(22682): POST RESPONSE STATUS=200 : HTTP/1.1 200 OK
01-20 16:47:23.186: I/CASCLIENT(22682): Authentication to service 'http://watcher.rcac.purdue.edu/nagios' unsuccessul for username .
以下是CAS客户端代码:

public class CasClient
{
    private static final String TAG = "CASCLIENT";
    private static final String CAS_LOGIN_URL_PART = "login";
    private static final String CAS_LOGOUT_URL_PART = "logout";
    private static final String CAS_SERVICE_VALIDATE_URL_PART = "serviceValidate";
    private static final String CAS_TICKET_BEGIN = "ticket=";
    private static final String CAS_LT_BEGIN = "name=\"lt\" value=\"";
    private static final String CAS_USER_BEGIN = "<cas:user>";
    private static final String CAS_USER_END = "</cas:user>";

    /**
     *  An HTTP client (browser replacement) that will interact with the CAS server.
     *  Usually provided by the user, as it is this client that will be "logged in" to
     *  the CAS server. 
     */
    private HttpClient httpClient;
    /**
     * This is the "base url", or the root URL of the CAS server that is will be
     * providing authentication services. If you use <code>http://x.y.z/a/login</code> to login
     * to your CAS, then the base URL is <code>http://x.y.z/a/"</code>. 
     */
    private String casBaseURL;

    /**
     * Construct a new CasClient which uses the specified HttpClient
     * for its HTTP calls. If the CAS authentication is successful, it is the supplied HttpClient to
     * which the acquired credentials are attached.
     *
     * @param   httpClient The HTTP client ("browser replacement") that will 
     *          attempt to "login" to the CAS.
     * @param   casBaseUrl The base URL of the CAS service to be used. If you use 
     *          <code>http://x.y.z/a/login</code> to login to your CAS, then the base URL 
     *          is <code>http://x.y.z/a/"</code>.
     */
    public CasClient (HttpClient httpClient, String casBaseUrl)
    {
        this.httpClient = httpClient;
        this.casBaseURL = casBaseUrl;
    }

    /**
     * Authenticate the specified user credentials and request a service ticket for the
     * specified service. If no service is specified, user credentials are checks but no
     * service ticket is generated (returns null). 
     *
     * @param  serviceUrl The service to login for, yielding a service ticket that can be 
     *         presented to the service for validation. May be null, in which case the 
     *         user credentials are validated, but no service ticket is returned by this method.
     * @param  username
     * @param  password
     * @return A valid service ticket, if the specified service URL is not null and the
     *         (login; password) pair is accepted by the CAS server
     * @throws CasAuthenticationException if the (login; password) pair is not accepted
     *         by the CAS server.
     * @throws CasProtocolException if there is an error communicating with the CAS server
     */
    public String login (String serviceUrl, String username, String password) throws CasAuthenticationException, CasProtocolException
    {
        String serviceTicket = null;
        // The login method simulates the posting of the CAS login form. The login form contains a unique identifier
        // or "LT" that is only valid for 90s. The method getLTFromLoginForm requests the login form from the cAS
        // and extracts the LT that we need.  Note that the LT is _service specific_ : We need to use an identical
        // serviceUrl when retrieving and posting the login form.
        String lt = getLTFromLoginForm (serviceUrl);
        if (lt == null)
        {
            Log.d (TAG, "Cannot retrieve LT from CAS. Aborting authentication for '" + username + "'");
            throw new CasProtocolException ("Cannot retrieve LT from CAS. Aborting authentication for '" + username + "'");
        }
        else
        {
            // Yes, it is necessary to include the serviceUrl as part of the query string. The URL must be
            // identical to that used to get the LT.
            Log.d(TAG,"POST " + casBaseURL + CAS_LOGIN_URL_PART + "?service=" + serviceUrl);
            HttpPost httpPost = new HttpPost (casBaseURL + CAS_LOGIN_URL_PART + "?service=" + serviceUrl);
            try
            {
                // Add form parameters to request body
                List <NameValuePair> nvps = new ArrayList <NameValuePair> ();
                nvps.add(new BasicNameValuePair ("_eventId", "submit"));
                nvps.add(new BasicNameValuePair ("username", username));
                nvps.add(new BasicNameValuePair ("gateway", "true"));
                nvps.add(new BasicNameValuePair ("password", password));
                nvps.add(new BasicNameValuePair ("lt", lt));
                httpPost.setEntity(new UrlEncodedFormEntity(nvps));

                // execute post method      
                HttpResponse response = httpClient.execute(httpPost);
                Log.d (TAG, "POST RESPONSE STATUS=" + response.getStatusLine().getStatusCode() + " : " + response.getStatusLine().toString());

                //TODO It would seem that when the client is already authenticated, the CAS server 
                // redirects transparently to the service URL!
                // Success if CAS replies with a 302 HTTP status code and a Location header
                // We assume that if a valid ticket is provided in the Location header, that it is also a 302 HTTP STATUS
                Header headers[] = response.getHeaders("Location");
                if (headers != null && headers.length > 0) 
                    serviceTicket = extractServiceTicket (headers[0].getValue());
                HttpEntity entity = response.getEntity();
                entity.consumeContent();

                if (serviceTicket == null)
                {
                    Log.i (TAG, "Authentication to service '" + serviceUrl + "' unsuccessul for username '" + username + "'.");
                    throw new CasAuthenticationException ("Authentication to service '" + serviceUrl + "' unsuccessul for username '" + username + "'.");
                }
                else
                    Log.i (TAG, "Authentication to service '" + serviceUrl + "' successul for username '" + username + "'.");
            } 
            catch (IOException e) 
            {
                Log.d (TAG, "IOException trying to login : " + e.getMessage());
                throw new CasProtocolException ("IOException trying to login : " + e.getMessage());
            } 
            return serviceTicket;
        }
    }

    /**
     * Logout from the CAS. This destroys all local authentication cookies
     * and any tickets stored on the server.
     * 
     * @return <code>true</false> if the logout is acknowledged by the CAS server
     */
    public boolean logout ()
    {
        boolean logoutSuccess = false;
        HttpGet httpGet = new HttpGet (casBaseURL + CAS_LOGOUT_URL_PART);
        try
        {
            HttpResponse response = httpClient.execute(httpGet);
            logoutSuccess = (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK);
            Log.d (TAG, response.getStatusLine().toString());
        } 
        catch (Exception e)
        {
            Log.d(TAG, "Exception trying to logout : " + e.getMessage());
            logoutSuccess = false;
        } 
        return logoutSuccess;
    }

    /**
     * Validate the specified service ticket against the specified service.
     * If the ticket is valid, this will yield the clear text user name
     * of the authenticated user.
     * 
     * Note that each service ticket issued by CAS can be used exactly once
     * to validate.
     *
     * @param serviceUrl The serviceUrl to validate against
     * @param serviceTicket The service ticket (previously provided by the CAS) for the serviceUrl
     * @return Clear text username of the authenticated user.
     * @throws CasProtocolException if a protocol or communication error occurs
     * @throws CasClientValidationException if the CAS server refuses the ticket for the service
     */
    public String validate (String serviceUrl, String serviceTicket) throws CasAuthenticationException, CasProtocolException
    {
        HttpPost httpPost = new HttpPost (casBaseURL + CAS_SERVICE_VALIDATE_URL_PART );
        Log.d(TAG, "VALIDATE : " + httpPost.getRequestLine());
        String username = null;
        try
        {
            List <NameValuePair> nvps = new ArrayList <NameValuePair> ();
            nvps.add(new BasicNameValuePair ("service", serviceUrl));
            nvps.add(new BasicNameValuePair ("ticket", serviceTicket));
            httpPost.setEntity (new UrlEncodedFormEntity(nvps));
            HttpResponse response = httpClient.execute (httpPost);
            Log.d (TAG, "VALIDATE RESPONSE : " + response.getStatusLine().toString());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK)
            {
                Log.d (TAG,"Could not validate: " + response.getStatusLine());
                throw new CasAuthenticationException("Could not validate service: " + response.getStatusLine());
            } 
            else
            {   
                HttpEntity entity = response.getEntity();
                username = extractUser (entity.getContent());
                Log.d (TAG, "VALIDATE OK YOU ARE : " + username);
                entity.consumeContent();
            }
        } 
        catch (Exception e)
        {
            Log.d (TAG, "Could not validate: " + e.getMessage ());
            throw new CasProtocolException ("Could not validate : " + e.getMessage ());
        }
        return username;
    }

    /**
     * This method requests the original login form from CAS.
     * This form contains an LT, an initial token that must be
     * presented to CAS upon sending it an authentication request
     * with credentials.
     * 
     * If the (optional) service URL is provided, this method
     * will construct the URL such that CAS will correctly authenticate 
     * against the specified service when a subsequent authentication request 
     * is sent (with the login method).
     *
     * @param serviceUrl
     * @return The LT token if it could be extracted from the CAS response, else null.
     */
    protected String getLTFromLoginForm (String serviceUrl)
    {
        HttpGet httpGet = new HttpGet (casBaseURL + CAS_LOGIN_URL_PART + "?service=" + serviceUrl);

        String lt = null;
        try
        {
            Log.d (TAG, "Ready to get LT from " + casBaseURL + CAS_LOGIN_URL_PART + "?service=" + serviceUrl);
            HttpResponse response = httpClient.execute (httpGet);
            Log.d (TAG, "Response = " + response.getStatusLine().toString());
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
            {
                Log.d(TAG,"Could not obtain LT token from CAS: " + response.getStatusLine().getStatusCode() + " / " + response.getStatusLine());
            } 
            else
            {
                HttpEntity entity = response.getEntity();
                if (entity != null) lt = extractLt (entity.getContent());
                entity.consumeContent();
                Log.d (TAG, "LT=" + lt);
            }
        } 
        catch (ClientProtocolException e)
        {
            Log.d(TAG, "Getting LT client protocol exception", e);
        } 
        catch (IOException e) 
        {
            Log.d(TAG, "Getting LT io exception",e);
        }

        return lt;
    }

    /**
     * Helper method to extract the user name from a "service validate" call to CAS.
     *
     * @param data Response data.
     * @return The clear text username, if it could be extracted, null otherwise.
     */
    protected String extractUser (InputStream dataStream)
    {
        BufferedReader reader = new BufferedReader (new InputStreamReader(dataStream));
        String user = null;
        try 
        {
            String line = reader.readLine();
            while (user == null && line != null)
            {
                int start = line.indexOf (CAS_USER_BEGIN);
                if (start >= 0)
                {
                    start += CAS_USER_BEGIN.length();
                    int end = line.indexOf(CAS_USER_END, start);
                    user = line.substring (start, end);
                }
                line = reader.readLine();
            }
        } 
        catch (IOException e) 
        {
            Log.d (TAG, e.getLocalizedMessage());
        }
        return user;
    }

    /**
     * Helper method to extract the service ticket from a login call to CAS.
     *
     * @param data Response data.
     * @return The service ticket, if it could be extracted, null otherwise.
     */
    protected String extractServiceTicket (String data)
    {   
        Log.i(TAG, "ST DATA: " +data);
        String serviceTicket = null;
        int start = data.indexOf(CAS_TICKET_BEGIN);
        if (start > 0)
        {
            start += CAS_TICKET_BEGIN.length ();
            serviceTicket = data.substring (start);
        }
        return serviceTicket;
    }


    /**
     * Helper method to extract the LT from the login form received from CAS.
     *
     * @param data InputStream with HTTP response body.
     * @return The LT, if it could be extracted, null otherwise.
     */
    protected String extractLt (InputStream dataStream)
    {
        BufferedReader reader = new BufferedReader (new InputStreamReader(dataStream));
        String token = null;
        try 
        {
            String line = reader.readLine();
            while (token == null && line != null)
            {
                int start = line.indexOf (CAS_LT_BEGIN);
                if (start >= 0)
                {
                    start += CAS_LT_BEGIN.length();
                    int end = line.indexOf("\"", start);
                    token = line.substring (start, end);
                }
                line = reader.readLine();
            }
        } 
        catch (IOException e) 
        {
            Log.d (TAG, e.getMessage());
        }
        return token;
    }

}
这是我调用CAS客户机的活动

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        HttpClient client = new DefaultHttpClient();


        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();

        StrictMode.setThreadPolicy(policy); 
        CasClient c = new CasClient(client,"https://www.purdue.edu/apps/account/cas/");
        try {
            c.login("http://watcher.rcac.purdue.edu/nagios", "0025215948", "scholar1234");
        } catch (CasAuthenticationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (CasProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

CAS身份验证的想法本身并不困难,但是在没有HTTP经验的情况下实现它可能会使其复杂化。它是基于票证的,因此,当您想要在网站内进行身份验证时,您将被重定向到CAS站点的登录门户,您必须输入您的凭据并对其进行验证。当然,如果它们不匹配,您将得到一个错误,否则将生成一个TGT(票证授予票证)并返回给您的客户机。因此,每次执行需要进行身份验证的操作时,都必须获取该票证并将其传递给CAS身份验证servlet。票证可能会过期,在这种情况下,CAS服务器将向您发送一个新票证,该票证必须覆盖最后一个票证,而此票证正是您需要提交的票证

在这篇文章中,您详细解释了CAS的工作原理(基本上是工作流),并且有一个Java示例和部分实现。

我也遇到了同样的问题。 我认为github上的代码是为旧版本的sdk设计的

基本上,问题在于测试用户是否真的登录了:CAS服务器使用包含服务位置的302进行响应。 但是代码

httpClient.execute(httpPost) 
跟踪服务的重定向和状态,并通过其200响应加入。这里没有更多的位置,代码认为登录失败

编辑: 我找到了让代码运行的方法:

使用替换jar,而不是捆绑(和旧的)org.apache.http:,至少是版本4.3

他们以jar格式提供org.apache.http的最新稳定版本。 接下来,您必须用ch.boye.httpclientandroidlib替换org.apache.http中的所有导入

继续使用原作者提供的示例,但是,使用不同的选项创建httpClient:

HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new DefaultRedirectStrategy()).build();

这对我很有用。

谢谢你的解释。我开始尝试登录并尝试获取服务票。但是,我有一些问题。我已经在上面发布了我的代码。