我进行了搜索和搜索,但似乎无法找到看似简单的身份验证方案的答案。
我们有一个现有的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();
}
从服务器端成功登录返回的标头:
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
作为测试,我们写了一些似乎有效的黑客攻击,但是太不安全了,不可行:
答案 0 :(得分:1)
有点难以理解您的应用程序正在尝试做什么,但我最好的猜测是您的“门户”位于用户的浏览器和应用程序之间,并且您正在尝试使用应用程序的一些存储凭据代表用户进行身份验证。
您需要注意/处理两件事。
来自应用程序的响应将包含某种SetCookie
标头。需要谨慎处理cookie。根据您使用的安全模型:
另外,请注意,当登录成功时,SpringSecurity 会更改会话cookie。如果您没有捕获新会话cookie并在对应用程序的后续请求中使用它们,则不会对这些请求进行身份验证。
应用程序的登录机制显然是在登录后尝试将您(门户网站)重定向到“默认”位置,这是不合适的。有两个简单的解决方法:
让门户检测到最终的重定向并将其视为您已成功登录的指示。然后让门户网站使用新cookie重复请求您最初从应用程序请求的页面(请参阅上文)。
IIRC,您可以添加一个额外的参数给j_spring_security_check请求,该请求告诉应用程序成功登录后返回的位置。我记不起细节......
我认为将来自RA的setCookie响应标头转发到门户网站对浏览器的响应将是将cookie /会话ID传输到用户的新浏览器窗口所需的全部内容。这不正确吗?
这将导致浏览器为门户网站上下文设置RA的cookie。除非RA和门户网站在cookie的“范围”中(因为想要更好的词),否则这将无效。
问题是,如何在门户网站上/通过门户网站显示?我是否只需复制所有内容并相应地映射所有相关链接?并且,正如您所述,继续通过门户代理对应用程序的所有请求,每次都传递cookie?有没有办法避免复制/修改标记?
你做需要按摩标记。但究竟需要按摩的方法并不完全清楚。我认为您需要映射相对链接,以便在用户的浏览器看到它们时指向门户。然后,安排门户网站使用适当的cookie将请求转发给RA。
可用于处理相对链接的一个工具是HTML <base>
element。事实上,这可能比绝对链接更容易处理......如果你通过门户网站映射一切。
但要注意,在这个过程中会有各种各样的事情引起悲伤。例如,你必须要注意“相同的来源”限制,以及带有RA嵌入式URL的javascript。
答案 1 :(得分:1)
如果有人感兴趣,这就是一切都结果。
一旦我们意识到设置外国cookie的问题,我们决定我们有几个选择:
cookie怪物解决方案的工作原理如下: 用户点击中的链接 门户网站,我们的portlet将在内部 查找用户的凭据, 对远程进行身份验证 申请,并返回 身份验证cookie /令牌。我们 转换(以及 目标网址)到JSON并返回 它到浏览器。浏览器然后 将此JSON发布到远程cookie 应用程序在新窗口中。该 cookie被重组并放置 在响应中与302一起 和目标位置。瞧,瞧 页面重定向到应用程序 主页和用户已登录.Yay!
使用IBM WebSphere Portal的任何人的一些注意事项:
我确信还有其他更优雅的解决方案,但这对我们有用,直到我们启动并运行CAS / Tivoli。