【发布时间】:2011-02-23 22:58:10
【问题描述】:
我进行了多次搜索,但似乎无法找到看似简单的身份验证方案的答案。
我们有一个现有的 Java Web 应用程序,它使用 Spring 提供的基于表单的授权。我们正在尝试通过我们的门户网站访问此应用程序,而不要求用户输入他们的凭据 (SSO)。
门户有一个凭证保险库,我们可以成功地访问服务器端远程 Web 应用程序的机密。我们正在使用 Apache 的 HTTP 组件实用程序将登录请求发布到 j_spring_security_check 并成功进行身份验证。对这篇文章的响应会返回一个 302 重定向到应用程序主页,并设置一个带有会话 id 的 cookie。
现在我们必须以某种方式将此经过身份验证的会话发送回浏览器,这就是我们遇到问题的地方。简单地将浏览器重定向到主页是行不通的——它会将我们重定向到登录页面。将所有响应标头完全按照在服务器端接收到的方式转发回浏览器也不起作用 - 仍然返回到登录页面。
那么,我们如何在服务器端进行身份验证并且仍然能够在客户端加载目标页面?
我对此比较陌生,所以如果这是一个愚蠢的问题,我深表歉意。感谢您提供有关替代方法的任何帮助或建议。
注意事项:
HttpComponent 客户端代码:
DefaultHttpClient httpclient = new DefaultHttpClient();
try {
// try to get the home page
HttpGet httpget = new HttpGet("http://<host>/<root>/home.action");
HttpResponse httpClientResponse = httpclient.execute(httpget);
HttpEntity entity = httpClientResponse.getEntity();
// check status and close entity stream
System.out.println("Login form get: " + httpClientResponse.getStatusLine());
EntityUtils.consume(entity);
// check cookies
System.out.println("Initial set of cookies:");
List<Cookie> cookies = httpclient.getCookieStore().getCookies();
printCookies(cookies);
/*** Login ***/
HttpPost httppost = new HttpPost("http://<host>/<root>/j_spring_security_check");
// Prepare post parameters
List <NameValuePair> nvps = new ArrayList <NameValuePair>();
nvps.add(new BasicNameValuePair("j_username", getUserFromVault()));
nvps.add(new BasicNameValuePair("j_password", getPasswordFromVault()));
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
httpClientResponse = httpclient.execute(httppost);
// copy response headers and determine redirect location
Header[] allHeaders = httpClientResponse.getAllHeaders();
System.out.println("Headers: ");
String location = "";
for (Header header : allHeaders) {
System.out.println(header);
if("location".equalsIgnoreCase(header.getName())) location = header.getValue();
response.addHeader(header.getName(), header.getValue());
}
// check response body
entity = httpClientResponse.getEntity();
System.out.println("Response content: " + httpClientResponse.getStatusLine());
System.out.println(EntityUtils.toString(entity)); // always empty
EntityUtils.consume(entity);
// check cookies
System.out.println("Post logon cookies:");
cookies = httpclient.getCookieStore().getCookies();
printCookies(cookies);
// populate redirect information in response
System.out.println("Redirecting to: " + locationHeaderValue);
response.setStatus(httpClientResponse.getStatusLine().getStatusCode()); // 302
// test if server-side get works for home page at this point (it does)
httpget = new HttpGet(location);
httpClientResponse = httpclient.execute(httpget);
entity = httpClientResponse.getEntity();
// print response body (all home content is loaded)
System.out.println("home get: " + httpClientResponse.getStatusLine());
System.out.println("Response content: " + httpClientResponse.getStatusLine());
System.out.println(EntityUtils.toString(entity));
EntityUtils.consume(entity);
} finally {
httpclient.getConnectionManager().shutdown();
}
服务端登录成功返回的Headers:
HTTP/1.1 302 Found
Date: Wed, 23 Feb 2011 22:09:03 GMT
Server: Apache/2.2.3 (CentOS)
Set-Cookie: JSESSIONID=6F98B0B9A65BA6AFA0472714A4C816E5; Path=<root>
Location: http://<host>/<root>/home.action
Content-Type: text/plain; charset=UTF-8
Content-Length: 0
Via: 1.1 PPWebFilter.<host>:80 (IronPort-WSA/7.0.0-825)
Connection: keep-alive
来自客户端请求和响应的标头:
要求:
GET /<root>/home.action HTTP/1.1
Host: <host>
Connection: keep-alive
Referer: http://localhost:10039/SCMViewer/TestLoginServlet?launchScm=Launch+SCM+servlet
Accept:application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.98 Safari/534.13
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: JSESSIONID=FC8E823AB1A1545BE8518DB4D097E665
响应(重定向到登录):
HTTP/1.1 302 Found
Date: Wed, 23 Feb 2011 22:09:03 GMT
Server: Apache/2.2.3 (CentOS)
Location: http://<host>/<root>/security/login.action
Content-Type: text/plain; charset=UTF-8
Content-Length: 0
Via: 1.1 PPWebFilter.<host>:80 (IronPort-WSA/7.0.0-825)
Connection: keep-alive
作为测试,我们编写了一些看似可行的 hack,但由于太不安全而无法实现:
- 在 jsp 中嵌入了一个表单,它将登录凭据直接发布到远程站点的 j_spring_security_check。
- 编写了一个 servlet 方法来从保管库中检索凭据。
- 将客户端的凭据填充到隐藏的表单字段中,并通过 javascript 提交表单。
【问题讨论】:
-
您知道 JSESSIONID cookie 不匹配吗?
-
@donal-fellows:很好。 JSESSIONID 是不同的,因为 cookie 没有在客户端更新 - 大概是因为我试图保存一个归属于不同域的 cookie。
标签: java apache forms-authentication spring-security