作者:YON永世 | 来源:互联网 | 2023-07-12 13:43
我们正在研究一个需要SAML SSO的项目,因此我们决定使用keycloak进行此实现。
用例
-
具有外部IdP的SSO-密钥隐藏代理
- 将Keycloak作为IdP的SSO-使用keycloak服务器进行注册和用户管理
基本设置
我们还将Nginx用作独立Wildfly服务器之前的代理。
与密钥斗篷相关的nginx配置:
location ^~ /auth/ {
proxy_pass http://localhost:8180/auth/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
}
部署架构
我们部署了一个耳朵,其中包含常见的jar和部署描述符(如jboss-deployment-structure.xml)。
该耳朵还包含一个war文件,其中包含用于客户端配置的jboss-web.xml和keycloak-saml.xml。
在以下示例中,它还包含多个jar,例如auth.login.saml.web.jar。
战争本身有一个web.xml,而lib文件夹中的jar有web-fragment.xml文件。
app.ear
├── Meta-INF
│ ├── application.xml
│ ├── jboss-app.xml
│ ├── jboss-classloading.xml
│ ├── jboss-deployment-structure.xml
│ ├── MANIFEST.MF
└── mywar.war
├── Meta-INF
│ ├── MANIFEST.MF
└── WEB-INF
├── classes
├── jboss-web.xml
├── keycloak-saml.xml
├── lib
│ └── auth.login.saml.web.jar
│ ├── at
│ │ └── prismasolutions
│ │ └── ppcf
│ │ └── core
│ │ └── auth
│ │ ├── login
│ │ └── SAMLLoginServlet.class
│ ├── Meta-INF
│ │ ├── MANIFEST.MF
│ │ └── web-fragment.xml
└── web.xml
我们有一个基于权限的底层系统,该系统需要根据Principals属性从数据库中加载角色和权限。
该主体通过Jboss-Security或Keycloak客户端适配器登录。在SamlLoginServlet中,SamlPrincipal用于提取属性并将其在应用程序中使用。
问题
两种定义的用例的登录过程似乎都可以正常工作
但是
关于注销过程,我们确实有些挣扎和未解决的问题。关于我在2019年6月发送的另一个支持请求,注销不适用于我们记录的文档:
https://lists.jboss.org/pipermail/keycloak-user/2019-June/018550.html
文档指出:
“有多种方法可以从Web应用程序注销。对于Java EE servlet容器,可以调用HttpServletRequest.logout()。对于其他任何浏览器应用程序,可以将浏览器指向Web应用程序的任何url具有安全性约束,并传入查询参数GLO,即http://myapp?GLO=true http://myapp/?GLO=true。如果您与浏览器进行SSO会话,则会注销您的登录。”
在我们的应用程序中,注销是通过调用Servlet(称为GlobalLogoutServlet)来实现的。它在战争中包含的jar中的web-fragment.xml中注册:
GlobalLogoutServlet
/login
为避免混淆:客户端中有一个指向URL / login?logout的注销按钮,这就是为什么要在URL-Path / login上进行注册的原因。
实现:
public class GlobalLogoutServlet extends HttpServlet {
private static final String LOGOUT_PARAM = "logout";
@Override
protected void doGet(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException {
String queryString = req.getQueryString();
if (LOGOUT_PARAM.equalsIgnoreCase(queryString)) {
requestGlobalLogout(req,resp);
}
}
private void requestGlobalLogout(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,IOException {
AuthFacade authBean = new CommonBeanaccessorGeneric().getFacade(AuthFacade.BEAN_NAME,AuthFacade.class);
try {
authBean.logout(req,resp);
} catch (PrismaLoginException e) {
throw new ServletException(e);
}
}
如您所见,有一个对EJB(AuthFacade)的调用,它执行应用程序登录(从我们的数据库中加载角色和权限),在这种情况下,注销的实现如下:
@Override
protected void doLogout(HttpServletRequest request,HttpServletResponse response) throws PrismaLoginException {
//HttpSession session = request.getSession();
//session.invalidate();
try {
request.logout();
String ctp = getGlobalLogoutUrl(request);
response.sendRedirect(ctp);
}catch(Exception e) {
throw new PrismaLoginException(e);
}
}
private String getGlobalLogoutUrl(HttpServletRequest request) {
String ctp = request.getcontextPath();
ctp = cutTrailingSlash(ctp);
ctp = ctp + "?GLO=true";
return ctp;
}
引用https://lists.jboss.org/pipermail/keycloak-user/2018-August/015164.html对HttpServletRequest.logout()的调用不是非操作(它对SecurityContext有作用吗?),但是不会触发对IdP的LogoutRequest。
仅当我还使用queryParam“?GLO = true”调用URL时,才完成此操作。这会触发另一个AuthnRequest(WHY?),因此代理上还有另一个登录名
此后,发生以下两种情况之一:
-
在idp启动的登录设置中-响应发送到应用程序,并显示错误页面,其中显示“用户已登录”和“返回应用程序”的链接
-
在sp中启动登录设置-随后出现注销请求和状态成功的注销响应,导致重定向到主服务器,处理诸如https://myapp/webcontext/saml和403 Forbidden
请帮助我确定这些问题。我已经阅读了很多相关的文章,但无法使它正常工作。
https://lists.jboss.org/pipermail/keycloak-user/2018-August/015164.html
我还给Red Hat写了一条消息,将来我们可能会订阅以获得更多支持,但他们尚未答复。
我将提供由idp启动的第三方idp登录进行设置的一些配置详细信息:
jboss-web.xml:
keycloak
战争中的web.xml:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
myapp
resteasy.scan
true
登录模块jar的
web-fragment.xml:
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">
SAMLLoginFragment
GlobalLogoutServlet
at.prismasolutions.ppcf.core.auth.login.pvpsaml.GlobalLogoutServlet
GlobalLogoutServlet
/login
PublicContentServlet
at.prismasolutions.ppcf.core.common.service.PublicContentServlet
PublicContentServlet
/public/*
SAMLLoginServlet
at.prismasolutions.ppcf.core.auth.login.pvpsaml.SAMLLoginServlet
SAMLLoginServlet
/saml_login
*
The protected resources
/*
*
NONE
The unprotected resources
/public/*
/saml_login
NONE
KEYCLOAK-SAML
/
客户端keycloak-saml.xml
sslPolicy="EXTERNAL">
file="${jboss.server.config.dir}/keystore/keystore.jks"
password="*****">
alias="mykey"
password="****" />
alias="mycert" />
file="${jboss.server.config.dir}/keystore/keystore.jks"
password="*****">
alias="mykey"
password="*****" />
attribute="USERID" />
signatureCanOnicalizationmethod="http://www.w3.org/2001/10/xml-exc-c14n#">
validateRespOnseSignature="true" validateAssertiOnSignature="true"
requestBinding="POST"
bindingUrl="https://mybroker/auth/realms/MYREALM/protocol/saml" />
signRespOnse="true" validateRequestSignature="true"
validateRespOnseSignature="true" requestBinding="POST"
respOnseBinding="POST"
postBindingUrl="https://mybroker/auth/realms/MYREALM/protocol/saml"
redirectBindingUrl="https://mybroker/auth/realms/MYREALM/protocol/saml" />
standalone.xml中与Keycloak相关的摘要:
...
在第三方idp中为idp启动的设置导入的元数据:
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext">
urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
*****
*****
*****
*****
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://mybroker/auth/realms/MYREALM/broker/myidp/endpoint/clients/myapp"
index="1" isDefault="true" />
对于有关saml注销的任何其他信息,我将感到满意:
- 有关任何部分中与注销相关的所有配置的信息
系统的
- 有关所有Keycloak类的信息注销
过程取决于
对于解决此问题的任何帮助,我将深表感谢。请随时询问更多信息,我将竭尽所能。
非常感谢
Manuel Waltschek