user3673449
user3673449

Reputation: 357

Authentication with JDBC Realm

In my Java EE application I implemented autentication/autorization via a JDBC Realm (first time I apply this solution).

Following code does not have any problem on successful login, the problem is when I type wrong credentials: it logs in anyway and even though it catches a ServletException (Login failed), these lines of code never get executed (tried it in debug mode):

request.setAttribute("msg", "Error in login");

nextPage = "/errorPage.jsp";

Another strange thing: no matter what I pass to

getServletContext().getRequestDispatcher(nextPage).forward(request, response);

as nextPage (I tried to put statically "/errorPage.jsp"), it always forwards to index.jsp.

Login.jsp

 @WebServlet("/Login")
    public class Login extends HttpServlet {
        private static final long serialVersionUID = 1L;

/**
 * @see HttpServlet#HttpServlet()
 */
public Login() {
    super();
    // TODO Auto-generated constructor stub
}

/**
 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 */
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
}

/**
 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
 */
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    String username = request.getParameter("username").trim();
    String password = request.getParameter("password").trim();
    String nextPage = "/index.jsp";

    try {
        request.login(username, password);
    } 
    catch (ServletException ex) { 
        request.setAttribute("msg", "Error in login");
        nextPage = "/errorPage.jsp";
    }

        getServletContext().getRequestDispatcher(nextPage).forward(request, response);
    }
}

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
    request.logout();
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Welcome</title>
</head>
<body>
    <h1>Hi! You need to login.</h1>
    <form method="POST" action="/MyApp/Login">
        Usuario: <input type="text" name="username" /> Password: <input
            type="password" name="password" /> <input type="submit"
            value="Send" />
    </form>
</body>
</html>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <display-name>MyApp</display-name>
    <welcome-file-list>
        <welcome-file>login.jsp</welcome-file>
    </welcome-file-list>
    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>jdbcRealm</realm-name>
        <form-login-config>
            <form-login-page>/login.jsp</form-login-page>
            <form-error-page>/errorPage.jsp</form-error-page>
        </form-login-config>
    </login-config>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Admin stuff</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>User stuff</web-resource-name>
            <url-pattern>/user/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-role>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <role-name>user</role-name>
    </security-role>
</web-app>

Before this, I tried the container-managed security solution (with the login's form action calling the j_security_check component).

Login works fine with this (even with wrong credentials), but I got another serious issue I didn't have before: in one of the use cases, a User can see the projects is working on but it shouldn't be able to see other users' project. I implemented this with the following servlet, but the problem is (like the other solution) that it skips some of the instructions (the one that looks for the user in the DB, for example) and it goes in exception, redirecting to the error page.

public class ViewUserProjects extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public ViewUserProjects() {
        super();
        // TODO Auto-generated constructor stub
    }

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        DAO dao = (DAO) getServletContext().getAttribute("bd");

        Principal p = request.getUserPrincipal();
        String username = p.getName();

        try {
            User user = dao.getUserByName(name);
            request.getSession().setAttribute("user", user);

            ArrayList<Project> projects = new ArrayList<Project>();

            tareas = ad.getUserProjects(Integer.parseInt(user.getId()));

            request.setAttribute("projects", projects);

            getServletContext().getRequestDispatcher(
                    "/user/viewProjects.jsp").forward(request,
                            response);
        } catch (Exception ex) {
            request.setAttribute("msg",
                    "Error");
            getServletContext().getRequestDispatcher("/errorPage.jsp").forward(
                    request, response);
        }
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

}

Upvotes: 1

Views: 1938

Answers (2)

nackolysis
nackolysis

Reputation: 217

Where do you check to see if the username and password entered from the form input were correct. Are you authenticating by querying the database or are you authenticating by matching the username and password values set within the code? I believe this will give you a clue. Ensure that username and password coming from the form inputs matches those set within the code. Let me know if you are having any other issues. If this solve your problem, please mark it as correct answer

 response.setContentType("text/html");
    String msg = " ";

    String username = request.getParameter("username");
    String password = request.getParameter("password");
    try {

if (username.equals("nick") && password.equals("nick_password")) {


            nextPage = "HELLO" + username + "! Your login is SUCESSFULL";

        } else {
            nextPage = "HELLO" + username + "!Your login is UNSUCESSFULL";
        }

}// close try

Upvotes: -1

Esteban Rincon
Esteban Rincon

Reputation: 2110

When using a JDBCRealm, its a better practice to use container-managed security for application authentication/authorization instead of handling this from your application code (which you are doing)

So we allow the Server to handle this, this means that using form-based authentication (which you are using) as per the Servlet Specification would be something like this:

First the form:

<form action="j_security_check" method="POST">
    Username:<input type="text" name="j_username" placeholder="Username" />
    Password:<input type="password" name="j_password" placeholder="Password" />
    <input type="submit" value="Log In" />
</form>

Then in our Deployment Descriptor we must add some configuration which you already seem to have but here's another example:

Note I believe you're mising the * <error-page> tag where we use the 403 error which is Forbidden resource

<security-constraint>
    <display-name>securityConstraint1</display-name>
    <web-resource-collection>
        <web-resource-name>resources</web-resource-name>
        <description />
        <url-pattern>/protected/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>appUser</role-name>
        <role-name>appAdmin</role-name>
    </auth-constraint>
</security-constraint>

<security-constraint>
    <display-name>securityConstraint2</display-name>
    <web-resource-collection>
        <web-resource-name>resources</web-resource-name>
        <description />
        <url-pattern>/protected/admni/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>appAdmin</role-name>
    </auth-constraint>
</security-constraint>

<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>appRealm</realm-name>
    <form-login-config>
        <form-login-page>/index.xhtml</form-login-page>
        <form-error-page>/public/forbidden.xhtml</form-error-page>
    </form-login-config>
</login-config>

<security-role>
    <role-name>appUser</role-name>
</security-role>

<security-role>
    <role-name>appAdmin</role-name>
</security-role>   

<error-page>
    <error-code>403</error-code>
    <location>/public/forbidden.xhtml</location>
</error-page>

We cant forget about the Data Protection(you already have):

<user-data-constraint>
   <transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>

So now we need to define the ROLES for the application, this is done in mapping groups defined in the application server so first of all. ¿Which app server are you using?

Here's an example using GlassFish

We need to add a glassfish-web.xml or sun-web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD GlassFish Application Server 3.0 Servlet 3.0//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_3_0-0.dtd">
<sun-web-app error-url="">

    <security-role-mapping>
        <role-name>appUser</role-name>
        <group-name>1</group-name>
    </security-role-mapping>

    <security-role-mapping>
        <role-name>appAdmin</role-name>
        <group-name>2</group-name>
    </security-role-mapping>

  <class-loader delegate="true"/>
  <jsp-config>
    <property name="keepgenerated" value="true">
      <description>Keep a copy of the generated servlet class' java code.</description>
    </property>
  </jsp-config>
</sun-web-app>

So the roles are mapped to actual group names which exist in the real repository. (this is created directly on your app server).

in order for this to work we need to create a TABLE in our DB in order to define the groups of the users.

The Realm here is created in the Server Admin Console.

In GlassFish go to:

Configurations >> Server-Config >> Security >> Realms

And here's an example of the realm config.

JDBCRealm

Upvotes: 2

Related Questions