CodingYang

vuePress-theme-reco Rackar    2018 - 2024
CodingYang CodingYang

Choose mode

  • dark
  • auto
  • light
首页
类别
  • 技术
  • 个人
  • 思考
  • 儿童
标签
时间线
联系
  • 关于
  • RSS订阅 (opens new window)
  • GitHub (opens new window)
  • 简书 (opens new window)
  • CSDN (opens new window)
  • WeChat (opens new window)
GitHub (opens new window)
author-avatar

Rackar

67

文章

44

标签

首页
类别
  • 技术
  • 个人
  • 思考
  • 儿童
标签
时间线
联系
  • 关于
  • RSS订阅 (opens new window)
  • GitHub (opens new window)
  • 简书 (opens new window)
  • CSDN (opens new window)
  • WeChat (opens new window)
GitHub (opens new window)
  • Postgrest 完整示例教程

    • 准备环境
      • 初始化数据库
        • 配置 PostgREST
          • 前端交互演示
            • 结束
              • jwt 认证系统原理

          Postgrest 完整示例教程

          vuePress-theme-reco Rackar    2018 - 2024

          Postgrest 完整示例教程


          Rackar 2020-03-22 PostgREST Vue.js Linux 数据库

          两年前写过一篇介绍 PostgREST 的文章 (opens new window),还建了个 QQ 交流群。不过一直还在学前端的东西,一直没用根据这套技术做什么东西。

          这些天找出空来又研究了一下,还是觉得非常好玩非常便捷。所以写了一个完整的包括注册、登录鉴权,查询表和修改表的小示例,希望能给愿意尝试的同学一点帮助。

          主要参考了官方文档 (opens new window)和这篇文章 (opens new window)

          # 准备环境

          准备一个 CentOS 7.0 以上版本的 Linux 主机或者虚拟机,使用 ssh 连接。推荐一下 阿里云 1 折 (opens new window),活动优惠不是一般大, 100 就能用一年。

          安装 docker,参照这篇文章 (opens new window)。

          安好后设置一下 docker 国内镜像地址,解决下载 docker images 太慢的问题。这里使用腾讯云加速。shell 下执行下面的命令:

          sudo mkdir -p /etc/docker
          sudo tee /etc/docker/daemon.json <<-'EOF'
          {
            "registry-mirrors": ["https://mirror.ccs.tencentyun.com"]
          }
          EOF
          sudo systemctl daemon-reload
          sudo systemctl restart docker
          
          1
          2
          3
          4
          5
          6
          7
          8

          本文很多地方的操作需要 root 权限,如不切换 root 账户的话,命令前面加 sudo 获取权限,后续不再提示。

          # 初始化数据库

          首先在 docker 中下载和运行官方镜像,即安装好 postgeresql 数据库,密码设置为 mysecretpassword, 并启动在 5433 接口。同时为了插件文件的共享,在本机新建 dshare 目录并映射到 docker 内:

          mkdir /dshare
          docker run --name post -p 5433:5432 -v /dshare:/dshare \
              -e POSTGRES_PASSWORD=mysecretpassword -d postgres
          
          # 更新,如需持久化docker数据,需要将数据目录映射
          docker volume create pgdata
          docker run --name post -p 5433:5432 -v /dshare:/dshare -v /pgdata:/var/lib/postgresql/data \
              -e POSTGRES_PASSWORD=mysecretpassword -d postgres
          
          #wsl
          docker run --name post -p 5433:5432 -v /mnt/e/dshare:/dshare -v /pgdata:/var/lib/postgresql/data \
              -e POSTGRES_PASSWORD=mysecretpassword -d postgres
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12

          然后下载 jwt 插件源码及我提供的初始脚本到共享目录,如还没有安装 Git 用 yum install git 安装一下。

          cd /dshare
          git clone https://github.com/Rackar/pgjwt
          
          1
          2

          然后进入 docker 内命令行,更新和安装 make,之后安装 pgjwt 插件到数据库。

          docker exec -it post bash
          cd /dshare/pgjwt
          apt-get update
          apt-get install make
          make install
          psql -U postgres
          create extension if not exists pgcrypto;
          CREATE EXTENSION if not exists pgjwt;
          
          1
          2
          3
          4
          5
          6
          7
          8

          然后可以导入我编辑好的 music_lover.sql 脚本文件进行建表、创建用户及赋权限、创建函数和触发器等。内有详细注释:

          # docker内执行
          psql -d postgres -U postgres -f /dshare/pgjwt/music_lover.sql
          
          # 如主机内则执行
          docker exec -it post psql -U postgres -f /dshare/pgjwt/music_lover.sql
          
          1
          2
          3
          4
          5

          导入成功后,数据库已经全部准备完成。可以开启 postgrest。

          # 配置 PostgREST

          下面定位到合适的目录下,下载 postgrest 。我是放到了 /root/postgrest 下。

          mkdir /root/postgrest
          cd /root/postgrest
          
          # 官方地址下载
          wget https://github.com/PostgREST/postgrest/releases/download/v6.0.2/postgrest-v6.0.2-centos7.tar.xz
          
          # 或者我传的七牛云镜像,国内下载
          wget http://img.codingyang.com/postgrest-v6.0.2-centos7.tar.xz
          
          # 原地解压
          tar xfJ postgrest-v6.0.2-centos7.tar.xz
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11

          还需要先安装一个依赖,否则会报错 error:libpq.so.5 找不到:

          yum -y install postgresql-libs

          好了,现在可以直接执行 ./postgrest 命令,出现用法示例,说明正常。Ctrl+C 关闭。

          然后开始编写配置文件,执行下面的脚本:

          cat <<EOF > pgrest.conf
          db-uri = "postgres://app_role:change_this@localhost:5433/postgres"
          db-schema = "public"
          db-anon-role = "anon"
          jwt-secret = "iDYR4j2Qp3QT05kpd9oIcF2WPWEWVrI3"
          EOF
          
          1
          2
          3
          4
          5
          6

          即在当前目录下创建出了 pgrest.conf 文件,其中各项都在 sql 脚本文件中有体现。如登录用户 app_role 及其密码 change_this,匿名用户 anon,以及在 login 函数中 sign 方法中用到的密钥 iDYR4j2Qp3QT05kpd9oIcF2WPWEWVrI3 ,脚本和配置文件修改时要对应。密钥记得换成自己的。

          配置完毕,可以使用 ./postgrest pgrest.conf 执行。服务器会默认开启在 3000 端口。

          # 前端交互演示

          新建 index.html 文件,cdn 方式使用 vue.js 和 axios 两个库,复制下面代码。记得修改 axios.defaults.baseURL 为自己的 ip 地址。注册、登录、使用登录后获取的 token 请求数据,提交数据打分。全套演示程序完成。

          <!DOCTYPE html>
          <html>
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <script src="https://cdn.jsdelivr.net/npm/vue"></script>
              <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
          
              <title>demo</title>
            </head>
            <body style="-webkit-app-region: drag">
              <div id="app">
                {{message}}
                <div v-if="!token">
                  邮箱: <input v-model="email" /> 密码:
                  <input v-model="pass" type="password" />
                  <button @click="signup">注册</button>
                  <button @click="login">登录</button>
                </div>
                <div v-else>
                  <select v-model="selectedArtist">
                    <option v-for="option in optionsArtist" v-bind:value="option.value">
                      {{ option.text }}
                    </option>
                  </select>
                  <select v-model="selectedRatingType">
                    <option
                      v-for="option in optionsRatingType"
                      v-bind:value="option.value"
                    >
                      {{ option.text }}
                    </option>
                  </select>
                  <input type="number" v-model="rating" />
                  <button @click="submit">打分</button>
                </div>
              </div>
              <script>
                axios.defaults.baseURL = "http://49.232.137.34:3000";
                var app = new Vue({
                  el: "#app",
                  data() {
                    return {
                      email: "",
                      pass: "",
                      token: "",
                      selectedArtist: "",
                      selectedRatingType: "",
                      optionsArtist: [],
                      optionsRatingType: [],
                      rating: 0
                    };
                  },
                  computed: {
                    message() {
                      return this.token ? "已登录" : "未登录";
                    }
                  },
                  methods: {
                    signup() {
                      axios
                        .post("/rpc/signup", { email: this.email, pass: this.pass })
                        .then(info => {
                          if (info.status == 200) {
                            this.login();
                          } else {
                            alert("注册失败。");
                          }
                        });
                    },
                    login() {
                      axios
                        .post("/rpc/login", { email: this.email, pass: this.pass })
                        .then(info => {
                          console.log(info);
                          if (info.status == 200) {
                            this.token = info.data[0].token;
                            this.getData();
                          } else {
                            alert("登录失败。");
                          }
                        });
                    },
                    getData() {
                      axios.defaults.headers.common["Authorization"] =
                        "Bearer " + this.token;
                      axios.get("/artists").then(info => {
                        console.log(info);
                        this.optionsArtist = info.data.map(artist => {
                          return { text: artist.name, value: artist.name };
                        });
                        this.selectedArtist = this.optionsArtist[0].value;
                      });
                      axios.get("/rating_types").then(info => {
                        console.log(info);
                        this.optionsRatingType = info.data.map(type => {
                          return { text: type.name, value: type.name };
                        });
                        this.selectedRatingType = this.optionsRatingType[0].value;
                      });
                    },
                    submit() {
                      let obj = {
                        artist_name: this.selectedArtist,
                        email: this.email,
                        rating_type_name: this.selectedRatingType,
                        rating: this.rating
                      };
                      axios.post("/ratings", obj).then(info => {
                        console.log(info);
                        if (info.status == 201) {
                          alert("打分成功");
                        }
                      });
                    }
                  }
                });
              </script>
            </body>
          </html>
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          36
          37
          38
          39
          40
          41
          42
          43
          44
          45
          46
          47
          48
          49
          50
          51
          52
          53
          54
          55
          56
          57
          58
          59
          60
          61
          62
          63
          64
          65
          66
          67
          68
          69
          70
          71
          72
          73
          74
          75
          76
          77
          78
          79
          80
          81
          82
          83
          84
          85
          86
          87
          88
          89
          90
          91
          92
          93
          94
          95
          96
          97
          98
          99
          100
          101
          102
          103
          104
          105
          106
          107
          108
          109
          110
          111
          112
          113
          114
          115
          116
          117
          118
          119
          120

          # 结束

          使用 PostgREST 作为 API 服务器,真的不用写后端代码了。对个人做项目真的省去好多的心力。但是需要设计好表和关系,还需更加了解 POSTGRESQL 一些。

          PostgREST 的联表查询和分页查询等也是非常方便,这里没有做进一步的展示。希望有过深入研究的同学能出一些质量更高的教程。


          正文结束。以下内容为供参考内容,可跳过。


          记录的其他命令:

          # 安装完 pgjwt 插件后,可以创建自定义镜像
          sudo docker commit -m "add post ext pgjwt" -a "Docker rackar" 14f90fd7b69b rackar/posrgres:v1
          
          # 运行 sql 文件
          docker exec -it post psql -U postgres -f /dshare/pgjwt/music_lover.sql
          
          # 撤销权限。users 表需要匿名的 insert 权限,不需要 select 权限就可以返回 token。
          revoke select on users from anon;
          
          # 查看 docker 运行
          docker ps -a
          
          # 持续运行服务器
          nohup ./postgrest pgrest.conf &
          
          # linux 杀进程:
          ps -ef | grep postgrest
          kill -s 9 12931
          
          # psql命令
          # 列出所有表
          \dt
          # 列出数据库名
          \l
          # 列出所有schema SELECT nspname FROM pg_namespace;或者:
          \dnS
          # 创建schema
          create schema schema01;
          
          
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30

          # jwt 认证系统原理

          jwt 的 playload 中有一个关键词为 role。收到 token 并解析出 role 的时候,pgrest 会自动切换数据库账户名为 role 的值。当然这是在管理员已经 grant 后,允许切换才行。如GRANT auth_user TO authenticator;

          如果没有 token 或者没有 role,会使用 PG 数据设置中的匿名账户。所以要确保匿名账户要访问的内容已设置好。

          role 也很灵活,可以视为单个用户或者用户组。

          如果把 role 视为单个角色,可以对单个角色执行行级安全策略。

          同时 PG9.5 以上拥有行级访问控制,所以可以通过自定义策略来确定行级数据访问权限。如官网的一个聊天室示例,查询权限只能查 from or to 为本用户的行,插入和修改只能在 from 为本用户的行。

          如果 role 为用户组,你还可以在 playoad 中加入 email 或者 username 来做用户区分。

          这样通过current_setting('request.jwt.claim.email', true)来获取 email。

          参与编辑此文章 (opens new window)
          更新于: 10/29/2022, 12:13:12 AM