我很惭愧地承认,我花了整整四天时间试图让Spring Security 3.1在标准的JSF Web应用程序中与Google登录很好地配合。两者都是很棒的框架,但它们似乎不相容。我终于让它以某种方式工作,但强烈怀疑我错过了一些基本概念,而不是最好的方式。
我正在编写一个应用程序,当我们的系统关闭且无法托管应用程序时,我们的帮助台会在维护活动期间跟踪系统测试,因此它是在外部托管的。我们的Active Directory和IdP在此活动期间关闭,因此我无法使用正常的身份验证系统。 Google登录是一个完美的解决方案。
Google登录使用Google Javascript库和一些简单的代码在浏览器中运行良好。浏览器与Google通信以确定用户是否已登录,如果没有,则打开一个单独的窗口,用户可以在其中提交凭据并进行身份验证。然后,一小部分Javascript可以将从Google返回的简洁,短暂的id_token发送到服务器,服务器可以使用该服务器独立验证Google的身份验证。那部分很简单。美妙之处在于,如果用户已经登录Gmail或其他一些Google应用,则身份验证已经发生,Google也不会再次向用户提出质询。
Spring Security在服务器端运行良好,可以保护指定的资源,并使用用户名和密码对用户进行身份验证。但是,在这种情况下,我们永远不会看到用户名或密码 - 凭据受浏览器和Google之间的安全通信保护。我们所知道的是用户是否经过身份验证。我们可以获取Google用户名,但Spring Security需要可用于身份验证的凭据,数据库,内存用户群或任何其他系统。据我所知,它不是与另一个只在浏览器中提供知识或身份验证的系统兼容。
我在网上发现了许多使用Spring Boot和EnableOAuth2Sso的好例子(例如here),但很少有人在标准应用服务器中使用Spring Security而不支持EnableOAuth2Sso,而那些少数人没有显示任何解决方案我能辨别出来。
我是这样做的。我按照Google的简单指示here在浏览器中提供身份验证。我将此代码添加到onSignIn()方法,以将id_token发送到服务器。
var xhr = new XMLHttpRequest(); // Trigger an authentication for Spring security
xhr.open("POST", "/<my app context>/j_spring_security_check", true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var params = "profileID=" + profile.getId() + "&fullname=" + profile.getName() + "&email=" + profile.getEmail() + "&id_token=" + googleUser.getAuthResponse().id_token
+ "&j_username=" + profile.getEmail() + "&j_password=" + id_token;
xhr.send(params);
window.location.replace("/<my app context>/index.xhtml");
不幸的是,Spring Authentication对象在传递给我提供的AuthenticationProvider时,除了j_username和j_password参数之外不包含任何内容,如Authentication.getPrincipal()和Authentication.getCredentials(),但这是我真正需要的。这有点滥用这些参数,因为我已将它们设置为email和id_token,而不是用户名和密码。
我想将用户的全名和电子邮件(Google在Javascript中提供的googleUser.getName()和googleUser.getEmail())也传递给后端。由于Spring Security不能容纳除用户名/密码以外的任何内容,而且我使用的是Primefaces / JSF,因此我使用Primefaces RemoteCommand使用此信息调用辅助bean上的方法。这也有点笨拙。
此外,我不得不使用window.location.replace()(在上面的代码中),因为当我在上下文中设置它时,Spring Security没有按预期重定向到我的index.xhtml页面:
<security:form-login login-page='/login.xhtml' authentication-failure-url="/login.xhtml?error=true" default-target-url="/index.html" always-use-default-target="true" />
我不知道为什么这不起作用。
但是,应用程序现在的行为符合我的要求,因为它对用户进行身份验证,经过身份验证的用户可以访问Spring Security中指定的资源,我想分享这个,以防有人做类似的事情。谁能建议更清洁/更好的方式?提前谢谢。