当前位置: 首页 > news >正文

中国工程建设企业协会网站网页图片尺寸

中国工程建设企业协会网站,网页图片尺寸,搭建一个网页,怎么百度上搜到自己的网站Bearer认证 HTTP提供了一套标准的身份验证框架#xff1a;服务器可以用来针对客户端的请求发送质询(challenge)#xff0c;客户端根据质询提供身份验证凭证。质询与应答的工作流程如下#xff1a;服务器端向客户端返回401#xff08;Unauthorized#xff0c;未授权#x… Bearer认证 HTTP提供了一套标准的身份验证框架服务器可以用来针对客户端的请求发送质询(challenge)客户端根据质询提供身份验证凭证。质询与应答的工作流程如下服务器端向客户端返回401Unauthorized未授权状态码并在WWW-Authenticate头中添加如何进行验证的信息其中至少包含有一种质询方式。然后客户端可以在请求中添加Authorization头进行验证其Value为身份验证的凭证信息。 在HTTP标准验证方案中我们比较熟悉的是Basic和Digest前者将用户名密码使用BASE64编码后作为验证凭证后者是Basic的升级版更加安全因为Basic是明文传输密码信息而Digest是加密后传输。在前文介绍的Cookie认证属于Form认证并不属于HTTP标准验证。 本文要介绍的Bearer验证也属于HTTP协议标准验证它随着OAuth协议而开始流行详细定义见 RFC 6570。 A security token with the property that any party in possession of the token (a bearer) can use the token in any way that any other party in possession of it can. Using a bearer token does not require a bearer to prove possession of cryptographic key material (proof-of-possession). Bearer验证中的凭证称为BEARER_TOKEN或者是access_token它的颁发和验证完全由我们自己的应用程序来控制而不依赖于系统和Web服务器Bearer验证的标准请求方式如下 Authorization: Bearer [BEARER_TOKEN] 那么使用Bearer验证有什么好处呢 CORS: cookies CORS 并不能跨不同的域名。而Bearer验证在任何域名下都可以使用HTTP header头部来传输用户信息。 对移动端友好: 当你在一个原生平台(iOS, Android, WindowsPhone等)时使用Cookie验证并不是一个好主意因为你得和Cookie容器打交道而使用Bearer验证则简单的多。 CSRF: 因为Bearer验证不再依赖于cookies, 也就避免了跨站请求攻击。 标准在Cookie认证中用户未登录时返回一个302到登录页面这在非浏览器情况下很难处理而Bearer验证则返回的是标准的401 challenge。 JWT(JSON WEB TOKEN) 上面介绍的Bearer认证其核心便是BEARER_TOKEN而最流行的Token编码方式便是JSON WEB TOKEN。 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准RFC 7519。该token被设计为紧凑且安全的特别适用于分布式站点的单点登录SSO场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息以便于从资源服务器获取资源也可以增加一些额外的其它业务逻辑所必须的声明信息该token也可直接被用于认证也可被加密。 JWT是由.分割的如下三部分组成 头部(Header) Header 一般由两个部分组成 alg typ alg是是所使用的hash算法如HMAC SHA256或RSAtyp是Token的类型在这里就是JWT。 {alg: HS256,typ: JWT} 然后使用Base64Url编码成第一部分 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.second part.third part 载荷(Payload) 这一部分是JWT主要的信息存储部分其中包含了许多种的声明claims。 Claims的实体一般包含用户和一些元数据这些claims分成三种类型 reserved claims预定义的 一些声明并不是强制的但是推荐它们包括 iss (issuer), exp (expiration time), sub (subject),aud(audience) 等这里都使用三个字母的原因是保证 JWT 的紧凑。 public claims: 公有声明这个部分可以随便定义但是要注意和 IANA JSON Web Token 冲突。 private claims: 私有声明这个部分是共享被认定信息中自定义部分。 一个简单的Pyload可以是这样子的 {sub: 1234567890,name: John Doe,admin: true} 这部分同样使用Base64Url编码成第二部分 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.third part 签名(Signature) Signature是用来验证发送者的JWT的同时也能确保在期间不被篡改。 在创建该部分时候你应该已经有了编码后的Header和Payload然后使用保存在服务端的秘钥对其签名一个完整的JWT如下 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 因此使用JWT具有如下好处 通用因为json的通用性所以JWT是可以进行跨语言支持的像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。 紧凑JWT的构成非常简单字节占用很小可以通过 GET、POST 等放在 HTTP 的 header 中非常便于传输。 扩展JWT是自我包涵的包含了必要的所有信息不需要在服务端保存会话信息, 非常易于应用的扩展。 关于更多JWT的介绍网上非常多这里就不再多做介绍。下面演示一下 ASP.NET Core 中 JwtBearer 认证的使用方式。 示例 模拟Token ASP.NET Core 内置的JwtBearer验证并不包含Token的发放我们先模拟一个简单的实现 [HttpPost(authenticate)]public IActionResult Authenticate([FromBody]UserDto userDto){    var user _store.FindUser(userDto.UserName, userDto.Password);       if (user null)      return Unauthorized();      var tokenHandler new JwtSecurityTokenHandler();      var key Encoding.ASCII.GetBytes(Consts.Secret);       var authTime DateTime.UtcNow;       var expiresAt authTime.AddDays(7);     var tokenDescriptor new SecurityTokenDescriptor{Subject new ClaimsIdentity(new Claim[]{            new Claim(JwtClaimTypes.Audience,api),            new Claim(JwtClaimTypes.Issuer,http://localhost:5200),            new Claim(JwtClaimTypes.Id, user.Id.ToString()),            new Claim(JwtClaimTypes.Name, user.Name),            new Claim(JwtClaimTypes.Email, user.Email),            new Claim(JwtClaimTypes.PhoneNumber, user.PhoneNumber)}),Expires expiresAt,SigningCredentials new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)};         var token tokenHandler.CreateToken(tokenDescriptor);          var tokenString tokenHandler.WriteToken(token);        return Ok(new{access_token tokenString,token_type Bearer,profile new{sid user.Id,name user.Name,auth_time new DateTimeOffset(authTime).ToUnixTimeSeconds(),expires_at new DateTimeOffset(expiresAt).ToUnixTimeSeconds()}}); } 如上使用微软提供的Microsoft.IdentityModel.Tokens帮助类(源码地址azure-activedirectory-identitymodel-extensions-for-dotnet)可以很容易的创建出JwtToen就不再多说。 注册JwtBearer认证 首先添加JwtBearer包引用: dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 2.0.0 然后在Startup类中添加如下配置 public void ConfigureServices(IServiceCollection services){services.AddAuthentication(x {x.DefaultAuthenticateScheme JwtBearerDefaults.AuthenticationScheme;x.DefaultChallengeScheme JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(o {o.TokenValidationParameters new TokenValidationParameters{NameClaimType JwtClaimTypes.Name,RoleClaimType JwtClaimTypes.Role,            // 将下面两个参数设置为false可以不验证Issuer和Audience但是不建议这样做。//ValidateIssuer false, // 默认为true//ValidateAudience false, // 默认为true      ValidIssuer http://localhost:5200,ValidAudience api,IssuerSigningKey new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Consts.Secret))};}); }public void Configure(IApplicationBuilder app){app.UseAuthentication(); } 在JwtBearerOptions的配置中通常IssuerSigningKey(签名秘钥), ValidIssuer(Token颁发机构), ValidAudience(颁发给谁) 三个参数是必须的后两者用于与TokenClaims中的Issuer和Audience进行对比不一致则验证失败与上面发放Token中的Claims对应。 而NameClaimType和RoleClaimType需与Token中的ClaimType一致在IdentityServer中也是使用的JwtClaimTypes否则会造成User.Identity.Name为空等问题。 添加受保护资源 创建一个需要授权的控制器直接使用Authorize即可 [Authorize] [Route(api/[controller])]public class SampleDataController : Controller{[HttpGet([action])]    public IEnumerableWeatherForecast WeatherForecasts()    {         return ...} } 运行 最后运行直接访问/api/SampleData/WeatherForecasts将返回一个401: HTTP/1.1 401 Unauthorized Server: Kestrel Content-Length: 0 WWW-Authenticate: Bearer 让我们调用api/oauth/authenticate获取一个JWT: 请求 POST http://localhost:5200/api/oauth/authenticate HTTP/1.1 content-type: application/json{username: alice,password: alice }响应 HTTP/1.1 200 OK {access_token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJuYW1lIjoiYWxpY2UiLCJlbWFpbCI6ImFsaWNlQGdtYWlsLmNvbSIsInBob25lX251bWJlciI6IjE4ODAwMDAwMDAxIiwibmJmIjoxNTA5NDY0MzQwLCJleHAiOjE1MTAwNjkxNDAsImlhdCI6MTUwOTQ2NDM0MH0.Y1TDz8KjLRh_vjQ_3iYP4oJw-fmhoboiAGPqIZ-ooNc,token_type:Bearer,profile:{sid:1,name:alice,auth_time:1509464340,expires_at:1510069140}} 最后使用该Token再次调用受保护资源 GET http://localhost:5200/api/SampleData/WeatherForecasts HTTP/1.1 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJuYW1lIjoiYWxpY2UiLCJlbWFpbCI6ImFsaWNlQGdtYWlsLmNvbSIsInBob25lX251bWJlciI6IjE4ODAwMDAwMDAxIiwibmJmIjoxNTA5NDY0MzQwLCJleHAiOjE1MTAwNjkxNDAsImlhdCI6MTUwOTQ2NDM0MH0.Y1TDz8KjLRh_vjQ_3iYP4oJw-fmhoboiAGPqIZ-ooNc 授权成功返回了预期的数据 HTTP/1.1 200 OK Content-Type: application/json; charsetutf-8[{dateFormatted:2017/11/3,temperatureC:35,summary:Chilly,temperatureF:94}] 扩展 自定义Token获取方式 JwtBearer认证中默认是通过Http的Authorization头来获取的这也是最推荐的做法但是在某些场景下我们可能会使用Url或者是Cookie来传递Token那要怎么来实现呢 其实实现起来非常简单如前几章介绍的一样JwtBearer也在认证的各个阶段为我们提供了事件来执行我们的自定义逻辑 .AddJwtBearer(o {o.Events new JwtBearerEvents(){OnMessageReceived context {context.Token context.Request.Query[access_token];            return Task.CompletedTask;}};o.TokenValidationParameters new TokenValidationParameters{...}; 然后在Url中添加access_token[token]直接在浏览器中访问 同样的我们也可以很容易的在Cookie中读取Token就不再演示。 除了OnMessageReceived外还提供了如下几个事件 TokenValidated在Token验证通过后调用。 AuthenticationFailed: 认证失败时调用。 Challenge: 未授权时调用。 使用OIDC服务 在上面的示例中我们简单模拟的Token颁发功能非常简单并不适合在生产环境中使用可是微软也没有提供OIDC服务的实现好在.NET社区中提供了几种实现可供我们选择 Name Description AspNet.Security.OpenIdConnect.Server (ASOS) Low-level/protocol-first OpenID Connect server framework for ASP.NET Core and OWIN/Katana IdentityServer4 OpenID Connect and OAuth 2.0 framework for ASP.NET Core - officially certified by the OpenID Foundation and under governance of the .NET Foundation OpenIddict Easy-to-use OpenID Connect server for ASP.NET Core PwdLess Simple, stateless, passwordless authentication for ASP.NET Core 我们在这里使用IdentityServer4来搭建一个OIDC服务器并添加如下配置 /********************OIDC服务器代码片段********************/public void ConfigureServices(IServiceCollection services){services.AddMvc();    // 配置IdentitryServerservices.AddIdentityServer().AddInMemoryPersistedGrants().AddInMemoryApiResources(Config.GetApis()).AddInMemoryIdentityResources(Config.GetIdentityResources()).AddInMemoryClients(Config.GetClients()).AddTestUsers(Config.GetUsers()).AddDeveloperSigningCredential(); }new Client {ClientId jwt.implicit,ClientName Implicit Client (Web),AllowedGrantTypes GrantTypes.Implicit,AllowAccessTokensViaBrowser true,RedirectUris { http://localhost:5200/callback },PostLogoutRedirectUris { http://localhost:5200/home },AllowedCorsOrigins { http://localhost:5200 },AllowedScopes { openid, profile, email, api }, } 而JwtBearer客户端的配置就更加简单了因为OIDC具有配置发现的功能 public void ConfigureServices(IServiceCollection services){services.AddAuthentication(x {x.DefaultAuthenticateScheme JwtBearerDefaults.AuthenticationScheme;x.DefaultChallengeScheme JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(o {o.Authority https://oidc.faasx.com/;o.Audience api;o.TokenValidationParameters new TokenValidationParameters{NameClaimType JwtClaimTypes.Name,RoleClaimType JwtClaimTypes.Role,};}); } 如上最重要的是Authority参数用来表示OIDC服务的地址然后便可以自动发现Issuer, IssuerSigningKey等配置而o.Audience与o.TokenValidationParameters new TokenValidationParameters { ValidAudience api }是等效的后面分析源码时会介绍。 OIDC兼容OAuth2协议我们可以使用上一章介绍的授权码模式来获取Token也可以直接用户名密码模式来获取Token 请求 POST https://oidc.faasx.com/connect/token HTTP/1.1 Content-Type: application/x-www-form-urlencodedclient_idclient.ropclient_secretsecretgrant_typepasswordscopeapiusernamealicepasswordalice响应 HTTP/1.1 200 OK Content-Type: application/json{access_token:eyJhbGciOiJSUzI1NiIsImtpZCI6IjdlYzk5MjVlMmUzMTA2NmY2ZmU2ODgzMDRhZjU1ZmM0IiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MDk2NzI1NjksImV4cCI6MTUwOTY3NjE2OSwiaXNzIjoiaHR0cHM6Ly9vaWRjLmZhYXN4LmNvbSIsImF1ZCI6WyJodHRwczovL29pZGMuZmFhc3guY29tL3Jlc291cmNlcyIsImFwaSJdLCJjbGllbnRfaWQiOiJjbGllbnQucm9wIiwic3ViIjoiMDAxIiwiYXV0aF90aW1lIjoxNTA5NjcyNTY5LCJpZHAiOiJsb2NhbCIsIm5hbWUiOiJBbGljZSBTbWl0aCIsImVtYWlsIjoiQWxpY2VTbWl0aEBlbWFpbC5jb20iLCJzY29wZSI6WyJhcGkiXSwiYW1yIjpbInB3ZCJdfQ.PM93LThOZA3lkgPFVwieqGQQQtgmYDCY0oSFVmudv1hpKO6UaaZsmnn4ci9QjbGl5g2433JkDks5UIZsZ0xE62Qqq8PicPBBuaNoYrCf6dxR7j-0uZcoa7-FCKGu-0TrM8OL-NuMvN6_KEpbWa3jlkwibCK9YDIwJZilVoWUOrbbIEsKTa-DdLScmzHLUzksT8GBr0PAVhge9PRFiGqg8cgMLjsA62ZeDsR35f55BucSV5Pj0SAj26anYvrBNTHKOF7ze1DGW51Dbz6DRu1X7uEIxSzWiNi4cRVJ6Totjkwk5F78R9R38o_mYEdehZBjRHFe6zLd91hXcCKqOEh5eQ,expires_in:3600,token_type:Bearer} 我在本章的示例代码中使用前端Angular框架演示了如何从本地登录获取Tokek或使用简化模式implicit从OIDC服务器获取Token然后保存到sesstionStorage在发送请求时附加到请求头中的示例可供大家参考JwtBearerSample。 源码探索 JwtBearerPostConfigureOptions 在ASP.NET Core 2.0 Options框架中新增了一种PostConfigure模式用来在我们所注册的Options配置执行完之后再对Options做一些修改。 JwtBearerPostConfigureOptions用来实现配置发现 public class JwtBearerPostConfigureOptions : IPostConfigureOptionsJwtBearerOptions {    public void PostConfigure(string name, JwtBearerOptions options)    {        // 如果未设置options.TokenValidationParameters.ValidAudience则使用options.Audienceif (string.IsNullOrEmpty(options.TokenValidationParameters.ValidAudience) !string.IsNullOrEmpty(options.Audience)){options.TokenValidationParameters.ValidAudience options.Audience;}        if (options.ConfigurationManager null){            // 如果未设置MetadataAddress则使用options.Authority.well-known/openid-configuration....options.ConfigurationManager new ConfigurationManagerOpenIdConnectConfiguration(options.MetadataAddress, new OpenIdConnectConfigurationRetriever(), new HttpDocumentRetriever(httpClient) { RequireHttps options.RequireHttpsMetadata });}}} } JwtBearerHandler JwtBearerHandler相对于前几章介绍的CookieHandler, OpenIdConnectHandler等都简单的多。 首先便是从请求中获取Token protected override async TaskAuthenticateResult HandleAuthenticateAsync(){    var messageReceivedContext new MessageReceivedContext(Context, Scheme, Options);    // 先触发MessageReceived事件来获取Tokenawait Events.MessageReceived(messageReceivedContext);    if (messageReceivedContext.Result ! null){        return messageReceivedContext.Result;}token messageReceivedContext.Token;    // Token为空时从Authorization头中获取if (string.IsNullOrEmpty(token)){        string authorization Request.Headers[Authorization];        if (string.IsNullOrEmpty(authorization)){            return AuthenticateResult.NoResult();}        if (authorization.StartsWith(Bearer , StringComparison.OrdinalIgnoreCase)){token authorization.Substring(Bearer .Length).Trim();}        if (string.IsNullOrEmpty(token)){            return AuthenticateResult.NoResult();}}...         } 然后初始化TokenValidationParameters参数为Token验证做准备 if (_configuration null Options.ConfigurationManager ! null) {_configuration await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); }var validationParameters Options.TokenValidationParameters.Clone();if (_configuration ! null) {    var issuers new[] { _configuration.Issuer };validationParameters.ValidIssuers validationParameters.ValidIssuers?.Concat(issuers) ?? issuers;validationParameters.IssuerSigningKeys validationParameters.IssuerSigningKeys?.Concat(_configuration.SigningKeys)?? _configuration.SigningKeys; } 可以看到从OIDC服务器提供的配置发现中获取ValidIssuers和IssuerSigningKeys。 最后对Token进行验证 // Options.SecurityTokenValidators 默认为 new ListISecurityTokenValidator { new JwtSecurityTokenHandler() }foreach (var validator in Options.SecurityTokenValidators) {    if (validator.CanReadToken(token)){ClaimsPrincipal principal;        try{principal validator.ValidateToken(token, validationParameters, out validatedToken);}        catch (Exception ex){  // RefreshOnIssuerKeyNotFound默认为True, 在SignatureKey未找到时重新从OIDC服务器获取if (Options.RefreshOnIssuerKeyNotFound Options.ConfigurationManager ! null ex is SecurityTokenSignatureKeyNotFoundException){Options.ConfigurationManager.RequestRefresh();}            continue;}...        // 触发TokenValidated事件await Events.TokenValidated(tokenValidatedContext);        // 默认为true保存Token到AuthenticationProperties中可以通过context.AuthenticateAsync()来获取在我们需要在服务端使用用户Token调用其他资源是非常有用。if (Options.SaveToken){tokenValidatedContext.Properties.StoreTokens(new[]{                new AuthenticationToken { Name access_token, Value token }});}        // 验证成功tokenValidatedContext.Success();        return tokenValidatedContext.Result;} } 其核心的验证也是在Microsoft.IdentityModel.Tokens中就不在深究。 当使用JwtBearer认证时我们肯定不希望在未登录时返回一个302因此在前面的示例中我们配置了x.DefaultChallengeScheme JwtBearerDefaults.AuthenticationScheme;对应的会执行JwtBearerHandler的HandleChallengeAsync方法 protected override async Task HandleChallengeAsync(AuthenticationProperties properties){    var authResult await HandleAuthenticateOnceSafeAsync();     var eventContext new JwtBearerChallengeContext(Context, Scheme, Options, properties){AuthenticateFailure authResult?.Failure};       if (Options.IncludeErrorDetails eventContext.AuthenticateFailure ! null){eventContext.Error invalid_token;eventContext.ErrorDescription CreateErrorDescription(eventContext.AuthenticateFailure);}            await Events.Challenge(eventContext);       if (eventContext.Handled){        return;}Response.StatusCode 401;    // 最终将相应报文拼接成如下// https://tools.ietf.org/html/rfc6750#section-3.1// WWW-Authenticate: Bearer realmexample, errorinvalid_token, error_descriptionThe access token expired} ASP.NET Core JwtBearer认证的完整源码地址Microsoft.AspNetCore.Authentication.JwtBearer。 总结 JwtToken其实与Cookie认证中加密后的Cookie值很像他们都是基于Claim的认证时无需STS(Security token service)的参与这在分布式环境下提供了极大的便利。而他们的本质上的区别是Cookie是微软式的很难与其他语言集成而JwtToken则是开放再开放与平台语言无关在前端也可以直接解析出Claims。 PS: 在使用在Bearer认证时通常还需与刷新Token配合来使用因为JwtToken的验证是无需经过STS的而当用户执行了退出修改密码等操作时是无法使该Token失效的。所以通常会给access_token设置一个较短的有效期(JwtBearer认证默认会验证有效期通过notBefore和expires来验证)当access_token过期后可以在用户无感知的情况下使用refresh_token自动从STS重新获取access_token但这就不属于Bearer认证的范畴了在后续介绍IdentityServer时再来详细介绍一下。 原文地址http://www.cnblogs.com/RainingNight/p/jwtbearer-authentication-in-asp-net-core.html NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注
http://www.zqtcl.cn/news/347106/

相关文章:

  • 网站开发优势wordpress 密码破解
  • 做网站空间需要多大深圳服装网站建设
  • 建网站wordpress制作app多少钱一个
  • 怎么做装修网站torrentkitty磁力猫
  • 网站建立站点wordpress手机网站模板制作
  • 宁夏建设工程招标投标信息网站教师做网站赚钱
  • 潍坊网站制作价格网站维护入门教程
  • 微信网站怎么做下载附件wordpress英文主题汉化
  • 桂平网站设计python基础教程第二版
  • wordpress hermit杭州企业seo网站优化
  • 贵州做团队培训的网站法学网站阵地建设
  • 网站死链是什么西宁高端网站开发公司
  • 做团购网站的公司wordpress附件存放位置
  • 成都最专业做网站的仿win8网站模板
  • 国外设计类网站男女做暖暖试看网站
  • 网站设计哪个好珠海微网站进入
  • 云主机开网站教程模板网会员
  • 网站建设无锡虚拟网站官网
  • 品牌网站设计联系东莞网站优化公
  • 自己做整个网站的流程php装修网站源码
  • 天津网站建设班模拟网站建设软件有哪些
  • 服务类的网站怎么做做软件的网站担保网站
  • 最新电子产品网站模板海口网站排名提升
  • 北京社保网站减员怎么做phpcms v9 实现网站搜索
  • 视频运营管理网站济南网站建设 济南货梯
  • html电影网站模板下载工具阿里云网站建设 部署与发布笔记
  • 建设跨境网站微信seo是什么意思
  • 我做彩票网站开发彩票网站搭建织梦如何仿手机网站源码下载
  • 东仓建设网站手机便宜的网站建设
  • 吕梁市住房与城乡建设厅网站wordpress 乐趣公园