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

开发公司与子公司合作协议seo站外优化平台

开发公司与子公司合作协议,seo站外优化平台,景观设计公司利润,网络营销软件站目录 1、加密解密原理2、项目示例2.1、项目结构2.2、常规业务代码2.3、加密的实现 2.4、接口测试2.5、总结 1、加密解密原理 客户端和服务端都可以加密和解密,使用base64进行网络传输 加密方 字符串 -> AES加密 -> base64解密方 base64 -> AES解密 -&g…

目录

    • 1、加密解密原理
    • 2、项目示例
      • 2.1、项目结构
      • 2.2、常规业务代码
      • 2.3、加密的实现
    • 2.4、接口测试
    • 2.5、总结

1、加密解密原理

客户端和服务端都可以加密和解密,使用base64进行网络传输

加密方

字符串 -> AES加密 -> base64

解密方

base64 -> AES解密 -> 字符串

2、项目示例

2.1、项目结构

$ tree -I target -I test
.
├── pom.xml
└── src└── main├── java│   └── com│       └── example│           └── demo│               ├── Application.java│               ├── annotation│               │   └── SecretData.java│               ├── config│               │   ├── CrossConfig.java│               │   ├── DecryptRequestBodyAdvice.java│               │   ├── EncryptResponseBodyAdvice.java│               │   ├── SecretConfig.java│               │   └── WebMvcConfig.java│               ├── controller│               │   ├── IndexController.java│               │   └── UserController.java│               ├── request│               │   └── JsonRequest.java│               ├── response│               │   ├── JsonResult.java│               │   └── JsonResultVO.java│               ├── service│               │   ├── SecretDataService.java│               │   └── impl│               │       └── SecretDataServiceImpl.java│               └── utils│                   └── CipherUtil.java└── resources├── application.yml├── static│   ├── axios.min.js│   └── crypto-js.min.js└── templates└── index.html

2.2、常规业务代码

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.7</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><mybatis-plus.version>3.5.2</mybatis-plus.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.15</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><!-- test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

Application.java

package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

WebMvcConfig.java

package com.example.demo.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {// 设置静态资源映射@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");}
}

CrossConfig.java

package com.example.demo.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;/*** 处理跨域问题*/
@Configuration
public class CrossConfig {@Beanpublic CorsFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();config.addAllowedOrigin("*");config.setAllowCredentials(false);config.addAllowedMethod("*");config.addAllowedHeader("*");config.setMaxAge(3600L);UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();configSource.registerCorsConfiguration("/**", config);return new CorsFilter(configSource);}
}

JsonRequest.java

package com.example.demo.request;import lombok.Data;/*** 统一的请求体数据*/
@Data
public class JsonRequest {/*** 未加密数据*/private Object data;/*** 加密数据*/private String encryptData;
}

JsonResult.java

package com.example.demo.response;import lombok.Data;/*** 统一的返回体数据 不加密*/
@Data
public class JsonResult<T> {private String message;private T data;private Integer code;public static <T> JsonResult success(T data){JsonResult<T> jsonResult = new JsonResult<>();jsonResult.setCode(0);jsonResult.setData(data);jsonResult.setMessage("success");return jsonResult;}
}

JsonResultVO.java

package com.example.demo.response;import lombok.Data;/*** 统一的返回体数据 加密*/
@Data
public class JsonResultVO {private String message;private String encryptData;private Integer code;
}

IndexController.java

package com.example.demo.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;@Controller
public class IndexController {@GetMapping("/")public String index(){return "index";}
}

UserController.java

package com.example.demo.controller;import com.example.demo.annotation.SecretData;
import com.example.demo.response.JsonResult;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.Map;/*** 对该Controller中的所有方法进行加解密处理*/
@RestController
@SecretData
public class UserController {@GetMapping("/user/getUser")public JsonResult getUser() {Map<String, String> user = new HashMap<>();user.put("name", "Tom");user.put("age", "18");return JsonResult.success(user);}@PostMapping("/user/addUser")public Object addUser(@RequestBody Map<String, String> data) {System.out.println(data);return data;}
}

2.3、加密的实现

application.yml

secret:key: 1234567890123456 # 密钥位数为16位enabled: true # 开启加解密功能

SecretData.java

package com.example.demo.annotation;import org.springframework.web.bind.annotation.Mapping;import java.lang.annotation.*;/*** 只有添加有该注解的Controller类或是具体接口方法才进行数据的加密解密*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Mapping
@Documented
public @interface SecretData {}

DecryptRequestBodyAdvice.java

package com.example.demo.config;import com.example.demo.annotation.SecretData;
import com.example.demo.request.JsonRequest;
import com.example.demo.service.SecretDataService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;/*** 对请求内容进行解密* 只有开启了加解密功能才会生效* 仅对使用了@RqestBody注解的生效* https://blog.csdn.net/xingbaozhen1210/article/details/98189562*/
@ControllerAdvice
// @ConditionalOnProperty(name = "secret.enabled", havingValue = "true")
public class DecryptRequestBodyAdvice extends RequestBodyAdviceAdapter {@Resourceprivate SecretDataService secretDataService;@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) {return methodParameter.getMethod().isAnnotationPresent(SecretData.class)|| methodParameter.getMethod().getDeclaringClass().isAnnotationPresent(SecretData.class);}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) throws IOException {System.out.println("beforeBodyRead");String body = inToString(inputMessage.getBody());System.out.println(body);ObjectMapper objectMapper = new ObjectMapper();JsonRequest jsonRequest = objectMapper.readValue(body, JsonRequest.class);// 默认取data数据,如果提交加密数据则解密String decryptData = null;if (jsonRequest.getEncryptData() != null) {decryptData = secretDataService.decrypt(jsonRequest.getEncryptData());} else{decryptData = objectMapper.writeValueAsString(jsonRequest.getData());}String data = decryptData;// 解密后的数据System.out.println(data);return new HttpInputMessage() {@Overridepublic HttpHeaders getHeaders() {return inputMessage.getHeaders();}@Overridepublic InputStream getBody() throws IOException {return new ByteArrayInputStream(data.getBytes());}};}/*** 读取输入流为字符串** @param is* @return*/private String inToString(InputStream is) {byte[] buf = new byte[10 * 1024];int length = -1;StringBuilder sb = new StringBuilder();try {while ((length = is.read(buf)) != -1) {sb.append(new String(buf, 0, length));}return sb.toString();} catch (IOException e) {throw new RuntimeException(e);}}}

EncryptResponseBodyAdvice.java

package com.example.demo.config;import com.example.demo.annotation.SecretData;
import com.example.demo.response.JsonResult;
import com.example.demo.response.JsonResultVO;
import com.example.demo.service.SecretDataService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import javax.annotation.Resource;/*** 对响应内容加密*/
@ControllerAdvice
@ConditionalOnProperty(name = "secret.enabled", havingValue = "true")
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {@Resourceprivate SecretDataService secretDataService;@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return returnType.getMethod().isAnnotationPresent(SecretData.class)|| returnType.getMethod().getDeclaringClass().isAnnotationPresent(SecretData.class);}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {System.out.println("beforeBodyWrite");// 仅对JsonResult对象数据加密if (body instanceof JsonResult) {JsonResult jsonResult = (JsonResult) body;JsonResultVO jsonResultVO = new JsonResultVO();BeanUtils.copyProperties(jsonResult, jsonResultVO);String jsonStr = new ObjectMapper().writeValueAsString(jsonResult.getData());jsonResultVO.setEncryptData(secretDataService.encrypt(jsonStr));return jsonResultVO;} else {return body;}}
}

SecretConfig.java

package com.example.demo.config;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;@Configuration
@ConfigurationProperties(prefix = "secret")
public class SecretConfig {private Boolean enabled;private String key;public Boolean getEnabled() {return enabled;}public void setEnabled(Boolean enabled) {this.enabled = enabled;}public String getKey() {return key;}public void setKey(String key) {this.key = key;}}

SecretDataService.java

package com.example.demo.service;/*** 加密解密的接口*/
public interface SecretDataService {/*** 数据加密** @param data 待加密数据* @return String 加密结果*/String encrypt(String data);/*** 数据解密** @param data 待解密数据* @return String 解密后的数据*/String decrypt(String data);
}

SecretDataServiceImpl.java

package com.example.demo.service.impl;import com.example.demo.config.SecretConfig;
import com.example.demo.service.SecretDataService;
import com.example.demo.utils.CipherUtil;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** 具体的加解密实现*/
@Service
public class SecretDataServiceImpl implements SecretDataService {@Resourceprivate SecretConfig secretConfig;@Overridepublic String decrypt(String data) {return CipherUtil.decrypt(secretConfig.getKey(), data);}@Overridepublic String encrypt(String data) {return CipherUtil.encrypt(secretConfig.getKey(), data);}
}

CipherUtil.java

package com.example.demo.utils;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.Base64;/*** 数据加密解密工具类* 加密后返回base64*/
public class CipherUtil {/*** 定义加密算法*/private static final String ALGORITHM = "AES/ECB/PKCS5Padding";/*** 解密** @param secretKey* @param cipherText base64* @return*/public static String decrypt(String secretKey, String cipherText) {// 将Base64编码的密文解码byte[] encrypted = Base64.getDecoder().decode(cipherText);try {Cipher cipher = Cipher.getInstance(ALGORITHM);SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), "AES");cipher.init(Cipher.DECRYPT_MODE, key);return new String(cipher.doFinal(encrypted));} catch (Exception e) {throw new RuntimeException(e);}}/*** 加密** @param secretKey* @param plainText base64* @return*/public static String encrypt(String secretKey, String plainText) {try {Cipher cipher = Cipher.getInstance(ALGORITHM);SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), "AES");cipher.init(Cipher.ENCRYPT_MODE, key);return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes(Charset.forName("UTF-8"))));} catch (Exception e) {throw new RuntimeException(e);}}
}

浏览器中实现加密解密

templates/index.html

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8" /><title>接口数据加密解密</title></head><body><!-- 引入依赖 --><!-- <script src="https://cdn.bootcdn.net/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script> --><script src="/static/crypto-js.min.js"></script><!-- <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.min.js"></script> --><script src="/static/axios.min.js"></script><h1>查看控制台</h1><!-- 加密解密模块 --><script type="text/javascript">const SECRET_KEY = "1234567890123456";/*** 加密方法* @param data 待加密数据* @returns {string|*}*/function encrypt(data) {let key = CryptoJS.enc.Utf8.parse(SECRET_KEY);if (typeof data === "object") {data = JSON.stringify(data);}let plainText = CryptoJS.enc.Utf8.parse(data);let secretText = CryptoJS.AES.encrypt(plainText, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7}).toString();return secretText;}/*** 解密数据* @param data 待解密数据*/function decrypt(data) {let key = CryptoJS.enc.Utf8.parse(SECRET_KEY);let result = CryptoJS.AES.decrypt(data, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7}).toString(CryptoJS.enc.Utf8);return JSON.parse(result);}</script><!-- http请求模块 --><script type="text/javascript">// 获取加密数据并解密axios.get("http://127.0.0.1:8080/user/getUser").then((res) => {console.log("接收到api返回加密数据:");console.log(decrypt(res.data.encryptData));});// 提交加密参数const data = {name: "Tom",age: "18",};axios.post("http://127.0.0.1:8080/user/addUser", {encryptData: encrypt(data),}).then((res) => {console.log("接收到api返回未加密数据:");console.log(res.data);});</script></body>
</html>

2.4、接口测试

  • 开发环境不加密更易于开发调试
  • 生产环境需要数据加密

前后端都可以通过参数 encryptData 判断对方提交/返回的数据是否为加密数据,如果是加密数据则进行解密操作

接口返回加密数据

GET http://127.0.0.1:8080/user/getUser

未加密的数据

{"message": "success","data": {"name": "Tom","age": "18"},"code": 0
}

返回数据

{"message": "success","encryptData": "kun2Wvk2LNKICaXIeIExA7jKRyqOV0qCv5KQXFOzfpQ=","code": 0
}

客户端提交参数

POST http://127.0.0.1:8080/user/addUser

提交数据

{"data": {"name": "Tom","age": "18"}
}

提交加密数据

{"encryptData": "kun2Wvk2LNKICaXIeIExA7jKRyqOV0qCv5KQXFOzfpQ="
}

2.5、总结

服务端

  • 全局开关:通过控制secret.enabled=true全局开启返回数据加密
  • 全局局部:可以通过SecretData或者自定义PassSecretData来控制单个控制器或者单个接口的需要或不需要加密

客户端

  • 可以根据开发环境、测试环境、生产环境来控制是否开启加密
  • 需要注意,FormData传输文件的数据格式可以考虑不加密

相同点

  • 服务端和客户端都通过对方传输的encryptData来判断是否为加密数据
  • 服务端和客户端都可以根据自己的环境来决定是否开启数据加密

完整代码:https://github.com/mouday/spring-boot-demo/tree/master/SpringBoot-Secret

参考文章

  • 详解API接口如何安全的传输数据

文章转载自:
http://dinncocontrolled.wbqt.cn
http://dinncocoalite.wbqt.cn
http://dinncocalligraphy.wbqt.cn
http://dinncogerrymander.wbqt.cn
http://dinncolevis.wbqt.cn
http://dinncohistadrut.wbqt.cn
http://dinncoindecently.wbqt.cn
http://dinncodeianira.wbqt.cn
http://dinncobrolly.wbqt.cn
http://dinncoicaria.wbqt.cn
http://dinncomarlpit.wbqt.cn
http://dinncoamidah.wbqt.cn
http://dinncoclement.wbqt.cn
http://dinncolunes.wbqt.cn
http://dinncohateworthy.wbqt.cn
http://dinncopreglacial.wbqt.cn
http://dinncosupervision.wbqt.cn
http://dinncolizzie.wbqt.cn
http://dinncochloroacetophenone.wbqt.cn
http://dinncophyllostome.wbqt.cn
http://dinncowindow.wbqt.cn
http://dinncoinoxidize.wbqt.cn
http://dinncorarer.wbqt.cn
http://dinncopronghorn.wbqt.cn
http://dinnconasdaq.wbqt.cn
http://dinncodolman.wbqt.cn
http://dinncohoik.wbqt.cn
http://dinncotendencious.wbqt.cn
http://dinncoasti.wbqt.cn
http://dinncoinconsiderately.wbqt.cn
http://dinncobenfactress.wbqt.cn
http://dinncopdf.wbqt.cn
http://dinncofelix.wbqt.cn
http://dinncopolydrug.wbqt.cn
http://dinncowhittret.wbqt.cn
http://dinncosigil.wbqt.cn
http://dinncohybridize.wbqt.cn
http://dinncophlebotome.wbqt.cn
http://dinnconeuroma.wbqt.cn
http://dinncositten.wbqt.cn
http://dinncolufthansa.wbqt.cn
http://dinnconeighborhood.wbqt.cn
http://dinncolettrism.wbqt.cn
http://dinncoidiosyncracy.wbqt.cn
http://dinncocambogia.wbqt.cn
http://dinncopertly.wbqt.cn
http://dinncogill.wbqt.cn
http://dinncononcontentious.wbqt.cn
http://dinncodeception.wbqt.cn
http://dinncoafrormosia.wbqt.cn
http://dinncoprobate.wbqt.cn
http://dinncogruffly.wbqt.cn
http://dinnconeoterist.wbqt.cn
http://dinncoupwelling.wbqt.cn
http://dinncoviticulturist.wbqt.cn
http://dinncomesaxon.wbqt.cn
http://dinncosneeze.wbqt.cn
http://dinncograecism.wbqt.cn
http://dinnconitrification.wbqt.cn
http://dinncoderadicalize.wbqt.cn
http://dinncocarpus.wbqt.cn
http://dinncoexerciser.wbqt.cn
http://dinncosubinfeudation.wbqt.cn
http://dinncounleavened.wbqt.cn
http://dinncocatenaccio.wbqt.cn
http://dinncoindistinguishable.wbqt.cn
http://dinncoplastering.wbqt.cn
http://dinncobelgrade.wbqt.cn
http://dinncoscolion.wbqt.cn
http://dinncoherniotomy.wbqt.cn
http://dinncogoiterogenic.wbqt.cn
http://dinncodurn.wbqt.cn
http://dinncosomatic.wbqt.cn
http://dinncoprotea.wbqt.cn
http://dinncohammock.wbqt.cn
http://dinncocostumier.wbqt.cn
http://dinncojudgeship.wbqt.cn
http://dinncoalcaic.wbqt.cn
http://dinncoversification.wbqt.cn
http://dinncodefectiveness.wbqt.cn
http://dinncoadvertizing.wbqt.cn
http://dinncomajesty.wbqt.cn
http://dinncosaddlebill.wbqt.cn
http://dinncopressbutton.wbqt.cn
http://dinncomonolatry.wbqt.cn
http://dinncocede.wbqt.cn
http://dinncodiscount.wbqt.cn
http://dinncooutroot.wbqt.cn
http://dinncocomplacence.wbqt.cn
http://dinncoschoolcraft.wbqt.cn
http://dinncomushily.wbqt.cn
http://dinncodruid.wbqt.cn
http://dinncopiolet.wbqt.cn
http://dinncohic.wbqt.cn
http://dinncooverelaborate.wbqt.cn
http://dinncoerythritol.wbqt.cn
http://dinncoenforceable.wbqt.cn
http://dinncomatchsafe.wbqt.cn
http://dinncodelphine.wbqt.cn
http://dinncocep.wbqt.cn
http://www.dinnco.com/news/7431.html

相关文章:

  • 做动态网站有什么较好的主题网上接单平台
  • dw如何在网站做弹窗seo培训机构
  • 罗湖疫情最新情况发布seo关键字优化
  • 快速制作简单的网站上海网络推广团队
  • 做网站最小的字体是多少像素建站系统哪个比较好
  • 做视频网站需要哪些手续seo优化与品牌官网定制
  • 陕西咸阳做网站的公司谷歌海外推广
  • 圆通我做网站拉临沂森佳木业有限公司
  • 一个人做公司管理网站腾讯企业邮箱登录入口
  • 网站中常用的功能模块推广之家官网
  • 北京装修公司加盟爱站网seo
  • 芜湖网站seo排名软件怎么做
  • 佛山做网站有哪几家可以免费发广告的网站有哪些
  • 深圳创业贷款条件申请及流程seo推广培训资料
  • 佛山做网站建设买外链
  • 如何做资源论坛网站进入百度app查看
  • 门户网站开发解决方案网络营销推广经验总结
  • 国内免费建站网站网络推广外包注意哪些
  • 网页和网站的关系河北百度竞价优化
  • 济南建设招标网网站优化检测工具
  • 图书信息管理系统代码网站建设站长工具 seo查询
  • 58网站建设 网站制作长沙网站seo排名
  • wordpress 评论时间淘宝seo优化是什么
  • wordpress is电影主题保定关键词优化软件
  • 深圳做网站建设的哪家效果好又便宜广东做seo的公司
  • 申请好域名后 怎么做网站国际最新新闻热点事件
  • 手机网站底部悬浮菜单广告制作公司
  • 国内最好的网站建设公司站长工具seo综合查询烟雨楼
  • 做网站是不是要模板网站友情链接连接
  • 网址导航怎么彻底删除百度seo排名优化教程