The most basic ideas in security are Authentication and Authorization.
Authentication: Authentication is the process of verifying user identity. The most common method of authentication is by checking username and password.
Authorization: Authorization is the process of determining wether a user has access to a particular resource or task, and it is accomplished once a user is authenticated.
EJB 3 and Java EE security
Java EE security is largely based on Java Authentication and Authorization API (JAAS). JAAS essentially separates the authentication and authorization system from the Java EE application.
JAAS is designed so that both the authentication and authorization steps can be performed at any Java EE tier, including the web and EJB tiers. However, most Java EE applications are web accessible and share an authentication system across tiers. JAAS fully leverages this reality and once a user is authenticated at any Java EE tier, the authentication context is passed through tiers whenever possible, instead of repeating the authentication step. The Principal object we represents this sharable, validated authentication context.
A successful authentication results in a valid user Principal. At this point, the Principal is associated with one or more roles. For each secured web/EJB tier resource, the application server checks if the principal/role is authorized to access the resource.
EJB authentication and authorization
Like transaction management, authentication can be either declarative or programmatic, each of which provides a different level of control over the authentication process. In addition, like the transaction management, security applies to session beans and MDBs, and not the JPA entities.
Declarative security
In the next picture I show how declarative security works in my CartManagerBean. It includes a cancelItem method that only ADMIN can use.
This chunk of code, shows some of the security annotations used for declarative security:
javax.annotation.security.DeclareRoles, javax.annotation.security.RolesAllowed, and javax.annotation.security.PermitAll. Two other annotations that we have not used but will disifcuss are javax.annotation.security.DenyAll and javax.annotation.security.RunAs.
DeclareRoles: There are different ways of declaring roles, one of those is using the DeclareRoles annotation. This annotation applies either at class level or the method level and consists of an array of role names. In the above image I declared that CarlManagerBean uses ADMIN and BUYER roles. If we never declare roles, the container will authomatically build a list of roles by inspecting the @RolesAllowed annotation. Roles at deployment time must be mapped to groups in the runtime security environment.
RolesAllowed: This method can be applied on either an EJB business method or an entire class. When applied to the entire class, it tells the container which roles are allowed to access any method. On the other hand, when applied on a method, we declare a list of roles that can access that particular method. In the previous code I declare that only ADMIN role can access the cancelItem() method.
PermitAll & DenyAll: We can use PermitAll annotation to declare that any role can access the entire class or an EJB business method. We used this annotation on the showCart() method to instruct that any role can retrieve and see all the Items of the cart. You should use this annotation carefully especially at class level to not leave security holes in the application. DenyAll annotation does the opposite of the PermitAll. It renders the functionality inaccessible by any role. It could be useful if you consider that your application may be deployed in wide-ranging environments. You can invalidate methods or classes that might be inappropriate for a particular environment without changing the code but only using the DenyAll annotation. When applied at the method level, as the RolesAllowed annotation, it overrides the class level authorization settings.
The three security annotations, @PermitAll, @DenyAll, and @Role-
Allowed, cannot simultaneously be applied to the same class or the
same method.
RunAs: This annotation comes in hand if you want dinamically assign a role to the Principal in scope of an EJB method invocation. You want to do this for example if you want to invoke an EJB from your method but the other EJB requires a role that is different from the one that is in scope.
As you can see, declarative security gives you access to a powerful authentication framework while staying mostly out of the way. If you have ever rolled out your own security or authentication system, one weakness might have crossed your mind already. The problem is that although you can authenticate a role using declarative security, what if you need to provide security settings specific to individuals, or even simple changes in method behavior based on the current Principal’s role? This is where programmatic EJB security steps onto the stage.
Using EJB programmatic security
Programmatic security provides direct access to the Principal and to check the Principal's role in the code. Those methods are made available by the EJB context.
In the above code we first injects the EJB context. We use the isCallerInRole() method of the EJBContext to see if the underlying authenticated principal has the ADMIN role. If it does not, we throw a java.lang.SecurityException notifying the user about the authorization violation. Otherwise, the bid cancellation method is allowed to proceed normally.
isCallerInRole() & getCallerPrincipal()
Programmatic security is made up of the previously mentioned methods. These methods are defined in the javax.ejb.EJBContext interface.
Talking about the isCallerInRole() method, the context behind the scenes retrieves the Principal object associated with the current thread and checks if one of the roles matches the one you provided.
The getCallerPrincipal() method gives you direct access to the java.security.Principal representing the current authentication context.
The only interesting method in the Principal object is the getName() method, that most of the times is the name of the login validated user. Note, though, that there is not guarantee on what the Principal name might return. Depending on your environment it could return the group name, the role name or something in relation with the security role it belongs to.
Hello Alberto,
ReplyDeletethis article is really good if you struggle with Java Security :)
The most interesting part is the "Using EJB programmatic security" section and I also want to archive that.
Do you have a small example that use programmatic security? Maybe the CartManager application itself? I would be really pleased if you could help me out.
I have a security endpoint and it works when I use the annotation @DeclareRoles(...). If I remove that annotation, I cant get through the authentification.
Cheers Tobias