昨天已經建置好基本的API了,今天要來進行 API登入權限控管機制
的建置,這一系列蠻複雜的,大家可以看一看程式碼~ 這系列會有好幾天,程式碼的部分會居多,希望大家還有耐心看下去
在昨天的API專案建置後,因我們選擇個別帳戶的驗證機制,應會在資料庫內建立了好幾個ASP.NET Identity 相關的資料表,如下:
此時資料庫內容示意圖
可利用POST /api/Account/Register
進行帳號註冊
但為了方便後續資料儲存與驗證,這邊會先建立兩個資料表,分別為
dbo.sys_apiUser 儲存User的相關資訊,包含ID、建立時間、過期時間、是否鎖定等資訊。
這邊要特別說明一下 明碼紀錄密碼
的部分,password欄位應直接拿掉
,這邊只是因為是Demo網站,我方便測試資料而已,若要用在需上線的網站,請把password拿掉!
USE [OLDemo]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[sys_apiUser](
[id] [int] IDENTITY(1,1) NOT NULL,
[userId] [nvarchar](30) NULL,
[company] [nvarchar](30) NULL,
[note] [nvarchar](150) NULL,
[password] [nvarchar](50) NULL,
[issuedOn] [datetime] NULL,
[expiredOn] [datetime] NULL,
[lockYN] [nchar](1) NULL,
[aspnetUserId] [nvarchar](50) NULL,
[aspnetPassword] [nvarchar](50) NULL,
[Expiry_Date] [date] NULL,
PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
dbo.sys_tokens 資料表則是記錄每個來要token的使用者與token資料、token過期時間等。
USE [OLDemo]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[sys_tokens](
[id] [int] IDENTITY(1,1) NOT NULL,
[userid] [nvarchar](254) NULL,
[type] [nvarchar](254) NULL,
[token] [nvarchar](30) NULL,
[ip] [nvarchar](20) NULL,
[issuedOn] [datetime] NULL,
[expiredOn] [datetime] NULL
) ON [PRIMARY]
GO
並於 identityModels.cs
新增以下程式碼,用以管理使用者
#region Helpers
// Configuring UserManager
// UserManager類別 在Asp.Net Identity用來管理使用者。例如:註冊新使用者,驗證帳號密碼與讀取使用者資料
public class UserManager : UserManager<ApplicationUser>
{
// 加入ApplicationUserStore,建構函數要傳入 DbContext
public UserManager() : base(new UserStore<ApplicationUser>(new ApplicationDbContext()))
{
this.UserValidator = new UserValidator<ApplicationUser>(this)
{
AllowOnlyAlphanumericUserNames = false
};
}
}
#endregion
接下來就是要新增與帳號存許相關的程式碼,因此新增 acount/account.cs
(資料夾和程式碼頁面)
在其內新增以下幾個function,亦可擇部分建置
RoleExists
:判斷角色是否已在存在UserExists
:判斷使用者是否已在存在CreateRole
:新增角色CreateUser
:新增使用者CreateUser_string
:新增使用者,使用userNameDeleteUserOnlyASPNET
:刪除ASP.NET使用者DeleteUser
:刪除使用者 (含sys_apiUser)AddUserToRole
:將使用者加入角色中removeUserRole
:刪除使用者特定角色ClearUserRoles
:清除所有的角色設定ListUserRoles
:列出該使用者的角色設定checkRoles
:檢查使用者角色ListRoleUsers
:列出該角色的使用者getRolesFromUserId
:列出該使用者的角色getUserList
:列出使用者列表getRoleList
:列出權限列表UserisExist
:確認使用者是否重複changePassword
:變更密碼adminChangePassword
:admin幫使用者變更密碼deluser
:刪除使用者getUserListData
:列出使用者列表 (從sys_apiUser)using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Web.UI;
using System.Data;
using System.Configuration;
namespace OLMapAPI.Models
{
/// <summary>
/// account 的摘要描述
/// </summary>
public class account
{
public account()
{
//
// TODO: 在這裡新增建構函式邏輯
//
}
#region 帳號操作
// 判斷角色是否已在存在
public bool RoleExists(string name)
{
var rm = new RoleManager<IdentityRole>(
new RoleStore<IdentityRole>(new ApplicationDbContext()));
return rm.RoleExists(name);
}
// 判斷使用者是否已在存在
public bool UserExists(string name)
{
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
um.UserValidator = new UserValidator<ApplicationUser>(um) { AllowOnlyAlphanumericUserNames = false };
var user = um.FindByName(name);
if (user == null)
{
return false
}
else
{
return true;
}
}
// 新增角色
public bool CreateRole(string name)
{
var rm = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
var idResult = rm.Create(new IdentityRole(name));
return idResult.Succeeded;
}
// 新增使用者
public bool CreateUser(ApplicationUser userName, string password)
{
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
um.UserValidator = new UserValidator<ApplicationUser>(um) { AllowOnlyAlphanumericUserNames = false };
var idResult = um.Create(userName, password);
return idResult.Succeeded;
}
// 新增使用者_使用userName
public bool CreateUser_string(string userName, string password)
{
var user = new ApplicationUser() { UserName = userName };
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
um.UserValidator = new UserValidator<ApplicationUser>(um) { AllowOnlyAlphanumericUserNames = false };
var idResult = um.Create(user, password);
return idResult.Succeeded;
}
//刪除使用者
public bool DeleteUserOnlyASPNET(string username)
{
var Db = new ApplicationDbContext();
var user = Db.Users.First(u => u.UserName == username);
Db.Users.Remove(user);
Db.SaveChanges();
return true;
}
//刪除使用者
public bool DeleteUser(string username)
{
var Db = new ApplicationDbContext();
var user = Db.Users.First(u => u.UserName == username);
Db.Users.Remove(user);
Db.SaveChanges();
System.Web.UI.WebControls.SqlDataSource sds = new System.Web.UI.WebControls.SqlDataSource();
sds.ConnectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
String SQLStr = "DELETE AspNetLinkUserData Where username = @username";
sds.DeleteParameters.Add("username", username);
sds.DeleteCommand = SQLStr;
sds.Delete();
return true;
}
// 將使用者加入角色中
public bool AddUserToRole(string userName, string roleName)
{
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
um.UserValidator = new UserValidator<ApplicationUser>(um) { AllowOnlyAlphanumericUserNames = false };
var user = um.FindByName(userName);
var idResult = um.AddToRole(user.Id, roleName);
return idResult.Succeeded;
}
// 刪除使用者特定角色
public void removeUserRole(string userName, string RoleName)
{
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
um.UserValidator = new UserValidator<ApplicationUser>(um) { AllowOnlyAlphanumericUserNames = false };
var user = um.FindByName(userName);
if (user.Id != "")
{
um.RemoveFromRole(user.Id, RoleName);
}
}
// 清除所有的角色設定
public void ClearUserRoles(string userName, string RoleName)
{
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
um.UserValidator = new UserValidator<ApplicationUser>(um) { AllowOnlyAlphanumericUserNames = false };
var rm = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
var user = um.FindByName(userName);
var currentRoles = new List<IdentityUserRole>();
currentRoles.AddRange(user.Roles);
foreach (var role in currentRoles)
{
um.RemoveFromRole(user.Id, rm.FindById(role.RoleId).Name);
}
}
// 列出該使用者的角色設定
public List<string> ListUserRoles(string userName)
{
List<string> list = new List<string>();
if (userName != "")
{
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
um.UserValidator = new UserValidator<ApplicationUser>(um) { AllowOnlyAlphanumericUserNames = false };
var rm = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
var user = um.FindByName(userName);
if (user != null)
{
var currentRoles = new List<IdentityUserRole>();
currentRoles.AddRange(user.Roles);
foreach (var role in currentRoles)
{
list.Add(rm.FindById(role.RoleId).Name);
}
}
}
return list;
}
// 檢查使用者角色
public bool checkRoles(string username, string checkrole)
{
List<string> roles = ListUserRoles(username);
foreach (var role in roles)
{
if (role == checkrole)
{
return true;
}
}
return false;
}
// 列出該角色的使用者
public List<string> ListRoleUsers(string roleName)
{
List<string> list = new List<string>();
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
um.UserValidator = new UserValidator<ApplicationUser>(um) { AllowOnlyAlphanumericUserNames = false };
var rm = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
var role = rm.FindByName(roleName);
var currentUsers = new List<IdentityUserRole>();
currentUsers.AddRange(role.Users);
foreach (var user in currentUsers)
{
list.Add(um.FindById(user.UserId).UserName);
}
return list;
}
// 列出該使用者的角色
public List<string> getRolesFromUserId(string UserId)
{
List<string> list = new List<string>();
var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
um.UserValidator = new UserValidator<ApplicationUser>(um) { AllowOnlyAlphanumericUserNames = false };
var rm = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
var user = um.FindByName(UserId);
if (user != null)
{
var currentRoles = new List<IdentityUserRole>();
currentRoles.AddRange(user.Roles);
foreach (var role in currentRoles)
{
list.Add(rm.FindById(role.RoleId).Name);
}
}
return list;
}
//列出使用者列表
public List<string> getUserList()
{
List<string> list = new List<string>();
var Db = new ApplicationDbContext();
var users = Db.Users;
foreach (var user in users)
{
list.Add(user.UserName);
}
return list;
}
//列出權限列表
public List<string> getRoleList()
{
List<string> list = new List<string>();
var Db = new ApplicationDbContext();
var roles = Db.Roles;
foreach (var role in roles)
{
list.Add(role.Name);
}
return list;
}
// 確認使用者是否重複
public bool UserisExist(string userName)
{
var manager = new UserManager();
ApplicationUser user = manager.FindByName(userName);
bool bresult = false;
if (user != null)
{
bresult = true;
}
return bresult;
}
//變更密碼
public void changePassword(string userName, string Currentpassword, string Newpassword)
{
var manager = new UserManager();
manager.ChangePassword(userName, Currentpassword, Newpassword);
}
//admin幫使用者變更密碼
public void adminChangePassword(string userName, string Newpassword)
{
var manager = new UserManager();
manager.RemovePassword(userName);
manager.AddPassword(userName, Newpassword);
}
public void deluser(string userName)
{
var manager = new UserManager();
ApplicationUser user = manager.FindByName(userName);
manager.Delete(user);
}
public DataView getUserListData()
{
System.Web.UI.WebControls.SqlDataSource sds = new System.Web.UI.WebControls.SqlDataSource();
sds.ConnectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
String SQLStr = "SELECT username,name,dep,mail,note FROM AspNetLinkUserData";
sds.SelectParameters.Clear();
//sds.SelectParameters.Add("group_name", "%" + group_name + "%");
sds.SelectCommand = SQLStr;
DataView dv = (DataView)sds.Select(DataSourceSelectArguments.Empty);
return dv;
}
#endregion
}
}
依據昨天的API建置方式,照習慣依序建置 Model、Infrastructure、Controller
STEP1. 新增 apiUser/apiUserClass.cs
的類別檔
並建立apiUserObj
、insertApiUserObj
、removeApiUserObj
、lockApiUserObj
這四個針對API User進行操作的API。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace OLMapAPI.Models.apiUser
{
/// <summary>
/// apiUser
/// </summary>
public class apiUserObj
{
public string id;
public string userId;
public string unit;
public string note;
public string password;
public string lockYN;
public apiUserObj(string _id, string _userId, string _unit, string _note, string _password, string _lockYN)
{
id = _id;
userId = _userId;
unit = _unit;
note = _note;
password = _password;
lockYN = _lockYN;
}
public apiUserObj() { }
}
public class insertApiUserObj
{
public string userId;
public string unit;
public string note;
public string password;
public string lockYN;
public insertApiUserObj(string _userId, string _unit, string _note, string _password)
{
userId = _userId;
unit = _unit;
note = _note;
password = _password;
lockYN = "0";
}
public insertApiUserObj() { }
}
/// <summary>
/// removeApiUserObj
/// </summary>
public class removeApiUserObj
{
public string id;
public removeApiUserObj(string _id)
{
id = _id;
}
public removeApiUserObj() { }
}
/// <summary>
/// lockApiUserObj
/// </summary>
public class lockApiUserObj
{
public string id;
public lockApiUserObj(string _id)
{
id = _id;
}
public lockApiUserObj() { }
}
}
STEP2. 在API專案內新增 Infrastructure\apiUser\apiUserFunc.cs
,並建立getApiUserList()
、insertApiUser()
、removeApiUser()
、getApiUserIdFromId()
,這5個函式,並同時調用STEP 1. 建置的account
function。
using OLMapAPI.Models;
using System;
using System.Collections.Generic;
using System.Web;
using System.Configuration;
using System.Data.SqlClient;
using OLMapAPI.Models.apiUser;
namespace OLMapAPI.Infrastructure.apiUser
{
/// <summary>
/// admin編輯apiUser各項方法
/// </summary>
public class apiUserFunc
{
// 取得列表
public List<apiUserObj> getApiUserList()
{
List<apiUserObj> apiUsers = new List<apiUserObj>();
string sqlstr = @"SELECT id,userId,company,note,password,lockYN FROM sys_apiUser";
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand(sqlstr, conn);
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
string id = dr["id"].ToString();
string userId = dr["userId"].ToString();
string unit = dr["company"].ToString();
string note = dr["note"].ToString();
string password = dr["password"].ToString();
password = password.Substring(0, 1) + "****" + password.Substring(password.Length - 1, 1);
string lockYN = dr["lockYN"].ToString();
apiUserObj obj = new apiUserObj(id, userId, unit, note, password, lockYN);
apiUsers.Add(obj);
}
dr.Close(); dr.Dispose(); conn.Close(); conn.Dispose();
return apiUsers;
}
// 新增apiUser
public string insertApiUser(insertApiUserObj apiUser)
{
account acc = new account();
string aspnetUserId = "apiUser_" + apiUser.userId.Trim();
string aspnetPassword = "apiUser@_" + apiUser.password.Trim();
var newUser = new ApplicationUser() { UserName = aspnetUserId };
// part1.使用account建立User於AspNet.Identity產生的[AspNetUsers]資料表中
bool bresult = acc.CreateUser(newUser, aspnetPassword);
// part2.同時建立在自訂的[sys_apiUser]資料表中
if (bresult)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand("INSERT INTO sys_apiUser (userId,company,note,password,issuedOn,expiredOn,lockYN,aspnetUserId,aspnetPassword) Values (@userId,@company,@note,@password,@issuedOn,@expiredOn,@lockYN,@aspnetUserId,@aspnetPassword)", conn);
conn.Open();
cmd.Parameters.AddWithValue("@userId", apiUser.userId.Trim());
cmd.Parameters.AddWithValue("@company", apiUser.unit);
cmd.Parameters.AddWithValue("@note", apiUser.note);
cmd.Parameters.AddWithValue("@password", apiUser.password.Trim());
cmd.Parameters.AddWithValue("@issuedOn", DateTime.Now);
cmd.Parameters.AddWithValue("@expiredOn", DateTime.Now.AddYears(100));
cmd.Parameters.AddWithValue("@lockYN", "0");
cmd.Parameters.AddWithValue("@aspnetUserId", aspnetUserId);
cmd.Parameters.AddWithValue("@aspnetPassword", aspnetPassword);
SqlDataReader dr = cmd.ExecuteReader();
dr.Close(); dr.Dispose(); conn.Close(); conn.Dispose();
return "ok";
}
else
{
return "error";
}
}
// 移除ApiUser
public string removeApiUser(removeApiUserObj removeApiUser)
{
string aspnetUserId = getApiUserIdFromId(removeApiUser.id);
account acc = new account();
bool bresult = acc.DeleteUserOnlyASPNET(aspnetUserId);
if (bresult)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand("DELETE sys_apiUser Where id=@id", conn);
conn.Open();
cmd.Parameters.AddWithValue("@id", removeApiUser.id);
SqlDataReader dr = cmd.ExecuteReader();
dr.Close(); dr.Dispose(); conn.Close(); conn.Dispose();
return "ok";
}
else
{
return "error";
}
}
private string getApiUserIdFromId(string id)
{
string sqlstr = @"SELECT id,aspnetUserId FROM sys_apiUser where id=@id";
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand(sqlstr, conn);
conn.Open();
cmd.Parameters.AddWithValue("@id", id);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
string aspnetUserId = dr["aspnetUserId"].ToString();
return aspnetUserId;
}
dr.Close(); dr.Dispose(); conn.Close(); conn.Dispose();
return "";
}
// 鎖ApiUser
public string lockApiUser(lockApiUserObj lockApiUser)
{
string lockYN = "0";
if (getApiUserLockFromId(lockApiUser.id) == "0")
{
lockYN = "1";
}
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand("update sys_apiUser SET lockYN=@lockYN Where id=@id", conn);
conn.Open();
cmd.Parameters.AddWithValue("@id", lockApiUser.id);
cmd.Parameters.AddWithValue("@lockYN", lockYN);
SqlDataReader dr = cmd.ExecuteReader();
dr.Close(); dr.Dispose(); conn.Close(); conn.Dispose();
return "ok";
}
private string getApiUserLockFromId(string id)
{
string sqlstr = @"SELECT id,lockYN FROM sys_apiUser where id=@id";
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand(sqlstr, conn);
conn.Open();
cmd.Parameters.AddWithValue("@id", id);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
string lockYN = dr["lockYN"].ToString();
return lockYN;
}
dr.Close(); dr.Dispose(); conn.Close(); conn.Dispose();
return "";
}
}
}
STEP3. 新增 apiUserController
控制器,這些API使針對API User 進行操作的,一般不對外開放,記得要鎖上或是直接註解掉。
有以下幾個API:
getApiUserList()
:取得apiUser列表insertApiUser()
:新增apiUserremoveApiUser()
:移除apiUserlockApiUser()
:更新apiUser狀態using OLMapAPI.Infrastructure.apiUser;
using OLMapAPI.Models.apiUser;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace OLMapAPI.Controllers
{
public class apiUserController : ApiController
{
#region API User操作,一般不對外開放
/// <summary>
/// 取得apiUser列表
/// </summary>
/// <returns></returns>
[HttpPost]
public List<apiUserObj> getApiUserList()
{
apiUserFunc apiUserF = new apiUserFunc();
return apiUserF.getApiUserList();
}
/// <summary>
/// 新增apiUser
/// </summary>
/// <param name="apiUser"></param>
/// <returns></returns>
[HttpPost]
public string insertApiUser(insertApiUserObj apiUser)
{
apiUserFunc apiUserF = new apiUserFunc();
return apiUserF.insertApiUser(apiUser);
}
/// <summary>
/// 移除apiUser
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
[HttpPost]
public string removeApiUser(removeApiUserObj obj)
{
apiUserFunc apiUserF = new apiUserFunc();
return apiUserF.removeApiUser(obj);
}
/// <summary>
/// 更新apiUser狀態(鎖1→不鎖0、不鎖0→鎖1)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
[HttpPost]
public string lockApiUser(lockApiUserObj obj)
{
apiUserFunc apiUserF = new apiUserFunc();
return apiUserF.lockApiUser(obj);
}
#endregion
}
}
今天學會了建置一些帳號相關的函式和API ,實在有夠多,明天來實際驗證帳號密碼,並加filter、每一次執行API都去驗證一次,達到權限控管的目的。