Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSF: Authentication & Authorization, best way forward

I've spent all day Googling and looking at various questions on here, trying to come up with the best solution for implementing authentication and authorization. I've come up with part of the solution now, but am hoping someone can fill in the gaps. I realise there is a lot of text below, but please bear with me :O)

Background

I have inherited a part completed CRM application which currently uses JSF 2.0, JavaEE 6, JPA and a PostgreSQL database. Unfortunately, the guys who originally started building this web app in their infinite wisdom decided that it would be best to leave authentication/authorization to the end - I've now got to put it in.

The application is essentially split into three layers - the views, the managed beans and the DAO's. This means that the managed beans are particularly 'fat' since they contain all of the business logic, validation and navigation logic.

Authentication/Authorization requirements

  1. Forms based authentication, validating against credentials stored in the PostgreSQL database.
  2. The only page that will be publicly accessible (by anonymous users) will be the login page.
  3. I need to prevent access to certain areas of the application based on a users role. For example, only users with the 'Admin' role should be able to access the create/edit user page.
  4. I also need to be able to restrict access to certain area's of a page. For example, a user with the 'Sales Rep' role should be able to view a customers details, but the save/edit button should only be displayed if the user has the 'Customer Service' role.

Where I'm at

The first thing I plan on doing is to follow this User Authentication and Authorization using JAAS and Servlet 3.0 Login example. This I believe will fulfil my first 3 requirements.

In order to show/hide save buttons etc on pages, I can use the technique described in this SO answer. This will partly solve requirement 4, however I think that I still need to secure the action methods and or the managed beans themselves. For example, I would like to be able to add an annotation or something to the save() method on the customer bean to ensure that only users with the 'Customer Service' role can call it - this is where I begin to run into issues.

I guess one option would be to do something similar to what I am proposing to do in the view and use facesContext to check if the current user "is in role". I'm not keen on this as it will just clutter up my code and would rather use annotations instead. If I did go down this route however, how would I return a http 403 status?

The javax.annotation.security.* annotations seem to be a good fit for declaritively defininig access to areas of the application, however as far as I understand, they can only be added to EJB's. This would mean that I would need to move all of my business logic out of the managed beans where it currently resides to new EJB's. I think this would have the added benefit of separating the business logic out into it's own set of classes (delegates, services or whatever you chooses to call them). This would be quite a large refactor however which isn't going to be aided by a lack of unit test or integration tests. I'm not sure whether the responsibility of access control should be at this new service level either - I think it should be on the managed beans.

Other alternatives

During my research I have found lots of people mentioning frameworks such as Spring and Seam. I have some limited experience with Seam, I think it would have been a good fit for this project and from what I recall I believe it solves the authorization issues I am having, but I think it is too late in the day to introduce it now.

I have also seen Shiro mentioned in various places. Having looked at the 10 minute tutorial this seemed like a good fit, especially in conjunction with Deluan Quintao's taglib but I have been unable to find any tutorials or examples of how to integrate it with a JSF web app.

The other alternative I have come across surprisingly regularly is implementing a custom solution - this seems crazy to me!

Summary

In summary then, I'd really like some guidance on whether I'm heading down the right path in terms of implementing authentication and authorization and how I fill in that missing piece of securing individual methods and/or managed beans (or at least the code they delegate to) and/or how I can manually return a HTTP Status 403.

like image 213
s1mm0t Avatar asked May 11 '12 15:05

s1mm0t


2 Answers

Have you tried anything with Spring Security - Latest being version 3

http://janistoolbox.typepad.com/blog/2010/03/j2ee-security-java-serverfaces-jsf-spring-security.html

http://ocpsoft.org/java/jsf-java/spring-security-what-happens-after-you-log-in/

rather than using a request filter or using JAAS, spring security is a comprehensive security framework that will resolve most of your security concerns . You can use it to authenticate a user using a db realm, authorize him and redirect as necessary based on the provided authentication information.

you can secure the methods that you have written http://blog.solidcraft.eu/2011/03/spring-security-by-example-securing.html

@PreAuthorize("hasRole('ROLE_XXX')") is the way

to make certain elements of a page secure.. //content

more reading and examples http://static.springsource.org/spring-security/site/petclinic-tutorial.html

like image 171
Sanath Avatar answered Oct 16 '22 16:10

Sanath


After carrying out a lot of research I have come to the conclusion that firstly the requirements of my application would benefit from being deployed to an application server that fully implements the Java EE specification, rather than a servlet container like Tomcat. As the project I am working on uses Maven, the key thing here was getting the dependencies set up correctly - this wasn't easy and took a fair bit of googling and trial and error: I'm sure there is a more scientific approach that could be taken.

I then had to create a mysql module to get my application to talk to the database properly and then remove the factory that had been implemented to create DAO's and convert them to EJB's instead. I also had to update hibernate.cfg.xml to reference the datasource I added and persistence.xml to set the transaction type to JTA and also reference the JTA data source. The only other complication was that the Open Session In View pattern was being used which meant I ended up with hibernate lazy initialization errors when entities were accessed in the views. I reimplemented the filter as shown at the bottom of this answer, to get around this. I see this as a temporary measure to get things working again before I can hopefully refactor this area and remove the need for the filter.

Moving to JBoss took just over a day and I'm sure it could have been done much quicker if I was more experienced with Java EE and Maven. Now that I'm at that point I'm in a good position to be able to drop seam 3 security into the project and utilise that, rather than trying to hack together a solution which is essentially the direction I was going to take. The nice thing about Seam 3 is that you can to a certain extent pick and choose which modules you use rather than having to add the entire framework (like Seam 2). I think a number of the other modules are going to be helpful as well however and will help me amongst other things get rid of the open session in view pattern.

One thing that did concern me with using Seam was that I was told about DeltaSpike. This seems as though it will probably replace seam and there are no plans for any more versions of seam. I have decided that since seam is still being supported and if DeltaSpike takes as long to come to fruition as seam 3, then it is pretty safe to use seam 3.

I will hopefully get round to writing a proper blog post describing this migration in proper detail.

public class OSVRequestFilter implements Filter {

    private static final String UserTransaction = "java:comp/UserTransaction";

    private static Logger logger = LoggerFactory.getLogger(EntityManagerRequestFilter.class);

    public void init(FilterConfig config) throws ServletException {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request instanceof HttpServletRequest) {
            doFilter(request, response, chain, getUserTransaction());
        }
    }

    private UserTransaction getUserTransaction() throws ServletException {
        try {
            Context ctx = new InitialContext();
            return (UserTransaction)PortableRemoteObject.narrow(ctx.lookup(UserTransaction), UserTransaction.class);
        }
        catch (NamingException ex) {
            logger.error("Failed to get " + UserTransaction, ex);
            throw new ServletException(ex);
        }
    }

    private void doFilter(ServletRequest request, ServletResponse response, FilterChain chain, UserTransaction utx) throws IOException, ServletException {
        try {
            utx.begin();

            chain.doFilter(request, response);

            if (utx.getStatus() == Status.STATUS_ACTIVE)
                utx.commit();
            else 
                utx.rollback();
        }
        catch (ServletException ex) {
            onError(utx);
            throw ex;
        }
        catch (IOException ex) {
            onError(utx);
            throw ex;
        }
        catch (RuntimeException ex) {
            onError(utx);
            throw ex;
        }
        catch (Throwable ex){
            onError(utx);
            throw new ServletException(ex);
        }
    }

    private void onError(UserTransaction utx) throws IOException, ServletException {
        try {
            if ((utx != null) && (utx.getStatus() == Status.STATUS_ACTIVE))
                utx.rollback();
        }
        catch (Throwable e1) {
            logger.error("Cannot rollback transaction", e1);
        }
    }

    public void destroy() {
    }
}
like image 31
s1mm0t Avatar answered Oct 16 '22 17:10

s1mm0t