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

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

ibm網(wǎng)站導(dǎo)航特效代碼信息流廣告投放平臺

ibm網(wǎng)站導(dǎo)航特效代碼,信息流廣告投放平臺,單位網(wǎng)站建設(shè)的必要性,wordpress扒主題java - SpringBoot3.x接入Security6.x實現(xiàn)JWT認證 文章目錄 java - SpringBoot3.x接入Security6.x實現(xiàn)JWT認證一、引言二、環(huán)境三、Maven依賴四、認識JWT1. JWT組成 五、認識Security6.x1. 和舊版本的區(qū)別(Security5.7以前的版本)2. Security6.x的默認篩…

java - SpringBoot3.x接入Security6.x實現(xiàn)JWT認證

文章目錄

  • java - SpringBoot3.x接入Security6.x實現(xiàn)JWT認證
  • 一、引言
  • 二、環(huán)境
  • 三、Maven依賴
  • 四、認識JWT
    • 1. JWT組成
  • 五、認識Security6.x
    • 1. 和舊版本的區(qū)別(Security5.7以前的版本)
    • 2. Security6.x的默認篩選器
    • 3. 注冊SecurityFilterChain
  • 六、基于OncePerRequestFilter自定義JWT認證篩選器
    • 1. 標記認證成功
  • 七、遇到的問題
    • 1. 加入Security6后,一直出現(xiàn)登錄頁
    • 2. 配置完匿名訪問的URL后,仍然執(zhí)行自定的篩選器
  • 八、完成JWT認證的主要代碼
    • 1. JwtUtil
    • 2. JwtTokenFilter
    • 3. SecuritConfig
  • 總結(jié)

一、引言

SpringBoot3.x的安全默認依賴Security6.x,Security6.x于Security5.7以前的配置有了很大區(qū)別。我們將深入探討這兩個版本之間的差異,以及它們?nèi)绾斡绊懍F(xiàn)代Web應(yīng)用的安全架構(gòu)。特別是,我們將重點分析JWT(JSON Web Tokens)過濾器的工作原理,以及它是如何與匿名訪問相結(jié)合,為應(yīng)用提供更加靈活的安全控制。

二、環(huán)境

  • JDK 17
  • SpringBoot 3.2
  • Security 6.3

三、Maven依賴

<!-- Security安全 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><version>3.2.2</version>
</dependency>
<!-- jwt接口認證 -->
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version>
</dependency>      

四、認識JWT

JSON Web Token (JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用于作為JSON對象在各方之間安全地傳輸信息。該信息可以被驗證和信任,因為它是數(shù)字簽名的。

1. JWT組成

JSON Web Token由三部分組成,它們之間用圓點(.)連接,一個典型的JWT看起來是這個樣子的:

  • 第一部分:header典型的由兩部分組成:token的類型(“JWT”)和算法名稱(比如:HMAC SHA256或者RSA等等),然后,用Base64對這個JSON編碼就得到JWT的第一部分。
  • 第二部分:payload它包含聲明(要求),聲明是關(guān)于實體(通常是用戶)和其他數(shù)據(jù)的聲明。
  • 第三部分:簽名是用于驗證消息在傳遞過程中有沒有被更改,并且對于使用私鑰簽名的token,它還可以驗證JWT的發(fā)送方是否為它所稱的發(fā)送方。

注意:不要在JWT的payload或header中放置敏感信息,除非它們是加密的。

{alg: "RS256"
}.
{
//存儲自定義的用戶信息,屬性可以自定擴充login_name: "admin",user_id: "xxxxx",...
}.
[signature]
  • 請求header應(yīng)該是這樣的:Authorization: Bearer

五、認識Security6.x

1. 和舊版本的區(qū)別(Security5.7以前的版本)

SpringBoot3中默認Security升級到了6.x寫法上發(fā)生了很大的變化,最顯著的變化之一就是對WebSecurityConfigurerAdapter類的使用方式的改變。這個類在 Spring Security 中被廣泛用于自定義安全配置。以下是主要的差異和寫法上的變化:

  • 廢棄WebSecurityConfigurerAdapter:

在Security5.x 版本中,WebSecurityConfigurerAdapter 是實現(xiàn)安全配置的常用方法。用戶通過繼承這個類,并覆蓋其方法來自定義安全配置。到了 Spring Security 6.x,WebSecurityConfigurerAdapter 被標記為過時(deprecated),意味著它可能在未來的版本中被移除。這一變化是為了推動使用更現(xiàn)代的配置方法,即使用組件式配置。

  • 新版本建議使用組件式配置:

在 Spring Security 6.x 中,推薦使用組件式配置。這意味著你可以創(chuàng)建一個配置類,該類不再需要繼承 WebSecurityConfigurerAdapter。
你可以直接定義一個或多個 SecurityFilterChain Bean來配置安全規(guī)則。這種方式更加靈活,并且與 Spring Framework 的整體風(fēng)格更加一致。

2. Security6.x的默認篩選器

支持的所有篩選器在spring-security-config-6.2.1.jar包的org.springframework.security.config.annotation.web.builders.FilterOrderRegistration類的構(gòu)造函數(shù)中定義,并確定了執(zhí)行順序。

FilterOrderRegistration() {Step order = new Step(INITIAL_ORDER, ORDER_STEP);put(DisableEncodeUrlFilter.class, order.next());put(ForceEagerSessionCreationFilter.class, order.next());put(ChannelProcessingFilter.class, order.next());order.next(); // gh-8105put(WebAsyncManagerIntegrationFilter.class, order.next());put(SecurityContextHolderFilter.class, order.next());put(SecurityContextPersistenceFilter.class, order.next());put(HeaderWriterFilter.class, order.next());put(CorsFilter.class, order.next());put(CsrfFilter.class, order.next());put(LogoutFilter.class, order.next());this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",order.next());this.filterToOrder.put("org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter",order.next());put(X509AuthenticationFilter.class, order.next());put(AbstractPreAuthenticatedProcessingFilter.class, order.next());this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",order.next());this.filterToOrder.put("org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter",order.next());put(UsernamePasswordAuthenticationFilter.class, order.next());order.next(); // gh-8105put(DefaultLoginPageGeneratingFilter.class, order.next());put(DefaultLogoutPageGeneratingFilter.class, order.next());put(ConcurrentSessionFilter.class, order.next());put(DigestAuthenticationFilter.class, order.next());this.filterToOrder.put("org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter",order.next());put(BasicAuthenticationFilter.class, order.next());put(RequestCacheAwareFilter.class, order.next());put(SecurityContextHolderAwareRequestFilter.class, order.next());put(JaasApiIntegrationFilter.class, order.next());put(RememberMeAuthenticationFilter.class, order.next());put(AnonymousAuthenticationFilter.class, order.next());this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",order.next());put(SessionManagementFilter.class, order.next());put(ExceptionTranslationFilter.class, order.next());put(FilterSecurityInterceptor.class, order.next());put(AuthorizationFilter.class, order.next());put(SwitchUserFilter.class, order.next());
}

3. 注冊SecurityFilterChain

    private final String[] permitUrlArr = new String[]{"xxx"};/*** 配置Spring Security安全鏈。*/@Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {//初始化jwt過濾器,并設(shè)置jwt公鑰var jwtTokenFilter = new JwtTokenFilter();//Security6.x關(guān)閉默認登錄頁httpSecurity.removeConfigurers(DefaultLoginPageConfigurer.class);logger.info("注冊JWT認證SecurityFilterChain");var chain = httpSecurity// 自定義權(quán)限攔截規(guī)則.authorizeHttpRequests((requests) -> {//requests.anyRequest().permitAll(); //放行所有請求!!!//允許匿名訪問requests//自定可匿名訪問地址,放到permitAllUrl中即可.requestMatchers(permitUrlArr).permitAll()//除上面聲明的可匿名訪問地址,其它所有請求全部需要進行認證.anyRequest().authenticated();})// 禁用HTTP響應(yīng)標頭.headers(headersCustomizer -> {headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());})//會話設(shè)為無狀態(tài),基于token,所以不需要session.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))//添加自定義的JWT認證篩選器,驗證header中jwt有效性,將插入到UsernamePasswordAuthenticationFilter之前 .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)//禁用表單登錄.formLogin(formLogin -> formLogin.disable())//禁用httpBasic登錄.httpBasic(httpBasic -> httpBasic.disable())//禁用rememberMe.rememberMe(rememberMe -> rememberMe.disable())// 禁用CSRF,因為不使用session.csrf(csrf -> csrf.disable())//允許跨域請求.cors(Customizer.withDefaults()).build();return chain;}

六、基于OncePerRequestFilter自定義JWT認證篩選器

使用OncePerRequestFilter的優(yōu)點是,能保證一個請求只過一次篩選器。可以在filter中實現(xiàn)對jwt的校驗,驗證成功后需要對Security上下文進行標注。標記認證已經(jīng)通過,這點非常重要。如果認證完了不標注,后邊的過濾器還是認為未認證導(dǎo)致無權(quán)限失敗。

1. 標記認證成功

//接入Spring Security6.x上下文,標記為已認證狀態(tài)
JwtAuthenticationToken jwtToken = new JwtAuthenticationToken(null);
jwtToken.setAuthenticated(true); //標記認證通過
SecurityContextHolder.getContext().setAuthentication(jwtToken);

七、遇到的問題

1. 加入Security6后,一直出現(xiàn)登錄頁

關(guān)閉默認登錄頁有兩個設(shè)置可以完成,可以刪除DefaultLoginPageConfigurer類的加載,或者調(diào)用formLogin()函數(shù),具體如下:

    @Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {//Security6.x關(guān)閉默認登錄頁httpSecurity.removeConfigurers(DefaultLoginPageConfigurer.class);var chain = httpSecurity//禁用表單登錄.formLogin(formLogin -> formLogin.disable()).build();return chain;}

2. 配置完匿名訪問的URL后,仍然執(zhí)行自定的篩選器

如果出現(xiàn)配置完匿名訪問的URL后,仍然執(zhí)行自定的篩選器,的問題。那原因就在于這個自定義篩選器上了,
只通過requests.requestMatchers(…).permitAll(); 配置的匿名訪問只能對默認篩選器起效,如果想
對自定義刪除器起效,還需要構(gòu)建WebSecurityCustomizer Bean對象,基于匿名函數(shù)配置要匿名訪問的地址。
一下是官網(wǎng)推薦的一個寫法,這里建議把兩個位置,配置的匿名訪問地址,使用一個公共數(shù)組進行管理,這樣
能保證兩個位置配置的一致性。

    /** 其它不需要認證的地址 */private final String[] permitUrlArr = new String[]{"/login","/error"//靜態(tài)資源,"/static/**.ico","/static/**.js","/static/**.css"//匹配springdoc,"/doc.html","/webjars/**"//匹配swagger路徑(默認), "/swagger-ui.html", "/swagger-ui/index.html", "/v3/api-docs/**", "/swagger-ui/**"//監(jiān)控檢測, "/actuator/**"};@Beanpublic WebSecurityCustomizer ignoringCustomize(){return (web) -> web.ignoring().requestMatchers(permitUrlArr);}@Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {//初始化jwt過濾器,并設(shè)置jwt公鑰var jwtTokenFilter = new JwtTokenFilter();//Security6.x關(guān)閉默認登錄頁httpSecurity.removeConfigurers(DefaultLoginPageConfigurer.class);logger.info("注冊JWT認證SecurityFilterChain");var chain = httpSecurity// 自定義權(quán)限攔截規(guī)則.authorizeHttpRequests((requests) -> {//允許匿名訪問requests//自定可匿名訪問地址,放到permitAllUrl中即可.requestMatchers(permitUrlArr).permitAll()//除上面聲明的可匿名訪問地址,其它所有請求全部需要進行認證.anyRequest().authenticated();}).build();return chain;}                    

八、完成JWT認證的主要代碼

目前是對已有jwt的認證,下發(fā)的jwt是基于RSA加密的內(nèi)容,需要使用公鑰進行解密,公鑰一般配置在yml文件里。關(guān)鍵邏輯設(shè)計3部分,SecuritConfig、JwtTokenFilter、JwtUtil。

1. JwtUtil

公鑰是統(tǒng)一認證中心下發(fā)的,目前寫在yml中,格式如下:

jwt.keyValue: |-----BEGIN PUBLIC KEY-----xxxxxxxx-----END PUBLIC KEY-----

JwtUtil類提供了驗證方法,出于性能考慮使用了單例模式,驗證器只需要實例化一次。

public class JwtUtil {private static JwtUtil instance = new JwtUtil();private static JWTVerifier jwtVerifier;//配置文件中公鑰的key值private static final String jwtPublicKeyConfig="jwt.keyValue";private JwtUtil()  {}/*** 基于固定配置文件的公鑰初始化JWT驗證器* @return*/public static JwtUtil getInstance(){if (jwtVerifier == null){String publicKey = SpringUtil.getConfig(jwtPublicKeyConfig);return getInstance(publicKey);}return instance;}/*** 基于自定義公鑰初始化JWT驗證器* @return*/public static JwtUtil getInstance(String publicKey) {if (jwtVerifier == null){initVerifier(publicKey);}return instance;}// 靜態(tài)的初始化函數(shù)private static synchronized void initVerifier(String publicKey) {if (jwtVerifier != null)return;//替換為實際的Base64編碼的RSA公鑰字符串String publicKeyStr = publicKey.replaceAll("\\s", "") // 去除所有空白字符,包括換行符.replace("-----BEGINPUBLICKEY-----", "").replace("-----ENDPUBLICKEY-----", "");// 將Base64編碼的公鑰字符串轉(zhuǎn)換為PublicKey對象byte[] encodedPublicKey = Base64.getDecoder().decode(publicKeyStr);X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedPublicKey);KeyFactory keyFactory = null;try {keyFactory = KeyFactory.getInstance("RSA");PublicKey pubKey = keyFactory.generatePublic(keySpec);// 使用公鑰創(chuàng)建一個Algorithm對象,用于驗證token的簽名Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) pubKey, null);// 解析和驗證tokenjwtVerifier = JWT.require(algorithm).build();} catch (NoSuchAlgorithmException e) {throw new RuntimeException(e);} catch (InvalidKeySpecException e) {throw new RuntimeException(e);}catch (Exception e){throw new RuntimeException(e);}}/*** 解析和驗證JWT token。** @param token JWT token字符串* @return 解碼后的JWT對象* @throws Exception 如果解析或驗證失敗,拋出異常*/public DecodedJWT verifyToken(String token) {return jwtVerifier.verify(token);}
}

2. JwtTokenFilter

該類是校驗的主要邏輯,完成了jwt校驗、已認證的標注。

public class JwtTokenFilter extends OncePerRequestFilter {private static Logger logger = LoggerFactory.getLogger(JwtTokenFilter.class);private JwtUtil jwtUtil;//獲取yml中的配置public String getConfig(String configKey) {var bean = applicationContext.getBean(Environment.class);var val = bean.getProperty(configKey);return val;}public JwtTokenFilter() throws ServletException {String jwtPubliKey = getConfig("jwt.keyValue");initTokenFilter(jwtPubliKey);}public JwtTokenFilter(String jwtPubliKey) throws ServletException {initTokenFilter(jwtPubliKey);}@Overrideprotected void initFilterBean() throws ServletException {}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {var pass = doTokenFilter(request,response,filterChain);if(!pass){return;}filterChain.doFilter(request,response);}/*** 初始化Token過濾器。* @throws ServletException 如果在初始化過程中發(fā)生錯誤,則拋出ServletException異常*/public void  initTokenFilter(String publicKey) throws ServletException {logger.info("初始化TokenFilter");if(StringUtils.isBlank(publicKey)){throw new ServletException("jwtPublicKey is null");}logger.info("jwtPublicKey:{}",publicKey);jwtUtil = JwtUtil.getInstance(publicKey);logger.info("初始化JwtUtil完成");}protected Boolean doTokenFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;// 從請求頭中獲取tokenString token = request.getHeader("Authorization");if(StringUtils.isBlank(token)){logger.info("jwt token為空,{} {}",request.getMethod(),request.getRequestURI());// 驗證失敗,返回401狀態(tài)碼response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token");return false;}// 假設(shè)token是以"Bearer "開頭的,需要去掉這個前綴if (token.startsWith("Bearer")) {token = token.replaceAll("Bearer\s+","");}logger.debug(request.getRequestURI());try {// 調(diào)用JwtUtils進行token驗證DecodedJWT jwtDecode = jwtUtil.verifyToken(token);//接入Spring Security6.x上下文,標記為已認證狀態(tài)JwtAuthenticationToken jwtToken = new JwtAuthenticationToken(null);jwtToken.setAuthenticated(true);SecurityContextHolder.getContext().setAuthentication(jwtToken);//將登錄信息寫入spring security上下文} catch (JWTVerificationException ex) {logger.info("jwt token 非法");response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "非法token:"+ex.getMessage());return false;} catch (Exception ex) {throw ex;}logger.debug("token驗證通過");return true;}public static class JwtAuthenticationToken extends AbstractAuthenticationToken {private User userInfo;public JwtAuthenticationToken(User user) {super(null);this.userInfo =user;}@Overridepublic User getPrincipal() {return userInfo;}@Overridepublic Object getCredentials() {throw new UnsupportedOperationException();}@Overridepublic boolean implies(Subject subject) {return super.implies(subject);}}}

3. SecuritConfig

該類完成了對需要匿名訪問的地址的配置,還有自定義filter的注入。

@Configuration
public class SecurityConfig {private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);/** 其它不需要認證的地址 */private final String[] permitUrlArr = new String[]{"/login","/error"//靜態(tài)資源,"/static/**.ico","/static/**.js","/static/**.css"//匹配springdoc,"/doc.html","/webjars/**"//匹配swagger路徑(默認), "/swagger-ui.html", "/swagger-ui/index.html", "/v3/api-docs/**", "/swagger-ui/**"//監(jiān)控檢測, "/actuator/**"};@Beanpublic WebSecurityCustomizer ignoringCustomize(){return (web) -> web.ignoring().requestMatchers(permitUrlArr);}@Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {//初始化jwt過濾器,并設(shè)置jwt公鑰var jwtTokenFilter = new JwtTokenFilter();//Security6.x關(guān)閉默認登錄頁httpSecurity.removeConfigurers(DefaultLoginPageConfigurer.class);logger.info("注冊JWT認證SecurityFilterChain");var chain = httpSecurity// 自定義權(quán)限攔截規(guī)則.authorizeHttpRequests((requests) -> {//requests.anyRequest().permitAll(); //放行所有請求!!!//允許匿名訪問requests//自定可匿名訪問地址,放到permitAllUrl中即可.requestMatchers(permitUrlArr).permitAll()//除上面聲明的可匿名訪問地址,其它所有請求全部需要進行認證.anyRequest().authenticated();})// 禁用HTTP響應(yīng)標頭.headers(headersCustomizer -> {headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());})//會話設(shè)為無狀態(tài),基于token,所以不需要session.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))//添加自定義的JWT認證篩選器,驗證header中jwt有效性,將插入到UsernamePasswordAuthenticationFilter之前 .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)//禁用表單登錄.formLogin(formLogin -> formLogin.disable())//禁用httpBasic登錄.httpBasic(httpBasic -> httpBasic.disable())//禁用rememberMe.rememberMe(rememberMe -> rememberMe.disable())// 禁用CSRF,因為不使用session.csrf(csrf -> csrf.disable())//允許跨域請求.cors(Customizer.withDefaults()).build();return chain;}@Beanpublic FilterRegistrationBean disableSpringBootErrorFilter(ErrorPageFilter filter){FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();filterRegistrationBean.setFilter(filter);filterRegistrationBean.setEnabled(false);return filterRegistrationBean;}
}

總結(jié)

以上支持介紹了對于已有JWT統(tǒng)一認證系統(tǒng)的接入(JWT解析和認證),不涉及JWT生成和管理相關(guān)內(nèi)容。
目前的用戶信息是基于JWT動態(tài)解析的,所以暫時沒有基于AbstractAuthenticationToken在Security上下文中存放用戶信息,JwtAuthenticationToken已經(jīng)支持自定義用戶信息的存儲,只需要按需傳入即可?;赟ecurity上下文獲取用戶信息使用SecurityContextHolder.getContext().getAuthentication().getPrincipal();方法。

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

相關(guān)文章:

  • 高端做網(wǎng)站哪家好百度一下官網(wǎng)首頁登錄
  • 臺州網(wǎng)站建設(shè)優(yōu)化深圳seo推廣
  • 金華專業(yè)做網(wǎng)站建站推廣
  • 互聯(lián)網(wǎng)金融網(wǎng)站設(shè)計百度收錄查詢工具
  • 模型下載網(wǎng)站開發(fā)流程廣州網(wǎng)頁制作
  • 網(wǎng)站建設(shè)月總結(jié)怎么做百度關(guān)鍵詞排名
  • 網(wǎng)站首頁的動態(tài)視頻怎么做的公司seo排名優(yōu)化
  • 給網(wǎng)站做插畫分辨率seo也成搜索引擎優(yōu)化
  • 北京網(wǎng)站建設(shè)天下公司網(wǎng)絡(luò)營銷品牌
  • 公司怎么建網(wǎng)站做推廣日本疫情最新數(shù)據(jù)
  • 棗莊三合一網(wǎng)站開發(fā)百度安裝應(yīng)用
  • 實時視頻網(wǎng)站怎么做網(wǎng)站百度推廣
  • 新鄉(xiāng)網(wǎng)站開發(fā)的公司電話在線服務(wù)器網(wǎng)站
  • 黃金網(wǎng)站網(wǎng)址免費百度網(wǎng)訊科技有限公司官網(wǎng)
  • 做網(wǎng)站好處小程序制作一個需要多少錢
  • 簡單做網(wǎng)站的價格網(wǎng)頁設(shè)計一般用什么軟件
  • 北京網(wǎng)站優(yōu)化排名推廣站長工具網(wǎng)站查詢
  • 個人適合建什么網(wǎng)站廈門seo關(guān)鍵詞
  • 垡頭做網(wǎng)站的公司2021年網(wǎng)絡(luò)熱點輿論
  • 四川綿陽網(wǎng)站建設(shè)百度認證證書
  • 自己做的網(wǎng)站怎么上網(wǎng)百度站長平臺快速收錄
  • 專業(yè)做鞋子網(wǎng)站百度競價排名是什么
  • 中小學(xué)學(xué)校網(wǎng)站建設(shè)seo入門教程seo入門
  • 便宜的網(wǎng)站設(shè)計企業(yè)查詢官網(wǎng)入口
  • 遂寧網(wǎng)站開發(fā)廣告軟文小故事800字
  • 做社交網(wǎng)站有哪些全世界足球排名前十位
  • 小程序平臺商城seo搜索引擎優(yōu)化實戰(zhàn)
  • 建設(shè)企業(yè)網(wǎng)站目的查看域名每日ip訪問量
  • 中工信融營銷型網(wǎng)站建設(shè)百度精準獲客平臺
  • 做外貿(mào)翻譯用哪個網(wǎng)站好百度app安裝免費下載