Monday, September 8, 2014

WebApplication Security in JAVA/J2EE Applications.


Overview of Web Application Security

In the Java EE platform, web components provide the dynamic extension capabilities for a web server. Web components are either Java servlets, JSP pages, JSF pages, or web service endpoints. The interaction between a web client and a web application is illustrated in Figure 30-1.

Figure 30-1 Java Web Application Request Handling
Diagram of steps in web application request handling, showing web client, HttpServlet request, web and JavaBeans components, and HttpServlet response Web components are supported by the services of a runtime platform called a web container. A web container provides services such as request dispatching, security, concurrency, and life-cycle management.
Certain aspects of web application security can be configured when the application is installed, or deployed, to the web container. Annotations and/or deployment descriptors are used to relay information to the deployer about security and other aspects of the application. Specifying this information in annotations or in the deployment descriptor helps the deployer set up the appropriate security policy for the web application. Any values explicitly specified in the deployment descriptor override any values specified in annotations. This chapter provides more information on configuring security for web applications.
For secure transport, most web applications use the HTTPS protocol. For more information on using the HTTPS protocol, read Establishing a Secure Connection Using SSL.


Using Programmatic Security with Web Applications


Programmatic security is used by security-aware applications when declarative security alone is not sufficient to express the security model of the application.

Authenticating Users Programmatically


Servlet 3.0 specifies the following methods of the HttpServletRequest interface that enable you to authenticate users for a web application programmatically:
  • authenticate, which allows an application to instigate authentication of the request caller by the container from within an unconstrained request context. A login dialog box displays and collects the user name and password for authentication purposes.
  • login, which allows an application to collect user name and password information as an alternative to specifying form-based authentication in an application deployment descriptor.
  • logout, which allows an application to reset the caller identity of a request.
The following example code shows how to use the login and logout methods:
package test;

import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name="TutorialServlet", urlPatterns={"/TutorialServlet"})
public class TutorialServlet extends HttpServlet {
    @EJB
    private ConverterBean converterBean;

    /**
     * Processes requests for both HTTP <code>GET</code> 
     *    and <code>POST</code> methods.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, 
            HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {

            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet TutorialServlet</title>");
            out.println("</head>");
            out.println("<body>");
            request.login("TutorialUser", "TutorialUser");
            BigDecimal result = 
                converterBean.dollarToYen(new BigDecimal("1.0"));
            out.println("<h1>Servlet TutorialServlet result of dollarToYen= "
                + result + "</h1>");
            out.println("</body>");
            out.println("</html>");
        } catch (Exception e) {
            throw new ServletException(e);
        } finally {
            request.logout();
            out.close();
        }
    }
}
The following example code shows how to use the authenticate method:
package com.sam.test;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class TestServlet extends HttpServlet {

    protected void processRequest(HttpServletRequest request, 
            HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            request.authenticate(response);
            out.println("Authenticate Successful");
        } finally {
            out.close();
        }
    }

Checking Caller Identity Programmatically

In general, security management should be enforced by the container in a manner that is transparent to the web component. The security API described in this section should be used only in the less frequent situations in which the web component methods need to access the security context information.
Servlet 3.0 specifies the following methods that enable you to access security information about the component’s caller:
  • getRemoteUser, which determines the user name with which the client authenticated. The getRemoteUser method returns the name of the remote user (the caller) associated by the container with the request. If no user has been authenticated, this method returns null.
  • isUserInRole, which determines whether a remote user is in a specific security role. If no user has been authenticated, this method returns false. This method expects a String user role-name parameter.
    The security-role-ref element should be declared in the deployment descriptor with a role-name subelement containing the role name to be passed to the method. Using security role references is discussed in Declaring and Linking Role References.
  • getUserPrincipal, which determines the principal name of the current user and returns a java.security.Principal object. If no user has been authenticated, this method returns null. Calling the getName method on the Principal returned by getUserPrincipal returns the name of the remote user.
Your application can make business-logic decisions based on the information obtained using these APIs.

Example Code for Programmatic Security

The following code demonstrates the use of programmatic security for the purposes of programmatic login. This servlet does the following:
  1. It displays information about the current user.
  2. It prompts the user to log in.
  3. It prints out the information again to demonstrate the effect of the login method.
  4. It logs the user out.
  5. It prints out the information again to demonstrate the effect of the logout method.
package enterprise.programmatic_login;

import java.io.*;
import java.net.*;
import javax.annotation.security.DeclareRoles;
import javax.servlet.*;
import javax.servlet.http.*;

@DeclareRoles("javaee6user")
public class LoginServlet extends HttpServlet {

    /** 
     * Processes requests for both HTTP GET and POST methods.
     * @param request servlet request
     * @param response servlet response
     */
    protected void processRequest(HttpServletRequest request, 
                 HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            String userName = request.getParameter("txtUserName");
            String password = request.getParameter("txtPassword");
            
            out.println("Before Login" + "<br><br>");
            out.println("IsUserInRole?.." 
                        + request.isUserInRole("javaee6user")+"<br>");
            out.println("getRemoteUser?.." + request.getRemoteUser()+"<br>");
            out.println("getUserPrincipal?.." 
                        + request.getUserPrincipal()+"<br>");
            out.println("getAuthType?.." + request.getAuthType()+"<br><br>");
            
            try {
                request.login(userName, password); 
            } catch(ServletException ex) {
                out.println("Login Failed with a ServletException.." 
                    + ex.getMessage());
                return;
            }
            out.println("After Login..."+"<br><br>");
            out.println("IsUserInRole?.." 
                        + request.isUserInRole("javaee6user")+"<br>");
            out.println("getRemoteUser?.." + request.getRemoteUser()+"<br>");
            out.println("getUserPrincipal?.." 
                        + request.getUserPrincipal()+"<br>");
            out.println("getAuthType?.." + request.getAuthType()+"<br><br>");
            
            request.logout();
            out.println("After Logout..."+"<br><br>");
            out.println("IsUserInRole?.." 
                        + request.isUserInRole("javaee6user")+"<br>");
            out.println("getRemoteUser?.." + request.getRemoteUser()+"<br>");
            out.println("getUserPrincipal?.."
                        + request.getUserPrincipal()+"<br>");
            out.println("getAuthType?.." + request.getAuthType()+"<br>");
        } finally {
            out.close();
        }
    }
    ...
}

Declaring and Linking Role References

A security role reference is a mapping between the name of a role that is called from a web component using isUserInRole(String role) and the name of a security role that has been defined for the application. If no security-role-ref element is declared in a deployment descriptor and the isUserInRole method is called, the container defaults to checking the provided role name against the list of all security roles defined for the web application. Using the default method instead of using the security-role-ref element limits your flexibility to change role names in an application without also recompiling the servlet making the call.
The security-role-ref element is used when an application uses the HttpServletRequest.isUserInRole(String role). The value passed to the isUserInRole method is a String representing the role name of the user. The value of the role-name element must be the String used as the parameter to the HttpServletRequest.isUserInRole(String role). The role-link must contain the name of one of the security roles defined in the security-role elements. The container uses the mapping of security-role-ref to security-role when determining the return value of the call.
For example, to map the security role reference cust to the security role with role name bankCustomer, the syntax would be:
<servlet>
...
    <security-role-ref>
        <role-name>cust</role-name>
        <role-link>bankCustomer</role-link>
    </security-role-ref>
...
</servlet>
If the servlet method is called by a user in the bankCustomer security role, isUserInRole("cust") returns true.
The role-link element in the security-role-ref element must match a role-name defined in the security-role element of the same web.xml deployment descriptor, as shown here:
<security-role>
    <role-name>bankCustomer</role-name>
</security-role>
A security role reference, including the name defined by the reference, is scoped to the component whose deployment descriptor contains the security-role-ref deployment descriptor element.



Securing Java EE Applications

Java EE applications are made up of components that can be deployed into different containers. These components are used to build multitier enterprise applications. Security services are provided by the component container and can be implemented using declarative or programmatic techniques. Java EE security services provide a robust and easily configured security mechanism for authenticating users and authorizing access to application functions and associated data. Java EE security services are separate from the security mechanisms of the operating system.
The ways to implement Java EE security services are discussed in a general way in Securing Containers. This chapter provides more detail and a few examples that explore these security services as they relate to Java EE components. Java EE security services can be implemented in the following ways:
  • Metadata annotations (or simply, annotations) enable a declarative style of programming. Users can specify information about security within a class file using annotations. When the application is deployed, this information can either be used by or overridden by the application deployment descriptor.
  • Declarative security expresses an application’s security structure, including security roles, access control, and authentication requirements in a deployment descriptor, which is external to the application.
    Any values explicitly specified in the deployment descriptor override any values specified in annotations.
  • Programmatic security is embedded in an application and is used to make security decisions. Programmatic security is useful when declarative security alone is not sufficient to express the security model of an application.
Some of the material in this chapter assumes that you have already read Chapter 28, Introduction to Security in the Java EE Platform.
This chapter includes the following topics:

Best way for user authentication on JavaEE 6 using JSF 2.0?

Set up the security realm:

In my case, I had the users in the database. So I followed this blog post to create a JDBC Realm that could authenticate users based on username and MD5-hashed passwords in my database table:
http://blog.gamatam.com/2009/11/jdbc-realm-setup-with-glassfish-v3.html
Note: the post talks about a user and a group table in the database. I had a User class with a UserType enum attribute mapped via javax.persistence annotations to the database. I configured the realm with the same table for users and groups, using the userType column as the group column and it worked fine.

Use form authentication:

Still following the above blog post, configure your web.xml and sun-web.xml, but instead of using BASIC authentication, use FORM (actually, it doesn't matter which one you use, but I ended up using FORM). Use the standard HTML , not the JSF .
Then use BalusC's tip above on lazy initializing the user information from the database. He suggested doing it in a managed bean getting the principal from the faces context. I used, instead, a stateful session bean to store session information for each user, so I injected the session context:
 @Resource
 private SessionContext sessionContext;
With the principal, I can check the username and, using the EJB Entity Manager, get the User information from the database and store in my SessionInformation EJB.

Logout:

I also looked around for the best way to logout. The best one that I've found is using a Servlet:
 @WebServlet(name = "LogoutServlet", urlPatterns = {"/logout"})
 public class LogoutServlet extends HttpServlet {
  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   HttpSession session = request.getSession(false);

   // Destroys the session for this user.
   if (session != null)
        session.invalidate();

   // Redirects back to the initial page.
   response.sendRedirect(request.getContextPath());
  }
 }
Although my answer is really late considering the date of the question, I hope this helps other people that end up here from Google, just like I did.

I suppose you want form based authentication using deployment descriptors and j_security_check.
You can also do this in JSF by just using the same predefinied field names j_username and j_password as demonstrated in the tutorial.
E.g.
<form action="j_security_check" method="post">
    <h:outputLabel for="j_username" value="Username" />
    <h:inputText id="j_username" />
    <br />
    <h:outputLabel for="j_password" value="Password" />
    <h:inputSecret id="j_password" />
    <br />
    <h:commandButton value="Login" /></form>
You could do lazy loading in the User getter to check if the User is already logged in and if not, then check if the Principal is present in the request and if so, then get the User associated with j_username.
package com.stackoverflow.q2206911;import java.io.IOException;import java.security.Principal;import javax.faces.bean.ManagedBean;import javax.faces.bean.SessionScoped;import javax.faces.context.FacesContext;@ManagedBean@SessionScopedpublic class Auth {

    private User user; // The JPA entity.

    @EJB
    private UserService userService;

    public User getUser() {
        if (user == null) {
            Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
            if (principal != null) {
                user = userService.find(principal.getName()); // Find User by j_username.
            }
        }
        return user;
    }}
The User is obviously accessible in JSF EL by #{auth.user}.
To logout do a HttpServletRequest#logout() (and set User to null!). You can get a handle of the HttpServletRequest in JSF by ExternalContext#getRequest(). You can also just invalidate the session altogether.
public String logout() {
    FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
    return "login?faces-redirect=true";}
For the remnant (defining users, roles and constraints in deployment descriptor and realm), just follow the Java EE 6 tutorial and the servletcontainer documentation the usual way.

Update: you can also use the new Servlet 3.0 HttpServletRequest#login() to do a programmatic login instead of using j_security_check which may not per-se be reachable by a dispatcher in some servletcontainers. In this case you can use a fullworthy JSF form and a bean with username and password properties and a login method which look like this:
<h:form>
    <h:outputLabel for="username" value="Username" />
    <h:inputText id="username" value="#{auth.username}" required="true" />
    <h:message for="username" />
    <br />
    <h:outputLabel for="password" value="Password" />
    <h:inputSecret id="password" value="#{auth.password}" required="true" />
    <h:message for="password" />
    <br />
    <h:commandButton value="Login" action="#{auth.login}" />
    <h:messages globalOnly="true" /></h:form>
And this view scoped managed bean which also remembers the initially requested page:
@ManagedBean@ViewScopedpublic class Auth {

    private String username;
    private String password;
    private String originalURL;

    @PostConstruct
    public void init() {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        originalURL = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);

        if (originalURL == null) {
            originalURL = externalContext.getRequestContextPath() + "/home.xhtml";
        } else {
            String originalQuery = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_QUERY_STRING);

            if (originalQuery != null) {
                originalURL += "?" + originalQuery;
            }
        }
    }

    @EJB
    private UserService userService;

    public void login() throws IOException {
        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext externalContext = context.getExternalContext();
        HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();

        try {
            request.login(username, password);
            User user = userService.find(username, password);
            externalContext.getSessionMap().put("user", user);
            externalContext.redirect(originalURL);
        } catch (ServletException e) {
            // Handle unknown username/password in request.login().
            context.addMessage(null, new FacesMessage("Unknown login"));
        }
    }

    public void logout() throws IOException {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        externalContext.invalidateSession();
        externalContext.redirect(externalContext.getRequestContextPath() + "/login.xhtml");
    }

    // Getters/setters for username and password.}
This way the User is accessible in JSF EL by #{user}.



Link from the Java Tutorial on Using Programmatic Security with Web Applications: java.sun.com/javaee/6/docs/tutorial/doc/gjiie.html (using Servlets): On a servlet class you can use: @WebServlet(name="testServlet", urlPatterns={"/ testServlet "}) @ServletSecurity(@HttpConstraint(rolesAllowed = {"testUser", "admin”})) And on a per method level: @ServletSecurity(httpMethodConstraints={ @HttpMethodConstraint("GET"), @HttpMethodConstraint(value="POST", rolesAllowed={"testUser"})})

2 comments:

  1. Thanks for sharing this informative content , Great work
    Leanpitch provides online training in Product prototyping during this lockdown period everyone can use it wisely.
    icp-cat training

    ReplyDelete
  2. I really thank you for the valuable info on this great subject and look forward to more great posts. Thanks a lot for enjoying this beauty article with me. I am appreciating it very much! Looking forward to another great article. Good luck to the author! All the best! Hide My WP

    ReplyDelete

Java 9 and Java11 and Java17, Java 21 Features

 Java 9 and Java11 and Java17 features along with explanation and examples in realtime scenarios Here's a detailed breakdown of Java 9, ...