asp.net core 2.0的认证和授权

在asp.net core中,微软提供了基于认证(Authentication)和授权(Authorization)的方式,来实现权限管理的,本篇博文,介绍基于固定角色的权限管理和自定义角色权限管理,本文内容,更适合传统行业的BS应用,而非互联网应用。

在asp.net core中,我们认证(Authentication)通常是在Login的Post Action中进行用户名或密码来验证用户是否正确,如果通过验证,即该用户就会获得一个或几个特定的角色,通过ClaimTypes.Role来存储角色,从而当一个请求到达时,用这个角色和Controller或Action上加的特性 [Authorize(Roles = "admin,system")]来授权是否有权访问该Action。本文中的自定义角色,会把验证放在中间件中进行处理。

一、固定角色:

即把角色与具体的Controller或Action直接关联起来,整个系统中的角色是固定的,每种角色可以访问那些Controller或Action也是固定的,这做法比较适合小型项目,角色分工非常明确的项目。

项目代码:

https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/RolePrivilegeManagement

始于startup.cs

需要在ConfigureServices中注入Cookie的相关信息,options是CookieAuthenticationOptions,关于这个类型提供如下属性,可参考:https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?tabs=aspnetcore2x

它提供了登录的一些信息,或登录生成Cookie的一些信息,用以后

 1         public void ConfigureServices(IServiceCollection services)
 2         {
 3             services.AddMvc();
 4             //添加认证Cookie信息
 5             services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
 6              .AddCookie(options =>
 7              {
 8                  options.LoginPath = new PathString("/login");
 9                  options.AccessDeniedPath = new PathString("/denied");
10              });
11         }
12
13         public void Configure(IApplicationBuilder app, IHostingEnvironment env)
14         {
15             if (env.IsDevelopment())
16             {
17                 app.UseDeveloperExceptionPage();
18                 app.UseBrowserLink();
19             }
20             else
21             {
22                 app.UseExceptionHandler("/Home/Error");
23             }
24             app.UseStaticFiles();
25             //验证中间件
26             app.UseAuthentication();
27             app.UseMvc(routes =>
28             {
29                 routes.MapRoute(
30                     name: "default",
31                     template: "{controller=Home}/{action=Index}/{id?}");
32             });
33         }

HomeController.cs

对于Login Get的Action,把returnUrl用户想要访问的地址(有可能用户记录下想要访问的url了,但系统会转到登录页,登录成功后直接跳转到想要访问的returnUrl页)

对于Login Post的Action,验证用户密和密码,成功能,定义一个ClaimsIdentity,把用户名和角色,和用户姓名的声明都添回进来(这个角色,就是用来验证可访问action的角色 )作来该用户标识,接下来调用HttpContext.SignInAsync进行登录,注意此方法的第一个参数,必需与StartUp.cs中services.AddAuthentication的参数相同,AddAuthentication是设置登录,SigninAsync是按设置参数进行登录

对于Logout Get的Action,是退出登录

HomeController上的[Authorize(Roles=”admin,system”)]角色和权限的关系时,所有Action只有admin和system两个角色能访问到,About上的[Authorize(Roles=”admin”)]声明这个action只能admin角色访问,Contact上的[Authorize(Roles=”system”)]声明这个action只能system角色访问,如果action上声明的是[AllowAnomymous],说明不受授权管理,可以直接访问。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Diagnostics;
 4 using System.Linq;
 5 using System.Threading.Tasks;
 6 using Microsoft.AspNetCore.Mvc;
 7 using RolePrivilegeManagement.Models;
 8 using System.Security.Claims;
 9 using Microsoft.AspNetCore.Authentication;
10 using Microsoft.AspNetCore.Authentication.Cookies;
11 using Microsoft.AspNetCore.Authorization;
12
13 namespace RolePrivilegeManagement.Controllers
14 {
15     [Authorize(Roles = "admin,system")]
16     public class HomeController : Controller
17     {
18         public IActionResult Index()
19         {
20             return View();
21         }
22         [Authorize(Roles = "admin")]
23         public IActionResult About()
24         {
25             ViewData["Message"] = "Your application description page.";
26             return View();
27         }
28         [Authorize(Roles = "system")]
29         public IActionResult Contact()
30         {
31             ViewData["Message"] = "Your contact page.";
32             return View();
33         }
34         public IActionResult Error()
35         {
36             return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
37         }
38         [AllowAnonymous]
39         [HttpGet("login")]
40         public IActionResult Login(string returnUrl = null)
41         {
42             TempData["returnUrl"] = returnUrl;
43             return View();
44         }
45         [AllowAnonymous]
46         [HttpPost("login")]
47         public async Task<IActionResult> Login(string userName, string password, string returnUrl = null)
48         {
49             var list = new List<dynamic> {
50                 new { UserName = "gsw", Password = "111111", Role = "admin" },
51                 new { UserName = "aaa", Password = "222222", Role = "system" }
52             };
53             var user = list.SingleOrDefault(s => s.UserName == userName && s.Password == password);
54             if (user!=null)
55             {
56                 //用户标识
57                 var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
58                 identity.AddClaim(new Claim(ClaimTypes.Sid, userName));
59                 identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
60                 identity.AddClaim(new Claim(ClaimTypes.Role, user.Role));
61                 await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
62                 if (returnUrl == null)
63                 {
64                     returnUrl = TempData["returnUrl"]?.ToString();
65                 }
66                 if (returnUrl != null)
67                 {
68                     return Redirect(returnUrl);
69                 }
70                 else
71                 {
72                     return RedirectToAction(nameof(HomeController.Index), "Home");
73                 }
74             }
75             else
76             {
77                 const string badUserNameOrPasswordMessage = "用户名或密码错误!";
78                 return BadRequest(badUserNameOrPasswordMessage);
79             }
80         }
81         [HttpGet("logout")]
82         public async Task<IActionResult> Logout()
83         {
84             await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
85             return RedirectToAction("Index", "Home");
86         }
87         [AllowAnonymous]
88         [HttpGet("denied")]
89         public IActionResult Denied()
90         {
91             return View();
92         }
93     }
94 }

前端_Layout.cshtml布局页,在登录成功后的任何页面都可以用@User.Identity.Name就可以获取用户姓名,同时用@User.Claims.SingleOrDefault(s=>s.Type== System.Security.Claims.ClaimTypes.Sid).Value可以获取用户名或角色。

 1    <nav class="navbar navbar-inverse navbar-fixed-top">
 2         <div class="container">
 3             <div class="navbar-header">
 4                 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
 5                     <span class="sr-only">Toggle navigation</span>
 6                     <span class="icon-bar"></span>
 7                     <span class="icon-bar"></span>
 8                     <span class="icon-bar"></span>
 9                 </button>
10                 <a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">RolePrivilegeManagement</a>
11             </div>
12             <div class="navbar-collapse collapse">
13                 <ul class="nav navbar-nav">
14                     <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
15                     <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
16                     <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>
17                 </ul>
18                 <ul class="" style="float:right; margin:0;">
19                     <li style="overflow:hidden;">
20                         <div style="float:left;line-height:50px;margin-right:10px;">
21                             <span style="color:#ffffff">当前用户:@User.Identity.Name</span>
22                         </div>
23                         <div style="float:left;line-height:50px;">
24                             <a asp-area="" asp-controller="Home" asp-action="Logout">注销</a>
25                         </div>
26                     </li>
27                 </ul>
28             </div>
29         </div>
30     </nav>

现在可以用chrome运行了,进行登录页后F12,查看Network—Cookies,可以看到有一个Cookie,这个是记录returnUrl的Cookie,是否记得HomeController.cs中的Login Get的Action中代码:TempData["returnUrl"] = returnUrl;这个TempData最后转成了一个Cookie返回到客户端了,如下图:

输入用户名,密码登录,再次查看Cookies,发现多了一个.AspNetCore.Cookies,即把用户验证信息加密码保存在了这个Cookie中,当跳转到别的页面时,这两个Cookie会继续在客户端和服务传送,用以验证用户角色。

二、自定义角色

系统的角色可以自定义,用户是自写到义,权限是固定的,角色对应权限可以自定义,用户对应角色也是自定义的,如下图:

项目代码:

https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/PrivilegeManagement

始于startup.cs

自定义角色与固定角色不同之处在于多了一个中间件(关于中间件学习参看:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware),即在Configure方法中,一定要在app.UseAuthentication下面添加验证权限的中间件,因为UseAuthentication要从Cookie中加载通过验证的用户信息到Context.User中,所以一定放在加载完后才能去验用户信息(当然自己读取Cookie也可以)

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 using Microsoft.AspNetCore.Builder;
 6 using Microsoft.AspNetCore.Hosting;
 7 using Microsoft.Extensions.Configuration;
 8 using Microsoft.Extensions.DependencyInjection;
 9 using Microsoft.AspNetCore.Authentication.Cookies;
10 using Microsoft.AspNetCore.Http;
11 using PrivilegeManagement.Middleware;
12
13 namespace PrivilegeManagement
14 {
15     public class Startup
16     {
17         public Startup(IConfiguration configuration)
18         {
19             Configuration = configuration;
20         }
21         public IConfiguration Configuration { get; }
22
23         public void ConfigureServices(IServiceCollection services)
24         {
25             services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
26            .AddCookie(options =>
27            {
28                options.LoginPath = new PathString("/login");
29                options.AccessDeniedPath = new PathString("/denied");
30            }
31            );
32             services.AddMvc();
33         }
34
35         public void Configure(IApplicationBuilder app, IHostingEnvironment env)
36         {
37             if (env.IsDevelopment())
38             {
39                 app.UseDeveloperExceptionPage();
40                 app.UseBrowserLink();
41             }
42             else
43             {
44                 app.UseExceptionHandler("/Home/Error");
45             }
46
47             app.UseStaticFiles();
48             //验证中间件
49             app.UseAuthentication();
50             ////添加权限中间件, 一定要放在app.UseAuthentication后
51             app.UsePermission(new PermissionMiddlewareOption()
52             {
53                 LoginAction = @"/login",
54                 NoPermissionAction = @"/denied",
55                 //这个集合从数据库中查出所有用户的全部权限
56                 UserPerssions = new List<UserPermission>()
57                  {
58                      new UserPermission { Url="/", UserName="gsw"},
59                      new UserPermission { Url="/home/contact", UserName="gsw"},
60                      new UserPermission { Url="/home/about", UserName="aaa"},
61                      new UserPermission { Url="/", UserName="aaa"}
62                  }
63             });
64             app.UseMvc(routes =>
65             {
66                 routes.MapRoute(
67                     name: "default",
68                     template: "{controller=Home}/{action=Index}/{id?}");
69             });
70         }
71     }
72 }

下面看看中间件PermissionMiddleware.cs,在Invoke中用了context.User,如上面所述,首先要调用app.UseAuthentication加载用户信息后才能在这里使用,这个中间件逻辑较简单,如果没有验证的一律放过去,不作处理,如果验证过(登录成功了),就要查看本次请求的url和这个用户可以访问的权限是否匹配,如不匹配,就跳转到拒绝页面(这个是在Startup.cs中添加中间件时,用NoPermissionAction = @"/denied"设置的)

 1 using Microsoft.AspNetCore.Http;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.IO;
 5 using System.Linq;
 6 using System.Reflection;
 7 using System.Security.Claims;
 8 using System.Threading.Tasks;
 9
10 namespace PrivilegeManagement.Middleware
11 {
12     /// <summary>
13     /// 权限中间件
14     /// </summary>
15     public class PermissionMiddleware
16     {
17         /// <summary>
18         /// 管道代理对象
19         /// </summary>
20         private readonly RequestDelegate _next;
21         /// <summary>
22         /// 权限中间件的配置选项
23         /// </summary>
24         private readonly PermissionMiddlewareOption _option;
25
26         /// <summary>
27         /// 用户权限集合
28         /// </summary>
29         internal static List<UserPermission> _userPermissions;
30
31         /// <summary>
32         /// 权限中间件构造
33         /// </summary>
34         /// <param name="next">管道代理对象</param>
35         /// <param name="permissionResitory">权限仓储对象</param>
36         /// <param name="option">权限中间件配置选项</param>
37         public PermissionMiddleware(RequestDelegate next, PermissionMiddlewareOption option)
38         {
39             _option = option;
40             _next = next;
41             _userPermissions = option.UserPerssions;
42         }
43         /// <summary>
44         /// 调用管道
45         /// </summary>
46         /// <param name="context">请求上下文</param>
47         /// <returns></returns>
48         public Task Invoke(HttpContext context)
49         {
50             //请求Url
51             var questUrl = context.Request.Path.Value.ToLower();
52
53             //是否经过验证
54             var isAuthenticated = context.User.Identity.IsAuthenticated;
55             if (isAuthenticated)
56             {
57                 if (_userPermissions.GroupBy(g=>g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)
58                 {
59                     //用户名
60                     var userName = context.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Sid).Value;
61                     if (_userPermissions.Where(w => w.UserName == userName&&w.Url.ToLower()==questUrl).Count() > 0)
62                     {
63                         return this._next(context);
64                     }
65                     else
66                     {
67                         //无权限跳转到拒绝页面
68                         context.Response.Redirect(_option.NoPermissionAction);
69                     }
70                 }
71             }
72             return this._next(context);
73         }
74     }
75 }

扩展中间件类PermissionMiddlewareExtensions.cs

 1 using Microsoft.AspNetCore.Builder;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Threading.Tasks;
 6
 7 namespace PrivilegeManagement.Middleware
 8 {
 9     /// <summary>
10     /// 扩展权限中间件
11     /// </summary>
12     public static class PermissionMiddlewareExtensions
13     {
14         /// <summary>
15         /// 引入权限中间件
16         /// </summary>
17         /// <param name="builder">扩展类型</param>
18         /// <param name="option">权限中间件配置选项</param>
19         /// <returns></returns>
20         public static IApplicationBuilder UsePermission(
21               this IApplicationBuilder builder, PermissionMiddlewareOption option)
22         {
23             return builder.UseMiddleware<PermissionMiddleware>(option);
24         }
25     }
26 }

中间件属性PermissionMiddlewareOption.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5
 6 namespace PrivilegeManagement.Middleware
 7 {
 8     /// <summary>
 9     /// 权限中间件选项
10     /// </summary>
11     public class PermissionMiddlewareOption
12     {
13         /// <summary>
14         /// 登录action
15         /// </summary>
16         public string LoginAction
17         { get; set; }
18         /// <summary>
19         /// 无权限导航action
20         /// </summary>
21         public string NoPermissionAction
22         { get; set; }
23
24         /// <summary>
25         /// 用户权限集合
26         /// </summary>
27         public List<UserPermission> UserPerssions
28         { get; set; } = new List<UserPermission>();
29     }
30 }

中间件实体类UserPermission.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5
 6 namespace PrivilegeManagement.Middleware
 7 {
 8     /// <summary>
 9     /// 用户权限
10     /// </summary>
11     public class UserPermission
12     {
13         /// <summary>
14         /// 用户名
15         /// </summary>
16         public string UserName
17         { get; set; }
18         /// <summary>
19         /// 请求Url
20         /// </summary>
21         public string Url
22         { get; set; }
23     }
24 }

关于自定义角色,因为不需要授权时带上角色,所以可以定义一个基Controller类BaseController.cs,其他的Controller都继承BaseController,这样所有的action都可以通过中间件来验证,当然像登录,无权限提示页面还是在Action上加[AllowAnomymous]

1 using Microsoft.AspNetCore.Authorization;
2 using Microsoft.AspNetCore.Mvc;
3 namespace PrivilegeManagement.Controllers
4 {
5     [Authorize]
6     public class BaseController:Controller
7     {
8     }
9 }

HomeController.cs如下,与固定角色的HomeController.cs差异只在Controller和Action上的Authorize特性。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Diagnostics;
 4 using System.Linq;
 5 using System.Threading.Tasks;
 6 using Microsoft.AspNetCore.Mvc;
 7 using PrivilegeManagement.Models;
 8 using Microsoft.AspNetCore.Authorization;
 9 using System.Security.Claims;
10 using Microsoft.AspNetCore.Authentication.Cookies;
11 using Microsoft.AspNetCore.Authentication;
12
13 namespace PrivilegeManagement.Controllers
14 {
15
16     public class HomeController : BaseController
17     {
18         public IActionResult Index()
19         {
20             return View();
21         }
22
23         public IActionResult About()
24         {
25             ViewData["Message"] = "Your application description page.";
26
27             return View();
28         }
29
30         public IActionResult Contact()
31         {
32             ViewData["Message"] = "Your contact page.";
33
34             return View();
35         }
36
37         public IActionResult Error()
38         {
39             return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
40         }
41         [AllowAnonymous]
42         [HttpGet("login")]
43         public IActionResult Login(string returnUrl = null)
44         {
45             TempData["returnUrl"] = returnUrl;
46             return View();
47         }
48         [AllowAnonymous]
49         [HttpPost("login")]
50         public async Task<IActionResult> Login(string userName,string password, string returnUrl = null)
51         {
52             var list = new List<dynamic> {
53                 new { UserName = "gsw", Password = "111111", Role = "admin",Name="桂素伟" },
54                 new { UserName = "aaa", Password = "222222", Role = "system",Name="测试A" }
55             };
56             var user = list.SingleOrDefault(s => s.UserName == userName && s.Password == password);
57             if (user != null)
58             {
59                 //用户标识
60                 var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
61                 identity.AddClaim(new Claim(ClaimTypes.Sid, userName));
62                 identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
63                 identity.AddClaim(new Claim(ClaimTypes.Role, user.Role));
64
65                 await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
66                 if (returnUrl == null)
67                 {
68                     returnUrl = TempData["returnUrl"]?.ToString();
69                 }
70                 if (returnUrl != null)
71                 {
72                     return Redirect(returnUrl);
73                 }
74                 else
75                 {
76                     return RedirectToAction(nameof(HomeController.Index), "Home");
77                 }
78             }
79             else
80             {
81                 const string badUserNameOrPasswordMessage = "用户名或密码错误!";
82                 return BadRequest(badUserNameOrPasswordMessage);
83             }
84         }
85         [HttpGet("logout")]
86         public async Task<IActionResult> Logout()
87         {
88             await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
89             return RedirectToAction("Index", "Home");
90         }
91         [HttpGet("denied")]
92         public IActionResult Denied()
93         {
94             return View();
95         }
96     }
97 }

全部代码:https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86

时间: 2024-10-11 05:10:01

asp.net core 2.0的认证和授权的相关文章

[ASP.NET Core] 最简单 Cookie 认证与授权的范例

上一篇提到?[ASP.NET MVC][Owin] 用最简单 Cookie 认证方式, 这次改换 ASP.NET Core 的?Microsoft.AspNetCore.Authentication.Cookies 练习最简单的 Cookie 认证与授权, 在 project.json 的?dependencies 加入 "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0" 并记得每次加完要进行套件还原(Vis

ASP.NET Core 2.0利用Jwt实现授权认证

背景 在微服务架构下,一般都会按不同的业务或功能将整个系统切分成不同的独立子系统,再通过REST API或RPC进行通讯并相互调用,形成各个子系统之间的串联结构.在这里,我们将采用REST API的通讯方式.比如: 1.有一个“用户中心”独立子系统名为“Lezhima.UserHub”,是一个基于ASP.NET Core mvc 2.0的项目. 2.有一个处理用户订单的独立子系统名为“Lezhima.UserOrder”,是一个基于ASP.NET Core webp api 2.0的项目. 3.

ASP.NET Core 2.0 Cookie-based认证实现

using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace HelloCore2.Controllers { [Authorize] public class AdminController : Controller { public IActionResult Index() { return View(); } } } using Microsoft.AspNetCore.Authen

asp.net core 2.0 web api基于JWT自定义策略授权

JWT(json web token)是一种基于json的身份验证机制,流程如下: 通过登录,来获取Token,再在之后每次请求的Header中追加Authorization为Token的凭据,服务端验证通过即可能获取想要访问的资源.关于JWT的技术,可参考网络上文章,这里不作详细说明, 这篇博文,主要说明在asp.net core 2.0中,基于jwt的web api的权限设置,即在asp.net core中怎么用JWT,再次就是不同用户或角色因为权限问题,即使援用Token,也不能访问不该访

在ASP.NET Core 2.0中使用CookieAuthentication

在ASP.NET Core中关于Security有两个容易混淆的概念一个是Authentication(认证),一个是Authorization(授权).而前者是确定用户是谁的过程,后者是围绕着他们允许做什么,今天的主题就是关于在ASP.NET Core 2.0中如何使用CookieAuthentication认证. 在ASP.NET Core 2.0中使用CookieAuthentication跟在1.0中有些不同,需要在ConfigureServices和Configure中分别设置,前者我

ASP.NET Core集成现有系统认证

我们现在大多数转向ASP.NET Core来使用开发的团队,应该都不是从0开始搭建系统,而是老的业务系统已经在运行,ASP.NET Core用来开发新模块.那么解决用户认证的问题,成为我们的第一个拦路虎.本文将给大家简单阐述一下认证与授权的基本概念,以及基于ASP.NET Core 中间件实现的认证和改造JwtBearer 认证中间件来实现的认证达到与老系统(主要是token-based认证)的集成. 目录 认证与授权 什么是认证 何谓授权 用Middleware拦截 定制JWT Bearer 

ASP.NET Core 3.0:将会拥有更少的依赖

在ASP.NET Core项目中,我们使用一个叫做Microsoft.AspNetCore.App的综合包.它也被称为ASP.NET Core Shared Framework,在ASP.NET Core Shared Framework之中包含了很多依赖项,它能满足一般应用的需求.但是如果你查看它的依赖项,在ASP.NET Core3.0中它的需求在似乎变得宽松了. 当前版本的Microsoft.AspNetCore.App明确列出了150个依赖项,而7个月前的版本只需要144个.在这些包中,

ASP.NET Core 3.0 入门

原文:ASP.NET Core 3.0 入门 课程简介 与2.x相比发生的一些变化,项目结构.Blazor.SignalR.gRPC等 课程预计结构 ASP.NET Core 3.0项目架构简介 ASP.NET Core MVC 简介 Blazor SignalR Web API gRPC 发布 一. 创建项目 dotnet core 本质上是控制台应用 1. DI 依赖注入(Dependency Injection) IoC 容器(Inversion of Control)控制反转 注册(服务

丙申年把真假美猴王囚禁在容器中跑 ASP.NET Core 1.0

丙申年把真假美猴王囚禁在容器中跑 ASP.NET Core 1.0? 警告 您当前查看的页面是未经授权的转载! 如果当前版本排版错误,请前往查看最新版本:http://www.cnblogs.com/qin-nz/p/aspnetcore-run-on-mono-in-year-of-monkey.html 提示 更新时间:2016年02月07日. 各位程序媛/程序猿们,猴年快乐. 相信不少媛/猿都是被标题吸引来的,那我我先解释下标题. 提示 本文是一篇半科普文,不对技术细节进行深入探究. 标题