gRPC微服务搭建(学习阶段2:权限校验)

在gRPC的权限校验中,写一个类,然后继承grpc.ServerInterceptor即可使用权限校验,每隔函数在调用前,都会调用这个函数,这个函数可用于权限校验或是一些数据准备等操作。

文章目录

权限校验示例程序

服务端代码

py 复制代码
import json
from concurrent import futures
import grpc


# 业务函数:收到客户端请求后,返回问候消息。
def say_hello(request, _context):
    return {"message": f"Hello, {request['name']}!"}


# 拦截器是 gRPC 的固定扩展写法,可以在请求到达业务函数前做日志、鉴权等处理。
# 这里为了保持简单,只打印请求方法,不做真正的权限校验。
class LogInterceptor(grpc.ServerInterceptor):
    def intercept_service(self, next_step, handler_call_details):
        print(f"收到请求:{handler_call_details.method}")
        return next_step(handler_call_details)


def server(env="dev", address="[::]:50051", max_workers=2):
    """启动 gRPC 服务。

    env:运行环境名称,当前只用于打印提示。
    address:监听地址,格式通常是"主机:端口"。
    max_workers:线程池中的最大工作线程数。
    """
    print(f"当前运行环境:{env}")

    # 这是 gRPC 创建服务端的固定写法。
    grpc_server = grpc.server(futures.ThreadPoolExecutor(max_workers=max_workers), interceptors=[LogInterceptor()])

    # 下面是本示例的 JSON 请求处理器。
    # 标准 gRPC 项目通常会用 .proto 文件自动生成这部分代码。
    handler = grpc.unary_unary_rpc_method_handler(
        say_hello,
        request_deserializer=lambda data: json.loads(data.decode("utf-8")),
        response_serializer=lambda response: json.dumps(response).encode("utf-8"),
    )

    # 注册服务和方法,这是手动注册 RPC 方法的写法。
    grpc_server.add_generic_rpc_handlers((
        grpc.method_handlers_generic_handler("hello.Greeter", {"SayHello": handler}),))

    # 添加监听端口、启动服务、持续等待,是 gRPC 服务启动的固定流程。
    grpc_server.add_insecure_port(address)
    grpc_server.start()
    print(f"gRPC 服务已启动,监听地址:{address}")
    grpc_server.wait_for_termination()


# 程序从这里开始执行,调用 server() 启动服务。
if __name__ == "__main__":
    server()

客户端代码

py 复制代码
import json
import grpc

# 连接地址必须和 server.py 中的端口一致。
with grpc.insecure_channel("127.0.0.1:50051") as channel:
    # 指定要调用的服务方法:/服务名/方法名。
    say_hello = channel.unary_unary(
        "/hello.Greeter/SayHello",
        # 发送请求前:Python 字典转换为 JSON 字节。
        request_serializer=lambda data: json.dumps(data).encode("utf-8"),
        # 收到响应后:JSON 字节转换为 Python 字典。
        response_deserializer=lambda data: json.loads(data.decode("utf-8")),
    )

    # 发送名称,并获取服务端返回的问候消息。
    response = say_hello({"name": "World"})
    print(response["message"])

带Login的权限管理

使用带login的权限管理,核心在于AuthInterceptor这个类中:

  1. 当method == "/auth.Auth/Login"时,直接执行校验
  2. 当执行其他的method的时候,直接替换调用check_token这个函数校验token,如果可以,就通过handler.unary_unary(request, context)继续执行,如果不行就通过context.abort(grpc.StatusCode.UNAUTHENTICATED, "请先登录")直接返回,不进行后续的执行

服务端代码

py 复制代码
import json
from concurrent import futures
import grpc

# 服务端保存登录成功后的 Token。
TOKEN = None


# 登录接口不需要 Token,登录成功后生成并保存 Token。
def login(request, _context):
    global TOKEN
    if request["username"] != "admin" or request["password"] != "123456":
        return {"success": False, "message": "用户名或密码错误"}
    TOKEN = "token-123"
    return {"success": True, "token": TOKEN, "message": "登录成功"}


# 只有通过 Token 校验后,才会执行这个业务函数。
def say_hello(request, _context):
    return {"message": f"Hello, {request['name']}!"}


# 将普通 Python 函数注册成 gRPC 方法,并处理 JSON 转换。
def json_handler(function):
    return grpc.unary_unary_rpc_method_handler(
        function,
        request_deserializer=lambda data: json.loads(data.decode("utf-8")),
        response_serializer=lambda data: json.dumps(data).encode("utf-8"),
    )


# 在业务函数执行前检查 Token。
class AuthInterceptor(grpc.ServerInterceptor):
    def intercept_service(self, continuation, handler_call_details):
        method = handler_call_details.method
        handler = continuation(handler_call_details)
        if handler is None:
            return None
        # 登录接口不能检查 Token,否则客户端无法登录。
        if method == "/auth.Auth/Login":
            return handler

        # 其他接口都必须携带正确的 Token。
        def check_token(request, context):
            if request.get("token") != TOKEN:
                context.abort(grpc.StatusCode.UNAUTHENTICATED, "请先登录")
            return handler.unary_unary(request, context)  # Token 正确,继续执行原来的业务函数

        # 用权限检查函数替换原来的业务处理函数。
        return handler._replace(unary_unary=check_token)


def server(address="[::]:50051", max_workers=2):
    """启动 gRPC 服务。"""
    # 创建服务,并安装 AuthInterceptor 权限拦截器
    grpc_server = grpc.server(futures.ThreadPoolExecutor(max_workers=max_workers), interceptors=[AuthInterceptor()])
    # 注册登录接口
    grpc_server.add_generic_rpc_handlers(
        (
            grpc.method_handlers_generic_handler(
                "auth.Auth",
                {"Login": json_handler(login)},
            ),
            # 注册需要登录的问候接口。
            grpc.method_handlers_generic_handler(
                "hello.Greeter",
                {"SayHello": json_handler(say_hello)},
            ),
        )
    )
    grpc_server.add_insecure_port(address)
    grpc_server.start()
    print(f"gRPC 服务已启动,监听地址:{address}")
    grpc_server.wait_for_termination()


if __name__ == "__main__":
    server()

客户端代码

py 复制代码
import json
import grpc

# 连接地址必须和 server.py 中的端口一致。
with grpc.insecure_channel("127.0.0.1:50051") as channel:
    # 第一步:调用登录接口,获取服务端保存的 Token。
    login = channel.unary_unary(
        "/auth.Auth/Login",
        request_serializer=lambda data: json.dumps(data).encode("utf-8"),
        response_deserializer=lambda data: json.loads(data.decode("utf-8")),
    )
    login_result = login({"username": "admin", "password": "123456"})
    print(login_result["message"])

    # 第二步:调用需要登录的 SayHello 接口。
    say_hello = channel.unary_unary(
        "/hello.Greeter/SayHello",
        request_serializer=lambda data: json.dumps(data).encode("utf-8"),
        response_deserializer=lambda data: json.loads(data.decode("utf-8")),
    )

    # 登录成功后,把 Token 和名称一起发送给服务端。
    response = say_hello({"name": "World", "token": login_result["token"]})
    print(response["message"])

    # 第三步:不发送 Token,模拟未登录调用。
    try:
        say_hello({"name": "World"})
    except grpc.RpcError as error:
        print(f"未登录:{error.details()}")
相关推荐
呆萌的代Ma6 小时前
gRPC微服务搭建(学习阶段1:极简服务端&客户端)
rpc·grpc
EatFan17 小时前
Spring Boot 4 落地观察:从 yudao-cloud、matecloud、JPower 看国产脚手架的升级路线与迁移清单
java·spring boot·后端·spring cloud·微服务·后端开发·jdk 21
广州山泉婚姻1 天前
Go微服务落地:服务通信、注册发现完整实现分享
人工智能·深度学习·微服务
2601_962219011 天前
操作日志全留存架构:万象生鲜系统生鲜业务合规审计底层实现方案
微服务·云原生·架构
Thomas.Sir1 天前
第6课:Nacos核心原理 & 2025版适配SpringCloud环境搭建
spring cloud·微服务
海宇AI1 天前
零信任架构实战:基于海宇学历核验版构建自动化高并发资信评估网关
人工智能·微服务·架构·自动化
EatFan1 天前
Spring Boot 4 升级避坑指南:依赖变化、Undertow 弃用、4.0.5 补丁与 Java 25 虚拟线程落地
java·spring boot·微服务·虚拟线程·spring boot 4
无序的浪2 天前
测试博客-基于微服务的在线判题系统
java·spring cloud·docker·微服务·测试·在线判题
天远Date Lab2 天前
微服务架构实战:基于天远学历信息高级版构建自动化人才准入网关
人工智能·微服务·架构·自动化