- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试通过 Android 登录 CAS 系统,但我不确定如何处理。
This stackoverflow 链接谈到了类似的事情,但我无法理解问题的解决方案。我以前没有身份验证协议(protocol)和 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 客户端的 Activity 。
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 服务器将向您发送一张新票证,该新票证必须覆盖上一张票证,而这就是您需要出示的票证。
在此link您对 CAS 的工作原理(基本上是工作流程)有详细的解释,并且 here您有一个 Java 示例和部分实现。
关于java - Android 上的 Web CAS 身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21239956/
在 JSF2 应用程序中遇到验证属性的问题时,有两种主要方法。 使用 Annotation 在 ManagedBean 上定义验证 @ManagedBean public class MyBean {
我想实现一个不常见的功能,我认为 jquery 验证插件将是最好的方法(如果您在没有插件的情况下建议和回答,我们也会欢迎)。我想在用户在输入字段中输入正确的单词后立即隐藏表单。我试过这个: $("
我有几个下拉菜单(类名为month_dropdown),并且下拉菜单的数量不是恒定的。我怎样才能为它们实现 NotEqual 验证。我正在使用 jQuery 验证插件。 这就是我写的 - jQuery
我设法制作了这个网址验证代码并且它起作用了。但我面临着一个问题。我认为 stackoverflow 是获得解决方案的最佳场所。 function url_followers(){ var url=do
我目前正在使用后端服务,该服务允许用户在客户端应用程序上使用 Google Games 库登录。 用户可以通过他们的 gplay ID 向我们发送信息,以便登录或恢复旧帐户。用户向我们发送以下内容,包
我正在尝试验证输入以查看它是否是有效的 IP 地址(可能是部分地址)。 可接受的输入:172、172.112、172.112.113、172.112.113.114 Not Acceptable 输入
我从 Mongoose 验证中得到这条消息: 'Validator failed for path phone with value ``' 这不应该发生,因为不需要电话。 这是我的模型架构: var
我一直在尝试使用Python-LDAP (版本 2.4.19)在 MacOS X 10.9.5 和 Python 2.7.9 下 我想在调用 .start_tls_s() 后验证与给定 LDAP 服务
我正在处理一个仅与 IE6 兼容的旧 javascript 项目(抱歉...),我想仅在 VS 2017 中禁用此项目的 ESLint/CSLint/Javascript 验证/CSS 验证。 我知道
我正在寻找一种方法来验证 Spring 命令 bean 中的 java.lang.Double 字段的最大值和最小值(一个值必须位于给定的值范围之间),例如, public final class W
我正在尝试在 springfuse(JavaEE 6 + Spring Framework (针对 Jetty、Tomcat、JBoss 等)) 和 maven 的帮助下构建我的 webapps 工作
我试图在我们的项目中使用 scalaz 验证,但遇到了以下情况: def rate(username: String, params: Map[String, String]): Validation
我有一个像这样的 Yaml 文件 name: hhh_aaa_bbb arguments: - !argument name: inputsss des
我有一个表单,人们可以单击并向表单添加字段,并且我需要让它在单击时验证这些字段中的值。 假设我单击它两次并获取 2 个独立的字段集,我需要旋转 % 以确保它在保存时等于 100。 我已放入此函数以使其
在我的页面中有一个选项可以创建新的日期字段输入框。用户可以根据需要创建尽可能多的“截止日期”和“起始日期”框。就像, 日期_to1 || date_from1 日期到2 ||日期_from2 date
我有一个像这样的 Yaml 文件 name: hhh_aaa_bbb arguments: - !argument name: inputsss des
有没有办法在动态字段上使用 jquery 验证表单。 我想将其设置为必填字段 我正在使用 Jsp 动态创建表单字段。 喜欢 等等...... 我想使用必需的表单字段验证此表单字段。 最佳答
嗨,任何人都可以通过提供 JavaScript 代码来帮助我验证用户名文本框不应包含数字,它只能包含一个字符。 最佳答案 使用正则表达式: (\d)+ 如果找到匹配项,则字符串中就有一个数字。 关于J
我有两个输入字段holidayDate和Description(id=tags) $(document).ready(function() {
我遇到了这个问题,这些验证从电子邮件验证部分开始就停止工作。 我只是不明白为什么即使经过几天的观察,只是想知道是否有人可以在这里指出我的错误? Javascript部分: function valid
我是一名优秀的程序员,十分优秀!