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:
Can we check a user's role programmatically? Consider my servlet code:
The most fundamental object is SecurityContextHolder. From the spring docs:
The security information has been obtained in a different manner.
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>When a request is received with user credentials, Tomcat will
<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>
- Authenticate the user.
- Note the roles assigned to it.
<security-constraint>The above fragment is from web.xml. Only users with role ACCOUNT_HOLDER can access urls of the form /account/*
<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>
Can we check a user's role programmatically? Consider my servlet code:
protected void service(HttpServletRequest request,For such scenarios, the Servlet API provides role details within the request
HttpServletResponse response) throws ServletException, IOException {
// if (user in role account) {
// execute the update method
// } else {
// return a read only view
// }
}
protected void service(HttpServletRequest request,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:
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>");
}
Allowed to update account detailsAs 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 ?
Principal.getName() robin
Principal Class class org.apache.catalina.users.MemoryUser
Principal User username="robin", roles="ACCOUNT_HOLDER,Admin"
The most fundamental object is SecurityContextHolder. From the spring docs:
This is where we store details of the present security context of the application,To try this I decided to add a controller to my earlier 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.
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;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.
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 security information has been obtained in a different manner.
- As the documentation suggested we retrieve the SecurityContext for the current thread.
- To obtain the principal object we retrieved the Authentication instance.
- The authorization information is actually retrieved form the ServletRequest wrapper.
Allowed to update account detailsAs 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.
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