ソースを参照

feat: authing webguard & sync session withIdToken

MetaPunkGames 8 ヶ月 前
コミット
9c80547a46

+ 44 - 0
README.md

@@ -14,6 +14,50 @@ npm i -f
 npm run start
 ```
 
+## Authing 用户服务
+### 管理员
+https://lljydpt8egql.u2-dev.hep.com.cn/
+- 用户名 hanlei
+- 密码 13581837652
+
+### 单页面应用
+AppID 6682ab96b7bd5db59d6785a0
+
+AppSecret b800ac6533c640c3d1ea2b20ebe2e990
+
+Token 端点
+https://textbook.u2-dev.hep.com.cn/oidc/token
+
+用户信息端点
+https://textbook.u2-dev.hep.com.cn/oidc/me
+
+登出端点
+https://textbook.u2-dev.hep.com.cn/oidc/session/end
+
+JWKS 公钥端点
+https://textbook.u2-dev.hep.com.cn/oidc/.well-known/jwks.json
+
+认证端点
+https://textbook.u2-dev.hep.com.cn/oidc/auth
+
+Issuer
+https://textbook.u2-dev.hep.com.cn/oidc
+
+服务发现地址
+https://textbook.u2-dev.hep.com.cn/oidc/.well-known/openid-configuration
+
+认真地址
+https://textbook.u2-dev.hep.com.cn
+
+### 开发者AKSK
+对接 Authing SDK 的接口文档:https://api-explorer.authing.cn/?source=Authing%20%E7%AE%A1%E7%90%86%20API
+
+https://api-explorer.authing.cn/?source=Authing%20%E7%94%A8%E6%88%B7%E8%AE%A4%E8%AF%81%20API
+
+用户池 id:6672540d8756a503c6f6f03e
+
+测试用户池密钥:491670e7ef2fd553ce6d0668a1bb87f3
+
 # 国家级教材遴选系统——前端项目 edu-textbook
 
 This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.0.4.

+ 17 - 1
package-lock.json

@@ -18,6 +18,7 @@
         "@angular/platform-browser": "^18.0.0",
         "@angular/platform-browser-dynamic": "^18.0.0",
         "@angular/router": "^18.0.0",
+        "@authing/browser": "^0.0.1-alpha3",
         "@ionic/angular": "^8.2.2",
         "@ngx-translate/core": "^15.0.0",
         "@types/parse": "^3.0.9",
@@ -767,6 +768,14 @@
         "rxjs": "^6.4.0 || ^7.4.0"
       }
     },
+    "node_modules/@authing/browser": {
+      "version": "0.0.1-alpha3",
+      "resolved": "https://registry.npmmirror.com/@authing/browser/-/browser-0.0.1-alpha3.tgz",
+      "integrity": "sha512-m1KsQPZFa1BOElNPp7w4RWNY0VnpvgSx8R2a59h/DshJzVimIfpUnA1dVkcbcV9ZJWygJ2NaeWo+Sx6I0gmf5w==",
+      "dependencies": {
+        "axios": "^0.26.1"
+      }
+    },
     "node_modules/@babel/code-frame": {
       "version": "7.24.7",
       "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.24.7.tgz",
@@ -5772,6 +5781,14 @@
         "postcss": "^8.1.0"
       }
     },
+    "node_modules/axios": {
+      "version": "0.26.1",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-0.26.1.tgz",
+      "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
+      "dependencies": {
+        "follow-redirects": "^1.14.8"
+      }
+    },
     "node_modules/babel-loader": {
       "version": "9.1.3",
       "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-9.1.3.tgz",
@@ -7830,7 +7847,6 @@
       "version": "1.15.6",
       "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.6.tgz",
       "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
-      "dev": true,
       "funding": [
         {
           "type": "individual",

+ 2 - 1
package.json

@@ -20,6 +20,7 @@
     "@angular/platform-browser": "^18.0.0",
     "@angular/platform-browser-dynamic": "^18.0.0",
     "@angular/router": "^18.0.0",
+    "@authing/browser": "^0.0.1-alpha3",
     "@ionic/angular": "^8.2.2",
     "@ngx-translate/core": "^15.0.0",
     "@types/parse": "^3.0.9",
@@ -43,4 +44,4 @@
     "karma-jasmine-html-reporter": "~2.1.0",
     "typescript": "~5.4.2"
   }
-}
+}

+ 1 - 0
projects/textbook/src/app/app.component.ts

@@ -19,4 +19,5 @@ export class AppComponent {
     (Parse as any).serverURL = ("http://localhost:61337/parse");
     localStorage.setItem('company','RbIKpmuaMC')
   }
+
 }

+ 3 - 0
projects/textbook/src/index.html

@@ -8,6 +8,9 @@
   <link rel="icon" type="image/x-icon" href="favicon.ico">
   <link href="https://fonts.loli.net/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
   <link href="https://fonts.loli.net/icon?family=Material+Icons" rel="stylesheet">
+  <!-- Authing -->
+  <script type="text/javascript" src="https://cdn.authing.co/packages/guard/latest/guard.min.js"></script>
+  <link rel="stylesheet" href="https://cdn.authing.co/packages/guard/latest/guard.min.css" />
 </head>
 <style>
   *{

+ 1 - 215
projects/textbook/src/modules/login/login/login.component.html

@@ -38,221 +38,7 @@
           class="login-form"
           (ngSubmit)="submitForm('account')"
         >
-          <nz-form-item>
-            <nz-form-control>
-              <nz-input-group nzPrefixIcon="user">
-                <input
-                  class="ipt"
-                  type="text"
-                  nz-input
-                  formControlName="userName"
-                  placeholder="请输入用户名"
-                />
-              </nz-input-group>
-            </nz-form-control>
-          </nz-form-item>
-          <nz-form-item>
-            <nz-form-control>
-              <nz-input-group nzPrefixIcon="lock">
-                <input
-                  type="password"
-                  class="ipt"
-                  nz-input
-                  formControlName="password"
-                  placeholder="请输入密码"
-                />
-              </nz-input-group>
-            </nz-form-control>
-          </nz-form-item>
-          <nz-form-item class="row-code">
-            <nz-form-control>
-              <nz-input-group nzPrefixIcon="safety-certificate">
-                <input
-                  type="text"
-                  class="ipt"
-                  nz-input
-                  maxlength="4"
-                  formControlName="code"
-                  placeholder="请输入验证码"
-                />
-              </nz-input-group>
-            </nz-form-control>
-            @if(active == 0){
-            <fm-captcha
-              [type]="'text'"
-              [canvas_id]="'canvas_reset'"
-              style="width: 168px"
-              #codelogin
-              (changeVal)="onChangeCode($event)"
-              (onEnter)="submitForm('account')"
-            >
-            </fm-captcha>
-            }
-          </nz-form-item>
-          <div class="checked">
-            <label nz-checkbox formControlName="checked"></label>
-            <span
-              >我已阅读并同意<a href="">隐私协议</a>与<a href=""
-                >服务条款</a
-              ></span
-            >
-          </div>
-          <!-- <div nz-row class="login-form-margin">
-            <div nz-col [nzSpan]="12">
-              <a class="login-form-left" (click)="goUrl('/user/register')"
-                >立即注册</a
-              >
-            </div>
-            <div nz-col [nzSpan]="12">
-              <a
-                class="login-form-forgot"
-                (click)="goUrl('/user/reset_password')"
-                >忘记密码</a
-              >
-            </div>
-          </div> -->
-          <button
-            id="basic"
-            mat-raised-button
-            class="login-form-button"
-            type="button"
-            mat-button
-            (click)="submitForm('account')"
-          >
-            登录
-          </button>
-          <div style="text-align: left">
-            <a style="color: #756b6d"
-              ><span nz-icon nzType="question-circle" nzTheme="outline"></span
-            ></a>
-          </div>
-          <div class="menu">
-            <a nz-dropdown [nzDropdownMenu]="menu">
-              <span style="color: #231c1f99">English</span>
-              <span
-                nz-icon
-                nzType="down"
-                style="color: #756b6d; margin-left: 4px"
-              ></span>
-            </a>
-            <nz-dropdown-menu #menu="nzDropdownMenu">
-              <ul nz-menu nzSelectable>
-                <li nz-menu-item>English</li>
-                <li nz-menu-item>中文简体</li>
-              </ul>
-            </nz-dropdown-menu>
-          </div>
-        </form>
-      </nz-tab>
-      <nz-tab nzTitle="验证码登录">
-        <form
-          nz-form
-          [formGroup]="validateFormPhone"
-          class="login-form"
-          (ngSubmit)="submitForm('mobile')"
-        >
-          <nz-form-item>
-            <nz-form-control>
-              <nz-input-group nzPrefixIcon="mobile">
-                <input
-                  class="ipt"
-                  type="text"
-                  nz-input
-                  placeholder="请输入手机号 / 邮箱"
-                  maxlength="11"
-                  formControlName="phoneNumber"
-                />
-              </nz-input-group>
-            </nz-form-control>
-          </nz-form-item>
-          <nz-form-item class="row-code">
-            <nz-form-control>
-              <nz-input-group nzPrefixIcon="safety-certificate">
-                <input
-                  type="text"
-                  class="ipt"
-                  nz-input
-                  maxlength="4"
-                  formControlName="code"
-                  placeholder="请输入验证码"
-                />
-              </nz-input-group>
-            </nz-form-control>
-            @if(active == 1){
-            <fm-captcha
-              [type]="'text'"
-              [canvas_id]="'canvas_reset_mobile'"
-              style="width: 168px"
-              #codeloginSign
-              (changeVal)="onChangeCode($event)"
-              (onEnter)="submitForm('mobile')"
-            >
-            </fm-captcha>
-            }
-          </nz-form-item>
-          <nz-form-item class="row-code">
-            <nz-form-control>
-              <nz-input-group nzPrefixIcon="verified">
-                <input
-                  type="text"
-                  class="ipt"
-                  nz-input
-                  placeholder="请填写短信验证码"
-                  formControlName="checkCode"
-                  maxlength="8"
-                />
-              </nz-input-group>
-            </nz-form-control>
-            <button
-              class="vrifly-btn"
-              [disabled]="isCountingdown"
-              [style.background-color]="isCountingdown ? '#dcdcdc' : '#c6233f'"
-              type="button"
-              mat-button
-              (click)="startCountdown()"
-            >
-              {{ buttonText }}
-            </button>
-          </nz-form-item>
-          <div class="checked">
-            <label nz-checkbox formControlName="checked"></label>
-            <span
-              >我已阅读并同意<a href="">隐私协议</a>与<a href=""
-                >服务条款</a
-              ></span
-            >
-          </div>
-          <button
-            id="basic"
-            class="login-form-button"
-            type="button"
-            mat-button
-            (click)="submitForm('mobile')"
-          >
-            登录 / 注册
-          </button>
-
-          <div style="text-align: left">
-            <a href="" style="color: #756b6d"
-              ><span nz-icon nzType="question-circle" nzTheme="outline"></span
-            ></a>
-          </div>
-          <div class="menu">
-            <a nz-dropdown [nzDropdownMenu]="menu">
-              <span style="color: #231c1f99">English</span>
-              <span
-                nz-icon
-                nzType="down"
-                style="color: #756b6d; margin-left: 4px"
-              ></span>
-            </a>
-            <nz-dropdown-menu #menu="nzDropdownMenu">
-              <ul nz-menu nzSelectable>
-                <li nz-menu-item>English</li>
-                <li nz-menu-item>中文简体</li>
-              </ul>
-            </nz-dropdown-menu>
-          </div>
+          <div id="authing-guard-container"></div>
         </form>
       </nz-tab>
     </nz-tabset>

+ 20 - 2
projects/textbook/src/modules/login/login/login.component.ts

@@ -1,4 +1,4 @@
-import { Component, ViewChild } from '@angular/core';
+import { Component, ViewChild,OnInit } from '@angular/core';
 import { ReactiveFormsModule } from '@angular/forms';
 import { HttpClient } from "@angular/common/http";
 import { Router } from '@angular/router';
@@ -17,6 +17,8 @@ import { AuthServr } from '../../../services/auth.service';
 import { CaptchaComponent } from '../../../app/captcha/captcha.component';
 import Parse from "parse";
 import { NzMessageService } from 'ng-zorro-antd/message';
+import { ParseAuthing } from './parse-authing';
+
 @Component({
   selector: 'app-login',
   standalone: true,
@@ -24,10 +26,19 @@ import { NzMessageService } from 'ng-zorro-antd/message';
   templateUrl: './login.component.html',
   styleUrl: './login.component.scss',
 })
-export class LoginComponent {
+export class LoginComponent implements OnInit{
   @ViewChild("codelogin") codelogin: any; 
   @ViewChild("codeloginSign") codeloginSign: any; 
 
+  ngOnInit(){
+    let parseAuthing = new ParseAuthing({
+      // 监听事件:登陆成功后,返回用户信息
+      login:(user,authClient)=>{
+        console.log(user,authClient)
+      }
+    });
+    parseAuthing.initLoginModal();
+  }
   code:string = '' //本地生成验证码
 
   active:number = 0
@@ -216,4 +227,11 @@ export class LoginComponent {
       }
     }, 1000);
   }
+
+  loginAuthing(){
+    this.authServr.loginAuthing();
+  }
+  stateAuthing(){
+    this.authServr.getLoginState();
+  }
 }

+ 69 - 0
projects/textbook/src/modules/login/login/parse-authing.ts

@@ -0,0 +1,69 @@
+declare var GuardFactory:any;
+
+/**
+ * ParseAuthing 通过Authing实现登录逻辑,再同步至Parse.User.become身份
+ */
+export class ParseAuthing{
+    authingGurad:any
+
+    /**
+     * Event
+     * @desc
+     * https://cdn.authing.co/packages/guard/doc/v5/guide/essentials/events.html
+     *  */
+    event:{[key:string]:Function|undefined} = {
+        onLoad:undefined, // Guard初始化完成,开始渲染页面
+        login:undefined, // 登陆成功回调
+        loginError:undefined, // 登陆失败
+        beforeLogin:undefined, // 用户触发登录前(返回<boolean | Promise<boolean>>用于控制本次登录是否继续)。
+        register:undefined, // 用户注册成功
+        beforeRegister:undefined, // 用户触发注册前(返回<boolean | Promise<boolean>>用于控制本次注册是否继续)。
+    }
+    constructor(options?:{
+        login?:{(user:any,authenticationClient:any):void}
+        loginError?:Function
+        beforeLogin?:Function
+        register?:Function
+        beforeRegister?:Function
+    }){
+        this.event['login'] = options?.login
+        this.event['login-error'] = options?.loginError
+        this.event['beforeLogin'] = options?.beforeLogin
+        this.event['register'] = options?.register
+        this.event['beforeRegister'] = options?.beforeRegister
+    }
+    initLoginModal(){
+        this.authingGurad = new GuardFactory.Guard({
+            host: 'https://textbook.u2-dev.hep.com.cn', // 应用的认证地址
+            appId: '6682ab96b7bd5db59d6785a0',
+            redirectUri:location.origin
+        })
+        // 使用 start 方法挂载 Guard 组件到你指定的 DOM 节点,登录成功后返回 userInfo
+        this.authingGurad.start('#authing-guard-container').then((userInfo:any) => {
+            console.log('userInfo in start: ', userInfo)
+        })
+        // 生命周期事件绑定
+        Object.keys(this.event).forEach(key=>{
+            if(this.event[key]){
+                this.authingGurad.on(key,this.event[key])
+            }
+        })
+        // 定制特殊生命周期
+        this.authingGurad.on("login",(user:any,authClient:any)=>{
+            // 获取Authing
+            let token = user?.token;
+            // 执行回调
+            if(typeof this.event["login"] == 'function'){
+                this.event["login"](user,authClient)
+            }
+        })
+    }
+    logout(){
+        this.authingGurad.logout();
+    }
+    async current(){
+        // 获取当前用户信息
+        const userInfo = await this.authingGurad.trackSession();
+        console.log(userInfo);
+    }
+}

+ 35 - 1
projects/textbook/src/services/auth.service.ts

@@ -2,6 +2,9 @@ import { Injectable } from "@angular/core";
 import Parse from "parse";
 import { Router } from "@angular/router";
 import { textbookServer } from './textbook'
+import { Authing } from '@authing/browser';
+import { LoginState } from "@authing/browser/dist/types/global";
+
 @Injectable({
   providedIn: "root",
 })
@@ -43,7 +46,37 @@ export class AuthServr {
       route:'/nav-author/manage/space'
     },
   ];
-  constructor(public router: Router,private textbook:textbookServer) {}
+
+  authingSDK:Authing
+  constructor(public router: Router,private textbook:textbookServer) {
+     
+    this.authingSDK = new Authing({
+      domain: 'https://textbook.u2-dev.hep.com.cn', // 应用的认证地址
+      appId: '6682ab96b7bd5db59d6785a0',// 应用 ID
+      redirectUri: location.origin,// 登录回调地址
+    });
+
+
+  }
+
+  /** Authing云Login */
+  loginState: LoginState | null = null;
+  // 以跳转方式打开 Authing 托管的登录页
+  loginAuthing(){
+    this.authingSDK.loginWithRedirect();
+  }
+  // 获取用户的登录状态
+  async getLoginState() {
+    try {
+      const state = await this.authingSDK.getLoginState();
+      this.loginState = state;
+      console.log(this.loginState)
+    } catch(error) {
+      console.log(error);
+    }
+  }
+
+  /** Parse传统Login */
   login(username:any, password:any, company:string) {
     return new Promise(async (resolve, reject) => {
       let a = /^1[3456789]\d{9}$/;
@@ -87,4 +120,5 @@ export class AuthServr {
       this.router.navigate(["/user/login"]);
     });
   }
+
 }

+ 70 - 0
server/cloud/func-authing-session-sync.js

@@ -0,0 +1,70 @@
+const { AuthenticationClient } = require('authing-js-sdk')
+const { pgClient } = require('../db/pg-instance')
+
+/**
+ * 同步用户登录信息
+ * @desc
+ * https://docs.authing.cn/v2/reference/sdk-for-node/authentication/AuthenticationClient.html#获取当前登录的用户信息
+ * @param {*} token 
+ * @returns 
+ */
+async function syncSessionWithIdToken(token){
+    // 通过用户的 id_token 初始化之后获取用户信息
+
+    let authenticationClient = new AuthenticationClient({
+        appId: '6682ab96b7bd5db59d6785a0',
+        appHost: 'https://textbook.u2-dev.hep.com.cn', // 应用的认证地址
+        token: token
+    })
+
+    let user = await authenticationClient.getCurrentUser()
+    // console.log(user)
+
+    // 生成Parse库所需_Session记录 objectId唯一
+    let sessionObjectId = generateObjectId(user?.id+user?.token)
+    let username = user?.phone || user?.username || user?.email
+    let syncSessionSQL = `
+    INSERT INTO "_User" ("objectId", "username","mobile", "createdAt", "updatedAt")
+    VALUES
+    ($2,$7,$8,$5,$6)
+    ON conflict("objectId") DO UPDATE
+    SET 
+    "username" = excluded."username",
+    "updatedAt"=excluded."updatedAt";
+
+
+    INSERT INTO "_Session" ("objectId", "user", "sessionToken","expiresAt", "createdAt", "updatedAt")
+    VALUES
+    ($1, $2,$3,$4,$5,$6)
+    ON conflict("objectId") DO UPDATE
+    SET 
+    "user" = excluded."user",
+    "sessionToken" = excluded."sessionToken",
+    "expiresAt"=excluded."expiresAt",
+    "updatedAt"=excluded."updatedAt";
+    `
+    let params = [sessionObjectId,user?.id,user?.token,user?.expiresAt,new Date(),new Date(),username,user?.phone]
+    if(user?.id&&user?.token){
+        try {
+            // 查询:数据库版本信息
+            const data = await pgClient.any(syncSessionSQL,params);
+            // console.log(data)
+            return {
+                sid:sessionObjectId,
+                uid:user?.id,
+                sessionToken:user?.token
+            };
+        } catch (error) {
+            console.error('Error executing query:', error);
+            return null;
+        }
+    }
+}
+module.exports.syncSessionWithIdToken = syncSessionWithIdToken
+
+const crypto = require('crypto');
+function generateObjectId(inputString) {
+    const hash = crypto.createHash('sha256').update(inputString).digest('hex');
+    const objectId = hash;
+    return objectId;
+}

+ 4 - 0
server/cloud/test/test-authing-session-sync.js

@@ -0,0 +1,4 @@
+const { syncSessionWithIdToken } = require("../func-authing-session-sync");
+
+
+syncSessionWithIdToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cGRhdGVkX2F0IjoiMjAyNC0wNy0wMVQxNTowNzozNi4xMDdaIiwiYWRkcmVzcyI6eyJjb3VudHJ5IjpudWxsLCJwb3N0YWxfY29kZSI6bnVsbCwicmVnaW9uIjpudWxsLCJmb3JtYXR0ZWQiOm51bGx9LCJwaG9uZV9udW1iZXJfdmVyaWZpZWQiOnRydWUsInBob25lX251bWJlciI6IjE4NjkxNzcwMzQzIiwibG9jYWxlIjpudWxsLCJ6b25laW5mbyI6bnVsbCwiYmlydGhkYXRlIjpudWxsLCJnZW5kZXIiOiJVIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJlbWFpbCI6bnVsbCwid2Vic2l0ZSI6bnVsbCwicGljdHVyZSI6Imh0dHBzOi8vZmlsZXMuYXV0aGluZy5jby9hdXRoaW5nLWNvbnNvbGUvZGVmYXVsdC11c2VyLWF2YXRhci5wbmciLCJwcm9maWxlIjpudWxsLCJwcmVmZXJyZWRfdXNlcm5hbWUiOm51bGwsIm5pY2tuYW1lIjpudWxsLCJtaWRkbGVfbmFtZSI6bnVsbCwiZmFtaWx5X25hbWUiOm51bGwsImdpdmVuX25hbWUiOm51bGwsIm5hbWUiOm51bGwsInN1YiI6IjY2ODJiZmU4MzcxNWIzYmIwNmM4ZGNiMiIsImV4dGVybmFsX2lkIjpudWxsLCJ1bmlvbmlkIjpudWxsLCJ1c2VybmFtZSI6bnVsbCwic2Vjb25kYXJ5VXNlcklkcyI6bnVsbCwiZGF0YSI6eyJ0eXBlIjoidXNlciIsInVzZXJQb29sSWQiOiI2NjcyNTQwZDg3NTZhNTAzYzZmNmYwM2UiLCJhcHBJZCI6IjY2ODJhYjk2YjdiZDVkYjU5ZDY3ODVhMCIsImlkIjoiNjY4MmJmZTgzNzE1YjNiYjA2YzhkY2IyIiwidXNlcklkIjoiNjY4MmJmZTgzNzE1YjNiYjA2YzhkY2IyIiwiX2lkIjoiNjY4MmJmZTgzNzE1YjNiYjA2YzhkY2IyIiwicGhvbmUiOiIxODY5MTc3MDM0MyIsImVtYWlsIjpudWxsLCJ1c2VybmFtZSI6bnVsbCwidW5pb25pZCI6bnVsbCwib3BlbmlkIjpudWxsLCJjbGllbnRJZCI6IjY2NzI1NDBkODc1NmE1MDNjNmY2ZjAzZSJ9LCJ1c2VycG9vbF9pZCI6IjY2NzI1NDBkODc1NmE1MDNjNmY2ZjAzZSIsImF1ZCI6IjY2ODJhYjk2YjdiZDVkYjU5ZDY3ODVhMCIsImV4cCI6MTcyMTA1NjIwMywiaWF0IjoxNzE5ODQ2NjAzLCJpc3MiOiJodHRwczovL3RleHRib29rLnUyLWRldi5oZXAuY29tLmNuL29pZGMifQ.3agfjN19HR5oQCybrmJy29YK1W9MM6KeqKqlHMHZlTA")

+ 11 - 0
server/db/pg-instance.js

@@ -0,0 +1,11 @@
+const pgp = require('pg-promise')();
+
+// 连接到数据库
+const pgClient = pgp({
+    user: 'postgres',
+    password: 'postgres',
+    host: 'localhost',
+    port: 25432,
+    database: 'postgres'
+});
+module.exports.pgClient = pgClient

+ 59 - 2
server/package-lock.json

@@ -9,6 +9,7 @@
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {
+        "authing-js-sdk": "^4.23.51",
         "parse-dashboard": "^5.4.0",
         "parse-server": "^7.0.0"
       },
@@ -851,6 +852,14 @@
         "node": ">=14"
       }
     },
+    "node_modules/@fingerprintjs/fingerprintjs": {
+      "version": "3.4.2",
+      "resolved": "https://registry.npmmirror.com/@fingerprintjs/fingerprintjs/-/fingerprintjs-3.4.2.tgz",
+      "integrity": "sha512-3Ncze6JsJpB7BpYhqIgvBpfvEX1jsEKrad5hQBpyRQxtoAp6hx3+R46zqfsuQG4D9egQZ+xftQ0u4LPFMB7Wmg==",
+      "dependencies": {
+        "tslib": "^2.4.1"
+      }
+    },
     "node_modules/@firebase/app-check-interop-types": {
       "version": "0.3.2",
       "resolved": "https://registry.npmmirror.com/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz",
@@ -2636,6 +2645,22 @@
       "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
       "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
     },
+    "node_modules/authing-js-sdk": {
+      "version": "4.23.51",
+      "resolved": "https://registry.npmmirror.com/authing-js-sdk/-/authing-js-sdk-4.23.51.tgz",
+      "integrity": "sha512-HU5PBD2+RgGXqy9PcVAYpJSXCXOxoga55LiPNtQnHcQff5TvHlIEmLzXb3jtycaeU9nQbhtOewHYbcEM1Uzy9g==",
+      "dependencies": {
+        "@fingerprintjs/fingerprintjs": "^3.4.2",
+        "axios": "0.28.0",
+        "crypto-js": "^4.0.0",
+        "jsencrypt": "^3.1.0",
+        "jwt-decode": "^2.2.0",
+        "sm-crypto": "^0.3.7"
+      },
+      "engines": {
+        "node": ">=8.9"
+      }
+    },
     "node_modules/aws-sign2": {
       "version": "0.7.0",
       "resolved": "https://registry.npmmirror.com/aws-sign2/-/aws-sign2-0.7.0.tgz",
@@ -2649,6 +2674,16 @@
       "resolved": "https://registry.npmmirror.com/aws4/-/aws4-1.13.0.tgz",
       "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g=="
     },
+    "node_modules/axios": {
+      "version": "0.28.0",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-0.28.0.tgz",
+      "integrity": "sha512-Tu7NYoGY4Yoc7I+Npf9HhUMtEEpV7ZiLH9yndTCoNhcpBH0kwcvFbzYN9/u5QKI5A6uefjsNNWaz5olJVYS62Q==",
+      "dependencies": {
+        "follow-redirects": "^1.15.0",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
     "node_modules/backo2": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/backo2/-/backo2-1.0.2.tgz",
@@ -3324,8 +3359,7 @@
     "node_modules/crypto-js": {
       "version": "4.2.0",
       "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
-      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
-      "optional": true
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
     },
     "node_modules/csrf": {
       "version": "3.1.0",
@@ -5620,6 +5654,11 @@
       "resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz",
       "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
     },
+    "node_modules/jsencrypt": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz",
+      "integrity": "sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A=="
+    },
     "node_modules/jsesc": {
       "version": "2.5.2",
       "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz",
@@ -5785,6 +5824,11 @@
         "safe-buffer": "^5.0.1"
       }
     },
+    "node_modules/jwt-decode": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/jwt-decode/-/jwt-decode-2.2.0.tgz",
+      "integrity": "sha512-86GgN2vzfUu7m9Wcj63iUkuDzFNYFVmjeDm2GzWpUk+opB0pEpMsw6ePCMrhYkumz2C1ihqtZzOMAg7FiXcNoQ=="
+    },
     "node_modules/keygrip": {
       "version": "1.1.0",
       "resolved": "https://registry.npmmirror.com/keygrip/-/keygrip-1.1.0.tgz",
@@ -7497,6 +7541,11 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
     "node_modules/pseudomap": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/pseudomap/-/pseudomap-1.0.2.tgz",
@@ -8630,6 +8679,14 @@
         "is-arrayish": "^0.3.1"
       }
     },
+    "node_modules/sm-crypto": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmmirror.com/sm-crypto/-/sm-crypto-0.3.13.tgz",
+      "integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==",
+      "dependencies": {
+        "jsbn": "^1.1.0"
+      }
+    },
     "node_modules/smart-buffer": {
       "version": "4.2.0",
       "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz",

+ 1 - 0
server/package.json

@@ -13,6 +13,7 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
+    "authing-js-sdk": "^4.23.51",
     "parse-dashboard": "^5.4.0",
     "parse-server": "^7.0.0"
   },