Search This Blog

Wednesday, 27 November 2013

Spring Security And Authorization

In a Java Web application, where authorization is involved, roles come into the picture. If a request is authorized to access a particular resource, then only will the Servlet Container allow it to work with the resource. How is the authorization decision made ?
Every existing user is assigned with one or many roles. This role assignment is closely associated with the authentication process used.For e.g. if I am using Tomcat managed authentication, then the details will be present in my tomcat-users.xml file:
<tomcat-users>
  <role rolename="tomcat" />
  <role rolename="ACCOUNT_HOLDER" />
  <role rolename="Admin" />

  <user username="tomcat" password="tomcat" roles="tomcat" />
  <user username="robin" password="robin" roles="ACCOUNT_HOLDER,Admin" />
</tomcat-users>
When a request is received with user credentials, Tomcat will
  1. Authenticate the user.
  2. Note the roles assigned to it.
When the request now tries to refer to a secured resource, Tomcat checks if the user's role authorizes him to access the resource. If yes, then the resource is made available to him. For e.g.:
  <security-constraint>
    <display-name>CompleteSecurityConstraint</display-name>
    <web-resource-collection>
      <web-resource-name>AllResources</web-resource-name>
      <url-pattern>/account/*</url-pattern>
    </web-resource-collection>

    <auth-constraint>
      <description>Only authenticated users must proceed from here</description>
      <role-name>ACCOUNT_HOLDER</role-name>
    </auth-constraint>
  </security-constraint>
The above fragment is from web.xml. Only users with role ACCOUNT_HOLDER can access urls of the form /account/*
Can we check a user's role programmatically? Consider my servlet code:
    protected void service(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // if (user in role account) {
        // execute the update method
        // } else {
        // return a read only view
        // }
    }
For such scenarios, the Servlet API provides role details within the request
    protected void service(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        Principal principal = request.getUserPrincipal();
        PrintWriter printWriter = response.getWriter();
        printWriter.write("<html><head></head><body>");

        // if user in role account execute the update method
        if (request.isUserInRole("ACCOUNT_HOLDER")) {
            // do logic
            printWriter.write("Allowed to update account details<br/><br/>");
        } else {
            printWriter.write("Account read only view !! <br/><br/>");
        }

        printWriter.write("<br/>Principal.getName() " + principal.getName()
                + " <br/> Principal Class " + principal.getClass()
                + " <br/> Principal " + principal + "</body></html>");
    }
The request includes a isUserInRole method that checks against a role name. Running the above code for user robin would display the message on screen:
Allowed to update account details
Principal.getName() robin
Principal Class class org.apache.catalina.users.MemoryUser
Principal User username="robin", roles="ACCOUNT_HOLDER,Admin"
As seen the Principal represents an authorized user within the system. The user is associated with the system via a request. Hence the request holds the information on the principal. How does Spring manage this information ?
The most fundamental object is SecurityContextHolder. From the spring docs:
This is where we store details of the present security context of the application, 
which includes details of the principal currently using the application. By default 
the SecurityContextHolder uses a ThreadLocal to store these details, which means 
that the security context is always available to methods in the same thread of 
execution, even if the security context is not explicitly passed around as an 
argument to those methods.
To try this I decided to add a controller to my earlier application:
    public void processRequest(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        PrintWriter printWriter = response.getWriter();

        printWriter.write("<html><head></head><body>");
        String username = "-";

        SecurityContext securityContext = SecurityContextHolder.getContext();
        Authentication authentication = securityContext.getAuthentication();
        Object principal = authentication.getPrincipal();

        if (principal instanceof UserDetails) {
            username = ((UserDetails) principal).getUsername();
        }

        if (request instanceof SecurityContextHolderAwareRequestWrapper) {
            SecurityContextHolderAwareRequestWrapper requestWrapper = 
                       (SecurityContextHolderAwareRequestWrapper) request;
            if (requestWrapper.isUserInRole("ACCOUNT_HOLDER")) {
                // do logic
                printWriter.write("Allowed to update account details <br/><br/>");
            } else {
                printWriter.write("Account read only view !! <br/><br/>");
            }
        }
        printWriter.write("<br/>Principal.getName() " + username
                + " <br/> Principal Class " + principal.getClass()
                + " <br/> Principal " + principal + "</body></html>");

    }
The code pretty much does the same thing as the earlier servlet. For users we used the authentication-provider element to list out the user credentials and roles.
The security information has been obtained in a different manner.
  1. As the documentation suggested we retrieve the SecurityContext for the current thread.
  2.  To obtain the principal object we retrieved the Authentication instance. 
  3. The authorization information is actually retrieved form the ServletRequest wrapper. 
 If I run the code the screen would display:
Allowed to update account details
Principal.getName() robin
Principal Class class org.springframework.security.core.userdetails.User
Principal org.springframework.security.core.userdetails.User@67a69aa: 
Username: robin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; 
credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ACCOUNT_HOLDER
As can be seen, Spring's Authroization method and classes seem to resemble the Container managed security. But Spring is not using Container managed security. It is actually using a combination of filters, wrapper classes and information saved in session to manage security.

3 comments:

  1. A Principal does not really represent the authenticated user directly. Instead, it represents a property of that. The Subject class (not exposed by the Servlet API) represents the full user instead.

    ReplyDelete
  2. A Subject would be something that fits from the JAAS principle yes. The above post was an attempt to look at how Spring Security works to resemble the Servlet API and security. A good description of the User Subject Principal terms is available here at http://stackoverflow.com/questions/4989063/what-is-the-meaning-of-subject-vs-user-vs-principal-in-a-security-context

    ReplyDelete
  3. thank you for all of the sharings, they help me a lot

    ReplyDelete