【问题标题】:How to change User Status FORCE_CHANGE_PASSWORD?如何更改用户状态 FORCE_CHANGE_PASSWORD?
【发布时间】:2017-03-10 06:42:49
【问题描述】:

使用 AWS Cognito,我想创建虚拟用户以进行测试。

然后我使用 AWS 控制台 创建这样的用户,但用户的 status 设置为 FORCE_CHANGE_PASSWORD。使用该值,此用户无法通过身份验证。

有没有办法改变这个状态?

更新 从 CLI 创建用户时的行为相同

【问题讨论】:

  • @joe 提供的用户回答

标签: amazon-web-services aws-cli amazon-cognito


【解决方案1】:

这终于被添加到 AWSCLI:https://docs.aws.amazon.com/cli/latest/reference/cognito-idp/admin-set-user-password.html

您可以使用以下方法更改用户密码和更新状态:

aws cognito-idp admin-set-user-password \
  --user-pool-id <your-user-pool-id> \
  --username <username> \
  --password <password> \
  --permanent

在使用它之前,您可能需要使用以下方法更新您的 AWS CLI:

pip3 install awscli --upgrade

【讨论】:

  • 这是最新最有效的解决方案!
  • 这应该是答案。
  • 2020 年最佳和最简单的解决方案。谢谢!
  • 注意Permanent必须设置为True;否则用户仍将处于FORCE_CHANGE_PASSWORD 状态。
  • 该命令在第 1 行末尾缺少“\”:必须是 `aws cognito-idp admin-set-user-password \`
【解决方案2】:

我知道这已经有一段时间了,但我认为这可能会对看到这篇文章的其他人有所帮助。

您可以使用 AWS CLI 更改用户密码,但这是一个多步骤过程:


第 1 步:为所需用户获取会话令牌:

aws cognito-idp admin-initiate-auth --user-pool-id %USER POOL ID% --client-id %APP CLIENT ID% --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters USERNAME=%USERS USERNAME%,PASSWORD=%USERS CURRENT PASSWORD%

如果这返回有关 Unable to verify secret hash for client 的错误,创建另一个没有密码的应用客户端并使用该客户端 ID。

第 2 步: 如果第 1 步成功,它将以挑战 NEW_PASSWORD_REQUIRED、其他挑战参数和用户会话密钥进行响应。然后,您可以运行第二个命令来发出质询响应:

aws cognito-idp admin-respond-to-auth-challenge --user-pool-id %USER POOL ID% --client-id %CLIENT ID% --challenge-name NEW_PASSWORD_REQUIRED --challenge-responses NEW_PASSWORD=%DESIRED PASSWORD%,USERNAME=%USERS USERNAME% --session %SESSION KEY FROM PREVIOUS COMMAND with ""%

如果您收到有关 Invalid attributes given, XXX is missing 的错误消息,请使用 userAttributes.$FIELD_NAME=$VALUE 格式传递缺少的属性

上述命令应返回有效的身份验证结果和适当的令牌。


重要提示:为此,Cognito 用户池必须有一个配置了ADMIN_NO_SRP_AUTH 功能的应用客户端this doc 中的第 5 步)。

【讨论】:

  • 非常有帮助。另外两个提示:如果您收到有关“无法验证客户端的秘密哈希”的错误,请创建另一个没有秘密的应用程序客户端并使用它 (stackoverflow.com/questions/37438879/…)。如果您收到有关“给定属性无效,缺少 XXX”的错误消息,请使用 userAttributes.$FIELD_NAME=$VALUE (github.com/aws/aws-sdk-js/issues/1290) 格式传递缺少的属性。
  • 如果您无法让您的用户退出 FORCE_CHANGE_PASSWORD,请使用任何 CLI 命令(包括此答案)尝试 'admin-disable-user' 然后 'admin-enable-user' 或使用控制台。然后使用此过程,或者您可以使用正常的密码重置流程。有时,如果未在预定义的限制内登录 cognito,用户将“过期”。 (我认为默认 7 天)
  • 用 CLI 在 lambda 中尝试过,得到这个错误:给定的属性无效,名称丢失
  • @misher 由于必需的属性,您会得到它。您可以将它们包含在调用中,但语法有点奇怪:--challenge-responses NEW_PASSWORD=password,USERNAME=username,userAttributes.picture=picture,userAttributes.name=name
【解决方案3】:

只需在您的登录功能中的onSuccess: function (result) { ... }, 之后添加此代码即可。然后,您的用户将获得状态 CONFIRMED

newPasswordRequired: function(userAttributes, requiredAttributes) {
    // User was signed up by an admin and must provide new
    // password and required attributes, if any, to complete
    // authentication.

    // the api doesn't accept this field back
    delete userAttributes.email_verified;

    // unsure about this field, but I don't send this back
    delete userAttributes.phone_number_verified;

    // Get these details and call
    cognitoUser.completeNewPasswordChallenge(newPassword, userAttributes, this);
}

【讨论】:

  • 这对我有用。如果您不想更改,甚至可以传入当前密码。
  • 递归 FTW!谢谢! (递归是完整的新密码挑战中的this
【解决方案4】:

您可以通过在用户上调用 respondToAuthChallenge() 来更改该用户状态 FORCE_CHANGE_PASSWORD,如下所示:

var params = {
  ChallengeName: 'NEW_PASSWORD_REQUIRED', 
  ClientId: 'your_own3j6...0obh',
  ChallengeResponses: {
    USERNAME: 'user3',
    NEW_PASSWORD: 'changed12345'
  },
  Session: 'xxxxxxxxxxZDMcRu-5u...sCvrmZb6tHY'
};

cognitoidentityserviceprovider.respondToAuthChallenge(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
});

之后,您会在控制台中看到user3 的状态为CONFIRMED

【讨论】:

  • 我不明白你是怎么来的。你打电话给你 XXX 出去的 Session 是怎么回事?当我调用 adminInitiateAuth 时,我收到一条错误消息,提示 UserNotFoundException。
  • 对不起,如果这个答案不是很清楚。以下是更多细节: 1. 用户池有一个名为“your_own3j63rs8j16bxxxsto25db00obh”的客户端,它是在没有生成密钥的情况下创建的。如果为客户端分配了密钥,则上述代码将不起作用。 2) 会话键是调用cognitoidentityserviceprovider.adminInitiateAuth({ AuthFlow: 'ADMIN_NO_SRP_AUTH', ClientId: 'your_own3j63rs8j16bxxxsto25db00obh', UserPoolId: 'us-east-1_DtNSUVT7n', AuthParameters: { USERNAME: 'user3', PASSWORD: 'original_password' } }, callback);返回的值
  • 3) user3 是在控制台中创建的,最初的密码是 'original_password'
  • 好的。我现在明白为什么我会收到 UserNotFoundException。这是因为我使用别名作为登录用户名,这在 JS API 中运行良好,但显然不适用于 adminInitiateAuth。谢谢 Ariel Araza,感谢您的帮助。
  • 耶!我终于让这个工作了。谢谢!谢谢爱丽儿!
【解决方案5】:

抱歉,您遇到了困难。我们没有一个步骤,您可以直接创建用户并对其进行身份验证。我们将来可能会更改此设置,例如允许管理员设置用户可直接使用的密码。目前,当您使用AdminCreateUser 或通过应用程序注册用户来创建用户时,需要执行额外的步骤,或者强制用户在登录时更改密码,或者让用户验证电子邮件或电话号码以更改状态用户CONFIRMED

【讨论】:

  • 目前,当您使用 AdminCreateUser 或通过应用程序注册用户来创建用户时,需要执行额外步骤,强制用户在登录时更改密码或让用户验证电子邮件或电话号码将用户的状态更改为 CONFIRMED。这些额外的努力到底是什么,我如何从 JS SDK 触发它们。
  • @joe 指出它现在是可能的,因为它被添加了。寻找--permanent 标志:stackoverflow.com/a/56948249/3165552
【解决方案6】:

不确定您是否还在为此奋斗,但只是为了创建一堆测试用户,我使用了awscli

  1. 使用 cognito-idp 中的 sign-up 子命令创建用户
aws cognito-idp sign-up \
   --region %aws_project_region% \
   --client-id %aws_user_pools_web_client_id% \
   --username %email_address% \
   --password %password% \
   --user-attributes Name=email,Value=%email_address%
  1. 使用 admin-confirm-sign-up 确认用户
aws cognito-idp admin-confirm-sign-up \
--user-pool-id %aws_user_pools_web_client_id% \
--username %email_address%

【讨论】:

    【解决方案7】:

    更新:

    有一些更新,不再需要 Amplify 客户端。 在 adminCreateUser() 之后,您现在可以使用

    cisp.adminSetUserPassword({
      UserPoolId: pool_id,
      Username: login,
      Password: password,
      Permanent: true
    }) 
    
    

    [https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminSetUserPassword.html]

    这会将用户设置为“已确认”。

    更新:

    我现在在 NodeJS Lambda 中使用这个,翻译成放大:

    // enable node-fetch polyfill for Node.js
    global.fetch = require("node-fetch").default;
    global.navigator = {};
    
    const AWS = require("aws-sdk");
    const cisp = new AWS.CognitoIdentityServiceProvider();
    
    const Amplify = require("@aws-amplify/core").default;
    const Auth = require("@aws-amplify/auth").default;
    
    ...
    
    
    /*
      this_user: {
        given_name: string,
        password: string,
        email: string,
        cell: string
      }
    */
    const create_cognito = (this_user) => {
      let this_defaults = {
        password_temp: Math.random().toString(36).slice(-8),
        password: this_user.password,
        region: global._env === "prod" ? production_region : development_region,
        UserPoolId:
          global._env === "prod"
            ? production_user_pool
            : development_user_pool,
        ClientId:
          global._env === "prod"
            ? production_client_id
            : development_client_id,
        given_name: this_user.given_name,
        email: this_user.email,
        cell: this_user.cell,
      };
    
      // configure Amplify
      Amplify.configure({
        Auth: {
          region: this_defaults.region,
          userPoolId: this_defaults.UserPoolId,
          userPoolWebClientId: this_defaults.ClientId,
        },
      });
      if (!Auth.configure())
        return Promise.reject("could not configure amplify");
    
      return new Promise((resolve, reject) => {
        let _result = {};
    
        let this_account = undefined;
        let this_account_details = undefined;
    
        // create cognito account
        cisp
          .adminCreateUser({
            UserPoolId: this_defaults.UserPoolId,
            Username: this_defaults.given_name,
            DesiredDeliveryMediums: ["EMAIL"],
            ForceAliasCreation: false,
            MessageAction: "SUPPRESS",
            TemporaryPassword: this_defaults.password_temp,
            UserAttributes: [
              { Name: "given_name", Value: this_defaults.given_name },
              { Name: "email", Value: this_defaults.email },
              { Name: "phone_number", Value: this_defaults.cell },
              { Name: "email_verified", Value: "true" },
            ],
          })
          .promise()
          .then((user) => {
            console.warn(".. create_cognito: create..");
            _result.username = user.User.Username;
            _result.temporaryPassword = this_defaults.password_temp;
            _result.password = this_defaults.password;
    
            // sign into cognito account
            return Auth.signIn(_result.username, _result.temporaryPassword);
          })
          .then((user) => {
            console.warn(".. create_cognito: signin..");
    
            // complete challenge
            return Auth.completeNewPassword(user, _result.password, {
              email: this_defaults.email,
              phone_number: this_defaults.cell,
            });
          })
          .then((user) => {
            console.warn(".. create_cognito: confirmed..");
            this_account = user;
            // get details
            return Auth.currentAuthenticatedUser();
          })
          .then((this_details) => {
            if (!(this_details && this_details.attributes))
              throw "account creation failes";
    
            this_account_details = Object.assign({}, this_details.attributes);
    
            // signout
            return this_account.signOut();
          })
          .then(() => {
            console.warn(".. create_cognito: complete");
            resolve(this_account_details);
          })
          .catch((err) => {
            console.error(".. create_cognito: error");
            console.error(err);
            reject(err);
          });
      });
    };
    
    

    我正在设置一个临时密码,然后将其重置为用户请求的密码。

    旧帖:

    您可以使用 amazon-cognito-identity-js SDK 解决此问题,方法是在使用 cognitoidentityserviceprovider.adminCreateUser() 创建帐户后使用临时密码进行身份验证,并在 cognitoUser.authenticateUser( ,{newPasswordRequired}) 内运行 cognitoUser.completeNewPasswordChallenge() - 所有这些都在创建用户的函数中.

    我在 AWS lambda 中使用以下代码来创建启用的 Cognito 用户帐户。我相信它可以优化,请耐心等待。这是我的第一篇文章,我对 JavaScript 还是很陌生。

    var AWS = require("aws-sdk");
    var AWSCognito = require("amazon-cognito-identity-js");
    
    var params = {
        UserPoolId: your_poolId,
        Username: your_username,
        DesiredDeliveryMediums: ["EMAIL"],
        ForceAliasCreation: false,
        MessageAction: "SUPPRESS",
        TemporaryPassword: your_temporaryPassword,
        UserAttributes: [
            { Name: "given_name", Value: your_given_name },
            { Name: "email", Value: your_email },
            { Name: "phone_number", Value: your_phone_number },
            { Name: "email_verified", Value: "true" }
        ]
    };
    
    var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
    let promise = new Promise((resolve, reject) => {
        cognitoidentityserviceprovider.adminCreateUser(params, function(err, data) {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
    
    promise
        .then(data => {
            // login as new user and completeNewPasswordChallenge
            var anotherPromise = new Promise((resolve, reject) => {
                var authenticationDetails = new AWSCognito.AuthenticationDetails({
                    Username: your_username,
                    Password: your_temporaryPassword
                });
                var poolData = {
                    UserPoolId: your_poolId,
                    ClientId: your_clientId
                };
                var userPool = new AWSCognito.CognitoUserPool(poolData);
                var userData = {
                    Username: your_username,
                    Pool: userPool
                };
    
                var cognitoUser = new AWSCognito.CognitoUser(userData);
                let finalPromise = new Promise((resolve, reject) => {
                    cognitoUser.authenticateUser(authenticationDetails, {
                        onSuccess: function(authResult) {
                            cognitoUser.getSession(function(err) {
                                if (err) {
                                } else {
                                    cognitoUser.getUserAttributes(function(
                                        err,
                                        attResult
                                    ) {
                                        if (err) {
                                        } else {
                                            resolve(authResult);
                                        }
                                    });
                                }
                            });
                        },
                        onFailure: function(err) {
                            reject(err);
                        },
                        newPasswordRequired(userAttributes, []) {
                            delete userAttributes.email_verified;
                            cognitoUser.completeNewPasswordChallenge(
                                your_newPoassword,
                                userAttributes,
                                this
                            );
                        }
                    });
                });
    
                finalPromise
                    .then(finalResult => {
                        // signout
                        cognitoUser.signOut();
                        // further action, e.g. email to new user
                        resolve(finalResult);
                    })
                    .catch(err => {
                        reject(err);
                    });
            });
            return anotherPromise;
        })
        .then(() => {
            resolve(finalResult);
        })
        .catch(err => {
            reject({ statusCode: 406, error: err });
        });
    

    【讨论】:

    • @Tom - 它对你有用吗?有什么我可以澄清的吗?
    • 现在好多了。
    • @qqan.ny 同时使用承诺和回调?为什么?
    • @Iurii Golskyi - 那时我不知道更好,刚开始从头开始学习 AWS 和 JS。
    【解决方案8】:

    如果您尝试从控制台更改管理员身份。然后在创建用户后按照以下步骤操作。

    1. 在 Cognito 中转到 -> “管理用户池” ->
    2. 转到“应用集成”部分下的“应用客户端设置”。
    3. 检查以下项目 i) Cognito 用户池 ii) 授权码授予 iii) 隐式授予 iv) 电话 v) 电子邮件 vi) openid vii) aws.cognito.signin.user.admin viiii) 个人资料
    4. 输入应用程序的回调 url。如果您不确定输入例如:https://google.com 稍后您可以将其更改为您的实际回调 url
    5. 点击保存更改。
    6. 保存更改后,单击“启动托管 UI”链接
    7. 输入新创建用户的凭据
    8. 使用新凭据重置密码并将其共享给用户

    step 2

    step 3 4 5 6

    step 7

    step 8

    【讨论】:

      【解决方案9】:

      对于 Java SDK,假设您的 Cognito 客户端已设置并且您的用户处于 FORCE_CHANGE_PASSWORD 状态,您可以执行以下操作以确认您的用户...然后正常进行身份验证。

      AdminCreateUserResult createUserResult = COGNITO_CLIENT.adminCreateUser(createUserRequest());
      
      AdminInitiateAuthResult authResult = COGNITO_CLIENT.adminInitiateAuth(authUserRequest());
      
      
      Map<String,String> challengeResponses = new HashMap<>();
      challengeResponses.put("USERNAME", USERNAME);
      challengeResponses.put("NEW_PASSWORD", PASSWORD);
      RespondToAuthChallengeRequest respondToAuthChallengeRequest = new RespondToAuthChallengeRequest()
            .withChallengeName("NEW_PASSWORD_REQUIRED")
            .withClientId(CLIENT_ID)
            .withChallengeResponses(challengeResponses)
            .withSession(authResult.getSession());
      
      COGNITO_CLIENT.respondToAuthChallenge(respondToAuthChallengeRequest);
      

      希望它对那些集成测试有所帮助(抱歉格式化)

      【讨论】:

        【解决方案10】:

        基本上这是相同的答案,但适用于 .Net C# SDK:

        以下将使用所需的用户名和密码创建完整的管理员用户。 具有以下用户模型:

        public class User
        {
            public string Username { get; set; }
            public string Password { get; set; }
        }
        

        您可以使用以下方法创建用户并准备好使用:

           public void AddUser(User user)
            {
                var tempPassword = "ANY";
                var request = new AdminCreateUserRequest()
                {
                    Username = user.Username,
                    UserPoolId = "MyuserPoolId",
                    TemporaryPassword = tempPassword
                };
                var result = _cognitoClient.AdminCreateUserAsync(request).Result;
                var authResponse = _cognitoClient.AdminInitiateAuthAsync(new AdminInitiateAuthRequest()
                {
                    UserPoolId = "MyuserPoolId",
                    ClientId = "MyClientId",
                    AuthFlow = AuthFlowType.ADMIN_NO_SRP_AUTH,
                    AuthParameters = new Dictionary<string, string>()
                    {
                        {"USERNAME",user.Username },
                        {"PASSWORD", tempPassword}
                    }
                }).Result;
                _cognitoClient.RespondToAuthChallengeAsync(new RespondToAuthChallengeRequest()
                {
                 ClientId = "MyClientId",
                    ChallengeName = ChallengeNameType.NEW_PASSWORD_REQUIRED,
                    ChallengeResponses = new Dictionary<string, string>()
                    {
                        {"USERNAME",user.Username },
                        {"NEW_PASSWORD",user.Password }
                    },
                    Session = authResponse.Session
                });
            }
        

        【讨论】:

          【解决方案11】:

          好的。我终于有了管理员可以创建新用户的代码。流程是这样的:

          1. 管理员创建用户
          2. 用户收到一封包含临时密码的电子邮件
          3. 用户登录并被要求更改密码

          第 1 步是最难的部分。这是我在 Node JS 中创建用户的代码:

          let params = {
            UserPoolId: "@cognito_pool_id@",
            Username: username,
            DesiredDeliveryMediums: ["EMAIL"],
            ForceAliasCreation: false,
            UserAttributes: [
              { Name: "given_name", Value: firstName },
              { Name: "family_name", Value: lastName},
              { Name: "name", Value: firstName + " " + lastName},
              { Name: "email", Value: email},
              { Name: "custom:title", Value: title},
              { Name: "custom:company", Value: company + ""}
            ],
          };
          let cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider();
          cognitoIdentityServiceProvider.adminCreateUser(params, function(error, data) {
            if (error) {
              console.log("Error adding user to cognito: " + error, error.stack);
              reject(error);
            } else {
              // Uncomment for interesting but verbose logging...
              //console.log("Received back from cognito: " + CommonUtils.stringify(data));
              cognitoIdentityServiceProvider.adminUpdateUserAttributes({
                UserAttributes: [{
                  Name: "email_verified",
                  Value: "true"
                }],
                UserPoolId: "@cognito_pool_id@",
                Username: username
              }, function(err) {
                if (err) {
                  console.log(err, err.stack);
                } else {
                  console.log("Success!");
                  resolve(data);
                }
              });
            }
          });
          

          基本上,您需要发送第二个命令来强制将电子邮件视为已验证。用户仍然需要访问他们的电子邮件以获取临时密码(这也验证了电子邮件)。但是,如果没有第二次将电子邮件设置为已验证的电话,您将无法收到正确的电话来重置他们的密码。

          【讨论】:

          • 我还是没有收到邮件,有什么建议吗?
          • 奇怪,您是否在 Cognito 中正确设置了电子邮件地址,可以访问 SES 等? Cogntio 不会只向人们发送电子邮件,直到您验证了您尝试发送的电子邮件地址或您被批准发送给任何人。
          • 我收到了确认码邮件,所以设置是正确的。只是带有临时密码的电子邮件永远不会出现......我最终创建了一个附加到预注册触发器的 lambda 来发送电子邮件。
          【解决方案12】:

          我知道这是相同的答案,但认为它可能对 Go 开发者社区有所帮助。基本上它是发起身份验证请求,获取会话并响应挑战NEW_PASSWORD_REQUIRED

          func sessionWithDefaultRegion(region string) *session.Session {
              sess := Session.Copy()
              if v := aws.StringValue(sess.Config.Region); len(v) == 0 {
                  sess.Config.Region = aws.String(region)
              }
          
              return sess
          }
          
          
          
          func (c *CognitoAppClient) ChangePassword(userName, currentPassword, newPassword string)   error {
          
              sess := sessionWithDefaultRegion(c.Region)
              svc := cognitoidentityprovider.New(sess)
          
              auth, err := svc.AdminInitiateAuth(&cognitoidentityprovider.AdminInitiateAuthInput{
                  UserPoolId:aws.String(c.UserPoolID),
                  ClientId:aws.String(c.ClientID),
                  AuthFlow:aws.String("ADMIN_NO_SRP_AUTH"),
                  AuthParameters: map[string]*string{
                      "USERNAME": aws.String(userName),
                      "PASSWORD": aws.String(currentPassword),
                  },
          
              })
          
          
          
              if err != nil {
                  return err
              }
          
              request := &cognitoidentityprovider.AdminRespondToAuthChallengeInput{
                  ChallengeName: aws.String("NEW_PASSWORD_REQUIRED"),
                  ClientId:aws.String(c.ClientID),
                  UserPoolId: aws.String(c.UserPoolID),
                  ChallengeResponses:map[string]*string{
                      "USERNAME":aws.String(userName),
                      "NEW_PASSWORD": aws.String(newPassword),
                  },
                  Session:auth.Session,
              }
          
          
              _, err = svc.AdminRespondToAuthChallenge(request)
          
              return err 
          }
          

          这是一个单元测试:

          import (
              "fmt"
              "github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
              . "github.com/smartystreets/goconvey/convey"
              "testing"
          )
          
          
          func TestCognitoAppClient_ChangePassword(t *testing.T) {
          
          
              Convey("Testing ChangePassword!", t, func() {
                  err := client.ChangePassword("user_name_here", "current_pass", "new_pass")
          
          
          
                  Convey("Testing ChangePassword Results!", func() {
                      So(err, ShouldBeNil)
          
                  })
          
              })
          }
          

          【讨论】:

            【解决方案13】:

            我曾多次遇到同样的情况。因此,在 golang 中编写了小型 CLI,以完全以用户身份进行身份验证(用于进一步测试目的),或者只是以管理方式重置通行证。

            那么你作为命令运行的就是

            $ > go-cognito-authy --profile cloudy -region eu-central-1 admin reset-pass --username rafpe --pass-new 'Password.0ne2!' --clientID 2jxxxiuui123 --userPoolID  eu-central-1_CWNnTiR0j --session "bCqSkLeoJR_ys...."
            

            解决方案在github上https://github.com/RafPe/go-cognito-authy/tree/master

            【讨论】:

              猜你喜欢
              • 2017-08-03
              • 2020-06-17
              • 2017-07-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多