Solved

Authenticator: username/password not cached

Posted on 2004-08-22
19
829 Views
Last Modified: 2013-11-24
Hi Experts,

I have a login page where the user enters their username/password.  When they submit the info, it directs them to my servlet called verfiy. (see verify code below).  Verify uses MyAuthenticator to automatically enter the username/password to access protected content after checking my database to make sure the user has an active account.  If they have an active account, then I want verify to pass the "secret" username/password that only I know to the server for authentication.  Once authenticated, then they have access to all the protected content for that site (in this case in the onjava folder).

Problem:  When a user logs in the authenticator authenticates just fine and the user is redirected to the first page of the protected content.  This first page displays with missing images.. if you look at the properites of the missing images in the browser, the location is from verify.picture.jpg instead of onjava.picture.jpg.  (onjava has the protected content)  So it looks like the entire stream was read into memory with verify url even though I specified the onjava url. Then, if the user clicks on any of the other protected content, it is asking for the password again (meaning they were never really authenticated in the first place).   Once I enter the password, then it doesn't ask me again.  So it's like it's authenticated only half way? (half way meaning I can all the info on the protected page, except for images)  Another tidbit:  the resulting page has the following in the url address bar in the browser that may give you a clue:  

http://14.44.135.256:8080/weblocker/verify?username=sillyputty2&password=america  

instead of the new url that I had requested:

http://14.44.135.256:8080/onjava/maingallery.htm

How do I get the authentication passed to the server and stored in the browser until the window is closed?

package weblocker;

import java.io.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import java.text.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;


public class verify extends HttpServlet {
    MyAuthenticator myAuth;
    public verify() {
        myAuth = new MyAuthenticator("cxxxcxxx","bananabread");
        Authenticator.setDefault (myAuth);
    }
   
  public void doGet(HttpServletRequest request,
              HttpServletResponse response)
      throws ServletException, IOException {

      
     String strUsername = request.getParameter("username");
     String strPassword = request.getParameter("password");
     

    boolean isValidUser;
   
   


//Check if Valid User
    try
    {
     isValidUser = CheckLogin(strUsername, strPassword);
    }
    catch (Exception e)
    {
     throw new ServletException(e);
    }

    //If they are a valid user...
    if (isValidUser)
    {
        HttpSession session = request.getSession();
        session.setAttribute("IsLoggedIn", "true");

         //Redirect based on role.
         if (strUsername.equalsIgnoreCase("admin") == true)
         {
          session.setAttribute("IsAdmin", "true");
          response.sendRedirect("http://www.cnn.com");
         }
         else
         {
          session.setAttribute("IsAdmin", "false");
           
    // Access the page
    try {
        // Create a URL for the desired page
       
        URL url = new URL("http://14.44.135.256:8080/onjava/maingallery.htm");
       
        // Read all the text returned by the server
        BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
        String str;
       
      response.setContentType("text/html");
        while ((str = in.readLine()) != null) {
      
      PrintWriter out = response.getWriter();
            // str is one line of text; readLine() strips the newline character(s)
             out.println(str);
        }
        in.close();
    } catch (MalformedURLException e) {
   
   } catch (IOException e) {
   } catch (Exception e) {
     }
         
         }
    }
    else //they failed login
    {
     response.sendRedirect("http://www.kissmyass.com");
    }
}

    //--------------------------------------------------------------
    // CheckLogin - Validates login credentials against a database
    //--------------------------------------------------------------
     private boolean CheckLogin(String strUsername, String strPassword)
             throws ClassNotFoundException, SQLException
    {
        boolean result = false;
       
        //Load SQL Server driver
        Class.forName("com.mysql.jdbc.Driver");

        //Create the Connection and Statement
        String url = "jdbc:mysql://14.44.135.256:3306/Javaservlets";
        Connection Conn = DriverManager.getConnection(url, "sqlserver", "sqlserver");


        //Run query
        String sql = "select * from tblUsers where username = '" + strUsername + "' and password='" + strPassword + "'";
        java.sql.Statement stmt = Conn.createStatement();

        java.sql.ResultSet rs = stmt.executeQuery(sql);

        //Check if a record was found
        if (rs.next())
        {
            //rs.next();
            String submitdate = rs.getString(3);
            DateFormat df = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            try
            {
                java.util.Date subdate = df.parse(submitdate);
                java.util.Date now = new java.util.Date();
                long oneday = 1000 * 60 * 60 * 24;
                if ((now.getTime() - subdate.getTime()) < (oneday * 32))
                {
                    result = true;
                }

            }
            catch (ParseException e)
            {
                System.out.println("Unable to parse " + submitdate);
            }

        }
       
        return (result);
        //stmt.close();
        //Conn.close();
    }

public class MyAuthenticator extends Authenticator {
        // This method is called when a password-protected URL is accessed
        String uname;
        String pass;

       public MyAuthenticator(String uname, String pass) {
            super();
            this.uname = uname;
            this.pass = pass;
        }
       
        public String getUname() {
            return uname;
        }
       
        public void setUname(String uname) {
            this.uname = uname;
        }
             
        public String getPass() {
            return pass;
        }
       
        public void setPass(String pass) {
            this.pass = pass;
        }
       
       protected PasswordAuthentication getPasswordAuthentication() {
          String user = getUname();
          String pw = getPass();
          return (new PasswordAuthentication (user, pw.toCharArray()));
       }
    }

    }


0
Comment
Question by:cxxxcxxx
  • 8
  • 7
  • 4
19 Comments
 
LVL 35

Expert Comment

by:girionis
ID: 11864499
Why do you do manual authentication and do not let the container authenticate for you? It will be easier and clearer (less code is required).

Take a look at the following link:

http://java.sun.com/webservices/docs/1.0/tutorial/doc/WebAppSecurity4.html

The best thing you can do though is to look at your server's documentaiton and see how you can do it although the main principle is there (using the web.xml). What server are you using?
0
 
LVL 35

Expert Comment

by:girionis
ID: 11864506
0
 

Author Comment

by:cxxxcxxx
ID: 11864602
I am using Apache Tomcat server...  I will take a look at those links and get back with you.  Thanks girionis ;-)
0
 

Author Comment

by:cxxxcxxx
ID: 11864703
I am using a web.xml located in /onjava/WEB-INF folder (see web.xml below).  All the protected content is located in /onjava.  If I go directly to a url in that folder, I am prompted for username and password.  Once I enter the username and password, the protected content is displayed and I'm not asked again.  

Here's the scenario why I have so much code and it's not so easy:  I have a website setup so that users can create their own username and password once they've paid for a subscription.  The username and password that the user selects is entered into a MySQL database.  Then I have a separate login site where they enter the newly created id and password.  When they click submit, the verify servlet listed above goes and checks the database to see if they are active.  If they are active then I want to allow access to the /onjava resources.  There is no way that I can automatically edit the web.xml file and add when a new user subscribes and then remove them after the subscription period ends.  Therefore, I thought I would use code to verify the user, then tell the verify servlet to pass the username and password that I have defined in tomcat-users.xml for the role of "onjavauser".

So basically, if I key in the username and password that the web.xml wants then it lets me in.  But how do I let the verify servlet say "OK, I've checked the database and the user is valid and active.  Now I'm going to forward them to a page in the /onjava folder and I'm going to submit the username and password and authenticate them to /onjava so that they have access to all the /onjava resources."

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

<security-constraint>
    <web-resource-collection>
        <web-resource-name>OnJava Application</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>onjavauser</role-name>
    </auth-constraint>
</security-constraint>

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>OnJava Application</realm-name>
</login-config>


</web-app>
0
 
LVL 3

Expert Comment

by:Gunt
ID: 11864783
You can tell the Servlet to authenticate against a database. In tomcat, you need to define a table users and a table roles (or views).

Also, there are ways in which you can put your authentication before the container to check for additional data (you check, then forward to j_security_check with the parameters to let the container do the login).
I've done this to add the blocked account after X bad tries functionality.

Search through Tomcat documentation and you'll find this explained in the Authentication/Security part.
0
 
LVL 35

Expert Comment

by:girionis
ID: 11864804
Yes Gunt is right you can use a database. Look for the database realm with Tomcat:

http://jakarta.apache.org/tomcat/tomcat-5.0-doc/config/realm.html
http://www.onjava.com/pub/a/onjava/2001/07/24/tomcat.html
0
 

Author Comment

by:cxxxcxxx
ID: 11865434
Ok.  I've gone in and changed from a memoryrealm to a jdbcrealm as described in the article of the last link that girionis sent.  This went well enough, but the process still isn't working.

If I go to access something directly in /onjava (side stepping verify servlet) I can log in with my username and password.  I tried adding one of my regular users from the users table to the roles table and gave them onjavuser role, and then tried the same url.  That user cannot access the url...  just keeps asking for the password over and over until it times out.  Makes me think that my password may still be working from the old setup or something.  Maybe I need to do something with the /onjava web.xml file?  If so, I have no clue what it could be...

And I still have the same original problem... how do I verify the username/password in the database, then send them to the protected content?  If adding the users to the userroles table and then giving them onjavauser would work, then it would be redundant to place the users in both tables, but it would work... but as we see above, it does not.  But let's say we were able to get that going, then what code do I put behind the Submit button on the login page to query the JDBCRealm database, authenticate, and then refer them to the protected content?

P.S.  Those were good documents to read, and I had already read most of them.  That's how I was able to protect with a memoryrealm in the first place.  Perhaps there's something else we can tweak to make it work in the real world.
0
 
LVL 35

Expert Comment

by:girionis
ID: 11865500
Why do you need the verify servlet? The container hsould redirect you automatically to the page you are trying to access (or to the default page, usually index.html/jsp).
0
 
LVL 3

Expert Comment

by:Gunt
ID: 11865509
Remove the memory realm, leave JDBC realm alone.
Then, you define your users and roles in the tables.

When someone goes to a protected URL (described in web.xml), Tomcat prompts them for user/password. Then, it checks it against the database, and if they're OK, are automatically forwarded to the URL they asked for.

You can post your web.xml files and tomcat config files for further help.
0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 

Author Comment

by:cxxxcxxx
ID: 11865593
OK guys... we're getting close here.  I'm finally understanding that I need to give up my custom login page and just direct them to the URL and let the container do it for me.

That said, help me with making this happen.  Girionis, you're right, I don't HAVE to have the verify servlet.  Gunt, here's my web.xml file... I think it's set so that it's looking at BASIC (which I think is tomcat-users.xml) instead of the jdbcrealm database.  I think the <login-config> part of it is wrong and needs to tell it to point to jdbcrealm database.  Where to from here?

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

<security-constraint>
    <web-resource-collection>
        <web-resource-name>OnJava Application</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>onjavauser</role-name>
    </auth-constraint>
</security-constraint>

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>OnJava Application</realm-name>
</login-config>


</web-app>

0
 
LVL 3

Expert Comment

by:Gunt
ID: 11865857
No, BASIC does not stand for the realm used, it's the auth method.
BASIC means that Tomcat will send the pop up for password input.
If you put FORM there, you can define a jsp page login.jsp. Instead of prompting the user with the ugly popup, Tomcat will redirect the user to that page.
The page, made by you (and there you got your custom login page), must comply to certain rules:
Must have a form, that posts to the action "j_security_check". And must have two fields: j_username and j_password.
When the user submits that page, j_security_check action makes the verification, and if it's ok redirects the user to the page he requested. If it's wrong, it redirects the user to a custom login failed page you can also make yourself.

That's the login schema I've used some time ago. In addition, since I had two usernames (actually, username was composed from document type and number), I couldn't use this auth method as is, so instead of submiting to j_security_check, I submited to a custom action I made. That action, did the preprosessing (joining two parts of username and verifying if account was blocked), and then it reforwarded the request to the real j_security_check.

With this, you have total control of the login process, in a secure and pretty standard way.

For details on how to define the login page and the exact field names (I believe they are as I told you, but my memory can fail), refer again to the links with Tomcat documentation.
If you are having further trouble, let me know and I'll check my old project.

Now, for the realm name:
The realm name is OnJava Application, that's how you defined for that security constraint. The OnJava Application realm, must be defined in your server.xml (if I recall correctly), and there you must say that OnJava Application realm uses JDBC realm.

Try that, if doesn't work, I will get my old project and we'll make it work.
0
 

Author Comment

by:cxxxcxxx
ID: 11865949
Fantastic!  Let's go for realm name Onjava Application.  How do I properly define in server.xml to say that OnJava Application realm uses JDBC realm?  I think this will be the easiest way to go... even though the default popup is not pretty, I'd rather use that and get this project on the road. . . . .  u
0
 
LVL 3

Assisted Solution

by:Gunt
Gunt earned 250 total points
ID: 11866152
There's a really good step by step explaination of how to do it here: http://www.linux-sxs.org/internet_serving/c619.html#JDBC_REALMS

Basically, comment your current memory realm, uncomment the jdbc realm, and configure it for your database (URL, user, password, tables, etc).

The example there uses FORM authentication, but for BASIC authentication it's even easier, you don't have to create login and error pages.
0
 
LVL 35

Accepted Solution

by:
girionis earned 250 total points
ID: 11867579
As for your web.xml that's how it should look like:

The login part:

<login-config>
            <auth-method>FORM</auth-method>
            <form-login-config>
                  <form-login-page><your login page here></form-login-page>
                  <form-error-page><your error page here (in case user can't log in)></form-error-page>
            </form-login-config>
      </login-config>

The security constraints:

<security-constraint>
            <web-resource-collection>
                  <web-resource-name>SecurityConstraints</web-resource-name>
                  <description>We restrict access to the some files</description>
                  <url-pattern>file1.htm</url-pattern>
                  <url-pattern>file2.htm</url-pattern>
                  <http-method>POST</http-method>
                  <http-method>GET</http-method>
            </web-resource-collection>
            <auth-constraint>
                  <description>Only let the admin see the pages</description>
                  <role-name>AdminRole</role-name>
            </auth-constraint>
            <user-data-constraint>
                  <description>SSL not required</description>
                  <transport-guarantee>NONE</transport-guarantee>
            </user-data-constraint>
      </security-constraint>

And the security role:

<security-role>
          <description>The Administrator role</description>
          <role-name>AdminRole</role-name>
       </security-role>
0
 
LVL 35

Expert Comment

by:girionis
ID: 11867582
You should also define the roles somewhere. I am using WLS and I define them in my weblogic.xml, for Tomcat they should be somewhere else.
0
 

Author Comment

by:cxxxcxxx
ID: 11880328
OK.. I'm using JDBCRealm with FORM auth-method.  I've got everything set up (with a little heartburn) and it works fine if I try to access a page url in the /onjava folder directly.  It sends me to my custom login page and then let's me in after I enter a valid password.

Only problem left open:  Now that I'm using FORM auth-method, my redirect servlet is not able to get past the custom login page.  I know that's cryptic.  Let me explain.  Once they've entered their credit card info, the bank posts back to my redirect servlet.  The redirect servlet looks at the info from the bank and if declined sends them to a declined page, and if credit card approved then sends them to a page in /onjava to let them setup their username and password.  Now, when redirect servlet sends them to that page in /onjava the user is greeted with the custom login page as it is protected (and it needs to be protected otherwise any joeblow could access that page at will and create a new login for himself).  So is there anyway I can let redirect servlet bypass the FORM auth-method security constraint?  Or a way that redirect servlet can tell the server that he knows the password and allow redirect servlet to display the page?

Thanks for all your help... I'm getting very close!  
0
 
LVL 35

Expert Comment

by:girionis
ID: 11880470
Two things I can think of

1) Use the "referer" header to see who refered the user to that page. If the referrer matches the url of the servlet then bypass the authentication (or send the user to another page):

String referer = request.getHeader("Referer");

2) Upon successful completion of cc details create a temporary username/password and send it to the user's email. Then the user can login by using this username/password and chaneg the details. If you have the option to chosse his/her username and password that would be even better.
0
 

Author Comment

by:cxxxcxxx
ID: 11881169
Fantastic!  What I did was take the signup pages and move them to a whole new folder and used a WEB-INF that uses BASIC authentication so that the redirect servlet could access just like it was doing before using it's authenticator :-)  I'm going to split the points between girionis and Gunt as both of them gave me information that I needed to get this to work.  What a project!

Just a note in case somone else reads this problem/solution... some of the links above have documentation that is not necessarily incorrect, but has typos, so read very carefully and be sure to fit the example code to what you have in your databases.
0
 
LVL 35

Expert Comment

by:girionis
ID: 11881299
Thank you for accepting. Glad we were of help :)
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
array11 challenge 16 52
mapBully challenge 6 92
listing all functions in JavaScript 19 114
JList custom Cell Renderer refresh 15 43
I had a project requirement for a displaying a user workbench .This workbench would consist multiple data grids .In each grid the user will be able to see a large number of data. These data grids should allow the user to 1. Sort 2. Export the …
In this post we will learn how to connect and configure Android Device (Smartphone etc.) with Android Studio. After that we will run a simple Hello World Program.
Video by: Michael
Viewers learn about how to reduce the potential repetitiveness of coding in main by developing methods to perform specific tasks for their program. Additionally, objects are introduced for the purpose of learning how to call methods in Java. Define …
Viewers learn how to read error messages and identify possible mistakes that could cause hours of frustration. Coding is as much about debugging your code as it is about writing it. Define Error Message: Line Numbers: Type of Error: Break Down…

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now