📜  在 Node.js 中解释护照

📅  最后修改于: 2022-05-13 01:56:53.116000             🧑  作者: Mango

在 Node.js 中解释护照

Passport 非常容易集成 NodeJs 包,用于向我们的网站或 Web 应用程序添加身份验证功能。

为了展示 Passport 在 Nodejs 中的使用,我们从创建一个非常简单的 Node 应用程序开始。

使用 express 创建一个简单的 Node 应用程序:

步骤1:创建一个新文件夹(我将文件夹命名为“NODEAPP”),并在其中创建一个名为“Server.js”的新文件。

第 2 步:使用命令“npm init -y”初始化 npm。 Package.json 文件将被添加到您的项目文件夹中。

用于初始化 npm 的命令

运行命令“npm init -y”后的项目结构

第三步:安装所有必要的包,即expressbody-parser (稍后将用于获取 HTML 表单数据)、 mongoose (用于连接到我们的 MongoDB 数据库),使用命令:

执行上述命令以安装所需的软件包。

1 个名为“ package-lock.json ”的文件将添加到您的项目结构中,1 个名为“ node_modules ”的文件夹将在执行上述命令后添加到您的项目结构中。

项目结构:最终的项目结构应如下所示:

最终项目结构

第 4 步:将基本代码添加到我们的Server.js文件中。

Server.js
// Telling node to include the following
// external modules
var express = require('express');
var app = express();
  
// Mongoose for connecting to our database
const mongoose = require("mongoose");
  
// Body parser to fetch HTML form data later on
const bodyParser = require("body-parser");
  
// Connecting mongoose to our database
// named "userDatabase"
mongoose.connect(
'mongodb://localhost:27017/userDatabase', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});
  
// Handling get request on home route.
app.get("/", function (req, res) {
    res.send("This is the home route");
});
  
// Allowing app to listen on port 3000
app.listen(3000, function () {
    console.log("server started successfully");
})


Server.js
/*We are going to add simple authentication to our website
We are going to collect data(email and password) entered by
user in the HTML form, created in the INDEX.HTML file, and
we are going to store that data in our database
this is how we can simply register any new user */
  
/* if we want to log in our already registered user, 
then we collect email and password from HTML 
form created in LOGIN.HTML file, and
we can find data(if any) associated with this 
email, and return it to user */
  
var express = require('express');
var app = express();
const bodyParser = require("body-parser");
  
// Allowing app to use body parser
app.use(bodyParser.urlencoded({extended:true}));
  
// Connecting mongoose to our database
// named "userDatabase"
mongoose.connect(
'mongodb://localhost:27017/userDatabase' { 
  useNewUrlParser: true,
  useUnifiedTopology: true
});
  
const userSchema = new mongoose.Schema({
  email: String,
  password: String
});
  
// Creating the User model.
const User = new mongoose.model("User", userSchema);
  
  
/* setting a simple get request on the home route, 
and sending our index.html file containing a form 
which will allow user to enter his details and 
register. */
app.get("/", function (req, res) {
  res.sendFile(__dirname + "/index.html");
})
app.get("/login", function(req, res) {
  res.sendFile(__dirname + "/login.html");
})
  
// Handling the post request on /register route.
app.post("/register", function(req, res){
  console.log(req.body);
      
  // Getting the email and password entered
  // by the user
  var email = req.body.username;
  var password = req.body.password;
    
  // Creating a new user with entered credentials.
  var newuser = new User({
    email : email,
    password : password
  })
    
  // Saving the newuser.
  newuser.save();
  console.log("saved successfully");
    
  // Sending the response that user
  // is saved successfully
  res.send("saved successfully");
})
  
APP.post("/login", function(req, res) {
    console.log(req.body);
       
    // Getting the email and password entered
    // by the user
    var emailEntered = req.body.username;
    var passwordEntered = req.body.password;
      
    // Checking if the email entered exists
    // in database or not.
    User.findOne({email : emailEntered}, 
                 function(err, data){
       if(data) {
  
           // The email exists in the database.
           console.log(data);
              
           /* checking if the password entered 
           is matching the original password */
           if(data.password == passwordEntered){
               res.send("login successful!");
           }
           else {
  
               // Password is incorrect.
               res.send("Incorrect Password");
            }
       }
       else {
  
           // The email does not exist in the database
           console.log(err);
       }
    });
})
  
// Allowing app to listen on port 3000
app.listen(3000, function () {
  console.log("server started successfully");
})


Index.html


  

    Page Title

  

    
                               
  


Login.html


  

    
    

  

    
                               
  


Server.js
const session = require("express-session");
const passport = require("passport");
const passportLocalMongoose = require("passport-local-mongoose");
  
app.use(express.static("public"));
app.use(bodyParser.urlencoded({extended:true}));
  
// Below all the app.use methods
app.use(session({
    secret : "any long secret key",
    resave : false,
    saveUninitialized : false
}));


Server.js
app.use(session({
  secret: "any long secret key",
  resave: false,
  saveUninitialized: false
}));
  
// Initializing Passport
app.use(passport.initialize());
  
// Starting the session
app.use(passport.session());
  
// Creating user schema and adding a plugin to it
  
const userSchema = new mongoose.Schema({
  email: String,
  password: String
});
userSchema.plugin(passportLocalMongoose);


Server.js
const User = new mongoose.model("User", userSchema);
passport.use(User.createStrategy());
  
// Serializing and deserializing
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());


Server.js
// Handling get request on the home and login route
app.get("/", function (req, res) {
  
  /* req.isAuthentcated() returns true or 
  false depending upon whether a session 
  is already running or not.*/
  if(req.isAuthenticated()) {
   
    /* if the request is already authenticated, 
    i.e. the user has already logged in and 
    there is no need to login again. Or we 
    can say, the session is running. */  
    res.send("
     You have already logged in. No need to login again");
  }
    
  else{
  
    // If the user is new and no session
    // is Running already 
    res.sendFile(__dirname + "/index.html");
  }
})
  
// Handling get request on login route
app.get("/login", function(req, res) {
    if(req.isAuthenticated()){
        /* if request is already authenticated, 
        i.e. user has already logged in and 
        there is no need to login again. */ 
        res.send("
You have already logged in. No need to login again");
     }
     else{
       res.sendFile(__dirname + "/login.html");
   }
})


Server.js
/* The index.html file will be same as that
used in the earlier method of authentication*/ 
app.post("/register", function(req, res){
  console.log(req.body);
    
  // Getting Email and PAssword Entered by user
  var email = req.body.username;
  var password = req.body.password;
    
  /* Registering the user with email and
  password in our database  
  and the model used is "User" */
  User.register({ username : email }, 
  req.body.password, function (err, user) {      
    if (err) {
      
      // if some error is occurring, log that error
      console.log(err);
    }
    else {
      passport.authenticate("local")
      (req, res, function() {
        res.send("successfully saved!"); 
      })
    }
  })
})


Server.js
// All handling related to login is done below.
// Here we are handling the post request on
// /login route
app.post("/login", function (req, res) {
  console.log(req.body);
  
  const userToBeChecked = new User({
    username: req.body.username,
    password: req.body.password,
  });
  
  // Checking if user if correct or not
  req.login(userToBeChecked, function (err) {
    if (err) {
  
      console.log(err);
        
      // If authentication fails, then coming
      // back to login.html page
      res.redirect("/login");
    } else {
      passport.authenticate("local")(
        req, res, function () {
        User.find({ email: req.user.username }, 
          function (err, docs) {
          if (err) {
            console.log(err);
          } else {
            //login is successful
            console.log("credentials are correct");
            res.send("login successful");
          }
        });
      });
    }
  });
});


Index.html


  

    Page Title

  

    

REGISTER

    
                               
  


Login.html


  

    
    

  

    

LOGIN

    
                               
  


Server.js
var express = require('express');
var app = express();
const mongoose = require("mongoose");
   
/* Requiring body-parser package  
to fetch the data that is entered 
by the user in the HTML form.*/
const bodyParser = require("body-parser");
   
// Telling our Node app to include all these modules
const session = require("express-session");
const passport = require("passport");
const passportLocalMongoose = 
       require("passport-local-mongoose");
   
// Allowing app to use body parser
app.use(bodyParser.urlencoded({ extended: true }));
   
app.use(session({
    secret: "long secret key",
    resave: false,
    saveUninitialized: false
}));
   
// Initializing Passport
app.use(passport.initialize());
  
// Starting the session
app.use(passport.session());
   
// Connecting mongoose to our database 
mongoose.connect(
'mongodb://localhost:27017/userDatabase', {
    useNewUrlParser: true,
    useUnifiedTopology: true
}); 
   
/* Creating the schema of user which now 
include only email and password for 
simplicity.*/
const userSchema = new mongoose.Schema({
    email: String,
    password: String
});
   
/* Just after the creation of userSchema, 
we add passportLocalMongoose plugin 
to our Schema */
userSchema.plugin(passportLocalMongoose);
   
// Creating the User model
const User = new mongoose.model("User", userSchema);
   
/* After the creation of mongoose model, 
we have to write the following code */
passport.use(User.createStrategy());
   
// Serializing and deserializing
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
   
// Handling get request on the home route
app.get("/", function (req, res) {
  
    /* req.isAuthentcated() returns true or 
    false depending upon whether a session is 
    already running or not. */
    if (req.isAuthenticated()) {
   
        /* if request is already authenticated, 
        i.e. user has already logged in and 
        there is no need to login again or 
        we can say, session is running.*/
        res.send(
"You have already logged in. No need to login again");
    }
   
    else {
  
        // If the user is new and 
        // no session is Running already
        res.sendFile(__dirname + "/index.html");
    }
})
   
   
// Handling get request on login route
app.get("/login", function (req, res) {
    if (req.isAuthenticated()) {
  
        /* If request is already authenticated, 
        i.e. user has already logged in and 
        there is no need to login again. */
        res.send(
"You have already logged in. No need to login again");
    }
    else {
   
        /* if session has expired, then user 
         need to login back again and 
         we will send the Login.html */
        res.sendFile(__dirname + "/login.html");
    }
})
   
/* Registering the user for the first time
handling the post request on /register route.*/
app.post("/register", function (req, res) {
    console.log(req.body);
    var email = req.body.username;
    var password = req.body.password;
    User.register({ username: email },
        req.body.password, function (err, user) {
            if (err) {
                console.log(err);
            }
            else {
                passport.authenticate("local")
                 (req, res, function () {
                    res.send("successfully saved!");
                })
            }
        })
})
   
// Handling the post request on /login route
app.post("/login", function (req, res) {
    console.log(req.body);
  
    const userToBeChecked = new User({
        username: req.body.username,
        password: req.body.password
    });
   
    // Checking if user if correct or not
    req.login(userToBeChecked, function (err) {
        if (err) {
            console.log(err);
            res.redirect("/login");
        }
        else {
            passport.authenticate("local")
                (req, res,function () {
                User.find({ email: req.user.username },
                    function (err, docs) {
                      if (err) {
                         console.log(err);
                      }
                     else {
  
                       //login is successful
                       console.log("credentials are correct");
                     res.send("login successful");
                        }
                    });
            });
        }
    })
})
   
// Allowing app to listen on port 3000
app.listen(3000, function () {
    console.log("server started successfully");
})


第 5 步:您也可以通过打开浏览器并输入http://localhost:3000来检查。您应该会看到一个带有以下响应的白页。

在浏览器中键入“http://localhost:3000”后应该得到的响应。

创建基本的 Node 应用程序并检查到目前为止是否一切正常。

第 6 步:向我们的网站添加身份验证。向我们的网站添加身份验证功能的一种简单方法是获取用户输入的电子邮件和密码,并将其直接保存在数据库中。同样,当用户想登录时,询问他/她的邮箱和密码,如果有任何记录与输入的邮箱和密码匹配,则该用户是真实的并且登录成功。

服务器.js

/*We are going to add simple authentication to our website
We are going to collect data(email and password) entered by
user in the HTML form, created in the INDEX.HTML file, and
we are going to store that data in our database
this is how we can simply register any new user */
  
/* if we want to log in our already registered user, 
then we collect email and password from HTML 
form created in LOGIN.HTML file, and
we can find data(if any) associated with this 
email, and return it to user */
  
var express = require('express');
var app = express();
const bodyParser = require("body-parser");
  
// Allowing app to use body parser
app.use(bodyParser.urlencoded({extended:true}));
  
// Connecting mongoose to our database
// named "userDatabase"
mongoose.connect(
'mongodb://localhost:27017/userDatabase' { 
  useNewUrlParser: true,
  useUnifiedTopology: true
});
  
const userSchema = new mongoose.Schema({
  email: String,
  password: String
});
  
// Creating the User model.
const User = new mongoose.model("User", userSchema);
  
  
/* setting a simple get request on the home route, 
and sending our index.html file containing a form 
which will allow user to enter his details and 
register. */
app.get("/", function (req, res) {
  res.sendFile(__dirname + "/index.html");
})
app.get("/login", function(req, res) {
  res.sendFile(__dirname + "/login.html");
})
  
// Handling the post request on /register route.
app.post("/register", function(req, res){
  console.log(req.body);
      
  // Getting the email and password entered
  // by the user
  var email = req.body.username;
  var password = req.body.password;
    
  // Creating a new user with entered credentials.
  var newuser = new User({
    email : email,
    password : password
  })
    
  // Saving the newuser.
  newuser.save();
  console.log("saved successfully");
    
  // Sending the response that user
  // is saved successfully
  res.send("saved successfully");
})
  
APP.post("/login", function(req, res) {
    console.log(req.body);
       
    // Getting the email and password entered
    // by the user
    var emailEntered = req.body.username;
    var passwordEntered = req.body.password;
      
    // Checking if the email entered exists
    // in database or not.
    User.findOne({email : emailEntered}, 
                 function(err, data){
       if(data) {
  
           // The email exists in the database.
           console.log(data);
              
           /* checking if the password entered 
           is matching the original password */
           if(data.password == passwordEntered){
               res.send("login successful!");
           }
           else {
  
               // Password is incorrect.
               res.send("Incorrect Password");
            }
       }
       else {
  
           // The email does not exist in the database
           console.log(err);
       }
    });
})
  
// Allowing app to listen on port 3000
app.listen(3000, function () {
  console.log("server started successfully");
})

索引.html



  

    Page Title

  

    
                               
  

登录.html



  

    
    

  

    
                               
  

但是使用这种简单的身份验证方法有一些限制

  • 用户在注册过程中输入的密码对数据库中的每个人都是公开的,即组织中任何有权访问数据库的人都可以看到任何用户的密码。但是,密码不能暴露,我们需要强大的加密,以便我们可以将密码安全地存储在数据库中。

密码会向所有正在查看数据库的人公开。

  • 每次(可能每天 20-25 次甚至更多)我们想要使用这个网络应用程序或网站,我们都必须重新输入我们的电子邮件和密码,这变得非常耗时。
  • 我们无法使用这个简单的验证码添加社交网络登录功能。

Passport 为我们消除了所有这些限制。如果我们使用 Passport,那么:

  • 我们不必暴露我们的密码。整个加密和解密过程由 Passport 完成,包括密码的散列和加密。
  • Passport 允许我们创建和维护会话。例如,当您访问任何社交媒体网站或移动应用程序时,您无需在每次想要使用 Instagram 或 Facebook 时一次又一次地登录。相反,信息被保存,这意味着您不必在每次想要使用该网站时都登录用技术术语来说,会话已经创建,它将在接下来的几天、几周或几个月内保持。
  • Passport 还允许我们使用 Google、Facebook、LinkedIn 和各种其他社交网络服务轻松集成身份验证。

第七步:为了使用passport,我们需要安装4个npm包,分别是“ passport ”、“ passport-local ”、“ passport-local-mongoose ”和“ express-session ”(确保你下载了“express-session”而不是“快速会话”)。

在命令行中,编写以下命令来安装所有四个包:

以下命令将安装所需的软件包。

安装后,您需要在Server.js文件的顶部添加以下代码以包含护照模块。

服务器.js

const session = require("express-session");
const passport = require("passport");
const passportLocalMongoose = require("passport-local-mongoose");
  
app.use(express.static("public"));
app.use(bodyParser.urlencoded({extended:true}));
  
// Below all the app.use methods
app.use(session({
    secret : "any long secret key",
    resave : false,
    saveUninitialized : false
}));

第 8 步:初始化 Passport 并启动 Session。要初始化 Passport 并启动会话,请在会话声明代码下方编写以下代码。

服务器.js

app.use(session({
  secret: "any long secret key",
  resave: false,
  saveUninitialized: false
}));
  
// Initializing Passport
app.use(passport.initialize());
  
// Starting the session
app.use(passport.session());
  
// Creating user schema and adding a plugin to it
  
const userSchema = new mongoose.Schema({
  email: String,
  password: String
});
userSchema.plugin(passportLocalMongoose);

第 9 步:现在,我们希望即使关闭浏览器窗口也能保持登录状态一段时间,即我们希望建立会话。会话使用Cookie来存储数据和消息,并允许服务器向用户提供正确的会话数据。创建 Cookie 并将消息存储到其中的过程: 破碎 Cookie 并从 Cookie 中提取消息的序列化过程,以便向用户提供正确的数据:

服务器.js

const User = new mongoose.model("User", userSchema);
passport.use(User.createStrategy());
  
// Serializing and deserializing
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

第 10 步:现在,我们都准备好为我们的网站添加身份验证了。 我们已经安装了所需的包,配置了会话,初始化了 Passport 和会话,并告诉了 Passport 使用和管理 cookie。

处理获取请求:

服务器.js

// Handling get request on the home and login route
app.get("/", function (req, res) {
  
  /* req.isAuthentcated() returns true or 
  false depending upon whether a session 
  is already running or not.*/
  if(req.isAuthenticated()) {
   
    /* if the request is already authenticated, 
    i.e. the user has already logged in and 
    there is no need to login again. Or we 
    can say, the session is running. */  
    res.send("
     You have already logged in. No need to login again");
  }
    
  else{
  
    // If the user is new and no session
    // is Running already 
    res.sendFile(__dirname + "/index.html");
  }
})
  
// Handling get request on login route
app.get("/login", function(req, res) {
    if(req.isAuthenticated()){
        /* if request is already authenticated, 
        i.e. user has already logged in and 
        there is no need to login again. */ 
        res.send("
You have already logged in. No need to login again");
     }
     else{
       res.sendFile(__dirname + "/login.html");
   }
})

第 11 步:现在,在注册路线中,我们必须添加简单的代码,以便我们注册任何新用户。

服务器.js

/* The index.html file will be same as that
used in the earlier method of authentication*/ 
app.post("/register", function(req, res){
  console.log(req.body);
    
  // Getting Email and PAssword Entered by user
  var email = req.body.username;
  var password = req.body.password;
    
  /* Registering the user with email and
  password in our database  
  and the model used is "User" */
  User.register({ username : email }, 
  req.body.password, function (err, user) {      
    if (err) {
      
      // if some error is occurring, log that error
      console.log(err);
    }
    else {
      passport.authenticate("local")
      (req, res, function() {
        res.send("successfully saved!"); 
      })
    }
  })
})

类似的代码是处理登录请求。以下是处理 /login 路由上的 post 请求的代码。(login.html 文件将与之前的身份验证方法中使用的相同)

服务器.js

// All handling related to login is done below.
// Here we are handling the post request on
// /login route
app.post("/login", function (req, res) {
  console.log(req.body);
  
  const userToBeChecked = new User({
    username: req.body.username,
    password: req.body.password,
  });
  
  // Checking if user if correct or not
  req.login(userToBeChecked, function (err) {
    if (err) {
  
      console.log(err);
        
      // If authentication fails, then coming
      // back to login.html page
      res.redirect("/login");
    } else {
      passport.authenticate("local")(
        req, res, function () {
        User.find({ email: req.user.username }, 
          function (err, docs) {
          if (err) {
            console.log(err);
          } else {
            //login is successful
            console.log("credentials are correct");
            res.send("login successful");
          }
        });
      });
    }
  });
});

您只需要安装所有软件包,编写以下代码,启动服务器(使用“node server.js/node app.js”命令),您就可以使用 Passport 验证您的用户了。

索引.html



  

    Page Title

  

    

REGISTER

    
                               
  

登录.html



  

    
    

  

    

LOGIN

    
                               
  

服务器.js

var express = require('express');
var app = express();
const mongoose = require("mongoose");
   
/* Requiring body-parser package  
to fetch the data that is entered 
by the user in the HTML form.*/
const bodyParser = require("body-parser");
   
// Telling our Node app to include all these modules
const session = require("express-session");
const passport = require("passport");
const passportLocalMongoose = 
       require("passport-local-mongoose");
   
// Allowing app to use body parser
app.use(bodyParser.urlencoded({ extended: true }));
   
app.use(session({
    secret: "long secret key",
    resave: false,
    saveUninitialized: false
}));
   
// Initializing Passport
app.use(passport.initialize());
  
// Starting the session
app.use(passport.session());
   
// Connecting mongoose to our database 
mongoose.connect(
'mongodb://localhost:27017/userDatabase', {
    useNewUrlParser: true,
    useUnifiedTopology: true
}); 
   
/* Creating the schema of user which now 
include only email and password for 
simplicity.*/
const userSchema = new mongoose.Schema({
    email: String,
    password: String
});
   
/* Just after the creation of userSchema, 
we add passportLocalMongoose plugin 
to our Schema */
userSchema.plugin(passportLocalMongoose);
   
// Creating the User model
const User = new mongoose.model("User", userSchema);
   
/* After the creation of mongoose model, 
we have to write the following code */
passport.use(User.createStrategy());
   
// Serializing and deserializing
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
   
// Handling get request on the home route
app.get("/", function (req, res) {
  
    /* req.isAuthentcated() returns true or 
    false depending upon whether a session is 
    already running or not. */
    if (req.isAuthenticated()) {
   
        /* if request is already authenticated, 
        i.e. user has already logged in and 
        there is no need to login again or 
        we can say, session is running.*/
        res.send(
"You have already logged in. No need to login again");
    }
   
    else {
  
        // If the user is new and 
        // no session is Running already
        res.sendFile(__dirname + "/index.html");
    }
})
   
   
// Handling get request on login route
app.get("/login", function (req, res) {
    if (req.isAuthenticated()) {
  
        /* If request is already authenticated, 
        i.e. user has already logged in and 
        there is no need to login again. */
        res.send(
"You have already logged in. No need to login again");
    }
    else {
   
        /* if session has expired, then user 
         need to login back again and 
         we will send the Login.html */
        res.sendFile(__dirname + "/login.html");
    }
})
   
/* Registering the user for the first time
handling the post request on /register route.*/
app.post("/register", function (req, res) {
    console.log(req.body);
    var email = req.body.username;
    var password = req.body.password;
    User.register({ username: email },
        req.body.password, function (err, user) {
            if (err) {
                console.log(err);
            }
            else {
                passport.authenticate("local")
                 (req, res, function () {
                    res.send("successfully saved!");
                })
            }
        })
})
   
// Handling the post request on /login route
app.post("/login", function (req, res) {
    console.log(req.body);
  
    const userToBeChecked = new User({
        username: req.body.username,
        password: req.body.password
    });
   
    // Checking if user if correct or not
    req.login(userToBeChecked, function (err) {
        if (err) {
            console.log(err);
            res.redirect("/login");
        }
        else {
            passport.authenticate("local")
                (req, res,function () {
                User.find({ email: req.user.username },
                    function (err, docs) {
                      if (err) {
                         console.log(err);
                      }
                     else {
  
                       //login is successful
                       console.log("credentials are correct");
                     res.send("login successful");
                        }
                    });
            });
        }
    })
})
   
// Allowing app to listen on port 3000
app.listen(3000, function () {
    console.log("server started successfully");
})

使用此代码并使用 Passport 添加身份验证,我们消除了三个主要问题:

  • 一旦我们登录,就会创建一个会话。这意味着每当我们再次打开该站点时,我们都不需要再次登录。 Cookies 存储在浏览器中(当您第一次登录时),它们将在您再次访问时使用。 (req.isAuthenticated() 检查会话是否已经在运行)。
  • 我们用户的密码是安全的。它们不会暴露在我们的数据库中。我们已将所有加密/解密任务委托给 Passport,仅此而已。

密码是加密的,任何人都看不到。

  • 我们现在可以看到,没有“密码”列。我们的密码由 Passport 加密。并且每当需要它们进行登录时,Passport 都可以再次解密并使用它们。
  • 我们还可以使用 Google、Facebook、LinkedIn 和仅使用 Passport 的各种其他社交网络服务添加身份验证。(我们在这里没有讨论,但代码与此类似)

输出: