
Đăng nhập và phân quyền cơ bản trong C# với CSDL SQL Server

Phân quyền là một trong những bước rất quan trọng sau đăng nhập. Trong bài viết này mình sẽ hướng dẫn bạn cách phân quyền hạn dựa theo nhóm quyền, và tùy chỉnh chức năng của nhóm quyền hạn dựa vào mã định nghĩa thao tác.
Thiết kế cơ sở dữ liệu phân quyền
Trong bài viết này mình chỉ hướng dẫn về phân quyền nên mình sẽ làm một csdl nhỏ, chứa các bảng liên quan đến phân quyền như bảng User, bảng quyền hạn, bảng chi tiết quyền hạn và bảng quan hệ với quyền hạn.

Cơ sở dữ liệu phân quyền đơn giản
tbl_user
: bảng lưu người dùng bao gồm các thuộc tính như ID, Name, Username, Password. Bảng không có khóa ngoại.tbl_permision
: bảng chứa nhóm quyền hạn. bao gồm các thuộc tính, ID nhóm quyền hạn, tên nhóm quyền hạn, và mô tả nhóm quyền hạn.tbl_per_relationship
: là bảng lưu mối liên hệ giữa người dùng và nhóm quyền hạn. Mục đích của bảng này không phải là để một người dùng có nhiều nhóm quyền mà để không phải truy vấn lại bảng user chứa thông tin nhạy cảm như username và password. Bạn cũng có thể bỏ qua bảng này và liên hệ trực tiếp giữa bảng user và permision luôn, nhưng mình khuyên bạn nên sử dụng thêm bảng này thì tốt hơn.tbl_permision_detail
: là bảng sẽ chứa những quyền hạn cụ thể dành cho nhóm quyền hạn. thật ra trường này bạn có thể bỏ dòngname_action
thì nó không cần thiết lắm. Dòngcode_action
là để khi lập trình mình định nghĩa một thao tác nhất định trong bằng code này ví dụ quyền sửa thì code nó là EDIT chẳng hạn.
Tạo form đăng nhập
Quyền hạn phải được khởi tạo ngay từ đầu khi người dùng đăng nhập. và đây sẽ là bước duy nhất truy vấn đến bảng User chứa username và password của người dùng.

Tạo form đăng nhập
Bây giờ mình sẽ viết hàm login cơ bản để lấy ID người dùng.
Bước 1: Using System.Data.SqlClient;
Bước 2: Tạo hàm lấy ID:
Trước khi tạo hàm, mình cần tạo biến connection đến database bằng đoạn code sau:
SqlConnection con = new SqlConnection(@"Data Source=TECHMAC\SQLEXPRESS;Initial Catalog=db_QL;Integrated Security=True");
Cách lấy chuỗi connect bạn có thể xem lại bài: Thêm, đọc, sửa, xóa (CRUD) cơ sở dữ liệu SQL Server trong C#
Hàm lấy ID người dùng.
private string getID(string username, string pass)
{
string id = "";
try
{
con.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM tbl_user WHERE user_name ='"+username+"' and pass='"+pass+"'",con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
if(dt != null)
{
foreach (DataRow dr in dt.Rows)
{
id = dr["id_user"].ToString();
}
}
}
catch (Exception)
{
MessageBox.Show("Lỗi xảy ra khi truy vấn dữ liệu hoặc kết nối với server thất bại !");
}
finally
{
con.Close();
}
return id;
}
Code đăng nhập
Trước khi code cho sự kiện click của nút đăng nhập thì mình sẽ tạo ra một biến public để lưu ID người dùng sử dụng cho lần sau.
public static string ID_USER = "";
và đoạn code cụ thể.
private void btn_login_Click(object sender, EventArgs e)
{
ID_USER = getID(txt_username.Text, txt_pass.Text);
if (ID_USER != "")
{
frm_main fmain = new frm_main();
fmain.Show();
this.Hide();
}
else
{
MessageBox.Show("Tài khoảng và mật khẩu không đúng !");
}
}
Với cách làm trên mình không mã hóa mật khẩu và tên đăng nhập, tuy nhiên nếu làm dự án thực tế bạn không nên bỏ qua bước này. Bạn có thể xem bài hướng dẫn mã hóa mật khẩu 1 chiều với MD5 trong C# để biết rõ về cách làm này.
Kiểm tra đăng nhập
Bây giờ mình thêm dữ liệu vào cơ sở dữ liệu một user như sau.
Bạn mở phần code của form frm_main
là thêm đoạn code sau vào hàm form load để kiểm tra xem mình đã làm đúng chưa nhé.
private void frm_main_Load(object sender, EventArgs e)
{
MessageBox.Show("Xin chào User có ID là: " + frm_login.ID_USER);
}
Bây giờ thì run lên và thử đăng nhập thử.

Kết quả sau khi đăng nhập có ID là 1
Phân quyền trong C#
Như vậy là mình đã lấy được ID của người dùng khi đăng nhập. Bây giờ mình sẽ truy vấn để lấy các quyền hạn để phân quyền cho người dùng.
Tương tự bên form login, bên form main bạn cũng phải đưa using System.Data.SqlClient;
vào. Nếu bạn sử dụng class dữ liệu thì những việc này bạn sẽ không cần làm lại. Mình đã có trình bày cách làm đó trong bài Thêm, đọc, sửa, xóa (CRUD) cơ sở dữ liệu SQL Server trong C# bạn tìm hiểu nhé.
Tiếp theo mình sẽ tạo một hàm để truy vấn lấy ID nhóm quyền hạn như sau:
SqlConnection con = new SqlConnection(@"Data Source=TECHMAC\SQLEXPRESS;Initial Catalog=db_QL;Integrated Security=True");
private string id_per (string id_user)
{
string id = "";
try
{
con.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM tbl_per_relationship WHERE id_user_rel ='" + id_user + "'", con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
if (dt != null)
{
foreach (DataRow dr in dt.Rows)
{
if (dr["suspended"].ToString() == "False")
{
id = dr["id_per_rel"].ToString();
}
}
}
}
catch (Exception)
{
MessageBox.Show("Lỗi xảy ra khi truy vấn dữ liệu hoặc kết nối với server thất bại !");
}
finally
{
con.Close();
}
return id;
}
Hàm trên mình có sử dụng thêm một lệnh if để kiểm tra trường suspended
để xem quyền hạn này của người dùng này có bị đình chỉ không, nếu không thì mới lấy ID quyền hạn ra.
Thêm vào một nút button và viết một câu lệnh để kiểm tra kết quả như sau:
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("id của nhóm quyền đó là:"+ id_per(frm_login.ID_USER));
}
Trước khi kiếm tra mình phải thêm dữ liệu vào. Theo csdl của mình mình sẽ thêm vào 2 bảng như sau

Mình sẽ thêm 2 nhóm quyền

Set cho người dùng ID 1 vào nhóm quyền ID 2
Kết quả kiểm tra

Đã lấy được ID nhóm quyền hạn là 2
Lấy chi tiết quyền hạn của nhóm quyền
Sau khi có ID nhóm quyền, đây là lúc mình sẽ lấy chi tiết những quyền được cấp cho nhóm này bằng đoạn code sau:
private List<string> list_per(string id_per)
{
List<string> termsList = new List<string>();
try
{
con.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM tbl_permision_detail WHERE id_per ='" + id_per + "'", con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
if (dt != null)
{
foreach (DataRow dr in dt.Rows)
{
termsList.Add(dr["code_action"].ToString());
}
}
}
catch (Exception)
{
MessageBox.Show("Lỗi xảy ra khi truy vấn dữ liệu hoặc kết nối với server thất bại !");
}
finally
{
con.Close();
}
return termsList;
}
Bây giờ mình sẽ thêm một mẫu csdl như sau:

Cơ sử dữ liệu chi tiết quyền hạn
Bây giờ thì mình sẽ tạo một hàm để trả về giá trị True hoặc False khi có yêu cầu quyền hạn.
Trước khi thực hiện bạn cần tạo ra một biến list dùng chung list_detail
để lưu những code_acction
mà csdl trả về. Sau đó trong hàm load form bạn gán biến này vào lệnh gọi hàm list_per(id_per(frm_login.ID_USER));
ở đây mình gọi lần 2 hàm lồng nhau. Câu lệnh cụ thể như sau:
List<string> list_detail;
private void frm_main_Load(object sender, EventArgs e)
{
list_detail = list_per(id_per(frm_login.ID_USER));
}
Làm như thế này thì mình sẽ không mất công truy vấn trở lại, hàm list_detail
sẽ giữ các code_action
giúp mình kiểm tra khi cần nhanh chóng hơn.
Đã có danh sách quyền hạn của người đó, bây giờ sử dụng như thế nào.
Mình sẽ tạo ra một hàm nữa, hàm này sẽ cho phép truyền vào code_action
, và duyệt trong mãng để kiểm tra, sau đó trả về giá trị boolean
(true,false). Hàm này mình viết như sau:
private Boolean checkper(string code)
{
Boolean check = false;
foreach (string item in list_detail)
{
if(item==code)
{
check = true;
}
}
return check;
}
Bây giờ mình ví dụ nút click sẽ nút sửa với yêu cầu code_acction
là EDIT, code như sau
private void button1_Click(object sender, EventArgs e)
{
if (checkper("EDIT") == true)
{
MessageBox.Show("có quyền");
}
else
{
MessageBox.Show("Bạn không có quyền");
}
}

Kết quả là có quyền thực hiện hành động này
Bây giờ mình thử thay thế yêu cầu quyền quản lý người dùng với code_action
là MUSER thử nhé.

Kết quả là bạn không có quyền thực hiện hành động này
Như vậy là mình đã chia sẻ xong cách phần quyền theo nhóm quyền khá chi tiết rồi đấy, bạn có thể phát triển thêm nhiều code_action
để kiểm soát nhiều quyền hạn hơn. Mình sẽ không nói đến việc thêm các quyền hạn này như thế nào vì nó rất đơn giản rồi.
Các bạn có thể tải Project demo này tại đây: https://drive.google.com/open?id=1VMPjzX_O9bp_pGPsOKJZhzElUFfWwjsy
Chúc các bạn thành công !
