In general you need to use a Java EE authentication mechanism. Such authentication mechanism is called at the beginning of a request, and will interact with the caller to obtain credentials. If those credentials are validated correctly, it can set a caller name or caller principal alongside with several roles in the current request.
Where the caller name and roles come from is not important for Java EE. Your code can invent them on the fly, fetch them from an in-memory store, query them from a DB, get them from an LDAP server, etc etc.
Often, but not necessarily, an authentication mechanism delegates the task of validating credentials to a specialised component that interacts with a kind of store like mentioned above. This is called an "identity store" (there's about 20 other terms for this same thing, but Java EE standardised on that).
The following gives an example of an identity store:
@ApplicationScoped
public class TestIdentityStore implements IdentityStore {
public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) {
if (usernamePasswordCredential.compareTo("reza", "secret1")) {
return new CredentialValidationResult("reza", new HashSet<>(asList("foo", "bar")));
}
return INVALID_RESULT;
}
}
A custom authentication mechanism looks like this:
@ApplicationScoped
public class TestAuthenticationMechanism implements HttpAuthenticationMechanism {
@Inject
private IdentityStoreHandler identityStoreHandler;
@Override
public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthenticationException {
if (notNull(request.getParameter("name"), request.getParameter("password"))) {
// Get the (caller) name and password from the request
// NOTE: This is for the smallest possible example only. In practice
// putting the password in a request query parameter is highly
// insecure
String name = request.getParameter("name");
Password password = new Password(request.getParameter("password"));
// Delegate the {credentials in -> identity data out} function to
// the Identity Store
CredentialValidationResult result = identityStoreHandler.validate(
new UsernamePasswordCredential(name, password));
if (result.getStatus() == VALID) {
// Communicate the details of the authenticated user to the
// container. In many cases the underlying handler will just store the details
// and the container will actually handle the login after we return from
// this method.
return httpMessageContext.notifyContainerAboutLogin(
result.getCallerPrincipal(), result.getCallerGroups());
}
return httpMessageContext.responseUnauthorized();
}
return httpMessageContext.doNothing();
}
}
Note that the above is for DEMO purposes only and is not a realistic or even secure authentication mechanism!
See also: https://github.com/eclipse-ee4j/soteria/tree/master/test/app-mem/src/main/java/org/glassfish/soteria/test
Often time you would not write your own authentication mechanism or even identity store, but would re-use ones offered by Java EE. For example:
@CustomFormAuthenticationMechanismDefinition(
loginToContinue = @LoginToContinue(
loginPage="/login.jsf",
errorPage=""
)
)
@ApplicationScoped
public class ApplicationInit {
}
The notion of a user exists by the ability to query or inject the current caller/user principal. For instance using:
@Inject
private Principal principal;
Or using e.g. the Servlet API:
HttpServletRequest#getUserPrincipal();
This is same "user" (authenticated identity) that @RolesAllowed
and similar constructs also check against.
Group to role mapping is always optional, and defaults to groups being the same as roles. Some servers don't even support group to role mapping at all.