http://www.bubuko.com/infodetail-2130667.html 使用maven创建子工程时,子工程需要依赖父工程,创建以后报错Project build error: Non-resolvable parent POM: Failure to find…… 网上的答案绝大多数都是在<parent></parent>中间加一行:<relativePath>../../pom.xml</relativePath>(原因是找不到路径) 但是我加了依旧没有解决,我的解决方法是,在子项目名点击右键--> run as -->maven install。待自动下载完包后就便可以解决错误 from:https://blog.csdn.net/baidu_18607183/article/details/80479925
View Details在本部分中,将修改 HelloWorldController 类,进而使用 Razor 视图文件来顺利封装为客户端生成 HTML 响应的过程。 使用 Razor 创建视图模板文件。 当前,Index 方法返回带有在控制器类中硬编码的消息的字符串。 C#
1 2 3 4 |
public IActionResult Index() { return View(); } |
上面的代码调用控制器的 View 方法。 添加视图 Visual Studio Visual Studio Code Visual Studio for Mac 右键单击“视图”文件夹,然后单击“添加”>“新文件夹”,并将文件夹命名为“HelloWorld”。 右键单击“Views/HelloWorld”文件夹,然后单击“添加”>“新项”。 在“添加新项 – MvcMovie”对话框中 在右上角的搜索框中,输入“视图” 选择“Razor 视图” 保持“名称”框的值:Index.cshtml 。 选择“添加” 为 HelloWorldController 添加 Index 视图。 添加一个名为“Views/HelloWorld”的新文件夹。 向 Views/HelloWorld 文件夹添加名为“Index.cshtml”的新文件。 右键单击“视图”文件夹,然后单击“添加”>“新文件夹”,并将文件夹命名为“HelloWorld”。 右键单击“Views/HelloWorld”文件夹,然后单击“添加”>“新文件” 。 在“新建文件”对话框中 : 在左窗格中,选择“Web” 。 在中间窗格中,选择“空 HTML 文件” 。 在“名称”框中键入“Index.cshtml” 。 选择“新建” 。 使用以下内容替换 Razor 视图文件 Views/HelloWorld/Index.cshtml 的内容 : HTML
1 |
@{ ViewData["Title"] = "Index"; } <h2>Index</h2> <p>Hello from our View Template!</p> |
导航到 https://localhost:{PORT}/HelloWorld。 更改视图和布局页面 选择菜单链接(“MvcMovie”、“首页”和“隐私”) 。 布局模板使你能够在一个位置指定网站的 HTML 容器布局,然后将它应用到网站中的多个页面。 更改布局文件中的标题、页脚和菜单链接 在标题和页脚元素中,将 MvcMovie 更改为 Movie App。 将定位点元素 <a […]
View Details众所周知,在asp.net core中编写Razor视图的时候,用了一种新的写法--TagHelper 那这个TagHelper是怎么回事呢? 首先来看看TagHelper的项目位置,它是位于Microsoft.AspNetCore.Mvc.TagHelpers。 如果看到project.json,可以发现,它还依赖一个比较重要的东西Microsoft.AspNetCore.Mvc.Razor 为什么这么说呢,其实很简单,看了里面诸多TagHelper,就会发现,里面都是继承了 Microsoft.AspNetCore.Razor.TagHelpers下面的TagHelper这个抽象类。 下面就以我们天天用到的表单--FormTagHelper为例来说一下,他是怎么实现的。 首先要看看TagHelper这个抽象类:
1 2 3 4 5 6 7 8 |
public abstract class TagHelper : ITagHelper { protected TagHelper(); public virtual int Order { get; } public virtual void Init(TagHelperContext context); public virtual void Process(TagHelperContext context, TagHelperOutput output); public virtual Task ProcessAsync(TagHelperContext context, TagHelperOutput output); } |
里面包含两比较重要的方法:Process和ProcessAsync 其实看方法名就应该知道一个是同步的方法一个是异步的方法 因为这个是输出html的方法,你说,这能不重要吗?下面来看看FormTagHelper的具体实现吧!
1 |
[HtmlTargetElement("form", Attributes = ActionAttributeName)] |
先来看看HtmlTargetElement这个Attribute是用来干嘛的 简单来说,它指定了我们html标签(<form></form>)以及一些相关的元素。 可以看到,诸多Attributes = XXXAttributeName,其中的XXXAttributeName是在类里面定义的变量。
1 2 3 4 5 6 7 8 |
private const string ActionAttributeName = "asp-action"; private const string AntiforgeryAttributeName = "asp-antiforgery"; private const string AreaAttributeName = "asp-area"; private const string ControllerAttributeName = "asp-controller"; private const string RouteAttributeName = "asp-route"; private const string RouteValuesDictionaryName = "asp-all-route-data"; private const string RouteValuesPrefix = "asp-route-"; private const string HtmlActionAttributeName = "action"; |
再来看看下面的图,相对比一看,是不是就很清晰了呢? 我们可以看到下面的好几个属性,如Controller,它的上面是有 HtmlAttributeName来标注的 而且这个指向的名字还是ControllerAttributeName(也就是asp-controller)。这个就是用来接收asp-controller的值。
1 2 |
[HtmlAttributeName(ControllerAttributeName)] public string Controller { get; set; } |
相对来说,这样做只是起了个别名。
1 2 3 4 5 6 7 8 |
[HtmlTargetElement("form", Attributes = ActionAttributeName)] [HtmlTargetElement("form", Attributes = AntiforgeryAttributeName)] [HtmlTargetElement("form", Attributes = AreaAttributeName)] [HtmlTargetElement("form", Attributes = ControllerAttributeName)] [HtmlTargetElement("form", Attributes = RouteAttributeName)] [HtmlTargetElement("form", Attributes = RouteValuesDictionaryName)] [HtmlTargetElement("form", Attributes = RouteValuesPrefix + "*")] public class FormTagHelper : TagHelper |
当然,我们也是可以不指定别名的,也可以不用在HtmlTargetElement指明Attributes 好比如下的代码,就可以直接用Controller
1 2 3 4 5 |
[HtmlTargetElement("form")] public class FormTagHelper : TagHelper { public string Controller { get; set; } } |
还有一个RouteValues的属性,它是一个键值对,用来存放参数的,具体可以怎么用呢? 总的来说有两种用法。可以看到它指向asp-all-route-data和asp-route-
1 |
[HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)] |
用法如下:一种是用asp-all-route-data来接收一个IDictionary类型的变量,一种是通过asp-route-*的方式来接收参数*的值。 这两种写法是等价的。 下面就是FormTagHelper的构造函数和一个Generator属性
1 2 3 4 5 |
public FormTagHelper(IHtmlGenerator generator) { Generator = generator; } protected IHtmlGenerator Generator { get; } |
由于在Core中,依赖注入随处可见,看到这个写法马上就是想到了这个 果不其然,发现其对应了一个实现类:DefaultHtmlGenerator。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class DefaultHtmlGenerator : IHtmlGenerator { public DefaultHtmlGenerator(IAntiforgery antiforgery, IOptions<MvcViewOptions> optionsAccessor, IModelMetadataProvider metadataProvider, IUrlHelperFactory urlHelperFactory, HtmlEncoder htmlEncoder, ClientValidatorCache clientValidatorCache); public virtual TagBuilder GenerateActionLink(ViewContext viewContext, string linkText, string actionName, string controllerName, string protocol, string hostname, string fragment, object routeValues, object htmlAttributes); public virtual IHtmlContent GenerateAntiforgery(ViewContext viewContext); public virtual TagBuilder GenerateForm(ViewContext viewContext, string actionName, string controllerName, object routeValues, string method, object htmlAttributes); public virtual TagBuilder GenerateLabel(ViewContext viewContext, ModelExplorer modelExplorer, string expression, string labelText, object htmlAttributes); public virtual TagBuilder GenerateTextArea(ViewContext viewContext, ModelExplorer modelExplorer, string expression, int rows, int columns, object htmlAttributes); public virtual TagBuilder GenerateTextBox(ViewContext viewContext, ModelExplorer modelExplorer, string expression, object value, string format, object htmlAttributes); protected virtual TagBuilder GenerateInput(ViewContext viewContext, InputType inputType, ModelExplorer modelExplorer, string expression, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes); protected virtual TagBuilder GenerateLink(string linkText, string url, object htmlAttributes); ....省略部分 } |
这个类里面,我们看到了熟悉的TagBuilder,就算不去看它里面的实现都能知道它是用来干嘛的 它就是用来创建我们的Html标签,相信用过MVC的,多多少少都扩展过HtmlHelper,这是类似的。 最后,也是最最重要的重写的Process方法。 可以看到开始就判断了表单<form>中是否包含了action这个属性output.Attributes.ContainsName(HtmlActionAttributeName) 如果包含,就是正常的html标签。换句话说,正常的html写法和我们的TagHelper方法会有冲突,只能用其中一种。 当我们这样写的时候,编译能通过。 但是,运行的时候就会出错。 再下面的处理就是用了TagBuilder去处理了。 收集路由的数据放到一个字典中->区域是否存在->用Generator去创建form表单,返回TagBuilder对象->TagHelperOutput对象把tagbuilder的innerhtml等信息输出。 如下面的写法:
1 2 3 |
<form method="post" asp-action="Get" asp-controller="Product" asp-antiforgery="false" asp-route-id="2"> <button type="submit">submit</button> </form> |
生成对应的html如下:
1 2 3 |
<form method="post" action="/Product/Get/2"> <button type="submit">submit</button> </form> |
到这里,FormTagHelper的讲解就算是OK,至于其他的,原理都是差不多,就不再累赘了。 来看看,到底有多少种TagHelper(还没有部分没有列出来),以及它们包含的属性。 下面是我们自己写一个TagHelper——CatcherATagHelper,这个TagHelper是干什么的呢?它只是一个精简版的A标签。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.TagHelpers; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; namespace Catcher.EasyDemo.Controllers.TagHelpers { [HtmlTargetElement("catcher-a")] public class CatcherATagHelper:TagHelper { public CatcherATagHelper(IHtmlGenerator generator, IUrlHelperFactory urlHelperFactory) { this.Generator = generator; UrlHelperFactory = urlHelperFactory; } [HtmlAttributeNotBound] public IUrlHelperFactory UrlHelperFactory { get; } protected IHtmlGenerator Generator { get; } public override int Order { get { return -1000; } } public string Action { get; set; } public string Controller { get; set; } public string LinkText { get; set; } [ViewContext] [HtmlAttributeNotBound] public ViewContext ViewContext { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { //method 1 if (Action != null || Controller != null) { output.Attributes.Clear(); var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext); output.TagName = "a"; output.Attributes.SetAttribute("href", urlHelper.Action(Action, Controller)); //whether the inner html is null if (output.Content.IsEmptyOrWhiteSpace) { output.PreContent.SetContent(LinkText); } } //method 2 //TagBuilder tagBuilder; //if (Action != null || Controller != null) //{ // tagBuilder = Generator.GenerateActionLink( // ViewContext, // linkText: string.Empty, // actionName: Action, // controllerName: Controller, // protocol: string.Empty, // hostname: string.Empty, // fragment: string.Empty, // routeValues: null, // htmlAttributes: null); // output.TagName = "a"; // //whether the inner html is null // if (output.Content.IsEmptyOrWhiteSpace) // { // output.PreContent.SetContent(LinkText); // } // output.MergeAttributes(tagBuilder); //} } } } |
这里提供了两种写法供大家参考 一种是借助IUrlHelperFactory去生成链接 一种是借助IHtmlGenerator去生成链接 写好了之后要怎么用呢? 不知道大家有没有留意_ViewImports.cshtml这个文件
1 2 3 |
@using Catcher.EasyDemo.Website @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @inject Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration TelemetryConfiguration |
这个是默认情况下帮我们添加的TagHelper 我们可以在要用到那个TagHelper的地方添加就好 Index.cshtml addTagHelper的用法如下: @addTagHelper 你的TagHelper , 你的TagHelper所在的命名空间 或者更直接 @addTagHelper * , 你的TagHelper所在的命名空间 可以添加,当然也可以删除,删除是@removeTagHelper 当我们在自己的框架中完全重写了一套自己的TagHelper,那么这个时候,微软自己的TagHelper我们就可以通过下面的方法来移除了。 @removeTagHelper […]
View DetailsOcelot是一个用.NET Core实现并且开源的API网关,它功能强大,包括了:路由、请求聚合、服务发现、认证、鉴权、限流熔断、并内置了负载均衡器与Service Fabric、Butterfly Tracing集成。这些功能只都只需要简单的配置即可完成,下面我们会对这些功能的配置一一进行说明。 介绍 简单的来说Ocelot是一堆的asp.net core middleware组成的一个管道。当它拿到请求之后会用一个request builder来构造一个HttpRequestMessage发到下游的真实服务器,等下游的服务返回response之后再由一个middleware将它返回的HttpResponseMessage映射到HttpResponse上。 API网关—— 它是系统的暴露在外部的一个访问入口。这个有点像代理访问的家伙,就像一个公司的门卫承担着寻址、限制进入、安全检查、位置引导、等等功能。 Ocelot的基本使用 用一台web service来host Ocelot,在这里有一个json配置文件,里面设置了所有对当前这个网关的配置。它会接收所有的客户端请求,并路由到对应的下游服务器进行处理,再将请求结果返回。而这个上下游请求的对应关系也被称之为路由。 集成Identity Server 当我们涉及到认证和鉴权的时候,我们可以跟Identity Server进行结合。当网关需要请求认证信息的时候会与Identity Server服务器进行交互来完成。 网关集群 只有一个网关是很危险的,也就是我们通常所讲的单点,只要它挂了,所有的服务全挂。这显然无法达到高可用,所以我们也可以部署多台网关。当然这个时候在多台网关前,你还需要一台负载均衡器。 Consul 服务发现 在Ocelot已经支持简单的负载功能,也就是当下游服务存在多个结点的时候,Ocelot能够承担起负载均衡的作用。但是它不提供健康检查,服务的注册也只能通过手动在配置文件里面添加完成。这不够灵活并且在一定程度下会有风险。这个时候我们就可以用Consul来做服务发现,它能与Ocelot完美结合。 集成网关 在asp.net core 2.0里通过nuget即可完成集成,或者命令行dotnet add package Ocelot以及通过vs2017 UI添加Ocelot nuget引用都可以。
1 |
Install-Package Ocelot |
配置 我们需要添加一个.json的文件用来添加Ocelot的配置,以下是最基本的配置信息。
1 2 3 4 5 6 |
{ "ReRoutes": [], "GlobalConfiguration": { "BaseUrl": "https://api.mybusiness.com" } } |
要特别注意一下BaseUrl是我们外部暴露的Url,比如我们的Ocelot运行在http://123.111.1.1的一个地址上,但是前面有一个 nginx绑定了域名http://api.jessetalk.cn,那这里我们的BaseUrl就是 http://api.jessetalk.cn。 将配置文件加入ASP.NET Core Configuration 我们需要通过WebHostBuilder将我们添加的json文件添加进asp.net core的配置
1 2 3 4 5 6 7 8 9 |
public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration( (hostingContext,builder) => { builder .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) .AddJsonFile("Ocelot.json"); }) .UseStartup<Startup>() .Build(); |
配置依赖注入与中间件 在startup.cs中我们首先需要引用两个命名空间
1 2 |
using Ocelot.DependencyInjection; using Ocelot.Middleware; |
接下来就是添加依赖注入和中间件
1 2 3 4 |
public void ConfigureServices(IServiceCollection services) { services.AddOcelot(); } |
1 2 3 4 5 6 7 8 9 |
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseOcelot().Wait(); } |
Ocelot功能介绍 通过配置文件可以完成对Ocelot的功能配置:路由、服务聚合、服务发现、认证、鉴权、限流、熔断、缓存、Header头传递等。在配置文件中包含两个根节点:ReRoutes和GlobalConfiguration。ReRoutes是一个数组,其中的每一个元素代表了一个路由,我们可以针对每一个路由进行以上功能配置。下面是一个完整的路由配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
{ "DownstreamPathTemplate": "/", "UpstreamPathTemplate": "/", "UpstreamHttpMethod": [ "Get" ], "AddHeadersToRequest": {}, "AddClaimsToRequest": {}, "RouteClaimsRequirement": {}, "AddQueriesToRequest": {}, "RequestIdKey": "", "FileCacheOptions": { "TtlSeconds": 0, "Region": "" }, "ReRouteIsCaseSensitive": false, "ServiceName": "", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 51876, } ], "QoSOptions": { "ExceptionsAllowedBeforeBreaking": 0, "DurationOfBreak": 0, "TimeoutValue": 0 }, "LoadBalancer": "", "RateLimitOptions": { "ClientWhitelist": [], "EnableRateLimiting": false, "Period": "", "PeriodTimespan": 0, "Limit": 0 }, "AuthenticationOptions": { "AuthenticationProviderKey": "", "AllowedScopes": [] }, "HttpHandlerOptions": { "AllowAutoRedirect": true, "UseCookieContainer": true, "UseTracing": true }, "UseServiceDiscovery": false } |
Downstream是下游服务配置 UpStream是上游服务配置 Aggregates 服务聚合配置 ServiceName, LoadBalancer, UseServiceDiscovery 配置服务发现 AuthenticationOptions 配置服务认证 RouteClaimsRequirement 配置Claims鉴权 RateLimitOptions为限流配置 FileCacheOptions 缓存配置 QosOptions 服务质量与熔断 DownstreamHeaderTransform头信息转发 我们接下来将对这些功能一一进行介绍和配置 路由 路由是API网关最基本也是最核心的功能、ReRoutes下就是由多个路由节点组成。
1 2 3 4 |
{ "ReRoutes": [ ] } |
而每一个路由由以下几个基本信息组成: 下面这个配置信息就是将用户的请求 /post/1 转发到 localhost/api/post/1
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "DownstreamPathTemplate": "/api/post/{postId}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 80, } ], "UpstreamPathTemplate": "/post/{postId}", "UpstreamHttpMethod": [ "Get"] } |
DownstreamPathTemplate:下游戏 DownstreamScheme:下游服务http schema DownstreamHostAndPorts:下游服务的地址,如果使用LoadBalancer的话这里可以填多项 UpstreamPathTemplate: 上游也就是用户输入的请求Url模板 […]
View Details比赛规则
1.统一使用Realse版本的最新 DLL,Realse模式启用程序
2.为了平衡CPU和数据库空闲情况,使用车轮战,每场比赛连续10回合比试
3.多次重启电脑取平均成绩上图
比赛成员
1.SqlSugar 3.1.01
2.Dapper 1.5.0.2 Dapper.Contrib 1.5 官方DLL
View Details用户登录是一个非常常见的应用场景 .net core 2.0 的登录方式发生了点变化,应该是属于是良性的变化,变得更方便,更容易扩展。 配置 打开项目中的Startup.cs文件,找到ConfigureServices方法,我们通常在这个方法里面做依赖注入的相关配置。添加如下代码:
1 2 3 4 5 6 7 8 9 |
public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o => { o.LoginPath = new PathString("/Account/Login"); o.AccessDeniedPath = new PathString("/Error/Forbidden"); }); } |
这段代码的大概意思就是,添加授权支持,并添加使用Cookie的方式,配置登录页面和没有权限时的跳转页面。 再找到Configure方法,添加 app.UseAuthentication(),使用授权:
1 2 3 4 |
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseAuthentication(); } |
这样基本的配置就完成了。 登录 添加一个Controller,如AccountController,再添加一个Action,如 Login,所配置的路由,要与上面的配置对应,不然跳转登录时会跳错页面。 用户提交用户名和密码,登录代码大致如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[HttpPost] public async Task <IActionResult> Login(string userName, string password, string ReturnUrl) { var user = _userService.Login(userName, password); if (user != null) { user.AuthenticationType = CookieAuthenticationDefaults.AuthenticationScheme; var identity = new ClaimsIdentity(user); identity.AddClaim(new Claim(ClaimTypes.Name, user.UserID)); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity)); if (ReturnUrl.IsNullOrEmpty()) { return RedirectToAction("Index", "Dashboard"); } return Redirect(ReturnUrl); } ViewBag.Errormessage = "登录失败,用户名密码不正确"; return View(); } |
这里要注意的是 AuthenticationType 所设置的Scheme一定要与前面的配置一样,这样对应的登录授权才会生效。 使用登录身份 登录的目录,就是希望有些页面或者资源只有登录以后才可访问。使用AuthorizeAttribute来做限制。在需要做限制的Controller上加上[Authorize]特性来做限制。
1 2 3 4 |
[Authorize] public class ThemeController { } |
这样这个Controller下的所有的Action都必需要登录后才可访问。如果希望其中某些Action可以不用登录也可访问,可以添加例外:
1 2 3 4 5 |
[AllowAnonymous] public ActionResult Index() { return View(); } |
到这里一个最基础的登录就完成了。 在Web项目中,通常会遇到一个问题,后端管理员和前台用户。这两个用户都是可登录的,在 .net core 2.0,这个将很容易实现。 多用户登录 添加一个登录方案(Scheme) CookieAuthenticationDefaults.AuthenticationScheme,这是系统已经定义好的一个默认的登录方案,添加一个新的来实现一个不同的身份登录。代码如下:
1 2 3 4 5 6 7 8 |
public class CustomerAuthorizeAttribute : AuthorizeAttribute { public const string CustomerAuthenticationScheme = "CustomerAuthenticationScheme"; public CustomerAuthorizeAttribute() { this.AuthenticationSchemes = CustomerAuthenticationScheme; } } |
添加使用这个新的方案,在Startup.cs文件下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o => { o.LoginPath = new PathString("/Account/Login"); o.AccessDeniedPath = new PathString("/Error/Forbidden"); }) .AddCookie(CustomerAuthorizeAttribute.CustomerAuthenticationScheme, option => { option.LoginPath = new PathString("/Account/Signin"); option.AccessDeniedPath = new PathString("/Error/Forbidden"); }); } |
添加新的登录方案,并配置一个新的登录页面,登录的方法和刚才是一样,只是AuthenticationType使用了新的方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[HttpPost] public async Task <IActionResult> Login(string userName, string password, string ReturnUrl) { var user = _userService.Login(userName, password); if (user != null) { user.AuthenticationType = <strong>CustomerAuthorizeAttribute.CustomerAuthenticationScheme</strong>; var identity = new ClaimsIdentity(user); identity.AddClaim(new Claim(ClaimTypes.Name, user.UserID)); await HttpContext.SignInAsync(<strong>CustomerAuthorizeAttribute.CustomerAuthenticationScheme</strong>, new ClaimsPrincipal(identity)); if (ReturnUrl.IsNullOrEmpty()) { return RedirectToAction("Index", "Dashboard"); } return Redirect(ReturnUrl); } ViewBag.Errormessage = "登录失败,用户名密码不正确"; return View(); } |
验证登录状态 使用方法和之前的差不多,换成新的CustomerAuthorizeAttribute就行了:
1 2 3 4 |
[CustomerAuthorize] public class CustomerController { } |
CustomerAuthorizeAttribute这个类,不是必需的,只是为了方便使用而写,其实完全可以只定义一个新的方案(Scheme)就行了。 谁才是HttpContext.User? 登录了多个用户,那么谁才是HttpContext.User呢?如果你的Controller或者Action上有使用AuthorizeAttribute,那这个Attribute使用的登录方案是哪个,则这个HttpContext.User对应的就是那个方案的登录用户。如果没有使用,则AddAuthentication()方法默认指它的方案(Scheme)所登录的用户,就是这个HttpContext.User了。 如何获取对应方案的登录用户呢?使用HttpContext.AuthenticateAsync
1 2 3 4 5 |
var auth = await HttpContext.AuthenticateAsync(CustomerAuthorizeAttribute.CustomerAuthenticationScheme); if (auth.Succeeded) { auth.Principal.Identity... } |
退出登录 这个就简单了,指定方案退出就可以了。
1 2 3 4 5 |
public async Task Logout(string returnurl) { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); return Redirect(returnurl ?? "~/"); } |
from:http://www.zkea.net/codesnippet/detail/asp-net-core-multi-login-user.html
View Details大家在使用ASP.NET的时候一定都用过FormsAuthentication做登录用户的身份认证,FormsAuthentication的核心就是Cookie,ASP.NET会将用户名存储在Cookie中。 现在到了ASP.NET CORE的时代,但是ASP.NET CORE中没有FormsAuthentication这个东西,那么怎么做身份认证呢?答案是ASP.NET CORE已经为我们内置了Cookie身份认证的功能,而且使用起来非常方便,注意本文是基于ASP.NET CORE 2.0版本来阐述Cookie认证方式的。 1.从ASP.NET CORE OWIN框架中启用Cookie身份认证功能 要在ASP.NET CORE中使用Cookie身份认证,第一步就是在项目中的OWIN框架文件Startup.cs中启用Cookie身份认证中间件。 首先我们在Startup中的ConfigureServices方法中使用services.AddAuthentication注册Cookie认证服务,如下代码所示:
1 2 3 4 5 6 7 |
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); //注册Cookie认证服务 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(); } |
然后在Startup中的Configure方法中使用app.UseAuthentication启用Cookie认证中间件(注意其中app.UseAuthentication和app.UseMvc的调用顺序不能反),如下代码所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); //注意app.UseAuthentication方法一定要放在下面的app.UseMvc方法前面,否者后面就算调用HttpContext.SignInAsync进行用户登录后,使用 //HttpContext.User还是会显示用户没有登录,并且HttpContext.User.Claims读取不到登录用户的任何信息。 //这说明Asp.Net OWIN框架中MiddleWare的调用顺序会对系统功能产生很大的影响,各个MiddleWare的调用顺序一定不能反 app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } |
这里顺便说一下app.UseAuthentication是用来干什么的,app.UseAuthentication会启用Authentication中间件,该中间件会根据当前Http请求中的Cookie信息来设置HttpContext.User属性(后面会用到),所以只有在app.UseAuthentication方法之后注册的中间件才能够从HttpContext.User中读取到值,这也是为什么上面强调app.UseAuthentication方法一定要放在下面的app.UseMvc方法前面,因为只有这样ASP.NET Core的MVC中间件中才能读取到HttpContext.User的值。 2.登录用户 在ASP.NET CORE中使用Cookie认证登录用户的方法和传统的FormsAuthentication不太一样,大致步骤如下: 创建Claim类型的数组,将登录用户的所有信息(比如用户名)存储在Claim类型的字符串键值对中 将上面创建的Claim类型的数组传入ClaimsIdentity中,用来构造一个ClaimsIdentity对象 将上面创建的ClaimsIdentity对象传入ClaimsPrincipal中,用来构造一个ClaimsPrincipal对象 调用HttpContext.SignInAsync方法,传入上面创建的ClaimsPrincipal对象,完成用户登录 所以我们可以看到整个ASP.NET CORE的Cookie认证登录流程比以前ASP.NET的FormsAuthentication还是要复杂许多,毕竟以前一个FormsAuthentication.SetAuthCookie方法就搞定了。 在本文的例子中我们在项目中默认的HomeController中创建了一个Acion方法Login,来实现用户登录的代码。当然这里我们实现的是最简的Cookie登录,下面代码中实际上还可以设置Cookie是否持久化、Cookie多久过期、存储登录用户信息的Cookie的名字是什么等,我们就不做过多介绍了,大家可以阅读本文最后推荐的两份官方文档了解更多。 Login方法的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
/// <summary> /// 该Action登录用户Wangdacui到Asp.Net Core /// </summary> public IActionResult Login() { //下面的变量claims是Claim类型的数组,Claim是string类型的键值对,所以claims数组中可以存储任意个和用户有关的信息, //不过要注意这些信息都是加密后存储在客户端浏览器cookie中的,所以最好不要存储太多特别敏感的信息,这里我们只存储了用户名到claims数组, //表示当前登录的用户是谁 var claims = new[] { new Claim("UserName", "Wangdacui") }; var claimsIdentity = new ClaimsIdentity( claims, CookieAuthenticationDefaults.AuthenticationScheme); ClaimsPrincipal user = new ClaimsPrincipal(claimsIdentity); Task.Run(async () => { //登录用户,相当于ASP.NET中的FormsAuthentication.SetAuthCookie await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, user); //可以使用HttpContext.SignInAsync方法的重载来定义持久化cookie存储用户认证信息,例如下面的代码就定义了用户登录后60分钟内cookie都会保留在客户端计算机硬盘上, //即便用户关闭了浏览器,60分钟内再次访问站点仍然是处于登录状态,除非调用Logout方法注销登录。 //注意其中的AllowRefresh属性,如果AllowRefresh为true,表示如果用户登录后在超过50%的ExpiresUtc时间间隔内又访问了站点,就延长用户的登录时间(其实就是延长cookie在客户端计算机硬盘上的保留时间), //例如本例中我们下面设置了ExpiresUtc属性为60分钟后,那么当用户登录后在大于30分钟且小于60分钟内访问了站点,那么就将用户登录状态再延长到当前时间后的60分钟。但是用户在登录后的30分钟内访问站点是不会延长登录时间的, //因为ASP.NET Core有个硬性要求,是用户在超过50%的ExpiresUtc时间间隔内又访问了站点,才延长用户的登录时间。 //如果AllowRefresh为false,表示用户登录后60分钟内不管有没有访问站点,只要60分钟到了,立马就处于非登录状态(不延长cookie在客户端计算机硬盘上的保留时间,60分钟到了客户端计算机就自动删除cookie) /* await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, user, new AuthenticationProperties() { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(60), AllowRefresh = true }); */ }).Wait(); return View(); } |
如果当前Http请求本来登录了用户A,现在调用HttpContext.SignInAsync方法登录用户B,那么相当于注销用户A,登录用户B 3.读取登录用户信息 那么用户登录后怎么将登录用户的信息(比如用户名)读取出来呢?我们在HomeController的Index方法中演示了如何判断当前用户是否已经登录,并且读出登录用户的用户名,Index方法的代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/// <summary> /// 该Action判断用户是否已经登录,如果已经登录,那么读取登录用户的用户名 /// </summary> public IActionResult Index() { //如果HttpContext.User.Identity.IsAuthenticated为true, //或者HttpContext.User.Claims.Count()大于0表示用户已经登录 if (HttpContext.User.Identity.IsAuthenticated) { //这里通过 HttpContext.User.Claims 可以将我们在Login这个Action中存储到cookie中的所有 //claims键值对都读出来,比如我们刚才定义的UserName的值Wangdacui就在这里读取出来了 var userName = HttpContext.User.Claims.First().Value; } return View(); } |
注意,最好还是用HttpContext.User.Identity.IsAuthenticated来判断用户是否已经登录 4.注销用户 那么登录用户后怎么注销登录呢?我们在HomeController的Logout方法中演示了如何注销登录的用户,代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/// <summary> /// 该Action从Asp.Net Core中注销登录的用户 /// </summary> public IActionResult Logout() { Task.Run(async () => { //注销登录的用户,相当于ASP.NET中的FormsAuthentication.SignOut await HttpContext.SignOutAsync(); }).Wait(); return View(); } |
如果当前Http请求本来就没有登录用户,那么调用HttpContext.SignOutAsync方法时也不会报错 5.负载均衡 警告: ASP.NET Core使用 ASP.NET Core data protection stack 来实现Cookie身份认证。如果在服务器集群中必须配置 ASP.NET Core Data Protection,有关详细信息,请参阅 Configuring data protection。如果你的ASP.NET Core站点使用了负载均衡部署了多个实例,就要做ASP.NET Core Data Protection的配置,否则ASP.NET CORE跨多个实例进行Cookie身份认证会失败。 还可以参考:Host ASP.NET Core in a web farm 以及 Share authentication cookies among ASP.NET apps 如何管理ASP.NET Core Data Protection的过期key,可以查看:Data Protection – […]
View Details一、前方有坑 php在使用加减乘除等运算符计算浮点数的时候,经常会出现意想不到的结果,特别是关于财务数据方面的计算,给不少工程师惹了很多的麻烦。比如今天工作终于到的一个案例: $a = 2586; $b = 2585.98; var_dump($a-$b); 期望的结果是:float(0.02) 实际结果: float(0.019999999999982) 人生有坑,处处提防 二、防坑攻略: 1、通过乘100的方式转化为整数加减,然后在除以100转化回来…… 2、使用number_format转化成字符串,然后在使用(float)强转回来…… 3、php提供了高精度计算的函数库,实际上就是为了解决这个浮点数计算问题而生的。 主要函数有: bcadd — 将两个高精度数字相加 bccomp — 比较两个高精度数字,返回-1, 0, 1 bcdiv — 将两个高精度数字相除 bcmod — 求高精度数字余数 bcmul — 将两个高精度数字相乘 bcpow — 求高精度数字乘方 bcpowmod — 求高精度数字乘方求模,数论里非常常用 bcscale — 配置默认小数点位数,相当于就是Linux bc中的”scale=” bcsqrt — 求高精度数字平方根 bcsub — 将两个高精度数字相减 前两种流氓的办法就不测试了,使用bcsub测试第三种两数相减的例子, 先看bcsub用法(来自官网) string bcsub ( string $left_operand , string $right_operand [, int $scale = int ] ) 参数 left_operand 字符串类型的左操作数. right_operand 字符串类型的右操作数. scale 此可选参数用于设置结果中小数点后的小数位数。也可通过使用 bcscale() 来设置全局默认的小数位数,用于所有函数。 返回值 返回减法之后结果为字符串类型. 测试代码: var_dump(bcsub($a,$b,2)); 结果 0.02 其他的函数请参考PHP官方网站 三、为啥有坑: php的bug?不是,这是所有语言基本上都会遇到的问题,所以基本上大部分语言都提供了精准计算的类库或函数库。 要搞明白这个原因, […]
View Details1 准备 下载 tomcat 首先在 tomcat 官网上下载 windows 版本的 tomcat 包。因为只有 windows 版本中才有用于安装或卸载 Tomcat 服务的 service.bat。 解压为文件夹,假设 tomcat 的解压路径为 ${TOMCAT_HOME} 通过命令行,进入 ${TOMCAT_HOME}/bin 2 安装 Tomcat 服务 输入命令:
1 |
service.bat install 服务名 |
这里我们可以把启动类型改为 “自动”,这样如果服务器重启,就会自动启动服务啦。 快速进入 “服务” 列表的方法:win+R 打开运行窗口,然后输入 services.msc 建议把服务的名称加上端口号,这样方便管理哦O(∩_∩)O~ 如果启动服务时报错,查看 tomcat 日志发现是 “不是有效的 Win32 应用程序” 引起的。这可能是操作系统的问题(比如 windows server 2007),这时只需下载一个 32 位的 windows 版的 tomcat 包即可解决。 3 卸载 Tomcat 服务 输入命令:
1 |
service.bat remove 服务名 |
是不是很简单呀O(∩_∩)O哈哈~ from:https://blog.csdn.net/deniro_li/article/details/79093390
View Details