DotNet Core WebApi项目搭建
环境搭建
Core执行过程
IIS发布
https://dotnet.microsoft.com/download/
1 需要安装运行时和托管拥绑包
地址:https://dotnet.microsoft.com/download/dotnetcore/thank-you/runtime-aspnetcore-3.1.2-windows-hosting-bundle-installer
2 必须要发布不能直接执行系统的根目录
3 需要安装Core的运行时环境
4 指定为无代码托管
- 确认启用了WWW服务
- 配置Core的运行时环境,在IIS的模块中添加AspNetCoreModuleV2
- 设置无托代码(Core最新版本可以不设)
WebApi启动方式
- WebApi启动方式,使用CMD切换到DLL文件目录下通过命令可以运行WebApi
dotnet Test.CoreWebApi.dll --urls="http://*:8004"--ip="127.0.0.1"--port= 8004
配置Swagger
CoreWebApi-Swagger配置
1.nuget安装:Swashbuckle.AspNetCore程序包;
2.注册Swagger服务;
3.在中间件中使用Swagger;
-
在Startup.cs类中注册Swagger服务并增加中间件
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; namespace Test.Core.WebApi { /// <summary> /// Core 项目中导出都是IOC + DI /// </summary> public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } /// <summary> /// 被运行时调用的 还有什么? /// 执行且只执行一次; /// </summary> /// <param name="services"></param> // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { #region 注册Swagger服务 services.AddSwaggerGen(s => { //注册 Swagger s.SwaggerDoc("V1", new OpenApiInfo() { Title = "test",//显示名称 Version = "version-01",//显示版本 Description = "朝夕教育Api学习"//显示标题 }); }); #endregion services.AddControllers(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } //使用Swagger中间件 app.UseSwagger(); app.UseSwaggerUI(s => { s.SwaggerEndpoint("/swagger/V1/swagger.json", "test1");//不同版本的文件,test1与文件名对应可以进行切换。 }); //使用中间件 app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } }
-
访问路径:localhost:XXXX/swagger/index.html 注意大小写
-
在launchSettings.json文件中修改起始页面
{ "$schema": "http://json.schemastore.org/launchsettings.json", "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:50786", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "launchUrl": "swagger/index.html",//设置Swagger为初始页面 "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Test.Core.WebApi": { "commandName": "Project", "launchBrowser": true, "launchUrl": "weatherforecast", "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
Nginx负载均衡
Nginx环境
1.下载Nginx安装包:http://nginx.org/en/download.html
2.启动Nginx服务器;建议下载稳定版;
3.可以查看任务管理器;会出现Nginx进程
Nginx配置负载均衡
listen 9090; #设计Nginx的默认端口
server_name 9090.max.com;#注释:这里转发的设置
location /{
#root html;
#index index.html index.htm;
proxypass http://webApi;//设置代理转发;转发到服务器列表
}
服务器列表:
upstream webApi {
server localhost:8001;
server localhost:8002;
server localhost:8003;
-
Nginx配置文件内容~\nginx-1.16.1\conf\nginx.conf
#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; #注释:这里转发的设置 upstream webApi { server localhost:8001; server localhost:8002; server localhost:8003; } server { listen 9090; server_name 9090.max.com;#注释:这里转发的设置 #charset koi8-r; #access_log logs/host.access.log main; location / { proxy_pass http://webApi; #注释:这里转发的设置 } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } # another virtual host using mix of IP-, name-, and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # location / { # root html; # index index.html index.htm; # } #} # HTTPS server # #server { # listen 443 ssl; # server_name localhost; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} }
Log4Net集成
1.Nuget引入log4net/ Microsoft.Extensions.Logging.Log4Net.AspNetCore
2.增加配置log4net配置文件
3.使用配置文件
logging.AddFilter("System", LogLevel.Warning);
logging.AddFilter("Microsoft", LogLevel. Warning);
logging.AddLog4Net("ConfigFile/log4net.Config”);
-
Log4Net的配置文件(log4net.Config),配置文件需要设置为始终复制,否则发布后无法找到。
<?xml version="1.0" encoding="utf-8"?> <log4net> <!-- Define some output appenders --> <appender name="rollingAppender" type="log4net.Appender.RollingFileAppender"> <file value="log\log.txt" /> <!--追加日志内容--> <appendToFile value="true" /> <!--防止多线程时不能写Log,官方说线程非安全--> <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> <!--可以为:Once|Size|Date|Composite--> <!--Composite为Size和Date的组合--> <rollingStyle value="Composite" /> <!--当备份文件时,为文件名加的后缀--> <datePattern value="yyyyMMdd.TXT" /> <!--日志最大个数,都是最新的--> <!--rollingStyle节点为Size时,只能有value个日志--> <!--rollingStyle节点为Composite时,每天有value个日志--> <maxSizeRollBackups value="20" /> <!--可用的单位:KB|MB|GB--> <maximumFileSize value="3MB" /> <!--置为true,当前最新日志文件名永远为file节中的名字--> <staticLogFileName value="true" /> <!--输出级别在INFO和ERROR之间的日志--> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="ALL" /> <param name="LevelMax" value="FATAL" /> </filter> <!--必须结合起来用,第一个只过滤出WARN,第二个拒绝其它其它日志输出--> <!-- <filter type="log4net.Filter.LevelMatchFilter"> <param name="LevelToMatch" value="WARN" /> </filter> <filter type="log4net.Filter.DenyAllFilter" />--> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/> </layout> </appender> <!-- levels: OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL --> <root> <priority value="ALL"/> <level value="ALL"/> <appender-ref ref="rollingAppender" /> </root> </log4net>
-
在Program.cs中进行注册
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Test.Core.WebApi { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } /// <summary> /// Builder标准的建造者模式 /// </summary> /// <param name="args"></param> /// <returns></returns> public static IHostBuilder CreateHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) //Log4net注册 .ConfigureLogging(logging => //支持IOC 控制反转 { logging.AddFilter("System", LogLevel.Warning); logging.AddFilter("Microsoft", LogLevel.Warning); logging.AddLog4Net();//配置文件默认在根目录下,如果不在此目录下应使用重载方法 }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } } }
-
将Logger对象注入至控制器中进行调用
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace Test.Core.WebApi.Controllers { [Route("api/[controller]")] [ApiController] public class SecondController : ControllerBase { private ILogger<SecondController> _Logger = null; public SecondController(ILogger<SecondController> logger) { this._Logger = logger; _Logger.LogInformation("SecondController 被构造。。。。"); } [Route("Get")] [HttpGet] public string Get() { _Logger.LogInformation("SecondController.Get 被调用"); return "this is Test--003"; } } }
AOP-Filter
AOP(面向切面编程)
IResoureFilter做缓存例子
AOP在Core中通过Filter来实现,Core-Filter VS Asp.Net-Filter
Asp.Net WebApi | Core WehApi |
---|---|
授权过滤器IAuthorizationFilter | 授权过滤器AuthorizeAttribute |
异常过滤器lExceptionFilter | 异常过滤器lExceptionFilter, lAsyncExceptionFilter |
方法过滤器lActionFilter | 方法过滤器ActionFilterAttribute, lActionFilter, lAsyncActionFilter |
资源过滤器lResourceFilter | |
结果过滤器ResultFilterAttribute, IResultFilter, lAsyncResultFilter |
- Asp.net的Filter是在控制器实例化以后调用的。NetCoreWeiApi中的IResourceFilter是在控制器对象创建前先执行了OnResourceExecuting方法,在控制器返回数据时又执行OnResourceExecuted方法,适合做缓存使用。
以下是IResourceFilter做缓存应用的示例代码
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Test.Core.WebApi.Utility
{
public class CustomResourceFilterAttribute : Attribute, IResourceFilter
{
private static Dictionary<string, object> dictionaryCache = new Dictionary<string, object>();
public void OnResourceExecuting(ResourceExecutingContext context)
{
string key = context.HttpContext.Request.Path.ToString();
if (dictionaryCache.ContainsKey(key))
{
context.Result = dictionaryCache[key] as ObjectResult; //Result 短路器
}
}
public void OnResourceExecuted(ResourceExecutedContext context)//当Api的数据发生了变化时应将缓存清除
{
///把数据存储缓存;
//Key---path;
string key = context.HttpContext.Request.Path.ToString();
ObjectResult objectResult = context.Result as ObjectResult;
dictionaryCache[key] = objectResult;
}
}
}
IActionFilter做日志的例子(搭配Log4Net)
-
创建特性类继承于Attrbute并实现IActionFilter接口
-
将Logger对象依赖注入到特性类中
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Test.Core.WebApi.Utility { public class CustomActionFilterAttribute : Attribute, IActionFilter //ActionFilterAttribute //IActionFilter,//IAsyncActionFilter { private ILogger<CustomActionFilterAttribute> _Logger = null; //依赖注入logger public CustomActionFilterAttribute(ILogger<CustomActionFilterAttribute> logger) { _Logger = logger; } /// <summary> /// 方法执行后 /// </summary> /// <param name="context"></param> public void OnActionExecuted(ActionExecutedContext context) { var result = context.Result; ObjectResult objectResult= result as ObjectResult; var resultLog = $"{DateTime.Now} 调用 {context.RouteData.Values["action"]} api 完成;执行结果:{Newtonsoft.Json.JsonConvert.SerializeObject(objectResult.Value)}"; _Logger.LogInformation(resultLog); } /// <summary> /// 方法执行前 /// </summary> /// <param name="context"></param> public void OnActionExecuting(ActionExecutingContext context) { var actionLog = $"{DateTime.Now} 开始调用 {context.RouteData.Values["action"]} api;参数为:{Newtonsoft.Json.JsonConvert.SerializeObject(context.ActionArguments)}"; _Logger.LogInformation(actionLog); //这里就可以把信息写入到日志中来; } } }
-
特性注册方法(注意看代码注释)
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Test.Core.WebApi.Utility; namespace Test.Core.WebApi.Controllers { //能不能注册以后让所有的Api 都生效? //Startup.cs中进行注册 [Route("api/[controller]")] [ApiController] //2.Filter对控制器下的所有Api生效 //[ServiceFilter(typeof(CustomActionFilterAttribute))] //需要注册服务 [ServiceFilter(typeof(CustomControllerActionFilterAttribute))] public class FilterController : ControllerBase { public FilterController() { Console.WriteLine("FilterController 被构造。。。"); } //难道每个方法都需要去注册一下吗? //1.Filter方法注册 [Route("GetInfoByParamter")] [HttpGet] [CustomActionFilter] //特性的方式标记 只能支持带无参数构造函数的 [TypeFilter(typeof(CustomActionFilterAttribute))]//不需要注册服务 [ServiceFilter(typeof(CustomActionFilterAttribute))] //需要注册服务 [CustomIOCFilterFactory(typeof(CustomActionFilterAttribute))] //需要注册服务 public string GetInfoByParamter(int id,string Name) { return $"this is Id={id},Name={Name}"; } } }
-
注册服务Startup.cs
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { #region 注册Swagger服务 services.AddSwaggerGen(s => { #region 注册 Swagger s.SwaggerDoc("V1", new OpenApiInfo() { Title = "test", Version = "version-01", Description = "朝夕教育Api学习" }); #endregion }); #endregion services.AddControllers(); //注册服务,将类放在IOC容器中 services.AddSingleton<CustomActionFilterAttribute>(); services.AddSingleton<CustomGlobalActionFilterAttribute>(); services.AddSingleton<CustomControllerActionFilterAttribute>(); #region 全局注册Filter //3.对所有的方法都生效 services.AddMvc(option => { option.Filters.Add(typeof(CustomGlobalActionFilterAttribute)); }); #endregion }
-
自定义IOC工厂类CustomIOCFilterFactory的代码
using Microsoft.AspNetCore.Mvc.Filters; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Test.Core.WebApi.Utility { public class CustomIOCFilterFactoryAttribute : Attribute, IFilterFactory { private Type _FilterType = null; public CustomIOCFilterFactoryAttribute(Type type) { _FilterType = type; } public bool IsReusable => true; /// <summary> /// 在Core 中对象的创建 都是通过IOC 容器来创建 /// </summary> /// <param name="serviceProvider"></param> /// <returns></returns> public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) { return (IFilterMetadata)serviceProvider.GetService(this._FilterType); } } }
鉴权JWT
鉴权方式
传统的Seesion-Cookie方式
基于Token的无状态方式
### JWT介绍
鉴权中心搭建(CoreWebApi)
-
控制器代码
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Test.Core.AuthenticationCenter.Utility; namespace Test.Core.AuthenticationCenter.Controllers { [Route("api/[controller]")] [ApiController] public class AuthenticationController : ControllerBase { #region MyRegion private ILogger<AuthenticationController> _logger = null; private IJWTService _iJWTService = null; private readonly IConfiguration _iConfiguration; public AuthenticationController(ILoggerFactory factory, ILogger<AuthenticationController> logger, IConfiguration configuration , IJWTService service) { this._logger = logger; this._iConfiguration = configuration; this._iJWTService = service; } #endregion [Route("Get")] [HttpGet] public IEnumerable<int> Get() { return new List<int>() { 1, 2, 3, 4, 6, 7 }; } [Route("Login")] [HttpGet] public string Login(string name, string password) { ///这里肯定是需要去连接数据库做数据校验 if ("Richard".Equals(name) && "123456".Equals(password))//应该数据库 { string token = this._iJWTService.GetToken(name); return JsonConvert.SerializeObject(new { result = true, token }); } else { return JsonConvert.SerializeObject(new { result = false, token = "" }); } } } }
-
JWTService类代码
using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; namespace Test.Core.AuthenticationCenter.Utility { public interface IJWTService { string GetToken(string UserName); } /// <summary> /// 备注下:代码演示的是对称加密,所以只有一个key,在返回的信息里面是没有的 /// PPT介绍时,说的是非对称的,那样是把解密key公开的,前面是后台用私钥加密的, /// 可以保证别人解密后 拿到的数据 跟前面2部分hash后的结果一致 保证没有篡改 /// 此外,公钥不是在返回结果,那只是个打比方~ /// </summary> public class JWTService : IJWTService { private readonly IConfiguration _configuration; public JWTService(IConfiguration configuration) { _configuration = configuration; } public string GetToken(string UserName) { Claim[] claims = new[] { new Claim(ClaimTypes.Name, UserName), new Claim("NickName","Richard"), new Claim("Role","Administrator"),//传递其他信息 new Claim("abc","abccc") //new Claim }; SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["SecurityKey"])); SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); /** * Claims (Payload) Claims 部分包含了一些跟这个 token 有关的重要信息。 JWT 标准规定了一些字段,下面节选一些字段: iss: The issuer of the token,token 是给谁的 sub: The subject of the token,token 主题 exp: Expiration Time。 token 过期时间,Unix 时间戳格式 iat: Issued At。 token 创建时间, Unix 时间戳格式 jti: JWT ID。针对当前 token 的唯一标识 除了规定的字段外,可以包含其他任何 JSON 兼容的字段。 * */ var token = new JwtSecurityToken( issuer: _configuration["issuer"], audience: _configuration["audience"], claims: claims, expires: DateTime.Now.AddMinutes(5),//5分钟有效期 signingCredentials: creds); string returnToken = new JwtSecurityTokenHandler().WriteToken(token); return returnToken; } } }
-
DI依赖注入代码
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddScoped<IJWTService, JWTService>();//IOC DI容器依赖注入 services.AddControllers(); }
-
配置文件代码
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "audience": "http://localhost:10086", "issuer": "http://localhost:10086", "SecurityKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI2a2EJ7m872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI543nNDAPfnJsas96mSA7L/mD7RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB"//Token密钥,可以使用私钥加密RSA后 }
客户端(CoreWebApi)
-
控制器权限验证代码
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace Test.Core.WebApi.Controllers { [Route("api/[controller]")] [ApiController] [Authorize]//鉴权 public class AuthorizeController : ControllerBase { private ILogger<AuthorizeController> _Logger = null; public AuthorizeController(ILogger<AuthorizeController> logger) { this._Logger = logger; _Logger.LogInformation($"{nameof(AuthorizeController)} 被构造。。。。 "); } [HttpGet] [Route("Get")] [AllowAnonymous]//跳过鉴权 public IActionResult Get() { return new JsonResult(new { Data = "这个是OK的,这个Api并没有要求授权!" }); } [HttpGet] [Route("GetAuthorizeData")] // [Authorize] //Microsoft.AspNetCore.Authorization public IActionResult GetAuthorizeData() { //读取Token中的信息 var Name = base.HttpContext.AuthenticateAsync().Result.Principal.Claims.FirstOrDefault(a=>a.Type.Equals("Name"))?.Value; Console.WriteLine($"this is Name {Name}"); return new JsonResult(new { Data = "已授权", Type = "GetAuthorizeData" //Claims=Newtonsoft.Json.JsonConvert.SerializeObject(Claims) }); } } }
-
Startup.cs中注册服务
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { #region JWT鉴权授权 //1.Nuget引入程序包:Microsoft.AspNetCore.Authentication.JwtBearer //services.AddAuthentication();//禁用 var ValidAudience = this.Configuration["audience"]; var ValidIssuer = this.Configuration["issuer"]; var SecurityKey = this.Configuration["SecurityKey"]; services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) //默认授权机制名称; .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true,//是否验证Issuer ValidateAudience = true,//是否验证Audience ValidateLifetime = true,//是否验证失效时间 ValidateIssuerSigningKey = true,//是否验证SecurityKey ValidAudience = ValidAudience,//Audience ValidIssuer = ValidIssuer,//Issuer,这两项和前面签发jwt的设置一致 表示谁签发的Token IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey))//拿到SecurityKey //AudienceValidator = (m, n, z) => //{ // return m != null && m.FirstOrDefault().Equals(this.Configuration["audience"]); //},//自定义校验规则,可以新登录后将之前的无效 }; }); #endregion }
-
注册中间件
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //使用中间件 #region 通过中间件来支持鉴权授权 app.UseAuthentication(); #endregion }
跨域
同源策略
- 浏览器的同源策略阻止跨域
同源策略(Same Origin Policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略会阻止一个域的JS脚本和另外一个域的内容进行交互。
解决跨域问题的途径。
-
JSONP是基于前端使用特定的标签访问可以绕开浏览器的跨域验证。
-
前端网站不直接通过浏览器请求HTTP而是调用一个本地方法,方法中模拟了一个HTTP请求,从而绕开了浏览器。
- 在Index.cshtml文件url中将Http请求改为一个方法(基于MVC项目演示)
@{ ViewData["Title"] = "Home Page"; } <div class="text-center"> <h1 class="display-4">Welcome</h1> <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> </div> 请求数据: <div id="result"></div> <script src="~/js/jquery-3.4.1.js" charset="utf-8"></script> <script type="text/javascript"> $.ajax({ type: "Get", data: {}, url: "GetCrossDaminData", //为解决跨域,url中将Http请求改为一个方法 success: function (result) { $("#result").text(result); } }) </script>
- 在HomeController.cs对应的方法中模拟一个HTTP请求
[HttpGet] [Route("GetCrossDaminData")] public string GetCrossDaminData() { //我这里去模拟Http请求获取到数据以后再返回给前端 string url = "http://localhost:8004/api/CrossDamin/GetCrossDaminData"; using (HttpClient httpClient = new HttpClient()) { HttpRequestMessage message = new HttpRequestMessage(); message.Method = HttpMethod.Get; message.RequestUri = new Uri(url); var result = httpClient.SendAsync(message).Result; string content = result.Content.ReadAsStringAsync().Result; return content; } }
-
配置服务器使服务器允许跨域
-
在响应头中增加Access-Control-Allow-Origin字段
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Test.Core.WebApi.Utility; namespace Test.Core.WebApi.Controllers { [Route("api/[controller]")] [ApiController] //[CrossDomainFilterAttribute] //通过IActionFilter在响应头中增加Access-Control-Allow-Origin字段,实现跨域 public class CrossDaminController : ControllerBase { [HttpGet] [Route("GetCrossDaminData")] [CrossDomainFilterAttribute] //通过IActionFilter在响应头中增加Access-Control-Allow-Origin字段,实现跨域 public string GetCrossDaminData() { //base.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin", "*"); //在响应头中增加Access-Control-Allow-Origin字段,实现跨域 return Newtonsoft.Json.JsonConvert.SerializeObject( new { id=123, Name="开塔克撞火车", Description="这个Api 就是专门用测试跨域问题。。" }); } } }
using Microsoft.AspNetCore.Mvc.Filters; using System; namespace Test.Core.WebApi.Utility { public class CrossDomainFilterAttribute : Attribute, IActionFilter { //无论是在OnActionExecuted、OnActionExecuting 来指定跨域都可以;但是两个方法里面不能同时都指定; public void OnActionExecuted(ActionExecutedContext context) { #region 支持跨域 context.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin", "*"); #endregion } public void OnActionExecuting(ActionExecutingContext context) { //#region 支持跨域 //context.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin", "*"); //#endregion } } }
-
使用Cors Nuget包并注册服务
中间件支持跨域,依赖于程序包:Microsoft.AspNetCore.Cors
-
services.AddCors(option => option.AddPolicy("AllowCors", build =>build.AllowAnyOrigin().AllowAnyMethod()));
app.UseCors("AllowCors"); //必须要在UseRouting之后 在UseAuthorization之前。
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(option => option.AddPolicy("AllowCors", _build => _build.AllowAnyOrigin().AllowAnyMethod()));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//使用中间件
app.UseRouting();
app.UseCors("AllowCors");
#region 通过中间件来支持鉴权授权
app.UseAuthentication();
#endregion
app.UseAuthorization();
}