Lập Trình Web Với Java: Login (Phần 2)
Ở phần 1 mình đã hướng dẫn các bạn cài đặt cũng như cấu hình các môi trường để thực hiện lập trình một web app với Java. Trong phần này chúng ta cùng nhau thực hiện chức năng Login. Ở bài viết này mình sẽ sử dụng Expression Language và JSTL để xử lý phần frontend, toàn bộ phần code sẽ được thực hiện theo mô hình MVC2.
Xem thêm: Luồng Xử Lý Trong MVC Như Thế Nào?
Lý thuyết:
Expression Language (EL):
Cú pháp:
${tên biến}
Trong EL chúng ta cũng có thể sử dụng các toán tử tương tự Java ngoài ra còn một số toán tử thay thế như
Toán tử EL | Toán tử Java |
div | / |
mod | % |
eq | == |
ne | != |
lt | < |
gt | > |
le | <= |
ge | >= |
and | && |
or | || |
not | ! |
EL còn sử dụng toán tử empty
để kiểm tra xem biến có trống hay không
JSP Standard Tag Library (JSTL): là thư viện thẻ chuẩn cung cấp các thẻ để kiểm soát hành vi trang, lặp đi lặp lại và các lệnh điều khiển, các thẻ quốc tế hóa, và các thẻ SQL.
Khai báo:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
Thẻ | Mô tả |
---|---|
<c:out> | Để viết một cái gì đó trong trang JSP, bạn có thể sử dụng EL cũng có thẻ này |
<c:import> | Giống với <jsp:include> hoặc chỉ thị include (include directive) |
<c:redirect> | Chuyển hướng (redirect) yêu cầu tới một nguồn dữ liệu khác. |
<c:set> | Sét đặt giá trị biến cho bởi phạm vi. |
<c:remove> | Loại bỏ biến ra khỏi phạm vi đã cho. |
<c:catch> | Bắt ngoại lệ và gói vào một đối tượng. |
<c:if> | Điều kiện logic đơn giản, sử dụng với EL và bạn có thể sử dụng nó để xử lý các trường hợp ngoại lệ từ <c:catch> |
<c:choose> | Tag có điều kiện đơn giản mà thiết lập một bối cảnh cho các trường hợp loại trừ lẫn nhau có điều kiện, đánh dấu bằng <c:when> và <c:otherwise> |
<c:when> | Thẻ con của <c:choose>, khi một điều kiện tại when là đúng. |
<c:otherwise> | Thẻ con của <c:choose>, khi tất cả các điều kiện <c:when> là sai. |
<c:forEach> | Dùng để lặp trên một tập hợp. |
<c:forTokens> | dùng để lặp trên chuỗi (tokens) được phân cách bởi một dấu phân cách. |
<c:param> | Được sử dụng với <c:import> để truyền các tham số. |
<c:url> | Để tạo một URL với các tham số tùy chọn (optional query string parameters). |
Thực hành
Trước tiên ta tạo 1 database ở SQLSERVER
USE master
GO
CREATE DATABASE DemoWebapp
USE DemoWebapp
go
CREATE TABLE tblUsers(
userid VARCHAR(50) NOT NULL PRIMARY KEY,
fullname VARCHAR(50) NOT NULL,
password VARCHAR(64) NOT NULL,
roleid int NOT NULL
)
Quay lại project chúng ta đã tạo hôm trước.
View
Chúng ta sẽ bắt đầu từ View nhé, tạo 1 file index.html vì đây là file sẽ được load đầu tiên khi mở web.
<html>
<head>
<title>Login</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<form action="MainController">
<input type="text" name="txtUserID" placeholder="Username"/>
<input type="password" name="txtPassword" placeholder="Password"/>
<input type="submit" name="btnAction" value="Login"/>
<input type="reset" value="Reset"/>
</form>
</body>
</html>
Chúng ta khoan tạo các Controller, cứ chạy code lên và nhập vài thứ bất kỳ và nhìn lên thanh địa chỉ sẽ thấy thế này:
http://localhost:8080/DemoJavaWeb/MainController?txtUserID=abc&txtPassword=xyz&btnAction=Login
Phần id và password lộ liễu thế, vì vậy ta sẽ đổi method của form thành POST để giấu chúng đi.
<html>
<head>
<title>Login</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<form action="MainController" method="POST">
<input type="text" name="txtUserID" placeholder="Username"/>
<input type="password" name="txtPassword" placeholder="Password"/>
<input type="submit" name="btnAction" value="Login"/>
<input type="reset" value="Reset"/>
</form>
</body>
</html>
Giải thích:
- Khi bấm Login, các Parameter (txtUserID, txtPassword) sẽ được form truyền vào "MainController" với btnAction là Login (có nhiều cách để gọi cái MainController này như MainServlet, DispatcherServlet, DispatcherController nhưng mình quen gọi thế :P ).
- Thông qua method POST, các Parameter sẽ được ẩn đi, người dùng không thể trực tiếp nhìn thấy chúng trên thanh địa chỉ, tăng được 1 tý bảo mật.
Controller
Vừa rồi chúng ta vừa tạo 1 Form để truyền vào MainController, vậy thì tạo MainController để đưa đẩy chúng nó về đúng nơi nào. MainController chỉ là nơi trung gian để truyền dữ liệu thôi nhé, nhớ phân chia Package để dễ quản lý, vì sẽ có rất nhiều class tham gia vào đấy.
New > Servlet... > đặt tên class, package > Nhớ tích vào ô "Add informmation to ....".
Sau khi tạo xong Servlet thì cùng lúc đó file web.xml xuất hiện trong thư mục WEB-INF. Cùng xem trong đó có gì.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<servlet>
<servlet-name>MainController</servlet-name>
<servlet-class>com.controller.MainController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MainController</servlet-name>
<url-pattern>/MainController</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>
Ở đây chúng ta thấy 2 tag quan trọng đó là <servlet>, <servlet-mapping> nếu như ban nãy bạn không tích vào ô "Add informmation to ...." thì bạn "sẽ được" sang đây gõ tay hết đống này nhé.
MainController
package com.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Lenovo
*/
public class MainController extends HttpServlet {
private static final String LOGIN = "LoginController";
private static final String ERROR = "invalid.html";
/**
* 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");
String url = ERROR;
try {
String action = request.getParameter("btnAction");
if (action.equals("Login")) {
url = LOGIN;
}
} catch (Exception e) {
} finally {
request.getRequestDispatcher(url).forward(request, response);
}
}
}
Đối với các Servlet khác cũng thế, chúng ta chỉ cần sửa code trong hàm processRequest thôi.
Giải thích:
- Các hằng "LOGIN" , "ERROR" là nơi bạn muốn chuyển đến sau khi thực hiện xong code (có thể là Controller khác hoặc html, jsp...)
- Trong hàm processRequest, chúng ta lấy Parameter của button vừa được click, so sánh giá trị để quyết định chuyển các Parameter còn lại đi đâu
- Sau khi biết được nơi cần đến chúng ta sẽ dùng request để fordward chúng đi thay vì dùng respond.sendRedirect(url) vì các Parameter khi gặp respone sẽ biến mất.
LoginController
Phần này thì hơi rối hơn vì có thêm các class khác tham gia vào, vì thế chúng ta code tới đâu tạo class tới đó.
package com.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
*
* @author Lenovo
*/
public class LoginController extends HttpServlet {
public static final String SUCCESS = "default.jsp";
public static final String ERROR = "invalid.html";
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
HttpSession session = request.getSession(true);
String url = ERROR;
try {
String userID = request.getParameter("txtUserID");
String password = request.getParameter("txtPassword");
UserDAO dao = new UserDAO();
UserDTO user = dao.checkLogin(userID, password);
if (user != null) {
session.setAttribute("USER", user);
url = SUCCESS;
}
} catch (Exception e) {
log("error at login servlet: " + e.toString());
} finally {
response.sendRedirect(url);
}
}
}
Ở đây thay vì forward() thì mình lại dùng respond.sendRedirect() vì khi login thành công rồi các Parameter Username hay Password cũng chả cần thiết nữa, không nhất thiết phải giữ lại làm gì để khỏi phiền phức.
Model
UserDTO
Chúng ta reference tất cả những gì trong table User của database vào.
package com.dtos;
public class UserDTO {
private String id, fullName, password;
private int role;
public UserDTO() {
}
public UserDTO(String id, String fullName, String password, int role) {
this.id = id;
this.fullName = fullName;
this.password = password;
this.role = role;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
}
DBUtil
Thay vì mỗi Controller chúng ta viết 1 hàm để kết nối database thì chúng ta sẽ làm 1 class luôn để dùng nhiều lần cho đỡ lằng nhằng.
Nhưng muốn kết nối với database, chúng ta cần có hàm và thư viện để kết nối đã. Import thư viện JDBC đã đề cập ở chap 1.
package com.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import javax.naming.NamingException;
public class DBUtil {
public static Connection getConnection() throws ClassNotFoundException, SQLException, NamingException {
Connection conn = null;
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String url = "jdbc:sqlserver://127.0.0.1:1433;databaseName=DemoWebapp;";
conn = DriverManager.getConnection(url, "sa", "123");
return conn;
}
}
UserDAO
package com.daos;
import com.dtos.UserDTO;
import com.utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDAO {
private Connection conn = null;
private PreparedStatement stm = null;
private ResultSet rs = null;
private void closeConnection() throws Exception {
if (rs != null) {
rs.close();
}
if (stm != null) {
stm.close();
}
if (conn != null) {
conn.close();
}
}
public UserDTO checkLogin(String ID, String password) throws SQLException, Exception {
UserDTO result = null;
try {
conn = DBUtil.getConnection();
if (conn != null) {
String sql = "SELECT userid , fullname , password , roleid"
+ " FROM tblUsers WHERE userid = ? AND password = ?";
stm = conn.prepareStatement(sql);
stm.setString(1, ID);
stm.setString(2, password);
rs = stm.executeQuery();
if (rs.next()) {
result = new UserDTO(rs.getString("userid"),
rs.getString("fullname"),
"***", rs.getInt("roleid"));
}
}
} catch (Exception e) {
} finally {
closeConnection();
}
return result;
}
}
Sau đó quay về view, tiếp tục viết phần code cho trang default.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${sessionScope.USER.fullName}</title>
</head>
<body>
<h1>Hello ${sessionScope.USER.fullName}</h1>
</body>
</html>
Thế là hoàn tất phần code, bây giờ mình insert cái gì đó vào database để thử chức năng nào.
Deploy
Giờ thì tiến hành build và deploy project thôi (nếu bạn chạy Netbean bằng quyền admin, chỉ cần bấm F6 thì mọi thứ sẽ được tự động).
Sang tab Services>Servers>Apache Tomcat or TomEE click chuột phải bấm Start.
Quay về project click chuột phải vào project và bấm Deploy.
Sau khi deploy thành công, chúng ta chạy project lên và tận hưởng kết quả.
Kết quả:
Thành công (đúng username, password)
Thất bại: (ở đây nó ra page này vì mình chưa tạo file invalid.html)
Tạm kết
Qua bài viết này chúng ta đã hiện thực được chức năng login, và hiện tên người dùng bên phía JSP thông qua Session. Cơ mà chỉ có đăng nhập mà không đăng ký hay đăng xuất thì cũng kỳ lạ nhỉ??? Các bạn hãy hiện thực chức năng đăng ký dựa theo bài trên nhé :D.
Kỳ sau mình sẽ nói sơ qua chức năng đăng ký, đăng xuất và hiện thực các chức năng thêm, xóa, sửa, tìm kiếm (CRUD) một cái gì đó trên database thông qua JSTL.
Source Code tại đây.