I am not new to this forum, but it's the first time I am posting. It's a great community indeed, and I really hope to find a place to stick to.
I'm learning Java myself and currently working on developing a "test" application, using MySQL DB + Struts2 + hibernate. I won't post much code, but I have some questions regarding the application architecture and logic.
So far, the "Registration" works, as in, the data gets inserted in the DB.
- How can I implement a way that checks if the username already exists before inserting the username/password in the database? I already restricted the db design to not allow multiple entry for "username" (set it to Unique) but how to do this programatically?
- How can I implement the "actual" log in (authentication) ?
What I tried: query the db and return the password associated with the username I got from the user input. But then, where to store it? in what object shall I store the retrieved password and how to compare it with the password that the user typed in? I'm not using any encryptions by the way.
The queries to insert data into the db and read data from the db work fine, but I don't know how to manipulate the data efficiently; as in, I am retrieving matching password from the db (I can see it on console) but I don't know how to compare it then prompt to the user "login successful" or "login failed".
Any suggestions will be greatly appreciated.
Thank you all in advance.
You have to separate your concerns. One way of doing this is to structure your project as follows:
Platform contains things like:
3. Data Access
Application contains stuff like (for a web application):
1. Servlet/JSP/Struts/Spring MVC, etc. code
2. HTML, JSP, CSS
Application depends on platform. In platform, Domain holds representations of objects you are working with. Stuff like Person, User, Address, etc. For your purposes, you probably want to have a User class with two fields - username and password. Services manipulate domain objects. At a high level you might have a class called UserService, with several methods like "User saveUser(String username, String password);", "User getUser(String username, String password)", etc. The data access layer provides the service with the way to actually interact with the database (I usually roll it into the service layer so it is not visible to the outside). So, you might have a class like UserDao which would contain methods like "User saveUser(String username, String password);", "User getUser(String username, String password)", etc. In your simple example, the service methods simply call (pass through to) the dao methods.
At the application level, you will have to implement a way to display and process a form to login or register. I'm a Spring MVC guy, don't really use Struts, but I believe the process is similar. You will have a Controller to process the requests, you will have a command object (a form backing object), which will hold the data supplied by the user in the form, and you will have some way of displaying the actual page (something that will generate the html) - a JSP page perhaps. The controller needs basically two methods, "display" and "process form". The first will just render the form to the user. The second will be invoked after the form is submitted. The command object needs to be bound to the form and returned after form processing, and the frameworks handle it in various ways (Spring MVC makes it an input parameter to the "process" method). If you use a simple servlet, you can retrieve the form values from the request as parameters.
Once you have the data, you call the UserService, get the user, authenticate, save the user, etc. whatever you need to do.
Thank you very much for your input; It cleared some of my misunderstandings, but there are some things I still don't get.
I have built my application on the model as you suggested; I have a UserDetails class with getters/setters for user name, first name, last name, password and such. I have a Manager class, which does the adding/retrieving of data into/from the database; I RegistrationAction and LoginAction classes, which deal with the fields filled in by the user (check the form fields not to be null or blank).
Now I have 2 concerns:
1 - How am I supposed to prevent multiple user registration with same user name? I already restricted the database by design to have "unique" values in the username column, but is there a way to do it "programatically"? what I can think of is pretty much "brute": retrieve all usernames from db in a list, iterate and compare with what the user typed in; if no duplicate found, insert the data in the db. But how will this work once there are 100, 150.000, 2.000.000 users in the db? is there another way?
2 - How do I actually handle the Login validation? in my approach: I take the user typed in username (login form) and pass it as a query parameter, so that I retrieve the password matching that user from the db; if the return is null, then user needs to register. But I still have a problem: the password is returned in a list (object?) so, do I have to cast it? moreover, where do I store it? because I need to compare what I retrieved from the db, to what the user typed in the field at the login page. I can't properly wrap my head around this.
If someone could give me some hints and point me in the right direction, I would be very much grateful.
Going to add two snippets.
1 - The getPassword method of the Manager class;
What am I missing about properly manipulating "result" in line 08? secondly, the getUserName in line 09 sometimes returns "null" (I have some sys-outs in my code, that I didn't include here).
2 - The LoginAction; I know that the comparisons are wrong and stuff is missing, but my main concern is extracting good data from the db.
The database constraint is definitely the right thing to do, but you have the correct sentiment that you should also have your application ensure you do not try to violate the constraint by trying to insert the same user name twice. You do not have to iterate, just query the database for the UserDetails object with the given username before you attempt to save it. If the query result is null, you know you do not have a duplicate. Otherwise, you know you already have a UserDetails object with that username saved, and then you have a choice of either exiting with some error, or perhaps updating the object (if someone wants to change the password perhaps).
About your code snippet, line 08 currently retrieves a List of Strings, but since you have a unique constraint on username, it should always be a list of size 0 or 1. If it is empty, then you don't have that username in the database, if it is size 1, than the password for that username can be obtained by calling " String password = result.get(0);".
Also, it might be easier for the future to just retrieve a list of the whole UserDetails objects, like this:
If you do that, the password in a result of size 1 can be retrieved by calling "String password = result.get(0).getPassword();"
In line 9, you are passing the wrong thing for the query parameter. It should be more like this:
I am not sure where the object "user" came from - it's not defined in the scope of the snippet you provided. In any case, if you have a method which takes the userName as an argument, that's what should be set as the query parameter.
The LoginAction I assume is what takes the input from the user - it holds the username and password as provided by the user (or it has some command object do it). All it has to do is pass that input to the back-end UserService for validation. UserService takes username and password as input, looks up the UserDetails object in the database (via the query we discussed in Answer 1), if null - return error, if not null - compare password, if not matching - error, if matching - success.