10 - 将我的服务开放给用户:构建 API 接口和用户认证的实践指南 | 青训营

106 阅读10分钟

如何将我的服务开放给用户:构建 API 接口和用户认证的实践指南

如果你有一个提供某种功能或数据的服务,比如一个网站、一个应用、一个数据库等,你可能想要让其他人能够通过网络访问你的服务,或者让其他程序能够与你的服务交互。这样,你就可以扩大你的服务的影响力和价值,也可以提高你的服务的可用性和灵活性。为了实现这个目标,你需要构建一个 API 接口和用户认证系统。

API 接口是什么?

API(Application Programming Interface)是应用程序编程接口的缩写,它是一种定义了不同软件之间如何交换信息和请求的规范。API 接口就是提供了一组预定义的方法或函数,让外部的程序可以通过这些方法或函数来访问你的服务的功能或数据。比如,如果你有一个提供天气信息的服务,你可以提供一个 API 接口,让其他人可以通过调用你的 API 接口来获取某个地点的天气情况。

API 接口通常遵循一些标准的格式和协议,以便于不同的程序之间能够顺利地进行通信。最常见的 API 接口格式是 REST(Representational State Transfer),它基于 HTTP 协议,使用 URL(Uniform Resource Locator)来标识不同的资源,使用 HTTP 方法(如 GET, POST, PUT, DELETE 等)来表示不同的操作,使用 JSON(JavaScript Object Notation)或 XML(Extensible Markup Language)等格式来传输数据。例如,如果你想要获取北京市今天的天气情况,你可以向一个 REST API 接口发送一个 GET 请求,如下:

GET https://api.weather.com/v1/location/Beijing/today.json

这个请求中,https://api.weather.com/v1/是 API 接口的基本 URL,location/Beijing/today.json是资源的标识符,表示要获取北京市今天的天气信息,并且以 JSON 格式返回。如果请求成功,你可能会得到类似下面这样的响应:

{
  "status": 200,
  "message": "OK",
  "data": {
    "city": "Beijing",
    "date": "2023-08-25",
    "temperature": 28,
    "humidity": 65,
    "wind": 5,
    "condition": "Sunny"
  }
}

这个响应中,status表示响应的状态码,message表示响应的消息,data表示响应的数据内容。这样,你就可以根据响应中的数据来显示或处理北京市今天的天气情况。

用户认证是什么?

用户认证(User Authentication)是一种验证用户身份和权限的机制。用户认证通常用于保护你的服务不被未经授权或恶意的访问或操作。用户认证可以分为两个步骤:用户注册和用户登录。

用户注册是指用户向你的服务提供一些基本的信息,如用户名、密码、邮箱等,并且创建一个账户。用户注册通常需要验证用户提供的信息是否有效和唯一,并且需要加密和存储用户提供的密码等敏感信息。

用户登录是指用户向你的服务提供用户名和密码等凭证,并且请求访问你的服务。用户登录通常需要验证用户提供的凭证是否正确,并且生成一个令牌(Token)或一个会话(Session)来标识用户的身份和状态。

令牌是一种包含了用户信息和签名的字符串,它可以用于在不同的请求之间保持用户的身份和权限。令牌通常使用 JWT(JSON Web Token)等标准格式来生成和验证。例如,一个 JWT 令牌可能长这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFsaWNlIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNjI5ODkxMzQ1LCJleHAiOjE2Mjk4OTQ5NDV9.8y0oY7fQq7Y3nDxwLl8wLbqZuXvN3a0K9yTgkVxH3nE

这个令牌由三部分组成,分别是头部(Header),载荷(Payload)和签名(Signature),它们之间用点号(.)分隔。头部包含了令牌的类型和加密算法,载荷包含了用户的信息和过期时间,签名是用加密算法和密钥对头部和载荷进行加密的结果。这样,你的服务就可以根据令牌来验证用户的身份和权限,并且防止令牌被篡改或伪造。

会话是一种在服务器端存储用户信息和状态的机制,它可以用于在不同的请求之间保持用户的身份和权限。会话通常使用 Cookie或 Session ID等方式来标识用户的请求。Cookie 是一种在客户端存储数据的机制,它可以在每次请求时自动发送给服务器。Session ID 是一种在服务器端生成的唯一的字符串,它可以通过 Cookie 或其他方式传递给客户端。例如,一个 Cookie 可能长这样:

Set-Cookie: session_id=1234567890abcdef; Path=/; Expires=Fri, 25 Aug 2023 16:37:45 GMT

这个 Cookie 包含了会话的标识符(session_id),路径(Path),过期时间(Expires)等属性。这样,你的服务就可以根据 Cookie 中的会话标识符来获取用户的信息和状态,并且管理用户的会话。

如何构建 API 接口和用户认证?

构建 API 接口和用户认证需要使用一些编程语言和框架来实现。这里我们以 Python语言和 Flask框架为例,来演示如何构建一个简单的 API 接口和用户认证系统。

首先,我们需要安装 Python 和 Flask 等相关的库:

# 安装 Python
# 可以从 https://www.python.org/downloads/ 下载并安装 Python

# 安装 Flask 和其他相关库
# 在命令行中输入以下命令
pip install flask flask-restful flask-jwt-extended

然后,我们需要创建一个 app.py 文件,用于定义我们的 API 接口和用户认证逻辑:

# 导入 Flask 和相关库
from flask import Flask, request, jsonify
from flask_restful import Resource, Api
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity

# 创建一个 Flask 应用对象
app = Flask(__name__)

# 创建一个 Api 对象,用于管理 API 接口
api = Api(app)

# 创建一个 JWTManager 对象,用于管理用户认证
app.config["JWT_SECRET_KEY"] = "secret" # 设置一个密钥,用于加密和解密令牌
jwt = JWTManager(app)

# 创建一个模拟的数据库,用于存储用户信息和服务数据
# 这里我们使用字典来模拟数据库,实际开发中应该使用真正的数据库
users = {
    "alice": {
        "password": “123456”, # 用户的密码,实际开发中应该使用加密和哈希等方式来保护密码 “role”: “admin” # 用户的角色,用于控制用户的权限 }, “bob”: { “password”: “654321”, “role”: “user” } }

services = { “weather”: { # 一个提供天气信息的服务 “Beijing”: { # 北京市的天气信息 “date”: “2023-08-25”, “temperature”: 28, “humidity”: 65, “wind”: 5, “condition”: “Sunny” }, “Helsinki”: { # 赫尔辛基的天气信息 “date”: “2023-08-25”, “temperature”: 18, “humidity”: 75, “wind”: 10, “condition”: “Cloudy” } }, “calculator”: { # 一个提供计算功能的服务 # 这里没有具体的数据,只有一个函数,用于根据用户的输入进行计算 # 这个函数接受一个字符串参数,表示用户的计算表达式,如"1+2*3" # 这个函数返回一个数字,表示计算结果,如7 # 这个函数使用 Python 的 eval 函数来实现,实际开发中应该使用更安全和更强大的方式来实现 # 这个函数可能会抛出异常,如果用户的输入不合法或不可计算,如"1/0""abc" def calculate(self, expression): return eval(expression) } }

# 定义一个 UserRegister 资源类,用于处理用户注册的请求

class UserRegister(Resource): def post(self): # 定义一个 post 方法,用于接收用户注册的数据 data = request.get_json() # 获取请求中的 JSON 数据 username = data.get(“username”) # 获取用户名 password = data.get(“password”) # 获取密码 if username and password: # 如果用户名和密码都不为空 if username in users: # 如果用户名已经存在 return {“message”: “用户名已被占用”}, 400 # 返回一个错误消息和状态码 else: # 如果用户名不存在 users[username] = {“password”: password, “role”: “user”} # 创建一个新的用户,并设置默认的角色为 user return {“message”: “用户注册成功”}, 201 # 返回一个成功消息和状态码 else: # 如果用户名或密码为空 return {“message”: “用户名和密码不能为空”}, 400 # 返回一个错误消息和状态码

# 定义一个 UserLogin 资源类,用于处理用户登录的请求

class UserLogin(Resource): def post(self): # 定义一个 post 方法,用于接收用户登录的数据 data = request.get_json() # 获取请求中的 JSON 数据 username = data.get(“username”) # 获取用户名 password = data.get(“password”) # 获取密码 if username and password: # 如果用户名和密码都不为空 user = users.get(username) # 获取用户信息 if user and user[“password”] == password: # 如果用户存在且密码正确 access_token = create_access_token(identity=username) # 创建一个令牌,包含了用户名作为身份标识 return {“message”: “用户登录成功”, “access_token”: access_token}, 200 # 返回一个成功消息和令牌和状态码 else: # 如果用户不存在或密码错误 return {“message”: “用户名或密码错误”}, 401 # 返回一个错误消息和状态码 else: # 如果用户名或密码为空 return {“message”: “用户名和密码不能为空”}, 400 # 返回一个错误消息和状态码

# 定义一个 Weather 资源类,用于处理天气信息的请求

class Weather(Resource): @jwt_required() # 使用 jwt_required 装饰器,要求请求中必须包含有效的令牌才能访问该资源 def get(self, city): # 定义一个 get 方法,用于接收城市名称作为参数,并返回该城市的天气信息 weather = services[“weather”].get(city) # 获取天气服务中的城市信息 if weather: # 如果城市信息存在 return {“data”: weather}, 200 # 返回城市的天气信息和状态码 else: # 如果城市信息不存在 return {“message”: “城市不存在”}, 404 # 返回一个错误消息和状态码

# 定义一个 Calculator 资源类,用于处理计算的请求

class Calculator(Resource): @jwt_required() # 使用 jwt_required 装饰器,要求请求中必须包含有效的令牌才能访问该资源 def post(self): # 定义一个 post 方法,用于接收用户的计算表达式,并返回计算结果 data = request.get_json() # 获取请求中的 JSON 数据 expression = data.get(“expression”) # 获取计算表达式 if expression: # 如果计算表达式不为空 try: # 尝试进行计算 result = services[“calculator”].calculate(expression) # 调用计算服务中的函数,传入计算表达式,得到计算结果 return {“data”: result}, 200 # 返回计算结果和状态码 except Exception as e: # 如果发生异常 return {“message”: “计算错误:{}”.format(e)}, 400 # 返回一个错误消息和状态码 else: # 如果计算表达式为空 return {“message”: “计算表达式不能为空”}, 400 # 返回一个错误消息和状态码

# 将资源类和对应的 URL 路径绑定起来,创建 API 接口

api.add_resource(UserRegister, “/user/register”) # 用户注册接口,使用 post 方法发送用户名和密码 api.add_resource(UserLogin, “/user/login”) # 用户登录接口,使用 post 方法发送用户名和密码,返回令牌 api.add_resource(Weather, “/weather/[string:city]()”) # 天气信息接口,使用 get 方法发送城市名称,返回天气信息 api.add_resource(Calculator, “/calculator”) # 计算接口,使用 post 方法发送计算表达式,返回计算结果

# 运行 Flask 应用对象,启动服务器

if **name** == “**main**”: app.run(debug=True) # 设置 debug 参数为 True,可以在开发过程中看到更多的错误信息和日志信息

最后,我们可以使用 Postman 或其他工具来测试我们的 API 接口和用户认证系统。以下是一些测试用例:

  • 用户注册:向 /user/register 发送 post 请求,包含用户名和密码,如:
{ “username”: “alice”, “password”: “123456” }
  • 用户登录:向 /user/login 发送 post 请求,包含用户名和密码,如:
{ “username”: “alice”, “password”: “123456” }
  • 获取天气信息:向 /weather/Beijing 发送 get 请求,包含令牌(在请求头中设置 Authorization 字段为 Bearer ),如:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFmZSI6ImFsaWNlIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNjI5ODkxMzQ1LCJleHAiOjE2Mjk4OTQ5NDV9.8y0oY7fQq7Y3nDxwLl8wLbqZuXvN3a0K9yTgkVxH3nE
  • 进行计算:向 /calculator 发送 post 请求,包含令牌(在请求头中设置 Authorization 字段为 Bearer )和计算表达式(在请求体中设置 expression 字段),如:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFmZSI6ImFsaWNlIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNjI5ODkxMzQ1LCJ