I'm trying to implement Jakarta Security on my web project, to do so I'm following the amazing "The Definitive Guide to JSF in Java EE 8" by Bauke Scholtz and Arjan Tijms but it seems that I'm hitting a wall.
When I type in a restricted url in the address bar the forward happens properly and I see the login page but after submitting the form the page is not redirected and the login page seems to be reloaded (the fields are emptied, but the url in the address bar is still the one from the restricted page).
With the help of the logs I know that the AuthenticationStatus returns as SUCCESS but even then if I manually change the address bar to another restricted url it gets forwarded to the login page.
If I type /login in the address bar and submit the authentication and redirect happens properly.
It seems that if I make useForwardToLogin = false it works properly but then getForwardURL always returns null and I can not redirect the user to the page they wanted to get to.
Thanks for your help.
ApplicationConfig.java
@CustomFormAuthenticationMechanismDefinition(
loginToContinue = @LoginToContinue(
loginPage = "/login",
errorPage = ""
)
)
@FacesConfig
@ApplicationScoped
public class ApplicationConfig {
}
login.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"
xmlns:p="http://primefaces.org/ui"
xmlns:o="http://omnifaces.org/ui"
template="/WEB-INF/templates/login.xhtml">
<ui:define name="content">
<c:choose>
<c:when test="#{not empty request.userPrincipal}">
<div>Already logged</div>
</c:when>
<c:otherwise>
<o:form>
<p:inputText id="login" value="#{loginBacking.userName}" required="true"/>
<p:password id="pwd" value="#{loginBacking.password}" required="true"/>
<p:inputNumber id="otp" value="#{loginBacking.otp}" />
<o:messages id="messages" var="message">
<div class="#{fn:toLowerCase(message.severity)}">#{message.summary}</div>
</o:messages>
<p:commandButton value="Submit" action="#{loginBacking.login}" update="@form"/>
</o:form>
</c:otherwise>
</c:choose>
</ui:define>
</ui:composition>
LoginBacking.java
@Named
@RequestScoped
public class LoginBacking {
@Inject private SecurityContext securityContext;
@Getter @Setter private String userName;
@Getter @Setter private String password;
@Getter @Setter private Integer otp;
public void login() {
switch (continueAuthentication()) {
case SEND_CONTINUE:
Faces.responseComplete();
break;
case SEND_FAILURE:
Messages.addGlobalFatal("Login failed.");
break;
case SUCCESS:
redirect();
break;
}
}
private AuthenticationStatus continueAuthentication() {
return this.securityContext.authenticate(
Faces.getRequest(),
Faces.getResponse(),
AuthenticationParameters.withParams()
.newAuthentication(getForwardURL() == null)
.credential(new CustomCredential(userName, password, otp))
);
}
private void redirect() {
CustomUser user = securityContext.getPrincipalsByType(CustomPrincipal.class).stream()
.map(CustomPrincipal::getCustomUser)
.findAny()
.orElse(null);
if (user == null) Messages.addGlobalFatal("Something went wrong.");
String forwardURL = getForwardURL();
if (forwardURL != null) {
Faces.redirect(forwardURL);
return;
}
Faces.redirect(Faces.getRequestContextPath() + "/restricted/index");
}
public void logout() throws ServletException {
Faces.logout();
Faces.invalidateSession();
Faces.redirect("login");
}
public String getForwardURL() {
String requestURI = Faces.getRequestAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
String queryString = Faces.getRequestAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
if (requestURI == null) return null;
return (queryString == null) ? requestURI : (requestURI + "?" + queryString);
}
}
FIY, as you can see, I'm using Omnifaces, Primefaces and Lombok.