【OAuth2】:赋予用户控制权的安全通行证--代码模拟篇

发布时间:2023年12月27日

🥳🥳Welcome Huihui's Code World ! !🥳🥳

接下来看看由辉辉所写的关于OAuth2的相关操作吧?

?上篇已经讲了oauth2的相关知识,详解了oauth2的四种授权模式中的授权码模式,那么这一篇我们就来讲一下授权码模式的代码实现,但是为了更好的讲解其中的相关知识点,这里用的是模拟代码【没有连接数据库进行验证】

目录

🥳🥳Welcome Huihui's Code World ! !🥳🥳

一.代码准备

1.client

1.1pom

1.2controller

2.resource-owner

2.1pom

2.2controller

3.authorization-server

3.1pom

3.2controller

4.resource-server

4.1pom

4.2controller

5.整体项目的pom依赖

二.授权码模式的模拟代码流程讲解?

1.用户访问页面

2.访问的页面将请求重定向到认证服务器

?3.认证服务器向用户展示授权页面,等待用户授权

4.用户授权完成

5.获取令牌(Token)


一.代码准备

1.client

1.1pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         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>
    <groupId>com.example</groupId>
    <artifactId>client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>client</name>
    <description>client</description>

    <parent>
        <artifactId>Oauth</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.client</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.1</version>
                <configuration>
                    <mainClass>com.example.client.ClientApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

1.2controller

package com.example.client.controller;

import lombok.extern.slf4j.Slf4j;
import org.apache.oltu.oauth2.client.HttpClient;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthResourceResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

/**
 * 获取授权的控制器
 */
@Controller
@RequestMapping("/client")
@SuppressWarnings("all")
@Slf4j
public class GetAuthorizationController {

    /**
     * 客户端: 8080
     * 资源拥有者: 8081
     * 认证服务器: 8082
     * 资源服务器: 8083
     */

    private static String CLIENT_ID = "clientId";
    private static String CLIENT_SECRET = "clientSecret";
    private static String CODE_URL = "code";
    private static String RESPONSE_TYPE = "code";
    /*认证服务器地址*/
    private static String AUTH_SERVER_URL = "http://localhost:8082/authServer/token";
    /*资源拥有者地址*/
    private static String RESOURCE_OWNER_URL = "http://localhost:8081/owner/";
    /*资源服务器地址*/
    private static String RESOURCE_SERVER_URL = "http://localhost:8083/resourceServer/userinfo";
    /*授权码回调地址*/
    private static String CALLBACKCODE = "http://localhost:8080/client/callbackCode";
    /*获取资源地址*/
    private static String GETRESOURCE = "http://localhost:8080/client/getResource";

    /**
     * 客户向资源所有者获取授权码
     */
    @GetMapping("/getCode")
    public String getCode() throws OAuthSystemException {
        // 创建OAuthClientRequest对象,设置授权码请求的相关参数
        OAuthClientRequest oAuthClientRequest = OAuthClientRequest
                .authorizationLocation(CODE_URL) // 授权码请求的授权服务器地址
                .setClientId(CLIENT_ID) // 设置客户端ID
                .setRedirectURI(CALLBACKCODE) // 设置重定向URI
                .setResponseType(RESPONSE_TYPE) // 设置响应类型为授权码
                .buildQueryMessage(); // 构建查询消息
        String uriString = oAuthClientRequest.getLocationUri(); // 获取授权码请求的URI字符串
        // 重定向到资源所有者,获取验证码
        return "redirect:" + RESOURCE_OWNER_URL + uriString;
    }

    /**
     * 1. 资源所有者在生成授权码之后,会回调该接口将授权码传回给客户
     * 2. 客户获取授权码之后,使用该接口向认证服务器申请令牌
     */
    @RequestMapping("/callbackCode")
    public String callbackCode(HttpServletRequest request) throws OAuthSystemException, OAuthProblemException {
        // 从请求中获取授权码
        String code = request.getParameter("code");
        log.info(" --- 从资源拥有者获取code: {} -----------", code);
        // 创建OAuthClient对象,用于与认证服务器进行通信
        OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
        // 创建OAuthClientRequest对象,设置令牌请求的相关参数
        OAuthClientRequest tokenRequest = OAuthClientRequest
                .tokenLocation(AUTH_SERVER_URL) // 令牌请求的认证服务器地址
                .setClientId(CLIENT_ID) // 设置客户端ID
                .setClientSecret(CLIENT_SECRET) // 设置客户端密钥
                .setGrantType(GrantType.AUTHORIZATION_CODE) // 设置授权类型为授权码
                .setCode(code) // 设置授权码
                .setRedirectURI(CALLBACKCODE) // 设置重定向URI
                .buildQueryMessage(); // 构建查询消息
        // 通过Code,向认证服务器申请令牌
        OAuthJSONAccessTokenResponse tokenResp = oAuthClient.accessToken(tokenRequest, OAuth.HttpMethod.POST); // 发送请求并获取响应
        // 从响应中获取访问令牌
        String accessToken = tokenResp.getAccessToken();
        log.info("客户获取的访问令牌:" + accessToken);
        return "redirect:" + GETRESOURCE + "?accessToken=" + accessToken;
    }

    /**
     * 使用令牌获取资源服务器中的数据
     */
    @GetMapping("/getResource")
    @ResponseBody
    public String getResource(String accessToken) throws OAuthSystemException, OAuthProblemException {
        log.info("客户使用令牌向资源服务器获取数据 token = " + accessToken);
        // 创建OAuthClient对象,用于与资源服务器进行通信
        OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
        // 创建OAuthClientRequest对象,设置用户信息请求的相关参数
        OAuthClientRequest userInfoRequest =
                new OAuthBearerClientRequest(RESOURCE_SERVER_URL) // 用户信息请求的资源服务器地址
                        .setAccessToken(accessToken) // 设置访问令牌
                        .buildHeaderMessage(); // 构建请求头消息
        // 向资源服务器发送请求并获取响应
        OAuthResourceResponse resourceResponse = oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);
        // 从响应中获取用户信息
        String userInfo = resourceResponse.getBody();
        log.info("客户获取的资源服务器的数据: " + userInfo);
        return userInfo;
    }

}

2.resource-owner

2.1pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         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>
    <groupId>com.example</groupId>
    <artifactId>resource-owner</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>resource-owner</name>
    <description>resource-owner</description>

    <parent>
        <artifactId>Oauth</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.1</version>
                <configuration>
                    <mainClass>com.example.owner.ResourceOwnerApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2.2controller

package com.example.owner.controller;

import lombok.extern.slf4j.Slf4j;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller // 声明这是一个控制器类
@RequestMapping("/owner") // 设置请求映射路径为 /owner
@Slf4j // 使用 Lombok 的 @Slf4j 注解,简化日志记录
@SuppressWarnings("all") // 忽略所有警告
public class CodeController {

    @GetMapping("/code") // 定义一个处理 GET 请求的方法,映射路径为 /code
    public String sendCode(HttpServletRequest request) {
        log.info("resource owner send code"); // 记录日志信息
        try {
            OAuthAuthzRequest oathReq = new OAuthAuthzRequest(request); // 从请求中获取 OAuthAuthzRequest 对象
            if (StringUtils.hasLength(oathReq.getClientId())) { // 如果客户端ID存在
                //设置授权码
                String code = "authorizationCode";
                String responseType = oathReq.getResponseType();
                //获取构建响应的对象
                OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
                        OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_OK);
                builder.setCode(code); // 设置授权码
                String redirectURI = oathReq.getParam(OAuth.OAUTH_REDIRECT_URI); // 获取重定向URI
                OAuthResponse oauthResp = builder.location(redirectURI).buildQueryMessage(); // 构建查询消息
                log.info("resource owner send code "); // 记录日志信息
                String uri = oauthResp.getLocationUri(); // 获取重定向URI
                return "redirect:" + uri; // 返回重定向URL
            }
        } catch (OAuthSystemException e) { // 捕获 OAuthSystemException 异常
            e.printStackTrace(); // 打印异常堆栈信息
        } catch (OAuthProblemException e) { // 捕获 OAuthProblemException 异常
            e.printStackTrace(); // 打印异常堆栈信息
        }
        return null; // 如果发生异常或客户端ID不存在,返回 null
    }

}

3.authorization-server

3.1pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         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>
    <groupId>com.example</groupId>
    <artifactId>authorization-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>authorization-server</name>
    <description>authorization-server</description>

    <parent>
        <artifactId>Oauth</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.1</version>
                <configuration>
                    <mainClass>com.example.authorization.AuthorizationServerApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

3.2controller
?

package com.example.authorization.controller;

import lombok.extern.slf4j.Slf4j;
import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller // 声明这是一个控制器类
@RequestMapping("/authServer") // 设置请求映射路径为 /authServer
@Slf4j // 使用 Lombok 的 @Slf4j 注解,简化日志记录
@SuppressWarnings("all") // 忽略所有警告
public class AuthController {

    @PostMapping("/token") // 定义一个处理 POST 请求的方法,映射路径为 /token
    public HttpEntity getAccessToken(HttpServletRequest request) throws OAuthProblemException, OAuthSystemException {
        log.info("认证服务器,接收到客户的令牌请求 .... "); // 记录日志信息
        OAuthTokenRequest tokenReq = new OAuthTokenRequest(request); // 从请求中获取 OAuthTokenRequest 对象
        String clientSecret = tokenReq.getClientSecret(); // 获取客户端密钥
        if (StringUtils.hasLength(clientSecret)) { // 如果客户端密钥存在
            OAuthIssuerImpl oAuthIssuer = new OAuthIssuerImpl(new MD5Generator()); // 创建一个 OAuthIssuerImpl 对象,使用 MD5Generator 生成签名算法
            String token = oAuthIssuer.accessToken(); // 生成访问令牌
            log.info("token = " + token); // 记录日志信息
            //构造保护令牌的响应对象
            OAuthResponse oAuthResponse = OAuthASResponse
                    .tokenResponse(HttpServletResponse.SC_OK) // 设置响应状态码为 200 OK
                    .setAccessToken(token) // 设置访问令牌
                    .buildJSONMessage(); // 构建 JSON 格式的响应消息
            return new ResponseEntity(oAuthResponse.getBody(), HttpStatus.valueOf(oAuthResponse.getResponseStatus())); // 返回响应实体
        }
        return null; // 如果客户端密钥不存在,返回 null
    }

}

4.resource-server

4.1pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         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>
    <groupId>com.example</groupId>
    <artifactId>resource-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>resource-server</name>
    <description>resource-server</description>

    <parent>
        <artifactId>Oauth</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.1</version>
                <configuration>
                    <mainClass>com.example.resource.ResourceServerApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

4.2controller

package com.example.resource.controller;

import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.types.ParameterStyle;
import org.apache.oltu.oauth2.rs.request.OAuthAccessResourceRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@RestController // 声明这是一个控制器类
@RequestMapping("/resourceServer") // 设置请求映射路径为 /resourceServer
@SuppressWarnings("all") // 忽略所有警告
public class UserController {

    @GetMapping("/userinfo") // 定义一个处理 GET 请求的方法,映射路径为 /userinfo
    public Object getUserInfo(HttpServletRequest request) throws OAuthProblemException, OAuthSystemException {
        // 创建一个 OAuthAccessResourceRequest 对象,用于处理 OAuth 认证请求
        OAuthAccessResourceRequest oAuthAccessResourceRequest = new OAuthAccessResourceRequest(request, ParameterStyle.HEADER);
        // 获取访问令牌
        String accessToken = oAuthAccessResourceRequest.getAccessToken();
        // 创建一个 Map 对象,用于存储用户信息
        Map<String, Object> map = new HashMap<>();
        // 向 Map 中添加用户信息
        map.put("name", "zhengsuanfeng");
        map.put("phone", "13576472774");
        map.put("addr", "长沙岳麓区");
        // 返回用户信息
        return map;
    }

}

5.整体项目的pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>Oauth</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>client</module>
        <module>resource-owner</module>
        <module>authorization-server</module>
        <module>resource-server</module>
    </modules>

    <properties>
        <oauth2.version>0.31</oauth2.version>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.4.1</spring-boot.version>
    </properties>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.oltu.oauth2</groupId>
                <artifactId>org.apache.oltu.oauth2.client</artifactId>
                <version>${oauth2.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.oltu.oauth2</groupId>
                <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
                <version>${oauth2.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.oltu.oauth2</groupId>
                <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
                <version>${oauth2.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

二.授权码模式的模拟代码流程讲解?

1.用户访问页面


2.访问的页面将请求重定向到认证服务器


3.认证服务器向用户展示授权页面,等待用户授权


4.用户授权完成

认证服务器带上client_id发送给应用服务器资源服务器?生成一个code

然后,用户拿到code

5.获取令牌(Token)

将code、client_id、client_secret传给认证服务器换取access_token


?

6.将access_token传给资源服务器


7.验证token,访问真正的资源页面

拿到数据

好啦,今天的分享就到这了,希望能够帮到你呢!😊😊????

文章来源:https://blog.csdn.net/m0_74315688/article/details/135218605
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。