springsecurity和jwt整合的方法是什么

這篇文章主要介紹了spring security和jwt整合的方法是什么的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇spring security和jwt整合的方法是什么文章都會(huì)有所收獲,下面我們一起來看看吧。

創(chuàng)新互聯(lián)建站主要從事成都網(wǎng)站制作、成都做網(wǎng)站、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)共青城,十多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792

什么是 JWT

json web token (JWT),是為了在網(wǎng)絡(luò)環(huán)境中傳遞聲明而設(shè)計(jì)的一種基于JSON的開放標(biāo)準(zhǔn)(RFC 7519),該token 被設(shè)計(jì)為緊湊且安全的.特別使用于分布式站點(diǎn)的登陸(SSO)
場(chǎng)景.JWT一般被用來在服務(wù)提供者和服務(wù)認(rèn)證者之間傳遞身份信息,以便可以從服務(wù)器獲取資源.也可以增加一些額外的其它業(yè)務(wù)邏輯所必需的聲明信息.
該token可直接被用于認(rèn)證,也可用于被加密.

基于token的鑒權(quán)機(jī)制

基于token的鑒權(quán)機(jī)制也是類似于http協(xié)議無狀態(tài)的,它不需要在服務(wù)段保留用戶的認(rèn)證信息或者鑒權(quán)信息.這就意味著基于token認(rèn)證機(jī)制的用戶就不必考慮在哪一臺(tái)服務(wù)器登錄了.
這就為應(yīng)用的擴(kuò)展提供了遍歷.

認(rèn)證流程:

這個(gè)token必須在每次請(qǐng)求時(shí)傳遞給服務(wù)端,它應(yīng)該保存在請(qǐng)求頭里面.另外,服務(wù)器端要支持 CORS(跨來源資源共享策略) ,一般我們?cè)诜?wù)器上這么做就可以了, Access-Control-Allow-Origin: *

jwt的組成

jwt的三個(gè)組成部分共同構(gòu)成了一個(gè) 簽名信息 signature

**這個(gè)部分需要base64加密后的header和base64加密后的payload使用.連接組成的字符串.
然后通過header中聲明的加密方式進(jìn)行加鹽secret組合加密,然后就構(gòu)成了jwt的第三部分。**

注意:secret是保存在服務(wù)器端的,jwt的簽發(fā)生成也是在服務(wù)器端的,secret就是用來進(jìn)行jwt的簽發(fā)和jwt的驗(yàn)證,
所以,它就是你服務(wù)端的私鑰,在任何場(chǎng)景都不應(yīng)該流露出去。一旦客戶端得知這個(gè)secret, 那就意味著客戶端是可以自我簽發(fā)jwt了。

如何應(yīng)用

一般是在請(qǐng)求頭里加入Authorization,并加上Bearer標(biāo)注:如下:

fetch('api/user/1', {
  headers: {
    'Authorization': 'Bearer '

Spring Security 結(jié)合 jwt

我們之前介紹過,Spring security是基于過濾器(Filter)的,使用過濾器我們可以很容易的攔截某些請(qǐng)求.
因此通過上面對(duì)jwt的了解,我們就可以在過濾器中處理token的生成和校驗(yàn).

大致流程如下:

  • 1.當(dāng)用戶進(jìn)行提交登陸表單時(shí),自定義一個(gè)攔截器JWTLoginFilter進(jìn)行表單參數(shù)的獲取.

  • 2.驗(yàn)證提交的用戶名密碼是否正確.

  • 3.如果登陸成功,使用jwt頒發(fā)一個(gè)token給客戶端,之后的客戶端請(qǐng)求都要帶上這個(gè)token.

  • 4.token驗(yàn)證:再自定義一個(gè)過濾器JWTAuthenticationFilter,當(dāng)用戶訪問需要認(rèn)證的請(qǐng)求時(shí),攔截該請(qǐng)求,并進(jìn)行token校驗(yàn).

Spring Security 安全相關(guān)配置類

我們?yōu)榱撕?jiǎn)化開發(fā)使用spring boot進(jìn)行項(xiàng)目的快速搭建.需要引入如下依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

之后我們創(chuàng)建一個(gè)controller進(jìn)行不同級(jí)別的驗(yàn)證.

/**
 * @author itguang
 * @create
@RestController
public class UserController

    @Autowired
    private UserRepository applicationUserRepository;


    @RequestMapping("/hello")
    public String hello(){

        return "hello";
    }

    @RequestMapping("/userList")
    public Map<String, Object> userList(){
        List<User> myUsers = applicationUserRepository.findAll();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("users",myUsers);
        return map;
    }

    @RequestMapping("/admin")
    public String admin(){

        return "admin";
    }



}

接下來就是配置我們的安全管理類 SecurityConfig :

/**
 * @author itguang
 * @create
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;


    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {

       // auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
        // 使用自定義身份驗(yàn)證組件
        auth.authenticationProvider(new CustomAuthenticationProvider(userDetailsService,bCryptPasswordEncoder));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //禁用 csrf
        http.cors().and().csrf().disable().authorizeRequests()
                //允許以下請(qǐng)求
                .antMatchers("/hello").permitAll()
                // 所有請(qǐng)求需要身份認(rèn)證
                .anyRequest().authenticated()
                .and()
                //驗(yàn)證登陸
                .addFilter(new JWTLoginFilter(authenticationManager()))
                //驗(yàn)證token
                .addFilter(new

可以看到我們的Security繼承了 WebSecurityConfigurerAdapter ,關(guān)于WebSecurityConfigurerAdapter我們之前的文章已經(jīng)介紹過,
我們重點(diǎn)關(guān)注的是重載的兩個(gè) configure() 方法.

configure(HttpSecurity http):這個(gè)方法配置了對(duì)請(qǐng)求的攔截配置,在這里我們又添加了兩個(gè)自定義的過濾器,JWTLoginFilter 和JWTAuthenticationFilter,
分別負(fù)責(zé)登錄時(shí)用戶名密碼的驗(yàn)證,和攔截請(qǐng)求時(shí)對(duì)token的驗(yàn)證.

configure(AuthenticationManagerBuilder auth):這個(gè)方法有點(diǎn)奇怪,我們并沒有使用之前介紹幾種的用戶存儲(chǔ),而是使用了一個(gè)authenticationProvider()
方法,并傳入了一個(gè)我們自定義的 AuthenticationProvider 類型的對(duì)象作為參數(shù).稍后我們會(huì)詳細(xì)介紹這個(gè)類到底是什么.

登陸信息 驗(yàn)證過濾器: JWTLoginFilter

/**
 * @author itguang
 * @create
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter


    private AuthenticationManager authenticationManager;

    public JWTLoginFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }


    /**
     * 接收并解析用戶登陸信息  /login,
     *為已驗(yàn)證的用戶返回一個(gè)已填充的身份驗(yàn)證令牌,表示成功的身份驗(yàn)證
     *返回null,表明身份驗(yàn)證過程仍在進(jìn)行中。在返回之前,實(shí)現(xiàn)應(yīng)該執(zhí)行完成該過程所需的任何額外工作。
     *如果身份驗(yàn)證過程失敗,就拋出一個(gè)AuthenticationException
     *
     *
     * @param request  從中提取參數(shù)并執(zhí)行身份驗(yàn)證
     * @param response 如果實(shí)現(xiàn)必須作為多級(jí)身份驗(yàn)證過程的一部分(比如OpenID)進(jìn)行重定向,則可能需要響應(yīng)
     * @return 身份驗(yàn)證的用戶令牌,如果身份驗(yàn)證不完整,則為null。
     * @throws
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {


        //得到用戶登陸信息,并封裝到 Authentication 中,供自定義用戶組件使用.
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();


        ArrayList<GrantedAuthorityImpl> authorities = new ArrayList<>();

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password, authorities);

        //authenticate()接受一個(gè)token參數(shù),返回一個(gè)完全經(jīng)過身份驗(yàn)證的對(duì)象,包括證書.
        // 這里并沒有對(duì)用戶名密碼進(jìn)行驗(yàn)證,而是使用 AuthenticationProvider 提供的 authenticate 方法返回一個(gè)完全經(jīng)過身份驗(yàn)證的對(duì)象,包括證書.
//        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

//UsernamePasswordAuthenticationToken 是 Authentication 的實(shí)現(xiàn)類
        return authenticationToken;
    }


    /**
     * 登陸成功后,此方法會(huì)被調(diào)用,因此我們可以在次方法中生成token,并返回給客戶端
     *
     * @param request
     * @param response
     * @param chain
     * @param
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) {

        String token = Jwts.builder()
                .setSubject(authResult.getName())
                //有效期兩小時(shí)
                .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 2 * 1000))
                //采用什么算法是可以自己選擇的,不一定非要采用HS512
                .signWith(SignatureAlgorithm.HS512, "MyJwtSecret")
                .compact();

        response.addHeader("token", "Bearer "

我們可以看到 JWTLoginFilter 繼承了 UsernamePasswordAuthenticationFilter,
并且重寫了它的 attemptAuthentication() 方法和 successfulAuthentication() 方法.

在 attemptAuthentication()方法中,我們就可以得到 /login 提交的用戶名和密碼信息,但這里我們并沒有返回一個(gè)認(rèn)證后的 Authentication,
這是為什么呢?原因就在于,我們?cè)?SecurityConfigure 的方法中,使用了一個(gè)自定義的 AuthenticationProvider 實(shí)現(xiàn)類,如:

@Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {

       // auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
        // 使用自定義身份驗(yàn)證組件
        auth.authenticationProvider(new

那么 AuthenticationProvider 用來干嘛的呢? 查看他的源碼可以發(fā)現(xiàn):

public interface AuthenticationProvider

         /**
         * 驗(yàn)證登錄信息,若登陸成功,設(shè)置 Authentication
         *
         * @param authentication
         * @return 一個(gè)完全經(jīng)過身份驗(yàn)證的對(duì)象,包括憑證。
         *           如果AuthenticationProvider無法支持已通過的身份驗(yàn)證對(duì)象的身份驗(yàn)證,則可能返回null。
         *          在這種情況下,將會(huì)嘗試支持下一個(gè)身份驗(yàn)證類的驗(yàn)證提供者。
         * @throws  

    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;

    /**
         * 是否可以提供輸入類型的認(rèn)證服務(wù)
         *
         * 如果這個(gè)AuthenticationProvider支持指定的身份驗(yàn)證對(duì)象,那么返回true。
         * 返回true并不能保證身份驗(yàn)證提供者能夠?qū)ι矸蒡?yàn)證類的實(shí)例進(jìn)行身份驗(yàn)證。
         * 它只是表明它可以支持對(duì)它進(jìn)行更深入的評(píng)估。身份驗(yàn)證提供者仍然可以從身份驗(yàn)證(身份驗(yàn)證)方法返回null,
         * 以表明應(yīng)該嘗試另一個(gè)身份驗(yàn)證提供者。在運(yùn)行時(shí)管理器的運(yùn)行時(shí),可以選擇具有執(zhí)行身份驗(yàn)證的身份驗(yàn)證提供者。
         *
         * @param authentication
         * @return
    boolean

CustomAuthenticationProvider

AuthenticationProvider(身份驗(yàn)證提供者) 顧名思義,可以提供一個(gè) Authentication 供Spring Security的上下文使用.

通過 supports 方法我們對(duì)特定的 Authentication進(jìn)行認(rèn)證,如果返回 true,就交給 authenticate(Authentication authentication) 方法,
此方法一個(gè)完全經(jīng)過身份驗(yàn)證的對(duì)象,包括憑證。

如下我們自定義的 CustomAuthenticationProvider:

/**
 * AuthenticationProvider(身份驗(yàn)證提供者) 顧名思義,可以提供一個(gè) Authentication 供Spring Security的上下文使用,
 *
 * @author itguang
 * @create
public class CustomAuthenticationProvider implements AuthenticationProvider

    private UserDetailsService userDetailsService;

    private BCryptPasswordEncoder bCryptPasswordEncoder;

    public CustomAuthenticationProvider(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }


    /**
     * 是否可以提供輸入類型的認(rèn)證服務(wù)
     * <p>
     * 如果這個(gè)AuthenticationProvider支持指定的身份驗(yàn)證對(duì)象,那么返回true。
     * 返回true并不能保證身份驗(yàn)證提供者能夠?qū)ι矸蒡?yàn)證類的實(shí)例進(jìn)行身份驗(yàn)證。
     * 它只是表明它可以支持對(duì)它進(jìn)行更深入的評(píng)估。身份驗(yàn)證提供者仍然可以從身份驗(yàn)證(身份驗(yàn)證)方法返回null,
     * 以表明應(yīng)該嘗試另一個(gè)身份驗(yàn)證提供者。在運(yùn)行時(shí)管理器的運(yùn)行時(shí),可以選擇具有執(zhí)行身份驗(yàn)證的身份驗(yàn)證提供者。
     *
     * @param authentication
     * @return
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }


    /**
     * 驗(yàn)證登錄信息,若登陸成功,設(shè)置 Authentication
     *
     * @param authentication
     * @return 一個(gè)完全經(jīng)過身份驗(yàn)證的對(duì)象,包括憑證。
     * 如果AuthenticationProvider無法支持已通過的身份驗(yàn)證對(duì)象的身份驗(yàn)證,則可能返回null。
     * 在這種情況下,將會(huì)嘗試支持下一個(gè)身份驗(yàn)證類的驗(yàn)證提供者。
     * @throws
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 獲取認(rèn)證的用戶名 & 密碼
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        //通過用戶名從數(shù)據(jù)庫中查詢?cè)撚脩?
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);


        //判斷密碼(這里是md5加密方式)是否正確
        String dbPassword = userDetails.getPassword();
        String encoderPassword = DigestUtils.md5DigestAsHex(password.getBytes());

        if (!dbPassword.equals(encoderPassword)) {
            throw new UsernameIsExitedException("密碼錯(cuò)誤");
        }


        // 還可以從數(shù)據(jù)庫中查出該用戶所擁有的權(quán)限,設(shè)置到 authorities 中去,這里模擬數(shù)據(jù)庫查詢.
        ArrayList<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new GrantedAuthorityImpl("ADMIN"));

        Authentication auth = new UsernamePasswordAuthenticationToken(username, password, authorities);

        return

可見我們?cè)谶@個(gè) AuthenticationProvider 中對(duì) UsernamePasswordAuthenticationToken 進(jìn)行認(rèn)證,

在 authenticate(Authentication authentication)方法中, authentication 就是 我們之前返回的 UsernamePasswordAuthenticationToken,我們可以得到登陸的用戶名和密碼,進(jìn)行真正的認(rèn)證.

如果認(rèn)證成功 就給改 UsernamePasswordAuthenticationToken 設(shè)置對(duì)應(yīng)的權(quán)限,最后把已經(jīng)認(rèn)證的 UsernamePasswordAuthenticationToken 返回即可.

還有我們?cè)谕ㄟ^用戶名從數(shù)據(jù)庫查找用戶時(shí),返回了一個(gè) UserDetails 對(duì)象,關(guān)于UserdDetails對(duì)象,我們之前的文章已經(jīng)介紹過,不懂得可以去查看一下.

最后,當(dāng) CustomAuthenticationProvider 認(rèn)證成功之后,JWTLoginFilter 中的 successfulAuthentication() 方法機(jī)會(huì)執(zhí)行,因此我們就可以在這里設(shè)置token了,如下:

/**
     * 登陸成功后,此方法會(huì)被調(diào)用,因此我們可以在次方法中生成token,并返回給客戶端
     *
     * @param request
     * @param response
     * @param chain
     * @param
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) {

        String token = Jwts.builder()
                .setSubject(authResult.getName())
                //有效期兩小時(shí)
                .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 2 * 1000))
                //采用什么算法是可以自己選擇的,不一定非要采用HS512
                .signWith(SignatureAlgorithm.HS512, "MyJwtSecret")
                .compact();

        response.addHeader("token", "Bearer "

我們使用JWT構(gòu)造了一個(gè)token字符串,并把它放在了http請(qǐng)求頭中返回給了客戶端.

至此我們的登陸認(rèn)證并返回 token就已經(jīng)完成了,接下來就是客戶端攜帶這已經(jīng)獲得token訪問需要認(rèn)證的資源時(shí),我們需要對(duì)改token進(jìn)行驗(yàn)證了.

JWTAuthenticationFilter

/**
 * token校驗(yàn)
 *
 * @author itguang
 * @create
public class JWTAuthenticationFilter extends BasicAuthenticationFilter

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }


    /**
     * 在此方法中檢驗(yàn)客戶端請(qǐng)求頭中的token,
     * 如果存在并合法,就把token中的信息封裝到 Authentication 類型的對(duì)象中,
     * 最后使用  SecurityContextHolder.getContext().setAuthentication(authentication); 改變或刪除當(dāng)前已經(jīng)驗(yàn)證的 pricipal
     *
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        String token = request.getHeader("token");

        //判斷是否有token
        if (token == null || !token.startsWith("Bearer ")) {
            chain.doFilter(request, response);
            return;
        }

        UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(token);

        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        //放行
        chain.doFilter(request, response);


    }

    /**
     * 解析token中的信息,并判斷是否過期
     */
    private UsernamePasswordAuthenticationToken getAuthentication(String token) {


        Claims claims = Jwts.parser().setSigningKey("MyJwtSecret")
                .parseClaimsJws(token.replace("Bearer ", ""))
                .getBody();

        //得到用戶名
        String username = claims.getSubject();

        //得到過期時(shí)間
        Date expiration = claims.getExpiration();

        //判斷是否過期
        Date now = new Date();

        if (now.getTime() > expiration.getTime()) {

            throw new UsernameIsExitedException("該賬號(hào)已過期,請(qǐng)重新登陸");
        }


        if (username != null) {
            return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
        }
        return null;
    }


}

由此可以看到 JWTAuthenticationFilter 繼承了 BasicAuthenticationFilter,

BasicAuthenticationFilter 用來處理一個(gè)HTTP請(qǐng)求的基本授權(quán)標(biāo)頭,將結(jié)果放入安全上下文。
總之,這個(gè)過濾器負(fù)責(zé)處理任何具有HTTP請(qǐng)求頭的請(qǐng)求的請(qǐng)求,以及一個(gè)基本的身份驗(yàn)證方案和一個(gè)base64編碼的用戶名:密碼令牌。
如果身份驗(yàn)證成功,那么最終的身份驗(yàn)證對(duì)象將被放入安全上下文。

因此我們就可以繼承 BasicAuthenticationFilter 并重寫 doFilterInternal()方法,在該方法中進(jìn)行token的驗(yàn)證,如果驗(yàn)證成功,將結(jié)果放入安全上下文,如:

SecurityContextHolder.getContext().setAuthentication(authenticationToken);

大功告成

到此,我們就使用Spring Security + JWT ,搭建了一個(gè)安全的 resultful api ,接下來我們就進(jìn)行簡(jiǎn)單的測(cè)試,這里我是用postman,這是一個(gè)非常好用的 http 調(diào)試工具.
我們現(xiàn)在數(shù)據(jù)庫的users表中插入一條用戶信息,用戶名:itguang 密碼: 123456,

接下來,打開post滿,訪問 localhost/login?username=itguang&password=123456

如下:

我們可以看到響應(yīng)頭中多了一個(gè)token
properties
token →Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJpdGd1YW5nIiwiZXhwIjoxNTE0OTU2NjI3fQ.PIiH7dRrVgPc88kOPtGzvrqZf5l87FRe3h7s9YZVb2zkL_XwRc_v3uhn23bmKqu7G0pSZngdnX0rh_kT1YDwww

這就是我們使用jwt生成的token,現(xiàn)在是加密狀態(tài),接下來我們?cè)僭L問 localhost/admin ,并把這個(gè)token放到 請(qǐng)求頭中,如下:

會(huì)看到返回了正確的字符串,但是如果我們不帶該token值呢?

瀏覽器訪問: http://localhost/admin ,會(huì)發(fā)現(xiàn)

403,明顯的沒有權(quán)限禁止訪問,這正是我們想要的結(jié)果.

關(guān)于“spring security和jwt整合的方法是什么”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“spring security和jwt整合的方法是什么”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

新聞名稱:springsecurity和jwt整合的方法是什么
分享URL:http://muchs.cn/article30/pieoso.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈面包屑導(dǎo)航、標(biāo)簽優(yōu)化、響應(yīng)式網(wǎng)站、網(wǎng)站策劃、App設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化