From 2050c54eca4583a19f8bc72679ba26645117d37e Mon Sep 17 00:00:00 2001
From: Argo Zhang <5196060@qq.com>
Date: Tue, 5 Mar 2019 13:05:19 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8A=9F=E8=83=BD=EF=BC=9A?=
=?UTF-8?q?=E7=94=A8=E6=88=B7=E7=AE=A1=E7=90=86=E9=A1=B5=E9=9D=A2=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A0=E9=87=8D=E7=BD=AE=E5=AF=86=E7=A0=81=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=20#IS7V2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 此功能与首页忘记密码相关联
---
.../Controllers/Api/RegisterController.cs | 16 +++--
Bootstrap.Admin/Query/QueryUserOption.cs | 3 +-
Bootstrap.Admin/Views/Admin/Users.cshtml | 34 ++++++++++
Bootstrap.Admin/Views/Shared/_Admin.cshtml | 2 +-
Bootstrap.Admin/wwwroot/js/log.js | 3 +
Bootstrap.Admin/wwwroot/js/login.js | 2 +-
Bootstrap.Admin/wwwroot/js/users.js | 23 ++++++-
Bootstrap.DataAccess/Helper/UserHelper.cs | 21 +++++--
Bootstrap.DataAccess/ResetUsers.cs | 62 +++++++++++++++++++
Bootstrap.DataAccess/User.cs | 59 +++++++++++++++++-
DatabaseScripts/Install.sql | 27 ++++++++
UnitTest/BAWebHost.cs | 7 ++-
UnitTest/Bootstrap.Admin/Api/RegisterTest.cs | 21 +++++++
.../Bootstrap.DataAccess/ResetUserTest.cs | 61 ++++++++++++++++++
14 files changed, 325 insertions(+), 16 deletions(-)
create mode 100644 Bootstrap.DataAccess/ResetUsers.cs
create mode 100644 UnitTest/Bootstrap.DataAccess/ResetUserTest.cs
diff --git a/Bootstrap.Admin/Controllers/Api/RegisterController.cs b/Bootstrap.Admin/Controllers/Api/RegisterController.cs
index caa1c2be..156ea7a3 100644
--- a/Bootstrap.Admin/Controllers/Api/RegisterController.cs
+++ b/Bootstrap.Admin/Controllers/Api/RegisterController.cs
@@ -1,9 +1,12 @@
using Bootstrap.DataAccess;
+using Longbow.Web;
using Longbow.Web.SignalR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
+using System;
using System.Linq;
+using System.Net;
using System.Threading.Tasks;
namespace Bootstrap.Admin.Controllers.Api
@@ -41,15 +44,20 @@ namespace Bootstrap.Admin.Controllers.Api
return ret;
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpPut("{userName}")]
+ public bool Put(string userName, [FromBody]User user) => UserHelper.ResetPassword(userName, user.Password);
+
///
/// 忘记密码调用
///
///
///
[HttpPut]
- public bool Put([FromBody]User user)
- {
- return UserHelper.ForgotPassword(user.UserName, user.DisplayName, user.Description);
- }
+ public bool Put([FromBody]ResetUser user) => UserHelper.ForgotPassword(user.UserName, user.DisplayName, user.Reason);
}
}
diff --git a/Bootstrap.Admin/Query/QueryUserOption.cs b/Bootstrap.Admin/Query/QueryUserOption.cs
index 274f2e12..f5896636 100644
--- a/Bootstrap.Admin/Query/QueryUserOption.cs
+++ b/Bootstrap.Admin/Query/QueryUserOption.cs
@@ -63,7 +63,8 @@ namespace Bootstrap.Admin.Query
u.RegisterTime,
u.ApprovedTime,
u.ApprovedBy,
- u.Description
+ u.Description,
+ u.IsReset
});
return ret;
}
diff --git a/Bootstrap.Admin/Views/Admin/Users.cshtml b/Bootstrap.Admin/Views/Admin/Users.cshtml
index 63550265..b9b334c8 100644
--- a/Bootstrap.Admin/Views/Admin/Users.cshtml
+++ b/Bootstrap.Admin/Views/Admin/Users.cshtml
@@ -71,4 +71,38 @@
@section customModal {
@await Html.PartialAsync("RoleConfig")
@await Html.PartialAsync("GroupConfig")
+
}
\ No newline at end of file
diff --git a/Bootstrap.Admin/Views/Shared/_Admin.cshtml b/Bootstrap.Admin/Views/Shared/_Admin.cshtml
index 0619a342..02489b49 100644
--- a/Bootstrap.Admin/Views/Shared/_Admin.cshtml
+++ b/Bootstrap.Admin/Views/Shared/_Admin.cshtml
@@ -34,8 +34,8 @@
-
@RenderSection("javascript", false)
+
}
@await Html.PartialAsync("navigator")
diff --git a/Bootstrap.Admin/wwwroot/js/log.js b/Bootstrap.Admin/wwwroot/js/log.js
index 52cab6cf..7f7f477a 100644
--- a/Bootstrap.Admin/wwwroot/js/log.js
+++ b/Bootstrap.Admin/wwwroot/js/log.js
@@ -30,6 +30,9 @@
},
'#btnSubmitMenu': function () {
this.log({ crud: '分配菜单' });
+ },
+ '#btnReset': function () {
+ this.log({ crud: '重置密码' });
}
}
};
diff --git a/Bootstrap.Admin/wwwroot/js/login.js b/Bootstrap.Admin/wwwroot/js/login.js
index 23e07d06..7a22d06e 100644
--- a/Bootstrap.Admin/wwwroot/js/login.js
+++ b/Bootstrap.Admin/wwwroot/js/login.js
@@ -29,7 +29,7 @@
$('#btnForgot').on('click', function () {
$.bc({
url: "api/Register",
- data: { UserName: $('#f_userName').val(), DisplayName: $('#f_displayName').val(), Description: $('#f_desc').val() },
+ data: { UserName: $('#f_userName').val(), DisplayName: $('#f_displayName').val(), Reason: $('#f_desc').val() },
modal: '#dialogForgot',
method: "put",
callback: function (result) {
diff --git a/Bootstrap.Admin/wwwroot/js/users.js b/Bootstrap.Admin/wwwroot/js/users.js
index ba5d1637..a351c495 100644
--- a/Bootstrap.Admin/wwwroot/js/users.js
+++ b/Bootstrap.Admin/wwwroot/js/users.js
@@ -5,8 +5,11 @@
var $dialogGroup = $("#dialogGroup");
var $dialogGroupHeader = $('#myGroupModalLabel');
var $dialogGroupForm = $('#groupForm');
+ var $dialogReset = $('#dialogReset');
+ var $dialogResetHeader = $('#myResetModalLabel');
+ var $table = $('table');
- $('table').lgbTable({
+ $table.lgbTable({
url: User.url,
dataBinder: {
map: {
@@ -62,6 +65,9 @@
return $(element).val();
}).toArray();
$.bc({ id: userId, url: Group.url, method: 'put', data: groupIds, query: { type: "user" }, title: Group.title, modal: '#dialogGroup' });
+ },
+ '#btnReset': function (row) {
+ $.bc({ id: row.UserName, url: 'api/Register', method: 'put', data: { password: $('#resetPassword').val() }, modal: "#dialogReset", title: "重置密码", callback: function (result) { if (result) $table.bootstrapTable('refresh'); } });
}
},
callback: function (data) {
@@ -86,7 +92,20 @@
{ title: "注册时间", field: "RegisterTime", sortable: true },
{ title: "授权时间", field: "ApprovedTime", sortable: true },
{ title: "授权人", field: "ApprovedBy", sortable: true },
- { title: "说明", field: "Description", sortable: false }
+ { title: "说明", field: "Description", sortable: false },
+ {
+ title: "操作", field: "IsReset", formatter: function (value, row, index) {
+ return value === 1 ? '' : '';
+ },
+ events: {
+ 'click .reset': function (e, value, row, index) {
+ $table.bootstrapTable('uncheckAll');
+ $table.bootstrapTable('check', index);
+ $dialogResetHeader.text($.format("{0} - 重置密码窗口", row.UserName));
+ $dialogReset.modal('show');
+ }
+ }
+ }
]
}
});
diff --git a/Bootstrap.DataAccess/Helper/UserHelper.cs b/Bootstrap.DataAccess/Helper/UserHelper.cs
index e92f5607..7d124a64 100644
--- a/Bootstrap.DataAccess/Helper/UserHelper.cs
+++ b/Bootstrap.DataAccess/Helper/UserHelper.cs
@@ -124,6 +124,14 @@ namespace Bootstrap.DataAccess
///
public static bool ChangePassword(string userName, string password, string newPass) => DbContextManager.Create().ChangePassword(userName, password, newPass);
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool ResetPassword(string userName, string password) => DbContextManager.Create().ResetPassword(userName, password);
+
///
///
///
@@ -131,11 +139,7 @@ namespace Bootstrap.DataAccess
///
///
///
- public static bool ForgotPassword(string userName, string displayName, string desc)
- {
- // UNDONE 忘记密码涉及到安全问题,防止用户恶意重置其他用户,待定
- return true;
- }
+ public static bool ForgotPassword(string userName, string displayName, string desc) => DbContextManager.Create().ForgotPassword(userName, displayName, desc);
///
///
@@ -236,5 +240,12 @@ namespace Bootstrap.DataAccess
///
///
public static BootstrapUser RetrieveUserByUserName(string userName) => CacheManager.GetOrAdd(string.Format("{0}-{1}", RetrieveUsersByNameDataKey, userName), k => DbContextManager.Create().RetrieveUserByUserName(userName), RetrieveUsersByNameDataKey);
+
+ ///
+ /// 通过登录账号获得用户信息
+ ///
+ ///
+ ///
+ public static ResetUser RetrieveResetUserByUserName(string userName) => DbContextManager.Create().RetrieveUserByUserName(userName);
}
}
diff --git a/Bootstrap.DataAccess/ResetUsers.cs b/Bootstrap.DataAccess/ResetUsers.cs
new file mode 100644
index 00000000..4b3b3d58
--- /dev/null
+++ b/Bootstrap.DataAccess/ResetUsers.cs
@@ -0,0 +1,62 @@
+using PetaPoco;
+using System;
+
+namespace Bootstrap.DataAccess
+{
+ ///
+ ///
+ ///
+ [TableName("ResetUsers")]
+ public class ResetUser
+ {
+ ///
+ /// 获得/设置 用户主键ID
+ ///
+ public string Id { get; set; }
+
+ ///
+ ///
+ ///
+ public string UserName { get; set; }
+
+ ///
+ ///
+ ///
+ public string DisplayName { get; set; }
+
+ ///
+ ///
+ ///
+ public string Reason { get; set; }
+
+ ///
+ ///
+ ///
+ public DateTime ResetTime { get; set; }
+
+ ///
+ ///
+ ///
+ ///
+ public virtual bool Save()
+ {
+ var db = DbManager.Create();
+ db.Save(this);
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual ResetUser RetrieveUserByUserName(string userName) => DbManager.Create().FirstOrDefault("where UserName = @0 order by ResetTime desc", userName);
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual void DeleteByUserName(string userName) => DbManager.Create().Delete("where UserName = @0", userName);
+ }
+}
diff --git a/Bootstrap.DataAccess/User.cs b/Bootstrap.DataAccess/User.cs
index 5beed944..039af467 100644
--- a/Bootstrap.DataAccess/User.cs
+++ b/Bootstrap.DataAccess/User.cs
@@ -2,6 +2,7 @@
using Bootstrap.Security.DataAccess;
using Longbow.Data;
using Longbow.Security.Cryptography;
+using PetaPoco;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -68,6 +69,12 @@ namespace Bootstrap.DataAccess
///
public string NewPassword { get; set; }
+ ///
+ /// 获得/设置 是否重置密码
+ ///
+ [Ignore]
+ public int IsReset { get; set; }
+
///
/// 验证用户登陆账号与密码正确
///
@@ -114,7 +121,7 @@ namespace Bootstrap.DataAccess
///
///
///
- public virtual IEnumerable Retrieves() => DbManager.Create().Fetch("select ID, UserName, DisplayName, RegisterTime, ApprovedTime, ApprovedBy, Description from Users Where ApprovedTime is not null");
+ public virtual IEnumerable Retrieves() => DbManager.Create().Fetch("select u.ID, u.UserName, u.DisplayName, RegisterTime, ApprovedTime, ApprovedBy, Description, ru.IsReset from Users u left join (select 1 as IsReset, UserName from ResetUsers group by UserName) ru on u.UserName = ru.UserName Where ApprovedTime is not null");
///
/// 查询所有的新注册用户
@@ -149,6 +156,56 @@ namespace Bootstrap.DataAccess
return ret;
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual bool ResetPassword(string userName, string password)
+ {
+ var ret = false;
+ var resetUser = UserHelper.RetrieveResetUserByUserName(userName);
+ if (resetUser == null) return ret;
+
+ string sql = "set Password = @0, PassSalt = @1 where UserName = @2";
+ var passSalt = LgbCryptography.GenerateSalt();
+ var newPassword = LgbCryptography.ComputeHash(password, passSalt);
+ var db = DbManager.Create();
+ try
+ {
+ db.BeginTransaction();
+ ret = db.Update(sql, newPassword, passSalt, userName) == 1;
+ if (ret) db.Execute("delete from ResetUsers where UserName = @0", userName);
+ db.CompleteTransaction();
+ }
+ catch (Exception ex)
+ {
+ db.AbortTransaction();
+ throw ex;
+ }
+ return ret;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual bool ForgotPassword(string userName, string displayName, string desc)
+ {
+ ResetUser user = new ResetUser()
+ {
+ UserName = userName,
+ DisplayName = displayName,
+ Reason = desc,
+ ResetTime = DateTime.Now
+ };
+ return user.Save();
+ }
+
///
/// 新建前台User View调用/注册用户调用
///
diff --git a/DatabaseScripts/Install.sql b/DatabaseScripts/Install.sql
index 2c7a3bdd..dbe701b2 100644
--- a/DatabaseScripts/Install.sql
+++ b/DatabaseScripts/Install.sql
@@ -507,3 +507,30 @@ GO
SET ANSI_PADDING OFF
GO
+
+/****** Object: Table [dbo].[ResetUsers] Script Date: 03/05/2019 12:28:32 ******/
+SET ANSI_NULLS ON
+GO
+
+SET QUOTED_IDENTIFIER ON
+GO
+
+SET ANSI_PADDING ON
+GO
+
+CREATE TABLE [dbo].[ResetUsers](
+ [Id] [int] IDENTITY(1,1) NOT NULL,
+ [UserName] [varchar](50) NOT NULL,
+ [DisplayName] [nvarchar](50) NOT NULL,
+ [Reason] [nvarchar](500) NOT NULL,
+ [ResetTime] [datetime] NOT NULL,
+ CONSTRAINT [PK_ResetUsers] 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
+
+SET ANSI_PADDING OFF
+GO
diff --git a/UnitTest/BAWebHost.cs b/UnitTest/BAWebHost.cs
index 6b1489fa..c6426eee 100644
--- a/UnitTest/BAWebHost.cs
+++ b/UnitTest/BAWebHost.cs
@@ -76,10 +76,15 @@ namespace Bootstrap.Admin
public HttpClient CreateClient(string baseAddress)
{
var client = CreateDefaultClient(new Uri($"http://localhost/{baseAddress}/"), new RedirectHandler(7), new CookieContainerHandler(_cookie));
- client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.1 Safari/605.1.15");
return client;
}
+ protected override void ConfigureClient(HttpClient client)
+ {
+ base.ConfigureClient(client);
+ client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.1 Safari/605.1.15");
+ }
+
private readonly CookieContainer _cookie = new CookieContainer();
protected override void ConfigureWebHost(IWebHostBuilder builder)
diff --git a/UnitTest/Bootstrap.Admin/Api/RegisterTest.cs b/UnitTest/Bootstrap.Admin/Api/RegisterTest.cs
index 22816202..5adb6178 100644
--- a/UnitTest/Bootstrap.Admin/Api/RegisterTest.cs
+++ b/UnitTest/Bootstrap.Admin/Api/RegisterTest.cs
@@ -25,5 +25,26 @@ namespace Bootstrap.Admin.Api
nusr.Delete(nusr.RetrieveNewUsers().Where(u => u.UserName == nusr.UserName).Select(u => u.Id));
}
+
+ [Fact]
+ public async void Put_Ok()
+ {
+ var user = new ResetUser() { DisplayName = "UnitTest", UserName = "UnitTest", Reason = "UnitTest" };
+ var resp = await Client.PutAsJsonAsync(user);
+ Assert.True(resp);
+ }
+
+ [Fact]
+ public async void Put_UserName()
+ {
+ var user = new User() { Password = "1" };
+ var resp = await Client.PutAsJsonAsync("UnitTest", user);
+ Assert.False(resp);
+
+ // 重置Admin密码
+ await Client.PutAsJsonAsync(new ResetUser { UserName = "Admin", DisplayName = "Administrator", Reason = "UnitTest" });
+ resp = await Client.PutAsJsonAsync("Admin", new User() { Password = "123789" });
+ Assert.True(resp);
+ }
}
}
diff --git a/UnitTest/Bootstrap.DataAccess/ResetUserTest.cs b/UnitTest/Bootstrap.DataAccess/ResetUserTest.cs
new file mode 100644
index 00000000..b2597544
--- /dev/null
+++ b/UnitTest/Bootstrap.DataAccess/ResetUserTest.cs
@@ -0,0 +1,61 @@
+using System;
+using Xunit;
+
+namespace Bootstrap.DataAccess
+{
+ [Collection("SQLServerContext")]
+ public class ResetUserTest
+ {
+ [Fact]
+ public void Save_Ok()
+ {
+ var resetUser = new ResetUser()
+ {
+ UserName = "UnitTest",
+ Reason = "UnitTest",
+ DisplayName = "UnitTest",
+ ResetTime = DateTime.Now
+ };
+ var db = DbManager.Create();
+ db.Save(resetUser);
+ var count = db.ExecuteScalar("select count(Id) from ResetUsers");
+ Assert.True(count > 0);
+ }
+
+ [Fact]
+ public void RetrieveUserByUserName_Ok()
+ {
+ var resetUser = new ResetUser()
+ {
+ UserName = "UnitTest",
+ Reason = "UnitTest",
+ DisplayName = "UnitTest",
+ ResetTime = DateTime.Now
+ };
+ var db = DbManager.Create();
+ db.Save(resetUser);
+
+ var user = resetUser.RetrieveUserByUserName(resetUser.UserName);
+ Assert.Equal("UnitTest", user.UserName);
+ Assert.Equal("UnitTest", user.DisplayName);
+ }
+
+ [Fact]
+ public void DeleteByUserName_Ok()
+ {
+ var resetUser = new ResetUser()
+ {
+ UserName = "UnitTest",
+ Reason = "UnitTest",
+ DisplayName = "UnitTest",
+ ResetTime = DateTime.Now
+ };
+ var db = DbManager.Create();
+ db.Save(resetUser);
+
+ resetUser.DeleteByUserName(resetUser.UserName);
+ var count = db.ExecuteScalar("select count(Id) from ResetUsers");
+ Assert.Equal(0, count);
+ }
+ }
+}