Sunday, April 5. 2020
JavaEE 8 Security API in Wildfly
Unexpectedly I am going to write a continuation of the previous entry about JASPI. The past week I needed to work with the new Java EE Security API Specification (JSR 375) and, because it is based on JASPI, the new specification is somewhat related to the previous post. There are several good articles over the internet about the new javaee security framework, for example Jakarta EE 8 Security API by baeldung or the overview done by DZone. Today's entry is just going to use the JSR 375 specification with a Wildfly 19 server.
Following the previous guides about the new API, a little sample application is going to be developed. The framework lets you configure the application login just defining a ApplicationScoped bean. The bean defines the authentication mechanism and the storage to use.
package es.rickyepoderi;
import javax.enterprise.context.ApplicationScoped;
import javax.security.enterprise.authentication.mechanism.http.FormAuthenticationMechanismDefinition;
import javax.security.enterprise.authentication.mechanism.http.LoginToContinue;
import javax.security.enterprise.identitystore.LdapIdentityStoreDefinition;
@FormAuthenticationMechanismDefinition(loginToContinue = @LoginToContinue(
loginPage = "/login.html",
errorPage = "/login-error.html"
))
@LdapIdentityStoreDefinition(
url="ldap://localhost:1389",
bindDn="cn=Directory Manager",
bindDnPassword="XXXX",
callerSearchBase="ou=People,dc=sample,dc=com",
groupSearchBase="ou=Groups,dc=sample,dc=com",
groupNameAttribute="cn",
groupMemberAttribute="uniqueMember"
)
@ApplicationScoped
public class ApplicationConfig {
}
So the application is going to use a FORM login (an HTML page will be shown to the user to login) and the user info are going to be retrieved from an LDAP server. The API gives more options for the mechanism (BasicAuthenticationMechanismDefinition and CustomFormAuthenticationMechanismDefinition) and the storage (DatabaseIdentityStoreDefinition or a custom IdentityStore). With the bean done, a HelloServlet is introduced.
package es.rickyepoderi;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.HttpConstraint;
import javax.servlet.annotation.ServletSecurity;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet({"/hello"})
@ServletSecurity(@HttpConstraint(rolesAllowed={"static-group1"}))
public class HelloWorldServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try (PrintWriter writer = response.getWriter()) {
response.setContentType("text/plain;charset=UTF-8");
writer.append("Hello ")
.append(request.getUserPrincipal() == null? "World" : request.getUserPrincipal().getName())
.append("!");
}
}
}
The Servlet is annotated to request a role (static-group1) which will be a group of the user in the LDAP repository. In my humble opinion the security API needs to be extended to apply these constraints in other places, right now only servlets can be annotated and, if I am not wrong, this is quite short (why not annotate other generic URLs that can be JSP, JSF, jax-rs,...). Adding a general place (in the bean for example?) to include constraints for generic URLs would be awesome. I have to say that I am still surprised about this, if I missed something please add a comment in the bottom section.
The final step when using wildfly is defining the jaspitest security domain. Why? Because the JSR 375 works with JASPI and, therefore, the application should be configured to use a JASPI capable security domain. The default security domain other configured by wildfly is not JASPI. In a common application the jboss-web.xml should be placed setting the module to use.
<jboss-web>
<security-domain>jaspitest</security-domain>
</jboss-web>
And that is all. The application should work in wildfly 19 for example. Just download, install it and deploy the application. You can download the maven project for the sample app from here.
wget https://download.jboss.org/wildfly/19.0.0.Final/wildfly-19.0.0.Final.zip
unzip wildfly-19.0.0.Final.zip
cd wildfly-19.0.0.Final/bin
./add-user.sh -u admin -p XXXX
./standalone.sh
./jboss-cli.sh --connect
deploy /path/to/javaee8sec/target/javaee8sec.war
But what if I do not want to specify the jboss-web.xml in the application, in the end this is not part of the standard. Well, the solution is quite simple, you can just change the default security domain for the web/undertow to jaspitest.
./jboss-cli.sh --connect
/subsystem=undertow:write-attribute(name=default-security-domain, value=jaspitest)
/:reload
The application now will work without the jboss-web.xml, because the jaspitest security domain is used by default by the apps that do not specify one. But what happens with elytron, in the previous entry we saw that the new security subsystem supports JASPI too, so, how can we configure the server to use it instead of the old security domain? Just like it was shown in the old entry, an undertow/elytron domain should be configured with JASPI enabled but not integrated. Here it is an example to configure elytron globally and enable JASPI for the security API.
cd wildfly-19.0.0.Final
bin/jboss-cli.sh --file=docs/examples/enable-elytron.cli
cd bin
./standalone.sh
./jboss-cli.sh --connect
/subsystem=undertow/application-security-domain=other:write-attribute(name=integrated-jaspi, value=false)
/subsystem=undertow:write-attribute(name=default-security-domain, value=other)
/:reload
The provided enable-elytron.cli is first used to completely configure elytron security (now everything in wildfly uses elytron instead of the old security system). Finally the default other domain is changed to set integrated-jaspi to false. Remember that this property makes that JASPI alone is enough to authenticate users, if false the user is also located in the associated realm inside elytron and for the security API that needs to be avoided.
As a summary wildfly 19 is JavaEE 8 certified and, therefore, the new security API is available. In the entry a little sample application was deployed to authenticate users against an LDAP server. The final points were used to avoid the inclusion of the jboss-web.xml in the application and configure the server to automatically use a domain that is JASPI enabled, using both, the old jaspitest domain and the new elytron susbsystem.
Best regards!
Comments