Chủ Nhật, 9 tháng 4, 2017

Đăng nhập bằng tài khoản Google Plus trong ASP.NET MVC

Mình đã có bài viết hướng dẫn đăng nhập bằng mạng xã hội Facebook, Google Plus, Twitter trên series "Hướng dẫn sử dụng ASP.NET Identity", tuy nhiên công nghệ ASP.NET Identity chỉ hỗ trợ phiên bản MVC 5 trở lên, còn ở bài viết này mình sẽ hướng dẫn bạn đăng nhập bằng tài khoản Google Plus trên website ASP.NET MVC bằng cách sử dụng thư viện ASPSnippets.GoogleAPICách làm này thích hợp cho cả phiên bản MVC 3 và 4.

Hình demo



Bước 1: Bạn cần tạo 1 ứng dụng trên trang https://console.developers.google.com để có thể nhận 2 giá trị Client IDClient Secret vì method API của Google yêu cầu bạn truyền 2 giá trị này. Chi tiết cách tạo ứng dụng bạn truy cập địa chỉ https://developers.google.com/identity/sign-in/web/devconsole-project.

Trong phần cấu hình Credentials, bạn thêm giá trị của thuộc tính Authorized JavaScript origins diachiwebcuaban, giá trị của thuộc tính Authorized redirect URIs diachiawebcuaban/LoginSocial/LoginWithGooglePlus Xem ví dụ hình dưới mình cấu hình với url là http://localhost:50077.



Có thể bạn sẽ thắc mắc vì sao giá trị của Authorized redirect URIs lại là vậy? Câu trả lời là bởi vì khi người dùng chấp nhận cung cấp quyền cho phép ứng dụng xem thông tin tiểu sử và email của họ thì ứng dụng sẽ chuyển hướng về URL như trên để xử lý. Chắc chắn chúng ta sẽ phải tạo LoginSocialController và action method LoginWithGooglePlus trong LoginSocialController để xử lý lưu thông tin người dùng lấy được từ Google API.

Bước 2: Tải source demo tại địa chỉ https://goo.gl/ZtmiBm

Bước 3: Giải nén file vừa tải ra bạn sẽ thấy file ASPSnippets.GoogleAPI.dll trong thư mục Bin. Bạn tiến hành Add Reference file .dll này vào project của bạn như hình dưới.






Bước 4: Tiến hành tạo các class sau để chứa thông tin user lấy được từ Google API sau khi xác thực thành công.

public class GoogleProfile
{
    public string Id { getset; }
    public string DisplayName { getset; }
    public ImageProfile Image { getset; }
    public List<Email> Emails { getset; }
    public string Gender { getset; }
    public string ObjectType { getset; }
}
public class Email
{
    public string Value { getset; }
    public string Type { getset; }
}
public class ImageProfile
{
    public string Url { getset; }
}

Bước 5: Tạo LoginSocialController và khai báo thêm 3 namespace sau.

using ASPSnippets.GoogleAPI;
using System.Web.Script.Serialization;
using System.Net;

Bước 6: Tạo action method LoginWithGooglePlus trong LoginSocialController. Nội dung bên trong như sau:

[HttpPost]
[ValidateAntiForgeryToken]
public void LoginWithGooglePlus()
{
            GoogleConnect.ClientId = "Client-ID-Here";
            GoogleConnect.ClientSecret = "Client-Secret-Here";
            GoogleConnect.RedirectUri = Request.Url.AbsoluteUri.Split('?')[0];
            GoogleConnect.Authorize("profile", "email");
}

Bạn thay Client-ID-Here Client-Secret-Here thành giá trị Client ID và Client Secret từ ứng dụng Google API bạn vừa tạo.

Ý nghĩa đoạn lệnh trên như sau:

- 2 dòng đầu gán giá trị Client IDClient Secret qua 2 thuộc tính ClientIDClientSecret của class GoogleConnect.

- Dòng thứ 3 chính là gán URL chuyển hướng qua thuộc tính RedirectUri. Giá trị này chắc chắn sẽ có giá trị giống như giá trị chúng ta đã thiết lập cho Authorized redirect URIs ở trên thông qua method Split. Mình viết như vậy vì muốn lấy giá trị động thay vì truyền giá trị tĩnh thì sẽ không hay.

- 3 dòng đầu cần phải có là bởi vì ở dòng cuối chúng ta gọi method Authorize(),  method này yêu cầu chúng ta phải gửi kèm giá trị ClientID, ClientSecret, RedirectUri ở trên để yêu cầu quyền lấy thông tin tiểu sử (profile) và email từ người dùng.

Bước 7: Tạo thêm action method LoginWithGooglePlusConfirmed() trong LoginSocialController như sau:

  [ActionName("LoginWithGooglePlus")]
        public ActionResult LoginWithGooglePlusConfirmed()
        {
            if (!string.IsNullOrEmpty(Request.QueryString["code"]))
            {
                string code = Request.QueryString["code"];
                string json = GoogleConnect.Fetch("me", code);
                GoogleProfile profile = new JavaScriptSerializer().Deserialize<GoogleProfile>(json);
               
                BlogITEntities db = new BlogITEntities();

                if(db.KhachHangs.Find(profile.Id) != null)
                {
                    Session["khachhang"] = db.KhachHangs.Find(profile.Id);
                    return RedirectToAction("Index", "Home");
                }
                KhachHang khachHang = new KhachHang()
                {
                    ID = profile.Id,
                    Name = profile.DisplayName,
                    Email = profile.Emails.Find(email => email.Type == "account").Value,
                    Gender = profile.Gender,
                    Type = profile.ObjectType,
                    ImageURL = profile.Id + System.IO.Path.GetExtension(profile.Image.Url.Split('?')[0])
                };
                using (var client = new WebClient())
                {
                    client.DownloadFile(profile.Image.Url, Server.MapPath("~/Photos/Users/" + khachHang.ImageURL));
                }
                Session["khachhang"] = khachHang;
                db.KhachHangs.Add(khachHang);
                db.SaveChanges();
            }
            if (Request.QueryString["error"] == "access_denied")
            {
                return Content("access_denied");
            }
            return RedirectToAction("Index", "Home");

        }

Đây là  action sẽ xử lý lưu thông tin người dùng từ Google API truyền về (bởi vì URL của action này trùng với RedirectUri )

Chú ý: đoạn lệnh trên mình dùng Linq to Entity Framework để lưu thông tin người dùng vào bảng KhachHang trong CSDL của mình. Bạn phải chỉnh sửa lại đoạn lưu thông tin cho đúng với công nghệ kết nối CSDL bạn đang dùng (có thể là ADO.NET, Linq to SQL, ...). Việc này không khó nếu bạn rành C#, quan trọng là chúng ta đã lấy được thông tin người dùng từ biến profile.

Lệnh "using (var client = new WebClient())client.DownloadFile(profile.Image.Url, Server.MapPath("~/Photos/Users/" + khachHang.ImageURL)); }" để tải hình đại diện người dùng vào thư mục Photos/Users mình đã tạo sẵn.

Và khi đăng nhập thành công thì chúng ta cần tạo 1 biến Session, mình đặt là Session["khachhang"] và gán giá trị cho Session này bằng đúng đối tượng khachHang. Bạn chú ý rằng lệnh "if(db.KhachHangs.Find(profile.Id) != null)" để kiểm tra xem trong CSDL đã có user với Id lấy từ Google API chưa nếu có rồi thì không add thêm record vào csdl nữa.

Tiếp theo là lệnh "if (Request.QueryString["error"] == "access_denied")" nhằm kiểm tra nếu người dùng không cho phép ứng dụng truy cập thông tin của họ thì chúng ta sẽ trả về dòng báo lỗi access_denied.

Bước 8: Dán đoạn lệnh sau vào view bạn muốn hiển thị nút Login.

<form action="@Url.Action("LoginWithGooglePlus", "LoginSocial")" method="post">
   @Html.AntiForgeryToken()
   <button type="submit" class="btn btn-danger"><i class="fa fa-google-plus"></i>  Login with Google + </button>

</form>

Form có action gọi đến action method LoginWithGooglePlus có attribute [HttpPost] của chúng ta đã tạo trước đó.

Vậy là đã xong. Đây là kết quả của mình, bạn thử test xem có thành công như mình không nhé.

Click button login







Đăng nhập thành công




Lưu vào CSDL thành công.

Chúc bạn cài đặt thành công. Mọi thắc mắc bạn comment dưới bài viết nhé.


EmoticonEmoticon