Jetty单Web应用同时支持FORM与OpenID双认证方案咨询
Absolutely, there are solid solutions to get both FORM and OpenID authentication working side-by-side for your Jetty 9.4.32 web app without redeploys or duplicate deployments. Here are two practical approaches you can implement:
方案1:基于URL路径的认证分流
This approach lets you map specific URL patterns to different authentication methods—e.g., /openid/** uses OpenID, while /form/** uses FORM auth. Users can access the protected resources via their preferred path, and Jetty will automatically trigger the right auth flow.
Step 1: Configure Context XML with Dual Security Handlers
Update your context XML to define two separate ConstraintSecurityHandler instances, each tied to a different authentication method and URL path:
<Set name="handler"> <New class="org.eclipse.jetty.server.handler.HandlerCollection"> <Set name="handlers"> <Array type="org.eclipse.jetty.server.Handler"> <!-- OpenID Authentication Handler --> <New class="org.eclipse.jetty.security.ConstraintSecurityHandler"> <Set name="authenticator"> <New class="org.eclipse.jetty.security.openid.OpenIdAuthenticator"> <Set name="loginService"><Ref refid="openidLoginService"/></Set> <!-- Configure your OpenID provider details (e.g., issuer, client ID) --> <Set name="issuer">https://your-openid-provider.com</Set> <Set name="clientId">your-client-id</Set> <Set name="clientSecret">your-client-secret</Set> <Set name="redirectUri">https://your-app.com/openid/callback</Set> </New> </Set> <Set name="loginService"><Ref refid="openidLoginService"/></Set> <Call name="addMapping"> <Arg>/openid/*</Arg> <Arg> <New class="org.eclipse.jetty.security.ConstraintMapping"> <Set name="constraint"> <New class="org.eclipse.jetty.security.Constraint"> <Set name="name">auth</Set> <Set name="roles"><Array type="java.lang.String"><Item>user</Item></Array></Set> <Set name="authenticate">true</Set> </New> </Set> <Set name="pathSpec">/openid/*</Set> </New> </Arg> </Call> </New> <!-- FORM Authentication Handler --> <New class="org.eclipse.jetty.security.ConstraintSecurityHandler"> <Set name="authenticator"> <New class="org.eclipse.jetty.security.FormAuthenticator"> <Set name="loginPage">/form-login.html</Set> <Set name="errorPage">/form-error.html</Set> </New> </Set> <Set name="loginService"><Ref refid="localLoginService"/></Set> <Call name="addMapping"> <Arg>/form/*</Arg> <Arg> <New class="org.eclipse.jetty.security.ConstraintMapping"> <Set name="constraint"> <New class="org.eclipse.jetty.security.Constraint"> <Set name="name">auth</Set> <Set name="roles"><Array type="java.lang.String"><Item>user</Item></Array></Set> <Set name="authenticate">true</Set> </New> </Set> <Set name="pathSpec">/form/*</Set> </New> </Arg> </Call> </New> <!-- Your Web App Context --> <Ref refid="yourWebAppContext"/> </Array> </Set> </New> </Set> <!-- Define Login Services --> <New id="openidLoginService" class="org.eclipse.jetty.security.openid.OpenIdLoginService"> <Set name="name">OpenIDRealm</Set> <!-- Configure user role mapping if needed --> </New> <New id="localLoginService" class="org.eclipse.jetty.security.HashLoginService"> <Set name="name">LocalRealm</Set> <Set name="config">etc/realm.properties</Set> </New>
Step 2: Update web.xml to Match Path Constraints
Add corresponding security constraints to your WEB-INF/web.xml to enforce authentication for each path:
<!-- OpenID Protected Path --> <security-constraint> <web-resource-collection> <web-resource-name>OpenID Protected Area</web-resource-name> <url-pattern>/openid/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>user</role-name> </auth-constraint> </security-constraint> <!-- FORM Protected Path --> <security-constraint> <web-resource-collection> <web-resource-name>FORM Protected Area</web-resource-name> <url-pattern>/form/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>user</role-name> </auth-constraint> </security-constraint> <!-- Login Configurations --> <login-config> <auth-method>OPENID</auth-method> <realm-name>OpenIDRealm</realm-name> </login-config> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/form-login.html</form-login-page> <form-error-page>/form-error.html</form-error-page> </form-login-config> <realm-name>LocalRealm</realm-name> </login-config> <security-role> <role-name>user</role-name> </security-role>
方案2:自定义登录选择入口
If you want users to pick their authentication method from a single login page (instead of using different URLs), this approach lets you create a unified entry point that redirects to the appropriate auth flow.
Step 1: Create a Login Choice Page
Make a simple HTML page (login-choice.html) that lets users select their login method:
<!DOCTYPE html> <html> <head> <title>Select Login Method</title> </head> <body> <h1>Choose Your Login Method</h1> <form action="/form-login" method="GET"> <button type="submit">Login with Local Account</button> </form> <br> <form action="/openid-login" method="GET"> <button type="submit">Login with OpenID</button> </form> </body> </html>
Step 2: Configure Context XML for Dual Auth
Update your context XML to include both authenticators, and map the OpenID login endpoint:
<Set name="handler"> <New class="org.eclipse.jetty.security.ConstraintSecurityHandler"> <Set name="authenticator"> <!-- Use a delegating authenticator to support multiple methods --> <New class="org.eclipse.jetty.security.DelegatingAuthenticator"> <Set name="authenticators"> <Array type="org.eclipse.jetty.security.Authenticator"> <New class="org.eclipse.jetty.security.FormAuthenticator"> <Set name="loginPage">/login-choice.html</Set> <Set name="errorPage">/login-error.html</Set> </New> <New class="org.eclipse.jetty.security.openid.OpenIdAuthenticator"> <Set name="loginService"><Ref refid="combinedLoginService"/></Set> <!-- Configure OpenID provider details --> </New> </Array> </Set> </New> </Set> <Set name="loginService"><Ref refid="combinedLoginService"/></Set> <!-- Map all protected paths --> <Call name="addMapping"> <Arg>/*</Arg> <Arg> <New class="org.eclipse.jetty.security.ConstraintMapping"> <Set name="constraint"> <New class="org.eclipse.jetty.security.Constraint"> <Set name="name">auth</Set> <Set name="roles"><Array type="java.lang.String"><Item>user</Item></Array></Set> <Set name="authenticate">true</Set> </New> </Set> <Set name="pathSpec">/*</Set> <Set name="excludePaths"> <Array type="java.lang.String"> <Item>/login-choice.html</Item> <Item>/form-login.html</Item> <Item>/openid-login</Item> </Array> </Set> </New> </Arg> </Call> </New> </Set> <!-- Combined Login Service (or use separate services if needed) --> <New id="combinedLoginService" class="org.eclipse.jetty.security.CombinedLoginService"> <Set name="services"> <Array type="org.eclipse.jetty.security.LoginService"> <New class="org.eclipse.jetty.security.HashLoginService"> <Set name="name">LocalRealm</Set> <Set name="config">etc/realm.properties</Set> </New> <New class="org.eclipse.jetty.security.openid.OpenIdLoginService"> <Set name="name">OpenIDRealm</Set> </New> </Array> </Set> </New>
Step 3: Update web.xml
Set the default login page to your choice page, and ensure all protected paths are covered:
<security-constraint> <web-resource-collection> <web-resource-name>Protected Area</web-resource-name> <url-pattern>/*</url-pattern> <exclude-url-pattern>/login-choice.html</exclude-url-pattern> <exclude-url-pattern>/form-login.html</exclude-url-pattern> <exclude-url-pattern>/openid-login</exclude-url-pattern> </web-resource-collection> <auth-constraint> <role-name>user</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login-choice.html</form-login-page> <form-error-page>/login-error.html</form-error-page> </form-login-config> </login-config> <security-role> <role-name>user</role-name> </security-role>
Key Notes
- Activate OpenID Module: Ensure you've already activated Jetty's
openidmodule (you mentioned this is done, but double-check withjava -jar start.jar --list-modules). - Role Consistency: Make sure both authentication methods assign users to the same role(s) (like
user) so they can access the same protected resources. - OpenID Provider Setup: For OpenID to work, you'll need to register your app with your chosen provider (e.g., Google, Okta) to get client ID/secret values.
内容的提问来源于stack exchange,提问作者thodoris forreal

