When I inject an @EJB proxy in the Main
class of an application client, and that EJB has a method that require a user to be in a certain role, then the application client container (ACC) will require the user to login as soon as the application client launches. The ACC will not differentiate between what methods the client had in mind calling, thus what roles - if any at all - are actually required of the client to be in. In effect, that means my client cannot hook himself up with the server properly if just one single method of the EJB has a method that require a certain role. It does not matter if we explicitly annotate the EJB with @PermitAll
. The ACC will still prompt for username and password, and if not given, then my GlassFish 3.1.2.2 (build 5) implementation will abort everything and say "The user cancelled authentication".
Declare a remote interface like so:
@Remote
public interface IRemoteEJB
{
String echoGuest(String returnMe);
String echoAdmin(String returnMe);
}
Write the EJB (my problem relates to a singleton so I dare not use another session annotation here!):
@Singleton
/*
* See the following annotation. We explicitly "permit all" on class level.
* Should not cause any ambiguity at all!
*/
@PermitAll
public class myEJB implements IRemoteEJB
{
@Override
public String echoGuest(String returnMe) {
return returnMe; }
@Override
// ..but here we grant access only to administrators:
@RolesAllowed("admin")
public String echoAdmin(String returnMe) {
return returnMe; }
}
As for the client part, here's the smallest possible implementation I could think of:
public class Main
{
@EJB private static remoteProxy;
public static void main(String... args)
{
String reply = remoteProxy.echoGuest("Hello world!");
JOptionPane.showMessageDialog(null, reply);
}
}
Do you think that the client successfully makes an "anonymous" call to the echoGuest
method? No. I've found that the ACC will force the client to give username and password even before the @EJB
is injected. Simply put if, we cannot mix access authentication to an EJB from an application client. The solution above would be to remove the @RolesAllowed("admin")
annotation from the echoAdmin
method.
In one way, that makes perfectly sense. The ACC is only active during the boot of the client, the very reason why we need to put all injected resources in an application client both in the Main
class and make them static
too. The ACC simply cannot know exactly which methods in the EJB will be invoked or not.
Okay, but does that not break the specification and expected behavior of the @PermitAll
and @RolesAllowed
annotations? Is there no way on this earth that we could provide the application client with anonymous access rights to an EJB that has only one obsolete, never invoked, method requiring a certain role? As of now, I have to separate these methods not only with the annotations I thought I could and should have done, but also I have to refactor the logic to completely separate EJB:s. Feels hard :'(
The Java EE 7 specification JSR 342 says on page 9, that application client's deployment and management is not completely defined by this specification
. Thus we can expect different, non-portable behavior when it comes to the "management" of application clients.
The specification continues and talk about Lazy Authentication on pages 41 and 42:
There is a cost associated with authentication. For example, an authentication process may require exchanging multiple messages across the network. Therefore, it is desirable to use lazy authentication, that is, to perform authentication only when it is needed. With lazy authentication, a user is not required to authenticate until there is a request to access a protected resource.
Lazy authentication can be used with first-tier clients (applets, application clients) when they request access to protected resources that require authentication.
I cannot parse this quote and judge whether Lazy Authentication is required by the specification or not. Apparently, it is not. That is, GlassFish do not break any rule when he require authentication from our application client even before we accessed the protected resource (a method invocation in the example).
Moreover, the specification really hit where it hurts on page 44:
The techniques used may vary with the implementation of the application client container, and are beyond the control of the application
Thus my final judgement must be that the specification does not require lazy authentication and that "techniques" may vary.