中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

博客網(wǎng)站制作以網(wǎng)絡(luò)營(yíng)銷為主題的論文

博客網(wǎng)站制作,以網(wǎng)絡(luò)營(yíng)銷為主題的論文,dede網(wǎng)站日志,wordpress 修改文檔目錄名JWT:全稱是JSON Web Token是目前最流行的跨域身份驗(yàn)證、分布式登錄、單點(diǎn)登錄等解決方案。 通俗地來講,JWT是能代表用戶身份的令牌,可以使用JWT令牌在api接口中校驗(yàn)用戶的身份以確認(rèn)用戶是否有訪問api的權(quán)限。 授權(quán):這是使用JWT的…

JWT:全稱是JSON Web Token是目前最流行的跨域身份驗(yàn)證、分布式登錄、單點(diǎn)登錄等解決方案。

?通俗地來講,JWT是能代表用戶身份的令牌,可以使用JWT令牌在api接口中校驗(yàn)用戶的身份以確認(rèn)用戶是否有訪問api的權(quán)限。

授權(quán):這是使用JWT的最常見方案。一旦用戶登錄,每個(gè)后續(xù)請(qǐng)求將包括JWT,允許用戶訪問該令牌允許的路由,服務(wù)和資源。

在身份驗(yàn)證中,當(dāng)用戶使用其憑據(jù)成功登錄時(shí),將返回JSON Web令牌。由于令牌是憑證,因此必須非常小心以防止出現(xiàn)安全問題。一般情況下,您不應(yīng)該將令牌保留的時(shí)間超過要求。

每當(dāng)用戶想要訪問受保護(hù)的路由或資源時(shí),用戶代理應(yīng)該使用承載模式發(fā)送JWT,通常在Authorization標(biāo)頭中,標(biāo)題的內(nèi)容應(yīng)如下所示:

Authorization: Bearer <token>

1、應(yīng)用程序向授權(quán)服務(wù)器請(qǐng)求授權(quán);

2、校驗(yàn)用戶身份,校驗(yàn)成功,返回token;

3、應(yīng)用程序使用訪問令牌訪問受保護(hù)的資源。

?JWT的實(shí)現(xiàn)方式是將用戶信息存儲(chǔ)在客戶端,服務(wù)端不進(jìn)行保存。每次請(qǐng)求都把令牌帶上以校驗(yàn)用戶登錄狀態(tài),這樣服務(wù)就變成了無狀態(tài)的,服務(wù)器集群也很好擴(kuò)展。

更多理論知識(shí)可以查看官網(wǎng),或者查看相關(guān)網(wǎng)友的文章,如下推薦文章:

  • asp.net core 集成JWT(一):https://www.cnblogs.com/7tiny/archive/2019/06/13/11012035.html
  • 五分鐘帶你了解啥是JWT:https://zhuanlan.zhihu.com/p/86937325
  • C#分布式登錄——jwt:https://www.cnblogs.com/yswenli/p/13510050.html

在nuget里面引用jwt集成的程序包,這里需要注意的是,如果你用的是.NET Core 3.1的框架的話,程序包版本選擇3.1.7

Microsoft.AspNetCore.Authentication.JwtBearer

添加數(shù)據(jù)訪問模擬api,新建控制器ValuesController

其中api/value1是可以直接訪問的,api/value2添加了權(quán)限校驗(yàn)特性標(biāo)簽 [Authorize]

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;namespace jwtWebAPI.Controllers
{[ApiController]public class ValuesController : ControllerBase{[HttpGet][Route("api/values1")]public ActionResult<IEnumerable<string>> values1(){return new string[] { "value1", "value1" };}/*** 該接口用Authorize特性做了權(quán)限校驗(yàn),如果沒有通過權(quán)限校驗(yàn),則http返回狀態(tài)碼為401* 調(diào)用該接口的正確姿勢(shì)是:* 1.登陸,調(diào)用api/Auth接口獲取到token* 2.調(diào)用該接口 api/value2 在請(qǐng)求的Header中添加參數(shù) Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOiIxNTYwMzM1MzM3IiwiZXhwIjoxNTYwMzM3MTM3LCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiemhhbmdzYW4iLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAifQ.1S-40SrA4po2l4lB_QdzON_G5ZNT4P_6U25xhTcl7hI* Bearer后面有空格,且后面是第一步中接口返回的token值* */[HttpGet][Route("api/value2")][Authorize]public ActionResult<IEnumerable<string>> value2(){//這是獲取自定義參數(shù)的方法var auth = HttpContext.AuthenticateAsync().Result.Principal.Claims;var userName = auth.FirstOrDefault(t => t.Type.Equals(ClaimTypes.NameIdentifier))?.Value;return new string[] { "訪問成功:這個(gè)接口登陸過的用戶都可以訪問", $"userName={userName}" };}}
}

添加模擬登陸生成Token的api,新建控制器AuthController

這里模擬一下登陸校驗(yàn),只驗(yàn)證了用戶密碼不為空即通過校驗(yàn),真實(shí)環(huán)境完善校驗(yàn)用戶和密碼的邏輯。

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
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 jwtWebAPI.Controllers
{[ApiController]public class AuthController : Controller{/// <summary>/// 通過賬號(hào)+密碼獲取Token/// </summary>/// <param name="userName"></param>/// <param name="pwd"></param>/// <returns>Token</returns>[AllowAnonymous][HttpGet][Route("api/auth")]public IActionResult GetToken(string userName, string pwd){if (!string.IsNullOrEmpty(userName)){//每次登陸動(dòng)態(tài)刷新Const.ValidAudience = userName + pwd + DateTime.Now.ToString();// push the user’s name into a claim, so we can identify the user later on.//這里可以隨意加入自定義的參數(shù),key可以自己隨便起var claims = new[]{new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") ,new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddMinutes(3)).ToUnixTimeSeconds()}"),new Claim(ClaimTypes.NameIdentifier, userName)};//sign the token using a secret key.This secret will be shared between your API and anything that needs to check that the token is legit.var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey));var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);//.NET Core’s JwtSecurityToken class takes on the heavy lifting and actually creates the token.var token = new JwtSecurityToken(//頒發(fā)者issuer: Const.Domain,//接收者audience: Const.ValidAudience,//過期時(shí)間(可自行設(shè)定,注意和上面的claims內(nèi)部Exp參數(shù)保持一致)expires: DateTime.Now.AddMinutes(3),//簽名證書signingCredentials: creds,//自定義參數(shù)claims: claims);return Ok(new{token = new JwtSecurityTokenHandler().WriteToken(token)});}else{return BadRequest(new { message = "username or password is incorrect." });}}}
}

Startup添加JWT驗(yàn)證的相關(guān)配置

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace jwtWebAPI
{public class Startup{public Startup(IConfiguration configuration){Configuration = configuration;}public IConfiguration Configuration { get; }// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){//添加jwt驗(yàn)證:services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {options.TokenValidationParameters = new TokenValidationParameters{ValidateLifetime = true,//是否驗(yàn)證失效時(shí)間ClockSkew = TimeSpan.FromSeconds(30),  //時(shí)間偏移量(允許誤差時(shí)間)ValidateAudience = true,//是否驗(yàn)證Audience(驗(yàn)證之前的token是否失效)//ValidAudience = Const.GetValidudience(),//Audience//這里采用動(dòng)態(tài)驗(yàn)證的方式,在重新登陸時(shí),刷新token,舊token就強(qiáng)制失效了AudienceValidator = (m, n, z) =>{return m != null && m.FirstOrDefault().Equals(Const.ValidAudience);},ValidateIssuer = true,//是否驗(yàn)證Issuer(頒發(fā)者)ValidAudience = Const.Domain,//Audience    【Const是新建的一個(gè)常量類】  接收者 ValidIssuer = Const.Domain,//Issuer,這兩項(xiàng)和前面簽發(fā)jwt的設(shè)置一致      頒發(fā)者ValidateIssuerSigningKey = true,//是否驗(yàn)證SecurityKeyIssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey))//拿到秘鑰SecurityKey};options.Events = new JwtBearerEvents{OnAuthenticationFailed = context =>{//Token expiredif (context.Exception.GetType() == typeof(SecurityTokenExpiredException)){context.Response.Headers.Add("Token-Expired", "true");}return Task.CompletedTask;}};});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){ //添加jwt驗(yàn)證app.UseAuthentication();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseHttpsRedirection();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});}}
}

創(chuàng)建常量類Const

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;namespace jwtWebAPI
{public class Const{/// <summary>/// 這里為了演示,寫死一個(gè)密鑰。實(shí)際生產(chǎn)環(huán)境可以從配置文件讀取,這個(gè)是用網(wǎng)上工具隨便生成的一個(gè)密鑰(md5或者其他都可以)/// </summary>public const string SecurityKey = "48754F4C58F9EA428FE09D714E468211";/// <summary>/// 站點(diǎn)地址(頒發(fā)者、接受者),這里測(cè)試和當(dāng)前本地運(yùn)行網(wǎng)站相同,實(shí)際發(fā)到正式環(huán)境應(yīng)為域名地址/// </summary>public const string Domain = "https://localhost:44345";/// <summary>/// 受理人,之所以弄成可變的是為了用接口動(dòng)態(tài)更改這個(gè)值以模擬強(qiáng)制Token失效/// 真實(shí)業(yè)務(wù)場(chǎng)景可以在數(shù)據(jù)庫或者redis存一個(gè)和用戶id相關(guān)的值,生成token和驗(yàn)證token的時(shí)候獲取到持久化的值去校驗(yàn)/// 如果重新登陸,則刷新這個(gè)值/// </summary>public static string ValidAudience;}
}

JWT登錄授權(quán)測(cè)試成功

返回了狀態(tài)碼401,也就是未經(jīng)授權(quán):訪問由于憑據(jù)無效被拒絕。 說明JWT校驗(yàn)生效了,我們的接口收到了保護(hù)。

調(diào)用模擬登陸授權(quán)接口:https://localhost:44345/api/auth?userName=xiongze&pwd=123456

這里的用戶密碼是隨便寫的,因?yàn)槲覀兡M登陸只是校驗(yàn)了下非空,因此寫什么都能通過。

然后我們得到了一個(gè)xxx.yyy.zzz 格式的 token 值。我們把token復(fù)制出來。

在剛才401的接口(https://localhost:44345/api/values2)請(qǐng)求header中添加JWT的參數(shù),把我們的token加上去

再次調(diào)用我們的模擬數(shù)據(jù)接口,但是這次我們加了一個(gè)header,KEY:Authorization? ? ?Value:Bearer Tokne的值

這里需要注意?Bearer 后面是有一個(gè)空格的,然后就是我們上一步獲取到的token,

得到返回值,正確授權(quán)成功,我們是支持自定義返回參數(shù)的,上面代碼里面有相關(guān)內(nèi)容,比如用戶名這些不敏感的信息可以帶著返回。

等token設(shè)置的過期時(shí)間到了,或者重新生成了新的Token,沒有及時(shí)更新,那么我們的授權(quán)也到期,401,

升級(jí)操作:接口權(quán)限隔離

上面的操作是所有登錄授權(quán)成功的角色都可以進(jìn)行調(diào)用所有接口,那么我們現(xiàn)在想要進(jìn)行接口隔離限制,

也就是說,雖然授權(quán)登錄了,但是我這個(gè)接口是指定權(quán)限訪問的。

比如說:刪除接口只能管理員角色操作,那么其他角色雖然授權(quán)登錄了,但是沒有權(quán)限調(diào)用刪除接口。

我們?cè)谠瓉淼牟僮鬟M(jìn)行改造升級(jí)看一下。

添加類

新建一個(gè)AuthManagement文件夾,添加PolicyRequirement類PolicyHandler類

PolicyRequirement類:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;namespace jwtWebAPI.AuthManagement
{/// <summary>/// 權(quán)限承載實(shí)體/// </summary>public class PolicyRequirement : IAuthorizationRequirement{/// <summary>/// 用戶權(quán)限集合/// </summary>public List<UserPermission> UserPermissions { get; private set; }/// <summary>/// 無權(quán)限action/// </summary>public string DeniedAction { get; set; }/// <summary>/// 構(gòu)造/// </summary>public PolicyRequirement(){//沒有權(quán)限則跳轉(zhuǎn)到這個(gè)路由DeniedAction = new PathString("/api/nopermission");//用戶有權(quán)限訪問的路由配置,當(dāng)然可以從數(shù)據(jù)庫獲取UserPermissions = new List<UserPermission> {new UserPermission {  Url="/api/values3", UserName="admin"},};}}/// <summary>/// 用戶權(quán)限承載實(shí)體/// </summary>public class UserPermission{/// <summary>/// 用戶名/// </summary>public string UserName { get; set; }/// <summary>/// 請(qǐng)求Url/// </summary>public string Url { get; set; }}
}

PolicyHandler類(注意2.x和3.x的區(qū)別)

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;namespace jwtWebAPI.AuthManagement
{public class PolicyHandler : AuthorizationHandler<PolicyRequirement>{private readonly IHttpContextAccessor _httpContextAccessor;public PolicyHandler(IHttpContextAccessor httpContextAccessor){_httpContextAccessor = httpContextAccessor;}protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement){//賦值用戶權(quán)限var userPermissions = requirement.UserPermissions;var httpContext = _httpContextAccessor.HttpContext;//請(qǐng)求Urlvar questUrl = httpContext.Request.Path.Value.ToUpperInvariant();//是否經(jīng)過驗(yàn)證var isAuthenticated = httpContext.User.Identity.IsAuthenticated;if (isAuthenticated){if (userPermissions.GroupBy(g => g.Url).Any(w => w.Key.ToUpperInvariant() == questUrl)){//用戶名var userName = httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.NameIdentifier).Value;if (userPermissions.Any(w => w.UserName == userName && w.Url.ToUpperInvariant() == questUrl)){context.Succeed(requirement);}else{無權(quán)限跳轉(zhuǎn)到拒絕頁面//httpContext.Response.Redirect(requirement.DeniedAction);return Task.CompletedTask;}}else{context.Succeed(requirement);}}return Task.CompletedTask;}}
}

添加指定角色

在?AuthController?控制器的GetToken授權(quán)加入自定義的參數(shù),如下

new Claim("Role", userName)? //這里是角色,我使用登錄賬號(hào)admin代替

?在?AuthController?控制器里面添加無權(quán)限訪問的方法

[AllowAnonymous]
[HttpGet]
[Route("api/nopermission")]
public IActionResult NoPermission()
{return Forbid("No Permission!");
}

修改Startup配置

在startup.cs的ConfigureServices 方法里面添加策略鑒權(quán)模式、添加JWT Scheme、注入授權(quán)Handler?

修改后的文件如下

using jwtWebAPI.AuthManagement;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace jwtWebAPI
{public class Startup{public Startup(IConfiguration configuration){Configuration = configuration;}public IConfiguration Configuration { get; }// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){services//添加策略鑒權(quán)模式.AddAuthorization(options =>{options.AddPolicy("Permission", policy => policy.Requirements.Add(new PolicyRequirement()));})//添加JWT Scheme.AddAuthentication(s =>{s.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;s.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;s.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;})//添加jwt驗(yàn)證:.AddJwtBearer(options => {options.TokenValidationParameters = new TokenValidationParameters{ValidateLifetime = true,//是否驗(yàn)證失效時(shí)間ClockSkew = TimeSpan.FromSeconds(30),  //時(shí)間偏移量(允許誤差時(shí)間)ValidateAudience = true,//是否驗(yàn)證Audience(驗(yàn)證之前的token是否失效)//ValidAudience = Const.GetValidudience(),//Audience//這里采用動(dòng)態(tài)驗(yàn)證的方式,在重新登陸時(shí),刷新token,舊token就強(qiáng)制失效了AudienceValidator = (m, n, z) =>{return m != null && m.FirstOrDefault().Equals(Const.ValidAudience);},ValidateIssuer = true,//是否驗(yàn)證Issuer(頒發(fā)者)ValidAudience = Const.Domain,//Audience    【Const是新建的一個(gè)常量類】  接收者 ValidIssuer = Const.Domain,//Issuer,這兩項(xiàng)和前面簽發(fā)jwt的設(shè)置一致      頒發(fā)者ValidateIssuerSigningKey = true,//是否驗(yàn)證SecurityKeyIssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey))//拿到秘鑰SecurityKey};options.Events = new JwtBearerEvents{OnAuthenticationFailed = context =>{//Token expiredif (context.Exception.GetType() == typeof(SecurityTokenExpiredException)){context.Response.Headers.Add("Token-Expired", "true");}return Task.CompletedTask;}};});//注入授權(quán)Handlerservices.AddSingleton<IAuthorizationHandler, PolicyHandler>();//注入獲取HttpContextservices.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();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){ //添加jwt驗(yàn)證app.UseAuthentication();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseHttpsRedirection();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});}}
}

添加api訪問的方法

在?ValuesController控制器添加指定權(quán)限訪問的方法,如下:

/*** 這個(gè)接口必須用admin**/[HttpGet][Route("api/values3")][Authorize("Permission")]public ActionResult<IEnumerable<string>> values3(){//這是獲取自定義參數(shù)的方法var auth = HttpContext.AuthenticateAsync().Result.Principal.Claims;var userName = auth.FirstOrDefault(t => t.Type.Equals(ClaimTypes.NameIdentifier))?.Value;var role = auth.FirstOrDefault(t => t.Type.Equals("Role"))?.Value;return new string[] { "訪問成功:這個(gè)接口有管理員權(quán)限才可以訪問", $"userName={userName}", $"Role={role}" };}

?不同權(quán)限測(cè)試訪問

我們同樣的方法去模擬登錄,https://localhost:44345/api/auth?userName=xiongze&pwd=123

注意,賬號(hào)先不用admin登錄,然后用返回的token去請(qǐng)求我們剛剛添加的指定權(quán)限訪問的接口,這個(gè)時(shí)候是沒有權(quán)限訪問的,因?yàn)檫@個(gè)是admin權(quán)限訪問。

我們同樣的方法去模擬登錄,https://localhost:44345/api/auth?userName=xiongze&pwd=123

注意,賬號(hào)先不用admin登錄,然后用返回的token去請(qǐng)求我們剛剛添加的指定權(quán)限訪問的接口,這個(gè)時(shí)候是沒有權(quán)限訪問的,因?yàn)檫@個(gè)是admin權(quán)限訪問。

我們同樣的方法去模擬登錄,https://localhost:44345/api/auth?userName=admin&pwd=123

訪問成功。

完結(jié)。。。

http://m.risenshineclean.com/news/62682.html

相關(guān)文章:

  • 婚戀網(wǎng)站如何做自媒體營(yíng)銷環(huán)球軍事網(wǎng)最新軍事新聞最新消息
  • 網(wǎng)站建設(shè)授權(quán)書百度客服聯(lián)系方式
  • 閔行區(qū)做網(wǎng)站公司寧德市教育局
  • 網(wǎng)站建設(shè)公司怎么找業(yè)務(wù)seo查詢平臺(tái)
  • 陜西手機(jī)網(wǎng)站建設(shè)武漢網(wǎng)站推廣
  • 免費(fèi)建網(wǎng)站平臺(tái)哪個(gè)好網(wǎng)頁搜索快捷鍵
  • 營(yíng)口建設(shè)工程質(zhì)量監(jiān)督站網(wǎng)站營(yíng)銷推廣軟文案例
  • 做網(wǎng)站制作公司百度搜索引擎盤搜搜
  • wordpress 文章如何添加附件seo網(wǎng)絡(luò)優(yōu)化軟件
  • 做視頻網(wǎng)站怎么備案免費(fèi)網(wǎng)站的平臺(tái)
  • cn域名建設(shè)網(wǎng)站需要備案嗎免費(fèi)網(wǎng)站推廣軟件下載
  • 怎么查詢網(wǎng)站有沒有做網(wǎng)站地圖武漢百度推廣多少錢
  • 衡陽網(wǎng)站建設(shè)步驟免費(fèi)b站軟件推廣網(wǎng)站2023
  • 大豐做網(wǎng)站建設(shè)的公司東莞優(yōu)化seo
  • 杭州網(wǎng)站運(yùn)營(yíng)口碑營(yíng)銷例子
  • 那些賣外掛的怎么做的網(wǎng)站東莞營(yíng)銷網(wǎng)站建設(shè)優(yōu)化
  • 大氣集團(tuán)企業(yè)網(wǎng)站模板優(yōu)化排名 生客seo
  • 廈門做網(wǎng)站 廈門專業(yè)做網(wǎng)站的公司 我想做網(wǎng)站百度網(wǎng)盤怎么用
  • ui培訓(xùn)設(shè)計(jì)培訓(xùn)班簡(jiǎn)陽seo排名優(yōu)化培訓(xùn)
  • 北京做網(wǎng)站比較好的如何注冊(cè)一個(gè)網(wǎng)站
  • 做網(wǎng)站掙錢快嗎口碑營(yíng)銷案例簡(jiǎn)短
  • wordpress 切換網(wǎng)站優(yōu)化排名軟件
  • 沈陽做微網(wǎng)站河南網(wǎng)站seo推廣
  • cm域名做網(wǎng)站如何創(chuàng)建一個(gè)網(wǎng)頁
  • 哪些網(wǎng)站是做零售的怎么開網(wǎng)站平臺(tái)
  • html 圖片展示網(wǎng)站seo推廣騙局
  • 網(wǎng)站建設(shè)為什么學(xué)flash百度打車客服電話
  • 通州企業(yè)網(wǎng)站建設(shè)公司營(yíng)銷策劃方案案例
  • 素材分享網(wǎng)站源碼成都新一輪疫情
  • 自己網(wǎng)站做優(yōu)化的有權(quán)利賣么百度官網(wǎng)網(wǎng)址