diff --git a/Bootstrap.Admin/Controllers/AccountController.cs b/Bootstrap.Admin/Controllers/AccountController.cs index 56433c25..c5fa9ed1 100644 --- a/Bootstrap.Admin/Controllers/AccountController.cs +++ b/Bootstrap.Admin/Controllers/AccountController.cs @@ -2,9 +2,11 @@ using Bootstrap.DataAccess; using Longbow; using Longbow.Configuration; +using Longbow.Web; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Linq; @@ -34,13 +36,14 @@ namespace Bootstrap.Admin.Controllers /// Login the specified userName, password and remember. /// /// The login. + /// /// User name. /// Password. /// Remember. [HttpPost] - public async Task Login(string userName, string password, string remember) + public async Task Login([FromServices]IOnlineUsers onlineUserSvr, string userName, string password, string remember) { - if (UserHelper.Authenticate(userName, password)) + if (UserHelper.Authenticate(userName, password, loginUser => CreateLoginUser(onlineUserSvr, HttpContext, loginUser))) { var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.Name, userName)); @@ -51,6 +54,21 @@ namespace Bootstrap.Admin.Controllers return Redirect(originUrl); } + /// + /// + /// + /// + /// + /// + internal static void CreateLoginUser(IOnlineUsers onlineUserSvr, HttpContext context, LoginUser loginUser) + { + var agent = new UserAgent(context.Request.Headers["User-Agent"]); + loginUser.Ip = context.Connection.RemoteIpAddress?.ToString(); + loginUser.City = onlineUserSvr.RetrieveLocaleByIp(loginUser.Ip); + loginUser.Browser = $"{agent.Browser.Name} {agent.Browser.Version}"; + loginUser.OS = $"{agent.OS.Name} {agent.OS.Version}"; + } + /// /// Logout this instance. /// diff --git a/Bootstrap.Admin/Controllers/AdminController.cs b/Bootstrap.Admin/Controllers/AdminController.cs index a4ea5266..3d864ccd 100644 --- a/Bootstrap.Admin/Controllers/AdminController.cs +++ b/Bootstrap.Admin/Controllers/AdminController.cs @@ -52,6 +52,12 @@ namespace Bootstrap.Admin.Controllers /// public ActionResult Logs() => View(new NavigatorBarModel(this)); + /// + /// + /// + /// + public ActionResult Logins() => View(new NavigatorBarModel(this)); + /// /// /// diff --git a/Bootstrap.Admin/Controllers/Api/LoginController.cs b/Bootstrap.Admin/Controllers/Api/LoginController.cs index ad81582f..592163b2 100644 --- a/Bootstrap.Admin/Controllers/Api/LoginController.cs +++ b/Bootstrap.Admin/Controllers/Api/LoginController.cs @@ -3,6 +3,8 @@ using Bootstrap.Security; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Linq; namespace Bootstrap.Admin.Controllers.Api { @@ -16,20 +18,28 @@ namespace Bootstrap.Admin.Controllers.Api [ApiController] public class LoginController : ControllerBase { + /// + /// 获得登录历史记录 + /// + /// + [HttpGet] + public IEnumerable Get() => LoginHelper.Retrieves(); + /// /// /// + /// /// /// [AllowAnonymous] [HttpPost] - public string Post([FromBody]JObject value) + public string Post([FromServices]IOnlineUsers onlineUserSvr, [FromBody]JObject value) { string token = null; dynamic user = value; string userName = user.userName; string password = user.password; - if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password) && UserHelper.Authenticate(userName, password)) + if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password) && UserHelper.Authenticate(userName, password, loginUser => AccountController.CreateLoginUser(onlineUserSvr, HttpContext, loginUser))) { token = BootstrapAdminJwtTokenHandler.CreateToken(userName); } diff --git a/Bootstrap.Admin/Views/Admin/Logins.cshtml b/Bootstrap.Admin/Views/Admin/Logins.cshtml new file mode 100644 index 00000000..3caa048f --- /dev/null +++ b/Bootstrap.Admin/Views/Admin/Logins.cshtml @@ -0,0 +1,29 @@ +@model NavigatorBarModel +@{ + ViewBag.Title = "登录日志"; +} +@section css { + + + + + + +} +@section javascript { + + + + + + + + + +} +
+
在线用户
+
+
+
+
\ No newline at end of file diff --git a/Bootstrap.Admin/Views/Admin/Logs.cshtml b/Bootstrap.Admin/Views/Admin/Logs.cshtml index a93083ea..ca009ff5 100644 --- a/Bootstrap.Admin/Views/Admin/Logs.cshtml +++ b/Bootstrap.Admin/Views/Admin/Logs.cshtml @@ -1,6 +1,6 @@ @model NavigatorBarModel @{ - ViewBag.Title = "系统日志"; + ViewBag.Title = "操作日志"; } @section css { diff --git a/Bootstrap.Admin/appsettings.json b/Bootstrap.Admin/appsettings.json index 15dbe2ae..276bcdbd 100644 --- a/Bootstrap.Admin/appsettings.json +++ b/Bootstrap.Admin/appsettings.json @@ -65,6 +65,7 @@ "KeepExceptionsPeriod": 12, "KeepLogsPeriod": 12, "CookieExpiresDays": 7, + "KeepLoginLogsPeriod": 7, "LongbowCache": { "Enabled": true, "CorsItems": [ diff --git a/Bootstrap.Admin/wwwroot/js/logins.js b/Bootstrap.Admin/wwwroot/js/logins.js new file mode 100644 index 00000000..5bb832eb --- /dev/null +++ b/Bootstrap.Admin/wwwroot/js/logins.js @@ -0,0 +1,31 @@ +// 登录日志 +$(function () { + var apiUrl = "api/Login"; + var $table = $('table').smartTable({ + url: apiUrl, + method: "get", + sidePagination: "client", + showToggle: false, + showRefresh: false, + showColumns: false, + columns: [ + { + title: "序号", formatter: function (value, row, index) { + var options = $table.bootstrapTable('getOptions'); + return options.pageSize * (options.pageNumber - 1) + index + 1; + } + }, + { title: "登陆名称", field: "UserName" }, + { title: "登录时间", field: "LoginTime" }, + { title: "主机", field: "Ip" }, + { title: "登录地点", field: "City" }, + { title: "浏览器", field: "Browser" }, + { title: "操作系统", field: "OS" }, + { title: "登录结果", field: "Result" } + ] + }); + + $('#refreshUsers').tooltip().on('click', function () { + $table.bootstrapTable('refresh'); + }); +}); \ No newline at end of file diff --git a/Bootstrap.DataAccess.MongoDB/DbManager.cs b/Bootstrap.DataAccess.MongoDB/DbManager.cs index fb3cc457..75f6b98c 100644 --- a/Bootstrap.DataAccess.MongoDB/DbManager.cs +++ b/Bootstrap.DataAccess.MongoDB/DbManager.cs @@ -116,6 +116,17 @@ namespace Bootstrap.DataAccess.MongoDB return DBAccess.GetCollection("Navigations"); } } + + /// + /// + /// + public static IMongoCollection LoginUsers + { + get + { + return DBAccess.GetCollection("LoginLogs"); + } + } #endregion private static void InitDb() @@ -220,6 +231,15 @@ namespace Bootstrap.DataAccess.MongoDB md.IdMemberMap.SetIgnoreIfDefault(true); }); } + if (!BsonClassMap.IsClassMapRegistered(typeof(DataAccess.LoginUser))) + { + BsonClassMap.RegisterClassMap(md => + { + md.AutoMap(); + md.IdMemberMap.SetSerializer(new StringSerializer(BsonType.ObjectId)); + md.IdMemberMap.SetIgnoreIfDefault(true); + }); + } } } } diff --git a/Bootstrap.DataAccess.MongoDB/LoginUser.cs b/Bootstrap.DataAccess.MongoDB/LoginUser.cs new file mode 100644 index 00000000..5d46434f --- /dev/null +++ b/Bootstrap.DataAccess.MongoDB/LoginUser.cs @@ -0,0 +1,28 @@ +using MongoDB.Driver; +using System.Collections.Generic; + +namespace Bootstrap.DataAccess.MongoDB +{ + /// + /// + /// + public class LoginUser : DataAccess.LoginUser + { + /// + /// + /// + /// + /// + public override bool Log(DataAccess.LoginUser user) + { + DbManager.LoginUsers.InsertOne(user); + return true; + } + + /// + /// + /// + /// + public override IEnumerable Retrieves() => DbManager.LoginUsers.Find(FilterDefinition.Empty).SortByDescending(user => user.LoginTime).ToList(); + } +} diff --git a/Bootstrap.DataAccess/Helper/LoginHelper.cs b/Bootstrap.DataAccess/Helper/LoginHelper.cs new file mode 100644 index 00000000..20844f2a --- /dev/null +++ b/Bootstrap.DataAccess/Helper/LoginHelper.cs @@ -0,0 +1,34 @@ +using Longbow.Cache; +using Longbow.Data; +using System.Collections.Generic; + +namespace Bootstrap.DataAccess +{ + /// + /// + /// + public static class LoginHelper + { + /// + /// + /// + public const string RetrieveLoginLogsDataKey = "LoginHelper-Retrieves"; + + /// + /// + /// + /// + /// + public static bool Log(LoginUser user) + { + var ret = DbContextManager.Create().Log(user); + if (ret) CacheManager.Clear(RetrieveLoginLogsDataKey); + return ret; + } + + /// + /// 查询一个月内所有登录信息 + /// + public static IEnumerable Retrieves() => CacheManager.GetOrAdd(RetrieveLoginLogsDataKey, key => DbContextManager.Create().Retrieves()); + } +} diff --git a/Bootstrap.DataAccess/Helper/UserHelper.cs b/Bootstrap.DataAccess/Helper/UserHelper.cs index eaf761e3..e92f5607 100644 --- a/Bootstrap.DataAccess/Helper/UserHelper.cs +++ b/Bootstrap.DataAccess/Helper/UserHelper.cs @@ -1,6 +1,7 @@ using Bootstrap.Security; using Longbow.Cache; using Longbow.Data; +using System; using System.Collections.Generic; namespace Bootstrap.DataAccess @@ -29,7 +30,21 @@ namespace Bootstrap.DataAccess /// /// /// - public static bool Authenticate(string userName, string password) => DbContextManager.Create().Authenticate(userName, password); + public static bool Authenticate(string userName, string password, Action config) + { + var loginUser = new LoginUser() + { + UserName = userName, + LoginTime = DateTime.Now, + Result = "登录失败" + }; + config(loginUser); + if (string.IsNullOrEmpty(loginUser.Ip)) loginUser.Ip = System.Net.IPAddress.IPv6Loopback.ToString(); + var ret = DbContextManager.Create().Authenticate(userName, password); + if (ret) loginUser.Result = "登录成功"; + LoginHelper.Log(loginUser); + return ret; + } /// /// 查询所有的新注册用户 diff --git a/Bootstrap.DataAccess/LoginUser.cs b/Bootstrap.DataAccess/LoginUser.cs new file mode 100644 index 00000000..0dc50135 --- /dev/null +++ b/Bootstrap.DataAccess/LoginUser.cs @@ -0,0 +1,78 @@ +using Longbow; +using Longbow.Configuration; +using PetaPoco; +using System; +using System.Collections.Generic; + +namespace Bootstrap.DataAccess +{ + /// + /// + /// + [TableName("LoginLogs")] + public class LoginUser + { + /// + /// + /// + public string Id { get; set; } + + /// + /// + /// + public string UserName { get; set; } + + /// + /// + /// + public DateTime LoginTime { get; set; } + + /// + /// + /// + public string Ip { get; set; } + + /// + /// + /// + public string Browser { get; set; } + + /// + /// + /// + public string OS { get; set; } + + /// + /// + /// + public string City { get; set; } + + /// + /// + /// + public string Result { get; set; } + + /// + /// + /// + /// + /// + public virtual bool Log(LoginUser user) + { + var db = DbManager.Create(); + db.Save(user); + return true; + } + + /// + /// + /// + protected const string KeepLoginLogsPeriodKey = "KeepLoginLogsPeriod"; + + /// + /// + /// + /// + public virtual IEnumerable Retrieves() => DbManager.Create().Fetch("Where LoginTime > @0 Order by LoginTime desc", DateTime.Today.AddDays(0 - LgbConvert.ReadValue(ConfigurationManager.AppSettings[KeepLoginLogsPeriodKey], 7))); + } +} diff --git a/DatabaseScripts/InitData.sql b/DatabaseScripts/InitData.sql index 68719da3..9062d4fe 100644 --- a/DatabaseScripts/InitData.sql +++ b/DatabaseScripts/InitData.sql @@ -45,7 +45,9 @@ INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [C INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (10, 0, N'站内消息', 100, N'fa fa-envelope', N'~/Admin/Messages', N'0') INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (11, 0, N'任务管理', 110, N'fa fa fa-tasks', N'~/Admin/Tasks', N'0') INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (12, 0, N'通知管理', 120, N'fa fa-bell', N'~/Admin/Notifications', N'0') -INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (13, 0, N'系统日志', 130, N'fa fa-gears', N'~/Admin/Logs', N'0') +INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (13, 0, N'日志管理', 130, N'fa fa-gears', N'#', N'0') +INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (20, 13, N'操作日志', 10, N'fa fa-edit', N'~/Admin/Logs', N'0') +INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (21, 13, N'登录日志', 20, N'fa fa-user-circle-o', N'~/Admin/Logins', N'0') INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (14, 0, N'在线用户', 140, N'fa fa-users', N'~/Admin/Online', N'0') INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (15, 0, N'程序异常', 150, N'fa fa-cubes', N'~/Admin/Exceptions', N'0') INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (16, 0, N'工具集合', 160, N'fa fa-gavel', N'#', N'0') diff --git a/DatabaseScripts/Install.sql b/DatabaseScripts/Install.sql index 3a60f7cf..2c7a3bdd 100644 --- a/DatabaseScripts/Install.sql +++ b/DatabaseScripts/Install.sql @@ -476,4 +476,34 @@ EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'显示名称' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'注册时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'RejectUsers', @level2type=N'COLUMN',@level2name=N'RegisterTime' -GO \ No newline at end of file +GO + +/****** Object: Table [dbo].[LoginLogs] Script Date: 03/03/2019 20:05:42 ******/ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +SET ANSI_PADDING ON +GO + +CREATE TABLE [dbo].[LoginLogs]( + [ID] [int] IDENTITY(1,1) NOT NULL, + [UserName] [varchar](50) NOT NULL, + [LoginTime] [datetime] NOT NULL, + [Ip] [varchar](15) NOT NULL, + [OS] [varchar](50) NULL, + [Browser] [varchar](50) NULL, + [City] [nvarchar](50) NULL, + [Result] [nvarchar](50) NOT NULL, + CONSTRAINT [PK_LoginLogs] 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/DatabaseScripts/MongoDB/BootstrapAdmin.Navigations.json b/DatabaseScripts/MongoDB/BootstrapAdmin.Navigations.json index e00a5823..0fd52445 100644 --- a/DatabaseScripts/MongoDB/BootstrapAdmin.Navigations.json +++ b/DatabaseScripts/MongoDB/BootstrapAdmin.Navigations.json @@ -146,15 +146,39 @@ { "_id": ObjectId("5bd7b8445fa31256f77e4b9c"), "ParentId": "0", - "Name": "系统日志", + "Name": "日志管理", "Order": NumberInt(130), "Icon": "fa fa-gears", + "Url": "#", + "Category": "0", + "Target": "_self", + "IsResource": NumberInt(0), + "Application": "0" + }, + { + "_id": ObjectId("5bd9b3d868aa001661776f57"), + "ParentId": "5bd7b8445fa31256f77e4b9c", + "Name": "操作日志", + "Order": NumberInt(10), + "Icon": "fa fa-edit", "Url": "~/Admin/Logs", "Category": "0", "Target": "_self", "IsResource": NumberInt(0), "Application": "0" }, + { + "_id": ObjectId("5bd9b3d868aa001661776f58"), + "ParentId": "5bd7b8445fa31256f77e4b9c", + "Name": "登录日志", + "Order": NumberInt(20), + "Icon": "fa fa-user-circle-o", + "Url": "~/Admin/Logins", + "Category": "0", + "Target": "_self", + "IsResource": NumberInt(0), + "Application": "0" + }, { "_id": ObjectId("5bd7b8445fa31256f77e4b89"), "ParentId": "0", diff --git a/DatabaseScripts/MySQL/initData.sql b/DatabaseScripts/MySQL/initData.sql index d02e41e7..f06c3b2d 100644 --- a/DatabaseScripts/MySQL/initData.sql +++ b/DatabaseScripts/MySQL/initData.sql @@ -42,7 +42,9 @@ INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUE INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (10, 0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0'); INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (11, 0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0'); INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (12, 0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0'); -INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (13, 0, '系统日志', 130, 'fa fa-gears', '~/Admin/Logs', '0'); +INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (13, 0, '系统日志', 130, 'fa fa-gears', '#', '0'); +INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (20, 13, '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0'); +INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (21, 13, '登录日志', 20, 'fa fa-user-circle-o', '~/Admin/Logins', '0'); INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (14, 0, '在线用户', 140, 'fa fa-users', '~/Admin/Online', '0'); INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (15, 0, '程序异常', 150, 'fa fa-cubes', '~/Admin/Exceptions', '0'); INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (16, 0, '工具集合', 160, 'fa fa-gavel', '#', '0'); diff --git a/DatabaseScripts/MySQL/install.sql b/DatabaseScripts/MySQL/install.sql index fbb9057c..62b1f67e 100644 --- a/DatabaseScripts/MySQL/install.sql +++ b/DatabaseScripts/MySQL/install.sql @@ -146,4 +146,15 @@ CREATE TABLE RejectUsers( RejectedBy VARCHAR (50) NOT NULL, RejectedTime DATETIME NOT NULL, RejectedReason VARCHAR (50) NULL +); + +CREATE TABLE LoginLogs( + ID INTEGER PRIMARY KEY Auto_increment, + UserName VARCHAR (50) NOT NULL, + LoginTime DATETIME NOT NULL, + Ip VARCHAR NOT NULL, + OS VARCHAR (50) NULL, + Browser VARCHAR (50) NULL, + City VARCHAR (50) NULL, + Result VARCHAR (50) NOT NULL ); \ No newline at end of file diff --git a/DatabaseScripts/Postgresql/initData.sql b/DatabaseScripts/Postgresql/initData.sql index abbcc8ab..e9cd5335 100644 --- a/DatabaseScripts/Postgresql/initData.sql +++ b/DatabaseScripts/Postgresql/initData.sql @@ -42,7 +42,9 @@ INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0 INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0'); -INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '系统日志', 130, 'fa fa-gears', '~/Admin/Logs', '0'); +INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '系统日志', 130, 'fa fa-gears', '#', '0'); +INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (currval('navigations_id_seq') - 1, 0, '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0'); +INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (currval('navigations_id_seq') - 2, 0, '登录日志', 20, 'fa fa-user-circle-o', '~/Admin/Logins', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '在线用户', 140, 'fa fa-users', '~/Admin/Online', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '程序异常', 150, 'fa fa-cubes', '~/Admin/Exceptions', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '工具集合', 160, 'fa fa-gavel', '#', '0'); diff --git a/DatabaseScripts/Postgresql/install.sql b/DatabaseScripts/Postgresql/install.sql index 60e31eab..def463e3 100644 --- a/DatabaseScripts/Postgresql/install.sql +++ b/DatabaseScripts/Postgresql/install.sql @@ -146,4 +146,15 @@ CREATE TABLE RejectUsers( RejectedBy VARCHAR (50) NOT NULL, RejectedTime DATE NOT NULL, RejectedReason VARCHAR (50) NULL +); + +CREATE TABLE RejectUsers( + ID SERIAL PRIMARY KEY, + UserName VARCHAR (50) NOT NULL, + LoginTime DATE NOT NULL, + Ip VARCHAR NOT NULL, + OS VARCHAR (50) NULL, + Browser VARCHAR (50) NULL, + City VARCHAR (50) NULL, + Result VARCHAR (50) NOT NULL ); \ No newline at end of file diff --git a/DatabaseScripts/SQLite/InitData.sql b/DatabaseScripts/SQLite/InitData.sql index fadaf135..b0137ba0 100644 --- a/DatabaseScripts/SQLite/InitData.sql +++ b/DatabaseScripts/SQLite/InitData.sql @@ -39,7 +39,9 @@ INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Ca INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (10, 0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0'); INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (11, 0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0'); INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (12, 0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0'); -INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (13, 0, '系统日志', 130, 'fa fa-gears', '~/Admin/Logs', '0'); +INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (13, 0, '系统日志', 130, 'fa fa-gears', '#', '0'); +INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (20, 13, '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0'); +INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (21, 13, '登录日志', 20, 'fa fa-user-circle-o', '~/Admin/Logins', '0'); INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (14, 0, '在线用户', 140, 'fa fa-users', '~/Admin/Online', '0'); INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (15, 0, '程序异常', 150, 'fa fa-cubes', '~/Admin/Exceptions', '0'); INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (16, 0, '工具集合', 160, 'fa fa-gavel', '#', '0'); diff --git a/DatabaseScripts/SQLite/Install.sql b/DatabaseScripts/SQLite/Install.sql index 861b51f3..13334d96 100644 --- a/DatabaseScripts/SQLite/Install.sql +++ b/DatabaseScripts/SQLite/Install.sql @@ -146,4 +146,15 @@ CREATE TABLE RejectUsers( [RejectedBy] VARCHAR (50) NOT NULL COLLATE NOCASE, [RejectedTime] DATETIME NOT NULL, [RejectedReason] VARCHAR (50) NULL +); + +CREATE TABLE LoginLogs( + [ID] INTEGER PRIMARY KEY, + [UserName] VARCHAR (50) NOT NULL COLLATE NOCASE, + [LoginTime] DATETIME NOT NULL, + [Ip] VARCHAR (15) NOT NULL, + [OS] VARCHAR (50) NULL, + [Browser] VARCHAR (50) NULL, + [City] VARCHAR (50) NULL, + [Result] VARCHAR (50) NOT NULL ); \ No newline at end of file diff --git a/UnitTest/Bootstrap.Admin/Api/LoginTest.cs b/UnitTest/Bootstrap.Admin/Api/LoginTest.cs index 7c275c2a..6e18b793 100644 --- a/UnitTest/Bootstrap.Admin/Api/LoginTest.cs +++ b/UnitTest/Bootstrap.Admin/Api/LoginTest.cs @@ -1,4 +1,6 @@ -using System.Net.Http; +using Bootstrap.DataAccess; +using System.Collections.Generic; +using System.Net.Http; using Xunit; namespace Bootstrap.Admin.Api @@ -7,6 +9,13 @@ namespace Bootstrap.Admin.Api { public LoginTest(BAWebHost factory) : base(factory, "api/Login") { } + [Fact] + public async void Login_Get() + { + var users = await Client.GetAsJsonAsync>(); + Assert.NotEmpty(users); + } + [Fact] public async void Login_Ok() { diff --git a/UnitTest/Bootstrap.Admin/Controllers/AdminTest.cs b/UnitTest/Bootstrap.Admin/Controllers/AdminTest.cs index 80acdc5b..9a58e1cb 100644 --- a/UnitTest/Bootstrap.Admin/Controllers/AdminTest.cs +++ b/UnitTest/Bootstrap.Admin/Controllers/AdminTest.cs @@ -14,7 +14,8 @@ namespace Bootstrap.Admin.Controllers [InlineData("Dicts", "字典表维护")] [InlineData("Roles", "角色管理")] [InlineData("Menus", "菜单管理")] - [InlineData("Logs", "系统日志")] + [InlineData("Logs", "操作日志")] + [InlineData("Logins", "登录日志")] [InlineData("FAIcon", "图标集")] [InlineData("IconView", "图标分类")] [InlineData("Settings", "网站设置")] diff --git a/UnitTest/DB/UnitTest.db b/UnitTest/DB/UnitTest.db index 75955d9b..e5cacefd 100644 Binary files a/UnitTest/DB/UnitTest.db and b/UnitTest/DB/UnitTest.db differ