一、前言

IdentityServer4已经分享了一些应用实战的文章,从架构到授权中心的落地应用,也伴随着对IdentityServer4掌握了一些使用规则,但是很多原理性东西还是一知半解,故我这里持续性来带大家一起来解读它的相关源代码,本文先来看看为什么Controller或者Action中添加Authorize或者全局中添加AuthorizeFilter过滤器就可以实现该资源受到保护,需要通过access_token才能通过相关的授权呢?今天我带大家来了解AuthorizeAttributeAuthorizeFilter的关系及代码解读。

二、代码解读

解读之前我们先来看看下面两种标注授权方式的代码:

标注方式
  1. [Authorize]
  2. [HttpGet]
  3. public async Task<object> Get()
  4. {
  5. var userId = User.UserId();
  6. return new
  7. {
  8. name = User.Name(),
  9. userId = userId,
  10. displayName = User.DisplayName(),
  11. merchantId = User.MerchantId(),
  12. };
  13. }

代码中通过[Authorize]标注来限制该api资源的访问

全局方式
  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. //全局添加AuthorizeFilter 过滤器方式
  4. services.AddControllers(options=>options.Filters.Add(new AuthorizeFilter()));
  5. services.AddAuthorization();
  6. services.AddAuthentication("Bearer")
  7. .AddIdentityServerAuthentication(options =>
  8. {
  9. options.Authority = "http://localhost:5000"; //配置Identityserver的授权地址
  10. options.RequireHttpsMetadata = false; //不需要https
  11. options.ApiName = OAuthConfig.UserApi.ApiName; //api的name,需要和config的名称相同
  12. });
  13. }

全局通过添加AuthorizeFilter过滤器方式进行全局api资源的限制

AuthorizeAttribute

先来看看AuthorizeAttribute源代码:

  1. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
  2. public class AuthorizeAttribute : Attribute, IAuthorizeData
  3. {
  4. /// <summary>
  5. /// Initializes a new instance of the <see cref="AuthorizeAttribute"/> class.
  6. /// </summary>
  7. public AuthorizeAttribute() { }
  8. /// <summary>
  9. /// Initializes a new instance of the <see cref="AuthorizeAttribute"/> class with the specified policy.
  10. /// </summary>
  11. /// <param name="policy">The name of the policy to require for authorization.</param>
  12. public AuthorizeAttribute(string policy)
  13. {
  14. Policy = policy;
  15. }
  16. /// <summary>
  17. /// 收取策略
  18. /// </summary>
  19. public string Policy { get; set; }
  20. /// <summary>
  21. /// 授权角色
  22. /// </summary>
  23. public string Roles { get; set; }
  24. /// <summary>
  25. /// 授权Schemes
  26. /// </summary>
  27. public string AuthenticationSchemes { get; set; }
  28. }

代码中可以看到AuthorizeAttribute继承了IAuthorizeData抽象接口,该接口主要是授权数据的约束定义,定义了三个数据属性

  • Prolicy :授权策略
  • Roles : 授权角色
  • AuthenticationSchemes :授权Schemes 的支持

    Asp.Net Core 中的http中间件会根据IAuthorizeData这个来获取有哪些授权过滤器,来实现过滤器的拦截并执行相关代码。

    我们看看AuthorizeAttribute代码如下:
  1. public interface IAuthorizeData
  2. {
  3. /// <summary>
  4. /// Gets or sets the policy name that determines access to the resource.
  5. /// </summary>
  6. string Policy { get; set; }
  7. /// <summary>
  8. /// Gets or sets a comma delimited list of roles that are allowed to access the resource.
  9. /// </summary>
  10. string Roles { get; set; }
  11. /// <summary>
  12. /// Gets or sets a comma delimited list of schemes from which user information is constructed.
  13. /// </summary>
  14. string AuthenticationSchemes { get; set; }
  15. }

我们再来看看授权中间件UseAuthorization)的核心代码:

  1. public static IApplicationBuilder UseAuthorization(this IApplicationBuilder app)
  2. {
  3. if (app == null)
  4. {
  5. throw new ArgumentNullException(nameof(app));
  6. }
  7. VerifyServicesRegistered(app);
  8. return app.UseMiddleware<AuthorizationMiddleware>();
  9. }

代码中注册了AuthorizationMiddleware这个中间件,AuthorizationMiddleware中间件源代码如下:

  1. public class AuthorizationMiddleware
  2. {
  3. // Property key is used by Endpoint routing to determine if Authorization has run
  4. private const string AuthorizationMiddlewareInvokedWithEndpointKey = "__AuthorizationMiddlewareWithEndpointInvoked";
  5. private static readonly object AuthorizationMiddlewareWithEndpointInvokedValue = new object();
  6. private readonly RequestDelegate _next;
  7. private readonly IAuthorizationPolicyProvider _policyProvider;
  8. public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider)
  9. {
  10. _next = next ?? throw new ArgumentNullException(nameof(next));
  11. _policyProvider = policyProvider ?? throw new ArgumentNullException(nameof(policyProvider));
  12. }
  13. public async Task Invoke(HttpContext context)
  14. {
  15. if (context == null)
  16. {
  17. throw new ArgumentNullException(nameof(context));
  18. }
  19. var endpoint = context.GetEndpoint();
  20. if (endpoint != null)
  21. {
  22. // EndpointRoutingMiddleware uses this flag to check if the Authorization middleware processed auth metadata on the endpoint.
  23. // The Authorization middleware can only make this claim if it observes an actual endpoint.
  24. context.Items[AuthorizationMiddlewareInvokedWithEndpointKey] = AuthorizationMiddlewareWithEndpointInvokedValue;
  25. }
  26. // 通过终结点路由元素IAuthorizeData来获得对于的AuthorizeAttribute并关联到AuthorizeFilter中
  27. var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
  28. var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
  29. if (policy == null)
  30. {
  31. await _next(context);
  32. return;
  33. }
  34. // Policy evaluator has transient lifetime so it fetched from request services instead of injecting in constructor
  35. var policyEvaluator = context.RequestServices.GetRequiredService<IPolicyEvaluator>();
  36. var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, context);
  37. // Allow Anonymous skips all authorization
  38. if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)
  39. {
  40. await _next(context);
  41. return;
  42. }
  43. // Note that the resource will be null if there is no matched endpoint
  44. var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, context, resource: endpoint);
  45. if (authorizeResult.Challenged)
  46. {
  47. if (policy.AuthenticationSchemes.Any())
  48. {
  49. foreach (var scheme in policy.AuthenticationSchemes)
  50. {
  51. await context.ChallengeAsync(scheme);
  52. }
  53. }
  54. else
  55. {
  56. await context.ChallengeAsync();
  57. }
  58. return;
  59. }
  60. else if (authorizeResult.Forbidden)
  61. {
  62. if (policy.AuthenticationSchemes.Any())
  63. {
  64. foreach (var scheme in policy.AuthenticationSchemes)
  65. {
  66. await context.ForbidAsync(scheme);
  67. }
  68. }
  69. else
  70. {
  71. await context.ForbidAsync();
  72. }
  73. return;
  74. }
  75. await _next(context);
  76. }
  77. }

代码中核心拦截并获得AuthorizeFilter过滤器的代码

  1. var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();

前面我分享过一篇关于 Asp.Net Core EndPoint 终结点路由工作原理解读 的文章里面讲解到通过EndPoint终结点路由来获取ControllerAction中的Attribute特性标注,这里也是通过该方法来拦截获取对于的AuthorizeAttribute的.

而获取到相关authorizeData授权数据后,下面的一系列代码都是通过判断来进行AuthorizeAsync授权执行的方法,这里就不详细分享它的授权认证的过程了。

细心的同学应该已经发现上面的代码有一个比较特殊的代码:

  1. if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)
  2. {
  3. await _next(context);
  4. return;
  5. }

代码中通过endpoint终结点路由来获取是否标注有AllowAnonymous的特性,如果有则直接执行下一个中间件,不进行下面的AuthorizeAsync授权认证方法,

这也是为什么ControllerAction上标注AllowAnonymous可以跳过授权认证的原因了。

AuthorizeFilter 源码

有的人会问AuthorizeAttirbuteAuthorizeFilter有什么关系呢?它们是一个东西吗?

我们再来看看AuthorizeFilter源代码,代码如下:

  1. public class AuthorizeFilter : IAsyncAuthorizationFilter, IFilterFactory
  2. {
  3. /// <summary>
  4. /// Initializes a new <see cref="AuthorizeFilter"/> instance.
  5. /// </summary>
  6. public AuthorizeFilter()
  7. : this(authorizeData: new[] { new AuthorizeAttribute() })
  8. {
  9. }
  10. /// <summary>
  11. /// Initialize a new <see cref="AuthorizeFilter"/> instance.
  12. /// </summary>
  13. /// <param name="policy">Authorization policy to be used.</param>
  14. public AuthorizeFilter(AuthorizationPolicy policy)
  15. {
  16. if (policy == null)
  17. {
  18. throw new ArgumentNullException(nameof(policy));
  19. }
  20. Policy = policy;
  21. }
  22. /// <summary>
  23. /// Initialize a new <see cref="AuthorizeFilter"/> instance.
  24. /// </summary>
  25. /// <param name="policyProvider">The <see cref="IAuthorizationPolicyProvider"/> to use to resolve policy names.</param>
  26. /// <param name="authorizeData">The <see cref="IAuthorizeData"/> to combine into an <see cref="IAuthorizeData"/>.</param>
  27. public AuthorizeFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData)
  28. : this(authorizeData)
  29. {
  30. if (policyProvider == null)
  31. {
  32. throw new ArgumentNullException(nameof(policyProvider));
  33. }
  34. PolicyProvider = policyProvider;
  35. }
  36. /// <summary>
  37. /// Initializes a new instance of <see cref="AuthorizeFilter"/>.
  38. /// </summary>
  39. /// <param name="authorizeData">The <see cref="IAuthorizeData"/> to combine into an <see cref="IAuthorizeData"/>.</param>
  40. public AuthorizeFilter(IEnumerable<IAuthorizeData> authorizeData)
  41. {
  42. if (authorizeData == null)
  43. {
  44. throw new ArgumentNullException(nameof(authorizeData));
  45. }
  46. AuthorizeData = authorizeData;
  47. }
  48. /// <summary>
  49. /// Initializes a new instance of <see cref="AuthorizeFilter"/>.
  50. /// </summary>
  51. /// <param name="policy">The name of the policy to require for authorization.</param>
  52. public AuthorizeFilter(string policy)
  53. : this(new[] { new AuthorizeAttribute(policy) })
  54. {
  55. }
  56. /// <summary>
  57. /// The <see cref="IAuthorizationPolicyProvider"/> to use to resolve policy names.
  58. /// </summary>
  59. public IAuthorizationPolicyProvider PolicyProvider { get; }
  60. /// <summary>
  61. /// The <see cref="IAuthorizeData"/> to combine into an <see cref="IAuthorizeData"/>.
  62. /// </summary>
  63. public IEnumerable<IAuthorizeData> AuthorizeData { get; }
  64. /// <summary>
  65. /// Gets the authorization policy to be used.
  66. /// </summary>
  67. /// <remarks>
  68. /// If<c>null</c>, the policy will be constructed using
  69. /// <see cref="AuthorizationPolicy.CombineAsync(IAuthorizationPolicyProvider, IEnumerable{IAuthorizeData})"/>.
  70. /// </remarks>
  71. public AuthorizationPolicy Policy { get; }
  72. bool IFilterFactory.IsReusable => true;
  73. // Computes the actual policy for this filter using either Policy or PolicyProvider + AuthorizeData
  74. private Task<AuthorizationPolicy> ComputePolicyAsync()
  75. {
  76. if (Policy != null)
  77. {
  78. return Task.FromResult(Policy);
  79. }
  80. if (PolicyProvider == null)
  81. {
  82. throw new InvalidOperationException(
  83. Resources.FormatAuthorizeFilter_AuthorizationPolicyCannotBeCreated(
  84. nameof(AuthorizationPolicy),
  85. nameof(IAuthorizationPolicyProvider)));
  86. }
  87. return AuthorizationPolicy.CombineAsync(PolicyProvider, AuthorizeData);
  88. }
  89. internal async Task<AuthorizationPolicy> GetEffectivePolicyAsync(AuthorizationFilterContext context)
  90. {
  91. // Combine all authorize filters into single effective policy that's only run on the closest filter
  92. var builder = new AuthorizationPolicyBuilder(await ComputePolicyAsync());
  93. for (var i = 0; i < context.Filters.Count; i++)
  94. {
  95. if (ReferenceEquals(this, context.Filters[i]))
  96. {
  97. continue;
  98. }
  99. if (context.Filters[i] is AuthorizeFilter authorizeFilter)
  100. {
  101. // Combine using the explicit policy, or the dynamic policy provider
  102. builder.Combine(await authorizeFilter.ComputePolicyAsync());
  103. }
  104. }
  105. var endpoint = context.HttpContext.GetEndpoint();
  106. if (endpoint != null)
  107. {
  108. // When doing endpoint routing, MVC does not create filters for any authorization specific metadata i.e [Authorize] does not
  109. // get translated into AuthorizeFilter. Consequently, there are some rough edges when an application uses a mix of AuthorizeFilter
  110. // explicilty configured by the user (e.g. global auth filter), and uses endpoint metadata.
  111. // To keep the behavior of AuthFilter identical to pre-endpoint routing, we will gather auth data from endpoint metadata
  112. // and produce a policy using this. This would mean we would have effectively run some auth twice, but it maintains compat.
  113. var policyProvider = PolicyProvider ?? context.HttpContext.RequestServices.GetRequiredService<IAuthorizationPolicyProvider>();
  114. var endpointAuthorizeData = endpoint.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
  115. var endpointPolicy = await AuthorizationPolicy.CombineAsync(policyProvider, endpointAuthorizeData);
  116. if (endpointPolicy != null)
  117. {
  118. builder.Combine(endpointPolicy);
  119. }
  120. }
  121. return builder.Build();
  122. }
  123. /// <inheritdoc />
  124. public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context)
  125. {
  126. if (context == null)
  127. {
  128. throw new ArgumentNullException(nameof(context));
  129. }
  130. if (!context.IsEffectivePolicy(this))
  131. {
  132. return;
  133. }
  134. // IMPORTANT: Changes to authorization logic should be mirrored in security's AuthorizationMiddleware
  135. var effectivePolicy = await GetEffectivePolicyAsync(context);
  136. if (effectivePolicy == null)
  137. {
  138. return;
  139. }
  140. var policyEvaluator = context.HttpContext.RequestServices.GetRequiredService<IPolicyEvaluator>();
  141. var authenticateResult = await policyEvaluator.AuthenticateAsync(effectivePolicy, context.HttpContext);
  142. // Allow Anonymous skips all authorization
  143. if (HasAllowAnonymous(context))
  144. {
  145. return;
  146. }
  147. var authorizeResult = await policyEvaluator.AuthorizeAsync(effectivePolicy, authenticateResult, context.HttpContext, context);
  148. if (authorizeResult.Challenged)
  149. {
  150. context.Result = new ChallengeResult(effectivePolicy.AuthenticationSchemes.ToArray());
  151. }
  152. else if (authorizeResult.Forbidden)
  153. {
  154. context.Result = new ForbidResult(effectivePolicy.AuthenticationSchemes.ToArray());
  155. }
  156. }
  157. IFilterMetadata IFilterFactory.CreateInstance(IServiceProvider serviceProvider)
  158. {
  159. if (Policy != null || PolicyProvider != null)
  160. {
  161. // The filter is fully constructed. Use the current instance to authorize.
  162. return this;
  163. }
  164. Debug.Assert(AuthorizeData != null);
  165. var policyProvider = serviceProvider.GetRequiredService<IAuthorizationPolicyProvider>();
  166. return AuthorizationApplicationModelProvider.GetFilter(policyProvider, AuthorizeData);
  167. }
  168. private static bool HasAllowAnonymous(AuthorizationFilterContext context)
  169. {
  170. var filters = context.Filters;
  171. for (var i = 0; i < filters.Count; i++)
  172. {
  173. if (filters[i] is IAllowAnonymousFilter)
  174. {
  175. return true;
  176. }
  177. }
  178. // When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes that
  179. // were discovered on controllers and actions. To maintain compat with 2.x,
  180. // we'll check for the presence of IAllowAnonymous in endpoint metadata.
  181. var endpoint = context.HttpContext.GetEndpoint();
  182. if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
  183. {
  184. return true;
  185. }
  186. return false;
  187. }
  188. }

代码中继承了 IAsyncAuthorizationFilter, IFilterFactory两个抽象接口,分别来看看这两个抽象接口的源代码

IAsyncAuthorizationFilter源代码如下:
  1. /// <summary>
  2. /// A filter that asynchronously confirms request authorization.
  3. /// </summary>
  4. public interface IAsyncAuthorizationFilter : IFilterMetadata
  5. {
  6. ///定义了授权的方法
  7. Task OnAuthorizationAsync(AuthorizationFilterContext context);
  8. }

IAsyncAuthorizationFilter代码中继承了IFilterMetadata接口,同时定义了OnAuthorizationAsync抽象方法,子类需要实现该方法,然而AuthorizeFilter中也已经实现了该方法,稍后再来详细讲解该方法,我们再继续看看IFilterFactory抽象接口,代码如下:

  1. public interface IFilterFactory : IFilterMetadata
  2. {
  3. bool IsReusable { get; }
  4. //创建IFilterMetadata 对象方法
  5. IFilterMetadata CreateInstance(IServiceProvider serviceProvider);
  6. }

我们回到AuthorizeFilter 源代码中,该源代码中提供了四个构造初始化方法同时包含了AuthorizeDataPolicy属性,我们看看它的默认构造方法代码

  1. public class AuthorizeFilter : IAsyncAuthorizationFilter, IFilterFactory
  2. {
  3. public IEnumerable<IAuthorizeData> AuthorizeData { get; }
  4. //默认构造函数中默认创建了AuthorizeAttribute 对象
  5. public AuthorizeFilter()
  6. : this(authorizeData: new[] { new AuthorizeAttribute() })
  7. {
  8. }
  9. //赋值AuthorizeData
  10. public AuthorizeFilter(IEnumerable<IAuthorizeData> authorizeData)
  11. {
  12. if (authorizeData == null)
  13. {
  14. throw new ArgumentNullException(nameof(authorizeData));
  15. }
  16. AuthorizeData = authorizeData;
  17. }
  18. }

上面的代码中默认的构造函数默认给构建了一个AuthorizeAttribute对象,并且赋值给了IEnumerable<IAuthorizeData>的集合属性;

好了,看到这里AuthorizeFilter过滤器也是默认构造了一个AuthorizeAttribute的对象,也就是构造了授权所需要的IAuthorizeData信息.

同时AuthorizeFilter实现的OnAuthorizationAsync方法中通过GetEffectivePolicyAsync这个方法获得有效的授权策略,并且进行下面的授权AuthenticateAsync的执行

AuthorizeFilter代码中提供了HasAllowAnonymous方法来实现是否Controller或者Action上标注了AllowAnonymous特性,用于跳过授权

HasAllowAnonymous代码如下:

  1. private static bool HasAllowAnonymous(AuthorizationFilterContext context)
  2. {
  3. var filters = context.Filters;
  4. for (var i = 0; i < filters.Count; i++)
  5. {
  6. if (filters[i] is IAllowAnonymousFilter)
  7. {
  8. return true;
  9. }
  10. }
  11. //同样通过上下文的endpoint 来获取是否标注了AllowAnonymous特性
  12. var endpoint = context.HttpContext.GetEndpoint();
  13. if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
  14. {
  15. return true;
  16. }
  17. return false;
  18. }

到这里我们再回到全局添加过滤器的方式代码:

  1. services.AddControllers(options=>options.Filters.Add(new AuthorizeFilter()));

分析到这里 ,我很是好奇,它是怎么全局添加进去的呢?我打开源代码看了下,源代码如下:

  1. public class MvcOptions : IEnumerable<ICompatibilitySwitch>
  2. {
  3. public MvcOptions()
  4. {
  5. CacheProfiles = new Dictionary<string, CacheProfile>(StringComparer.OrdinalIgnoreCase);
  6. Conventions = new List<IApplicationModelConvention>();
  7. Filters = new FilterCollection();
  8. FormatterMappings = new FormatterMappings();
  9. InputFormatters = new FormatterCollection<IInputFormatter>();
  10. OutputFormatters = new FormatterCollection<IOutputFormatter>();
  11. ModelBinderProviders = new List<IModelBinderProvider>();
  12. ModelBindingMessageProvider = new DefaultModelBindingMessageProvider();
  13. ModelMetadataDetailsProviders = new List<IMetadataDetailsProvider>();
  14. ModelValidatorProviders = new List<IModelValidatorProvider>();
  15. ValueProviderFactories = new List<IValueProviderFactory>();
  16. }
  17. //过滤器集合
  18. public FilterCollection Filters { get; }
  19. }

FilterCollection相关核心代码如下:

  1. public class FilterCollection : Collection<IFilterMetadata>
  2. {
  3. public IFilterMetadata Add<TFilterType>() where TFilterType : IFilterMetadata
  4. {
  5. return Add(typeof(TFilterType));
  6. }
  7. //其他核心代码为贴出来
  8. }

代码中提供了Add方法,约束了IFilterMetadata类型的对象,这也是上面的过滤器中为什么都继承了IFilterMetadata的原因。

到这里代码解读和实现原理已经分析完了,如果有分析不到位之处还请多多指教!!!

结论:授权中间件通过获取IAuthorizeData来获取AuthorizeAttribute对象相关的授权信息,并构造授权策略对象进行授权认证的,而AuthorizeFilter过滤器也会默认添加AuthorizeAttribute的授权相关数据IAuthorizeData并实现OnAuthorizationAsync方法,同时中间件中通过授权策略提供者IAuthorizationPolicyProvider来获得对于的授权策略进行授权认证.

Asp.Net Core AuthorizeAttribute 和AuthorizeFilter 跟进及源码解读的更多相关文章

  1. ASP.Net Core MVC6 RC2 启动过程分析[偏源码分析]

    入口程序 如果做过Web之外开发的人,应该记得这个是标准的Console或者Winform的入口.为什么会这样呢? .NET Web Development and Tools Blog ASP.NE ...

  2. asp.net core结合docker实现自动化获取源码、部署、更新

    之前入坑dotnet core,由于一开始就遇到在windows上编译发布的web无法直接放到centos上执行.之后便直接研究docker,实现在容器中编译发布.然后就越玩越大,后来利用git的ho ...

  3. ASP.NET Core错误处理中间件[4]: 响应状态码页面

    StatusCodePagesMiddleware中间件与ExceptionHandlerMiddleware中间件类似,它们都是在后续请求处理过程中"出错"的情况下利用一个错误处 ...

  4. ASP.NET程序读取二代身份证(附源码)

    原文:ASP.NET程序读取二代身份证(附源码) 一般来说winform应用程序解决这个问题起来时很容易的,web应用程序就麻烦一点了. 这里我说说我的解决思路: 一.你必要有联机型居民身份证阅读器一 ...

  5. winserver的consul部署实践与.net core客户端使用(附demo源码)

    winserver的consul部署实践与.net core客户端使用(附demo源码)   前言 随着微服务兴起,服务的管理显得极其重要.都知道微服务就是”拆“,把臃肿的单块应用,拆分成多个轻量级的 ...

  6. Asp.net MVC - 使用PRG模式(附源码)

    阅读目录: 一. 传统的Asp.net页面问题 二.Asp.net MVC中也存在同样的问题 三.使用PRG模式 四.PRG模式在MVC上的实现 一. 传统的Asp.net页面问题 一个传统的Asp. ...

  7. ASP.NET MVC通用权限管理系统(响应布局)源码更新介绍

    一.asp.net mvc 通用权限管理系统(响应布局)源码主要以下特点: AngelRM(Asp.net MVC)是基于asp.net(C#)MVC+前端bootstrap+ztree+lodash ...

  8. ASP.NET Core Web 支付功能接入 微信-扫码支付篇

    这篇文章将介绍ASP.NET Core中使用 开源项目 Payment,实现接入微信-扫码支付及异步通知功能. 开发环境:Win 10 x64.VS2017 15.6.4..NET Core SDK ...

  9. 【转载】ASP.NET Core Web 支付功能接入 微信-扫码支付篇

    转自:http://www.cnblogs.com/essenroc/p/8630730.html 这篇文章将介绍ASP.NET Core中使用 开源项目 Payment,实现接入微信-扫码支付及异步 ...

随机推荐

  1. 使用Commons Logging

    Commons Logging 和Java标准库提供的日志不同,Commons Logging是一个第三方日志库,它是由Apache创建的日志模块,需要导入commons-logging-1.2.ja ...

  2. python有关汉字编码问题

    python分为:程序编码(python安装程序).文件编码. 查看程序编码方式:sys.getdefaultencoding() 查看文件编码方式:1.import  chardet  2. f = ...

  3. numpy array 分割

    import numpy as np A = np.array([1,1,1])[:,np.newaxis] B = np.array([2,2,2])[:,np.newaxis] #合并 C = n ...

  4. 从假图片到假新闻,AI就这样“控制”了我们

    在评论某位新蹿红的明星时,围观群众总是习惯性地先从长相上来判定,如"像周润发和梁朝伟的合体"."刘德华和郭富城的合体"等--反正比"黄渤和王宝强的合体 ...

  5. http 详解

    HTTP协议中GET.POST和HEAD的介绍 GET: 请求指定的页面信息,并返回实体主体. HEAD: 只请求页面的首部. POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体 ...

  6. HTML笔记02

    网页中的颜色有三种表示方法 颜色单词:blue.green.red.yellow 10进制表示:rgb(255,0,0).rgb(0,255,0).rgb(0,0,255) 16进制表示:#ff000 ...

  7. SpringMVC 使用注解完成登录拦截

    目录 为了实现用户登录拦截你是否写过如下代码呢? 1. 基于Filter 2. 基于Struts 3. 基于SpringMVC 如何使用自定义注解完成自定义拦截呢? 登录注解 SpringMVC 拦截 ...

  8. C++走向远洋——48(项目一1、复数类中的运算符重载、类的成员函数)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  9. NOI Online 赛前刷题计划

    Day 1 模拟 链接:Day 1  模拟 题单:P1042 乒乓球  字符串 P1015 回文数  高精 + 进制 P1088 火星人  搜索 + 数论 P1604 B进制星球  高精 + 进制 D ...

  10. http协议、加密解密、web安全

    今天,就简单讲讲,我学习的知识.http协议:http协议是超文本传输协议,是用于传输超媒文档的应用层协议,同时,http协议是无状态协议,意味着,在服务器两个请求之间不会保留任何数据.虽然通常基于T ...