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

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

關(guān)于網(wǎng)站開發(fā)的文獻(xiàn)縱橫seo

關(guān)于網(wǎng)站開發(fā)的文獻(xiàn),縱橫seo,醫(yī)學(xué)教育網(wǎng)站建設(shè)方案,網(wǎng)站系統(tǒng)管理功能本人寫博客,向來主張:代碼要完整,代碼可運(yùn)行,文中不留下任何疑惑。 最討厭寫博客,代碼只留下片段,文中關(guān)鍵的東西沒寫清楚。之前看了那么多文章,就是不告訴我clientId從哪來的。 官方資料地址&…

本人寫博客,向來主張:代碼要完整,代碼可運(yùn)行,文中不留下任何疑惑。

最討厭寫博客,代碼只留下片段,文中關(guān)鍵的東西沒寫清楚。之前看了那么多文章,就是不告訴我clientId從哪來的。

官方資料地址:

Sign in with Apple JS | Apple Developer Documentation

一、網(wǎng)頁客戶端代碼

clientId:這個(gè)會(huì)在下文中告訴你怎么來的

usePopup:如果設(shè)置為true,就會(huì)以彈框的方式打開蘋果登錄窗口。設(shè)置 為false,你自己試試吧

redirectURI:這在usePopup=true時(shí),沒啥用

state:在各種頁面跳轉(zhuǎn)時(shí)會(huì)原樣傳遞,你自己看著辦

nonce:一個(gè)隨機(jī)數(shù),至于作用么,你自己猜,照著做就好

<!DOCTYPE html>
<html><head><meta charset="utf-8"><meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" /><title></title><script type="text/javascript">var hostBase = "https://myserver.cn";function getQuery(i) { var j = location.search.match(new RegExp("[?&]" + i + "=([^&]*)(&?)", "i")); return j ? j[1] : j; }function getQueryIn(i, params) { var j = ("?&" + params).match(new RegExp("[?&]" + i + "=([^&]*)(&?)", "i")); return j ? j[1] : j; }</script>
</head><body><div id="app"><div id="appleid-signin" data-color="black" data-border="true" data-type="sign in"></div></div>
</body><style></style><script src="https://unpkg.com/vue/dist/vue.js"></script>
<script type="text/javascript"src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script><script>window.app = new Vue({el: '#app',data: function () {return {}},watch: {},created: function () {setTimeout(function () {AppleID.auth.init({clientId: '填上你的clientId',scope: 'name email',redirectURI: 'https://myclient.cn/login_apple_redirect.html',state: "這個(gè)參數(shù)在各種跳轉(zhuǎn)中會(huì)一直帶上,你可以用來標(biāo)記這次登錄過程",nonce: '' + new Date().getTime(),usePopup: true});}, 10);},methods: {OnAppleSignIn: function (authorization) {var that = this;var req = {state: authorization.state,code: authorization.code,idToken: authorization.id_token};$.ajax({url: hostBase + "/fawork/AppleLoginGetResult", //請求的url地址dataType: "json", //返回格式為jsoncontentType: "application/json; charset=utf-8",async: true, //請求是否異步,默認(rèn)為異步,這也是ajax重要特性data: JSON.stringify(req),async: true, //請求是否異步,默認(rèn)為異步,這也是ajax重要特性type: "POST", //請求方式beforeSend: function () {//請求前的處理},success: function (rsp) {console.log(rsp);// 返回 uid和token},complete: function () {//請求完成的處理},error: function () {//請求出錯(cuò)處理}});}}});// Listen for authorization success.document.addEventListener('AppleIDSignInOnSuccess', function (event) {// Handle successful response.console.log(event);window.app.OnAppleSignIn(event.detail.authorization);});// Listen for authorization failures.document.addEventListener('AppleIDSignInOnFailure', function (event) {// Handle error.console.log(event);});
</script></html>

登錄成功時(shí),前端 AppleIDSignInOnSuccess事件中,打印的event參數(shù)值

二、服務(wù)器端代碼

前端傳遞過來code和id_token,這是兩個(gè)不同的校驗(yàn)方法

code是一種,不過在蘋果登錄中我不知道怎么用,我用的是id_token校驗(yàn)。

對于id_token,這就是JWT校驗(yàn)技術(shù)。

JWT的介紹,請看我用ChatGpt問的結(jié)果。

在JWT(JSON Web Token)中,id token是一種用于身份驗(yàn)證和認(rèn)證的令牌。要驗(yàn)證id token的有效性,您可以遵循以下步驟:

  1. 解碼id token:JWT由三部分組成,即頭部、載荷和簽名。使用Base64解碼id token,您可以獲取其中的頭部和載荷信息。

  2. 驗(yàn)證簽名:使用頭部中提供的算法(通常是HMAC、RSA或ECDSA)和密鑰,驗(yàn)證簽名的正確性。您需要獲取與簽名算法相對應(yīng)的密鑰,并將其與頭部和載荷一起使用相同的算法進(jìn)行簽名驗(yàn)證。如果簽名驗(yàn)證失敗,則表示id token被篡改過或者是偽造的。

  3. 驗(yàn)證令牌的有效期:在載荷中,id token包含了發(fā)行時(shí)間(issued time)和過期時(shí)間(expiration time)。您需要檢查當(dāng)前時(shí)間是否在有效期范圍內(nèi)。如果當(dāng)前時(shí)間在過期時(shí)間之后,說明id token已過期,不能再繼續(xù)使用。

  4. 校驗(yàn)接收者:在載荷中,id token還可以包含一個(gè)接收者(audience)字段,用于指定該令牌的預(yù)期接收者。您可以檢查接收者字段是否與您的應(yīng)用程序的標(biāo)識符匹配,以確保id token只能被合法的接收者使用。

  5. 可選的附加校驗(yàn):根據(jù)您的需求,您還可以進(jìn)行其他的校驗(yàn),例如驗(yàn)證簽發(fā)者(issuer)字段、檢查令牌是否被撤銷等。

需要注意的是,為了保證安全性,您應(yīng)該將密鑰存儲在安全的位置,并定期更換密鑰以防止泄露和濫用。此外,使用受信任的JWT庫來處理JWT的解碼和驗(yàn)證操作,而不是自行編寫代碼,以確保正確性和安全性。

        <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred --><version>0.11.5</version><scope>runtime</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.22</version></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.15</version>
</dependency>
package cn.huali.fawork.constant;import cn.huali.fawork.exception.SelfException;
import cn.huali.fawork.utils.Base64Util;
import cn.huali.fawork.utils.HttpsUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import org.apache.commons.codec.binary.Base64;import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;
import java.util.Map;public class AppleAuthorizationConfig {private static final String APPLE_HOST_URL = "https://appleid.apple.com";private static final String APPLE_PUB_KEY_ENDPOINT = "https://appleid.apple.com/auth/keys";private static final String APPLE_AUTH_TOKEN_ENDPOINT = "https://appleid.apple.com/auth/token";private static final String  Apple_Client_Id = "填上你的clientId";public static AppleAuthCheckResult checkAuth(String idToken, long unixtimeAt) throws UnsupportedEncodingException {AppleAuthCheckResult result = new AppleAuthCheckResult();result.isOK = false;try {String[] identityTokens = idToken.split("\\.");String headerStr = new String(Base64Util.decodeWithUTF8(identityTokens[0]));JSONObject jsonObjectHeader = JSON.parseObject(headerStr);String contentStr = new String(Base64Util.decodeWithUTF8(identityTokens[1]));JSONObject jsonObjectContent = JSON.parseObject(contentStr);System.out.println(headerStr);System.out.println(contentStr);String kid = jsonObjectHeader.getString("kid");String alg = jsonObjectHeader.getString("alg");String iss = jsonObjectContent.getString("iss");String aud = jsonObjectContent.getString("aud");String exp = jsonObjectContent.getString("exp");String iat = jsonObjectContent.getString("iat");String sub = jsonObjectContent.getString("sub");String nonce = jsonObjectContent.getString("nonce");String c_hash = jsonObjectContent.getString("c_hash");String email = jsonObjectContent.getString("email");String email_verified = jsonObjectContent.getString("email_verified");boolean is_private_email = jsonObjectContent.getBooleanValue("is_private_email");String auth_time = jsonObjectContent.getString("auth_time");String nonce_supported = jsonObjectContent.getString("nonce_supported");result.email = email;result.sub = sub;result.is_private_email = is_private_email;result.tokenPayload = contentStr;JSONObject publicKey = getPublicKey(APPLE_PUB_KEY_ENDPOINT, kid);PublicKey rsaPublicKey = getRSAPublicKey(publicKey.getString("n"), publicKey.getString("e"));// require部分,是jwt自動(dòng)幫你校驗(yàn),如果校驗(yàn)不通過,會(huì)報(bào)異常
// 切記不要在require部分校驗(yàn)auth_time和iat兩部分,Jwt有bug,會(huì)報(bào)異常的,所以還是手動(dòng)校驗(yàn)比較好JwtParser jwtParser = Jwts.parserBuilder().setSigningKey(rsaPublicKey).requireAudience(Apple_Client_Id) //一般是項(xiàng)目包名稱.requireIssuer(APPLE_HOST_URL) //固定值//.require("auth_time", auth_time) //這里做了個(gè)簡單的驗(yàn)證,如果auth_time == iat則是有效的。.require("email", email).require("sub", sub).build();Jws<Claims> claimsJws = jwtParser.parseClaimsJws(idToken);Claims claims = claimsJws.getBody();if (!claims.get("auth_time").toString().equalsIgnoreCase(auth_time)) {result.isOK = false;result.msg = "auth_time不一致";} else if (!claims.get("iat").toString().equalsIgnoreCase(iat)) {result.isOK = false;result.msg = "iat不一致";} else if (!auth_time.equalsIgnoreCase(iat)) {result.isOK = false;result.msg = "iat和auth_time不一致";} else if (!claims.get("exp").toString().equalsIgnoreCase(exp)) {result.isOK = false;result.msg = "exp不一致";} else if (Long.parseLong(exp) < unixtimeAt) {result.isOK = false;result.msg = "exp已過期";}result.isOK = true;result.msg = "";}catch (SelfException e) {result.isOK = false;result.msg = e.msg;}catch (Exception e) {result.isOK = false;result.msg = e.getMessage();}return result;}public static class AppleAuthCheckResult {public boolean isOK;public String msg;/*** 用戶唯一賬號*/public String sub;public String email;public boolean is_private_email;public String tokenPayload;}private static volatile Map<String, JSONObject> pubKeyMap = new HashMap<>();private static PublicKey getRSAPublicKey(String modulus, String exponent) {try {BigInteger bigModule = new BigInteger(1, Base64.decodeBase64(modulus));BigInteger bigExponent = new BigInteger(1, Base64.decodeBase64(exponent));RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigModule, bigExponent);KeyFactory keyFactory = KeyFactory.getInstance("RSA");PublicKey publicKey = keyFactory.generatePublic(keySpec);return publicKey;} catch (Exception e) {return null;}}/*** {* "keys": [* {* "kty": "RSA",* "kid": "W6WcOKB",* "use": "sig",* "alg": "RS256",* "n": "2Zc5d0-zkZ5AKmtYTvxHc3vRc41YfbklflxG9SWsg5qXUxvfgpktGAcxXLFAd9Uglzow9ezvmTGce5d3DhAYKwHAEPT9hbaMDj7DfmEwuNO8UahfnBkBXsCoUaL3QITF5_DAPsZroTqs7tkQQZ7qPkQXCSu2aosgOJmaoKQgwcOdjD0D49ne2B_dkxBcNCcJT9pTSWJ8NfGycjWAQsvC8CGstH8oKwhC5raDcc2IGXMOQC7Qr75d6J5Q24CePHj_JD7zjbwYy9KNH8wyr829eO_G4OEUW50FAN6HKtvjhJIguMl_1BLZ93z2KJyxExiNTZBUBQbbgCNBfzTv7JrxMw",* "e": "AQAB"* },* {* "kty": "RSA",* "kid": "fh6Bs8C",* "use": "sig",* "alg": "RS256",* "n": "u704gotMSZc6CSSVNCZ1d0S9dZKwO2BVzfdTKYz8wSNm7R_KIufOQf3ru7Pph1FjW6gQ8zgvhnv4IebkGWsZJlodduTC7c0sRb5PZpEyM6PtO8FPHowaracJJsK1f6_rSLstLdWbSDXeSq7vBvDu3Q31RaoV_0YlEzQwPsbCvD45oVy5Vo5oBePUm4cqi6T3cZ-10gr9QJCVwvx7KiQsttp0kUkHM94PlxbG_HAWlEZjvAlxfEDc-_xZQwC6fVjfazs3j1b2DZWsGmBRdx1snO75nM7hpyRRQB4jVejW9TuZDtPtsNadXTr9I5NjxPdIYMORj9XKEh44Z73yfv0gtw",* "e": "AQAB"* },* {* "kty": "RSA",* "kid": "lVHdOx8ltR",* "use": "sig",* "alg": "RS256",* "n": "nXDu9MPf6dmVtFbDdAaal_0cO9ur2tqrrmCZaAe8TUWHU8AprhJG4DaQoCIa4UsOSCbCYOjPpPGGdE_p0XeP1ew55pBIquNhNtNNEMX0jNYAKcA9WAP1zGSkvH5m39GMFc4SsGiQ_8Szht9cayJX1SJALEgSyDOFLs-ekHnexqsr-KPtlYciwer5jaNcW3B7f9VNp1XCypQloQwSGVismPHwDJowPQ1xOWmhBLCK50NV38ZjobUDSBbCeLYecMtsdL5ZGv-iufddBh3RHszQiD2G-VXoGOs1yE33K4uAto2F2bHVcKOUy0__9qEsXZGf-B5ZOFucUkoN7T2iqu2E2Q",* "e": "AQAB"* }* ]* }** @param url* @param kid* @return*/private static JSONObject getPublicKey(String url, String kid) {if (!pubKeyMap.containsKey(kid)) {String allPubKeyJsonStr = getPublicKeyFromServer(url);JSONObject jsonObjectAllPubKey = JSON.parseObject(allPubKeyJsonStr);JSONArray keysArray = jsonObjectAllPubKey.getJSONArray("keys");if (keysArray.size() > 0) {pubKeyMap.clear();for (int i = 0; i < keysArray.size(); i++) {JSONObject key = keysArray.getJSONObject(i);String tmpKid = key.getString("kid");pubKeyMap.put(tmpKid, key);}}}JSONObject keyJsonObject = pubKeyMap.getOrDefault(kid, null);if (keyJsonObject == null) {throw new SelfException("沒有找到PublicKey:kid=" + kid);}return keyJsonObject;}private static String getPublicKeyFromServer(String url) {HttpsUtils.HttpRsp httpRsp = HttpsUtils.get(url);if (httpRsp.statusCode != 200) {throw new SelfException("獲取PublicKey出錯(cuò):" + httpRsp.statusCode + "," + httpRsp.statusDesc);}return httpRsp.content;}
}
package cn.huali.fawork.utils;import java.io.UnsupportedEncodingException;
import java.util.Base64;public class Base64Util {public static String encode(byte[] bytes) {byte[] newBytes = Base64.getEncoder().encode(bytes);String content = new String(newBytes);return content;}public static byte[] decode(String content) {byte[] newBytes = Base64.getDecoder().decode(content.getBytes());return newBytes;}public static String encodeWithUTF8(byte[] bytes) throws UnsupportedEncodingException {byte[] newBytes = Base64.getEncoder().encode(bytes);String content = new String(newBytes, "UTF-8");return content;}public static byte[] decodeWithUTF8(String content) throws UnsupportedEncodingException {byte[] newBytes = Base64.getDecoder().decode(content.getBytes("UTF-8"));return newBytes;}
}
/****/
package cn.huali.fawork.utils;import javax.net.ssl.HttpsURLConnection;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;/*** @author Administrator**/
public class HttpsUtils {public static HttpRsp get(String url) {return doGet(url, null, null);}public static HttpRsp get(String url, String param) {return doGet(url, param, null);}public static HttpRsp get(String url, Map<String, String> param) {return doGet(url, makeParam(param), null);}public static HttpRsp get(String url, Map<String, String> param, Map<String, String> headMap) {return doGet(url, makeParam(param), headMap);}public static HttpRsp get(String url, String param, Map<String, String> headMap) {return doGet(url, param, headMap);}private static HttpRsp doGet(String url, String param, Map<String, String> headMap) {HttpRsp rsp = new HttpRsp();BufferedReader in = null;try {if (param != null && param.isEmpty() == false) {if (url.endsWith("&") || url.endsWith("?")) {url += param;} else if (url.contains("?")) {url += "&" + param;} else {url += "?" + param;}}HttpURLConnection connection = (HttpURLConnection) (new URL(url)).openConnection();connection.setRequestMethod("GET");connection.setDoInput(true);if (headMap != null && headMap.isEmpty() == false) {// 設(shè)置包頭Iterator<Entry<String, String>> it = headMap.entrySet().iterator();Entry<String, String> entry = null;while (it.hasNext()) {entry = it.next();System.out.println(entry.getKey() + ":" + entry.getValue());connection.setRequestProperty(entry.getKey(), entry.getValue());}}in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));StringBuffer sb = new StringBuffer();String line;while ((line = in.readLine()) != null) {sb.append(line + System.lineSeparator());}rsp.content = sb.toString();rsp.headerFieldsMap = connection.getHeaderFields();rsp.statusCode = connection.getResponseCode();rsp.statusDesc = connection.getResponseMessage();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {try {if (in != null) {in.close();}} catch (Exception e) {e.printStackTrace();}}return rsp;}public static HttpRsp post(String url) {return doPost(url, null, null);}public static HttpRsp post(String url, String param) {return doPost(url, param, null);}public static HttpRsp post(String url, Map<String, String> param) {return doPost(url, makeParam(param), null);}public static HttpRsp post(String url, Map<String, String> param, Map<String, String> headMap) {return doPost(url, makeParam(param), headMap);}public static HttpRsp post(String url, String param, Map<String, String> headMap) {return doPost(url, param, headMap);}private static HttpRsp doPost(String url, String param, Map<String, String> headMap) {HttpRsp rsp = new HttpRsp();PrintWriter out = null;BufferedReader in = null;try {HttpURLConnection connection = (HttpURLConnection) (new URL(url)).openConnection();connection.setRequestMethod("POST");connection.setDoInput(true);connection.setDoOutput(true);if (headMap != null && headMap.isEmpty() == false) {// 設(shè)置包頭Iterator<Entry<String, String>> it = headMap.entrySet().iterator();Entry<String, String> entry = null;while (it.hasNext()) {entry = it.next();connection.setRequestProperty(entry.getKey(), entry.getValue());}}if (param != null && param.isEmpty() == false) {out = new PrintWriter(connection.getOutputStream());out.print(param);out.flush();}in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));StringBuffer sb = new StringBuffer();String line;while ((line = in.readLine()) != null) {sb.append(line + System.lineSeparator());}rsp.content = sb.toString();rsp.headerFieldsMap = connection.getHeaderFields();rsp.statusCode = connection.getResponseCode();rsp.statusDesc = connection.getResponseMessage();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {try {if (in != null) {in.close();}} catch (Exception e) {e.printStackTrace();}}return rsp;}/*** 將參數(shù)組織在一塊** @param map* @return*/public static String makeParam(Map<String, String> map) {StringBuffer sb = new StringBuffer();Iterator<Entry<String, String>> it = map.entrySet().iterator();Entry<String, String> entry = null;if (it.hasNext()) {entry = it.next();sb.append(entry.getKey() + "=" + entry.getValue());}while (it.hasNext()) {entry = it.next();sb.append("&" + entry.getKey() + "=" + entry.getValue());}return sb.toString();}public static class HttpRsp {public int statusCode;public String statusDesc;public String content;public Map<String, List<String>> headerFieldsMap = new HashMap<String, List<String>>();@Overridepublic String toString() {return "statusCode=" + statusCode + ", statusDesc=" + statusDesc + ", content=" + content;}public List<String> getCookie() {if (headerFieldsMap != null) {return headerFieldsMap.get("Set-Cookie");} else {return null;}}}
}
package cn.huali.fawork.exception;/*** 此處的異常一定要集成于RuntimeException,是為了數(shù)據(jù)庫事務(wù)回滾,請不要改動(dòng)*/
public class SelfException extends RuntimeException {public Integer code;public String msg;public Object data;public SelfException(Exception e) {super(e);this.code = 1;this.msg = e.getLocalizedMessage();}public SelfException(int code, String msg) {super(msg);this.code = code;this.msg = msg;}public SelfException(String msg) {super(msg);this.code = 1;this.msg = msg;}public SelfException(int code, String msg, Object data) {super(msg);this.code = code;this.msg = msg;this.data = data;}public String toString() {return "code=" + code + ",msg=" + msg;}
}

這段代碼是關(guān)鍵,即驗(yàn)證id_token是否有效;也驗(yàn)證require部分的字段是否存在,是否一致。注意,如果驗(yàn)證不通過,這段代碼會(huì)報(bào)Exception的,如果報(bào)了Exception說明id_token是無效的。

這一部分,你主要驗(yàn)證一下 auth_time 和 iat 兩個(gè)時(shí)間是否過了期限,其它無所謂。我寫的可代碼可能有點(diǎn)多余,你自己看著辦。

三、創(chuàng)建clientId

Sign In - Apple

1、創(chuàng)建一個(gè)Services IDs

看到?jīng)],這個(gè)Identifier就是clientId

后面我就不截圖了,反正會(huì)關(guān)聯(lián)一個(gè) appId;也會(huì)填一個(gè)域名,域名就是前段登錄的網(wǎng)頁的域名;redirectURI或者returnURI,就是登錄后跳轉(zhuǎn)的頁面,與客戶端網(wǎng)頁代碼中的那個(gè)字段保持一致即可,一般對于usePopup=true時(shí),這個(gè)字段用不上。

2、在AppId中,啟用登錄功能

點(diǎn)上圖中的那個(gè)Edit按鈕后,再進(jìn)行配置

http://www.risenshineclean.com/news/51566.html

相關(guān)文章:

  • 企業(yè)網(wǎng)站建設(shè)的實(shí)踐意義外鏈怎么做
  • 如何靠裁圖找到網(wǎng)站中國最好的營銷策劃公司
  • 網(wǎng)站項(xiàng)目建設(shè)申請匯報(bào)大綱網(wǎng)絡(luò)營銷推廣方案整合
  • 四網(wǎng)合一網(wǎng)站建設(shè)seo排名點(diǎn)擊軟件推薦
  • 個(gè)人新聞?lì)惥W(wǎng)站模板世界足球排名前100名
  • 體驗(yàn)好的網(wǎng)站陜西省人民政府
  • 互聯(lián)網(wǎng)行業(yè)環(huán)境分析seo博客
  • 網(wǎng)站備案要幕布照新手做銷售怎么開發(fā)客戶
  • 建網(wǎng)站公司seo技巧seo排名優(yōu)化
  • 網(wǎng)站建設(shè)賺錢嗎網(wǎng)站怎么快速排名
  • 昆明做網(wǎng)站建設(shè)價(jià)位50個(gè)市場營銷經(jīng)典案例
  • 中信建設(shè)有限責(zé)任公司洪波頁優(yōu)化軟件
  • 番禺做網(wǎng)站平臺湖南競價(jià)優(yōu)化專業(yè)公司
  • 做房產(chǎn)經(jīng)紀(jì)的那些網(wǎng)站可以進(jìn)客東莞網(wǎng)絡(luò)營銷推廣軟件
  • 網(wǎng)站建設(shè)與管理代碼app開發(fā)價(jià)格表
  • h5游戲搭建優(yōu)化技術(shù)
  • 品牌網(wǎng)站模板百度指數(shù)如何分析
  • 站長網(wǎng)站優(yōu)化公司手機(jī)網(wǎng)站建設(shè)價(jià)格
  • wordpress仿站難嗎廣東互聯(lián)網(wǎng)網(wǎng)絡(luò)營銷推廣
  • 網(wǎng)站建設(shè)宗旨是什么口碑營銷公司
  • 科技公司企業(yè)網(wǎng)站建設(shè)做一個(gè)企業(yè)網(wǎng)站大概需要多少錢
  • 營銷型網(wǎng)站建設(shè)極速建站廣州網(wǎng)絡(luò)seo公司
  • 視頻下載網(wǎng)站軟件做副屏做網(wǎng)頁設(shè)計(jì)一個(gè)月能掙多少
  • 什么網(wǎng)站可以看女人唔易做網(wǎng)絡(luò)營銷策劃書1500字
  • 做笑話網(wǎng)站常見的推廣方式
  • 免費(fèi)中文網(wǎng)站模板html關(guān)鍵詞排名哪里查
  • 手機(jī)點(diǎn)了釣魚網(wǎng)站怎么辦查詢網(wǎng)址域名ip地址
  • 廊坊網(wǎng)站制作公司小程序開發(fā)平臺有哪些
  • 個(gè)人網(wǎng)站可以做資訊小說類網(wǎng)絡(luò)優(yōu)化工程師是做什么的
  • 2017做那個(gè)網(wǎng)站能致富惠州seo關(guān)鍵字排名