有關 ASP.NET Core 的 Security - ASP.NET Core Identity 與 Google authentication

  • 1336
  • 0

依照我之前談過的網站基礎,最後一塊完全沒講過的區塊就是 Security.當然,Security 的內容包含的很廣泛,有存取控制方面的 security,也有傳輸資料方面的 security,也有要防止像 cross site scripting 等惡意入侵的 security,這些內容含蓋的範圍從 runtime, entity framework 到 MVC 到 Razor 都有.而這一篇文章中主要是淺談 ASP.NET Core Identity 以及如何將 Identity 使用 Google 帳號來做驗證.

時間很快地來到了六月底,這星期的頭一天 PM 的大老闆公告了 .Net Core 1.0 , ASP.NET Core 1.0, Entity Framework Core 1.0 正式發行,也傳了一些科技新聞的剪影讓大家參考,經歷了兩年多的時間終於把新版本的產品釋出了.從某個角度來看,整個團隊對微軟公司的營收直接貢獻是 零 元,而且所有的程式碼也都 open source 了,只能覺得這真是個佛心的公司,讓大家有免費的工具與執行平台可以寫 web 應用程式.這幾年也可以看到公司配合著世界在改變了.

回歸本文章主題 - Identity.

歷史

最早在 ASP.NET 剛問世時,其實沒有什麼 security 的設計,只有簡單的 form authentication 算是比較有用的,即便有 form authentication ,自己還是要寫不少的 code 做一些配套措施,才讓 authentication 比較完整些,而 authorization 仍是一空白.後來,ASP.NET 出了一些 membership provider 之類的東西,把相關的功能做的更多,但以我個人感覺來說,不太好用.後來在幾年前推出了 Identity,這個版本就好用多了,讓你可以不用寫太多額外的 code 就能做好 authentication ,而且還可以整合到其他的驗證伺服器.

ASP.NET Core Identity

整個專案的程式碼在 https://github.com/aspnet/Identity/tree/dev/src/Microsoft.AspNetCore.Identity ,在這些原始碼中看起來好像有很多的檔案,其實有大約 1/3 都是 interface 的定義.如果你熟悉之前版本的 Identity,那這一版對你來說並不會陌生,因為整個程式主要結構都是一樣的.基本上來說一樣是分為三個層次,最上面的 Manager,中間的 Store,以及最下面的資料存取方式.

Manager 類的程式定義了你的程式如何和 Identity 互動.例如,UserManager (https://github.com/aspnet/Identity/blob/master/src/Microsoft.AspNetCore.Identity/UserManager.cs) 定義了 GetUserName() 讓你的應用程式取得使用者名字,或是你的應用程式要新增一個使用者時,可以呼叫 CreateAsync() 來達成這工作.你可以從 UserManager 的定義裡看到許多你需要為 Users 做的事情.再舉另外一個例子,SignInManager (https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNetCore.Identity/SignInManager.cs) 是負責做與認證登入有關的工作,所以你可以看到它提供的方法有 SignInAsync(), SignOutAsync(), TwoFactorSignInAsync() 之類.

Store 類的程式定義了 Manager 該如何取得所需要的資訊,例如 UserManager 有 GetUserName() ,但 user name 是儲存在某一個地方 (例如資料庫等),於是 UserManager 就會呼叫 UserStore 的 GetUserNameAsync() 來取得.所以對 UserManager 來說,它並不知道 user name 在那裡,它只是去呼叫 User Store 的 GetUserNameAsync() 來取得資料,而 User Store 才是真正去取得資料的程式.因此, 在 UserManager 要建立時,你可以看到它的 constructor 第一個參數就是 IUserStore,而任何實做 IUserStore 的程式就是 UserStore,所以實做 Store 的程式就是實際放資料的角色,比如 user 資料都儲存在資料庫,那麼建立 User Store 的人需要和資料庫有直接互動,所以 Entity Framework 便是最適合的程式來產生 UserStore.因此,若你有特別的需求時,你能寫自己的程式來產生 UserStore 讓 UserManager 使用,這樣一來整個應用程式在執行時使用 UserManager 來操作 user 資料時便能用你的程式碼去運作.以後找個時間再來寫這樣的文章,這種應用應該是相當有趣的.

當你看原始碼時,你就能依檔案名稱 Manager, Store 來區分,依照這樣的想法就比較能幫助你知道那些原始碼是做什麼的.而最下層的資料存取方式,目前 Identity 只提供 Entity Framework 來執行,這部份的原始碼在 https://github.com/aspnet/Identity/tree/master/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore.如果你有需要實做特別的資料存取方式,只要參考這裡的做法照著做就行了.

目前的版本以提供 Entity Framework 為 Store 的運行者,所以 user, role 等等的相關資料都寫在資料庫中. 

使用 Identity 

在這裡用資料庫為儲存空間為例子來說明,整體的使用上相當簡單,如果你使用 Visual Studio 2015 產生 ASP.NET Core Web Application 時,在驗證方式上選擇使用 Indivudual User 驗證方式,則 Visual Studio 會為你載入合適的專案樣板.

然後在 Startup.cs 裡的 ConfigureServices() 裡,你會看到 ASP.NET Core 將 Entity Framework 和 Identity 加入,

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.AddMvc();

            // Add application services.
            services.AddTransient<IEmailSender, AuthMessageSender>();
            services.AddTransient<ISmsSender, AuthMessageSender>();

此時,請記得到 appsettings.json 將 DefaultConection 改成你所使用的資料庫連接字串.

在專案剛建立完成時,你也會看到專案目錄下有一個 Data 目錄,裡面存放的就是上面程式碼中 ApplicationDbContext 定義.你可以看到整個資料庫的部份是用 code first 的方式形成的,在你第一次執行專案時,程式會執行 ef migration,將整個 Identity 所用的 table 都在資料庫中建立起來,因此,你得確定你在連線字串中所設定的資料庫使用者有足夠的權限.在 Data 目錄裡還有一個 Migrations 目錄,裡面的 class 可以讓你看到整個 DbContext 的結構,以及套用到資料庫上的  db schema.

在上述程式碼後面兩行,將兩個 class 加入到 dependency injection 儲存空間中,這兩個是使用者加入的過程中,可以使用 email 或是 sms 的方式來做驗證.但這部份的程式碼是個空盒子.如果你需要這功能,就要到 AuthMessageSender 和 AuthMessageSender 將傳送 email , sms 的程式碼自行補上去.

其他的東西,相對應的 Controller,Model 和 View 都已經由樣板帶出來了,所以基本上不用特別修改,可以直接執行,你就會看到一個具有 user authentication 的樣板網站了.

Identity 使用 Google 驗證

在官方文件網站中,你可以找到一個文章教你如何使用 Facebook 帳號做為驗證方式 (https://docs.asp.net/en/latest/security/authentication/sociallogins.html),在這篇文章中,我用 Google 來做為例子.

其實 ASP.NET Core 在 Authentication 的部份支援 OAuth,也就是說驗證使用者是否為真可以交給其他可信賴的人來做,不用自己做,而我們只要相信別人告訴我的答案就好.許多大型的網站都支援 OAuth,ASP.NET Core 也做出了幾個常用的大型網站 OAuth 套件,讓你可以很方便地整合其他網站的驗證方式.有關其他網站的 OAuth 套件原始碼可以在這找到 https://github.com/aspnet/Security/tree/master/src

接下來,用 Google 驗證做範例.設定的過程在前面跟介紹 Facebook 驗證的文件是一樣的,所以我們直接跳到設定 Google 的部份.要使用 Google OAuth,你得先到 Google+ API 網站.https://console.developers.google.com/projectselector/apis/credentials

選擇建立新專案,輸入專案名稱,你會看到如下畫面.

然後請選擇 OAuth consent screen,設定專案名稱與 URL 等.

按下 Save 後,再回到 Credentials 去按下 Create Credentials.

選擇 OAuth client ID 

選擇 Web application,輸入名稱,在 Authorized redirect URIs 上輸入你的網站 URL,這樣才能讓 Google 知道使用者驗證完成後要回到什麼地方.

然後按下 Create,你會看畫面上出現如下

請把 client ID 和 client secret 記下來,等會要輸入在  ASP.NET Core 程式裡.

再回到你的 ASP.NET Core 程式的 Startup.cs,在 Configure() 裡把 Google 驗證加上去

            app.UseIdentity();

            app.UseGoogleAuthentication(new GoogleOptions()
            {
                ClientId= "your client id.apps.googleusercontent.com",
                ClientSecret= "your client secret"
            });

基本上這樣就完成了.

接著執行你的專案,然後試著 Log In,選擇 Google,畫面就會導向到 Google 驗證畫面,回來之後你就會可以看到已經成功經由 Google 驗證了,因為你的 Google 帳號名會出現在專案網站上右上角的位置.

Hope this helps :)