飞书开发学习笔记(六)-网页应用免登

飞书开发学习笔记(六)-网页应用免登

一.上一例的问题修正

在上一例中,飞书登录查看网页的界面显示是有误的,看了代码,理论上登录成功之后,应该显示用户名等信息。
最后的res.nickName是用户名,res.i18nName.en_us是英文名

function showUser(res) {// 展示用户信息// 头像$("#img_div").html(`<img src="${res.avatarUrl}" width="100%" height=""100%/>`);// 名称$("#hello_text_name").text(lang === "zh_CN" || lang === "zh-CN"? `${res.nickName}`: `${res.i18nName.en_us}`);// 欢迎语$("#hello_text_welcome").text(lang === "zh_CN" || lang === "zh-CN" ? "欢迎使用飞书" : "welcome to Feishu");
}

而这个页面结果显然是不正确的
在这里插入图片描述
仔细检查Console.log,原来是接收config参数时出现了错误,python使用了分步调试,发现问题出在

ticket = auth.get_ticket()

在这里插入图片描述
再进一步分析变量,发现是

APP_ID = os.getenv("APP_ID")
APP_SECRET = os.getenv("APP_SECRET")
FEISHU_HOST = os.getenv("FEISHU_HOST")

三个变量获取错误,最后锁定源头是.env中三个变量定义以后没有换行,应该是在查看后不小心修改了格式保存引起的,在改为标准格式,每个变量定义后换行,重新读入,成功获取用户名等信息,
这也是一个小插曲,对于学习来说却耽误了一个小时。
在这里插入图片描述
这才是登录成功的界面
在这里插入图片描述

二.应用免登

紧接着上一案例,就是应用免登的介绍
应用免登:应用免登

免登流程
网页应用免登为网页应用提供了获取当前登录用户的飞书身份的能力,网页应用免登流程如下:

步骤一:获取用户登录预授权码。
步骤二:使用预授权码获取user_access_token。
步骤三:获取用户信息并完成登录。
步骤四:刷新已过期的user_access_token。

实际应用分为四步,每个步骤与上一例基本相同。
不同点为第三步,配置网页应用访问地址时,需要同时填写
在这里插入图片描述
进入飞书开放平台: https://open.feishu.cn/app
完成重定向URL填写
在这里插入图片描述
从工作台打开应用后,同时获取用户基本信息
在这里插入图片描述
在这里插入图片描述

三.代码结构

3.1代码结构树

|── README.zh.md     ----- 说明文档
|── doc_images     ----- 说明文档的图片资源
|── public
|  |── svg     ----- 前端图形文件
|  |──index.css     ----- 前端展示样式
|  |── index.js     ----- 前端主要交互代码(免登流程函数、用户信息展示函数)
|── templates
|  |──err_info.html     ----- 前端错误信息展示页面
|  |── index.html     ----- 前端用户信息展示页面
|── auth.py     ----- 服务端免登流程类、错误信息处理类
|── server.py     ----- 服务端核心业务代码
|── requirements.txt     ----- 环境配置文件
└── .env     ----- 全局默认配置文件,主要存储 App ID 和 App Secret 等

3.2 index.html ----- 前端用户信息展示页面

在页面内部的js中,利用login_info判断是否已经登陆,如果已经登陆,就直接展示;否则走登陆程序
判断则转到index.js

<!DOCTYPE html>
<link rel="stylesheet" href="/public/index.css"/>
<html lang="en">
<head><meta charset="UTF-8"/><title>网页应用</title><script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head><!-- 在html文档中引入 JSSDK -->
<!-- JS 文件版本在升级功能时地址会变化,如有需要(比如使用新增的 API),请重新引用「网页应用开发指南」中的JSSDK链接,确保你当前使用的JSSDK版本是最新的。-->
<script type="text/javascript"
src="https://lf1-cdn-tos.bytegoofy.com/goofy/lark/op/h5-js-sdk-1.5.16.js">
</script><!-- 在页面上添加VConsole方便调试-->
<script src="https://unpkg.com/vconsole/dist/vconsole.min.js"></script>
<script>
// VConsole will be exported to `window.VConsole` by default.
var vConsole = new window.VConsole();
</script><body>
<div><div class="img"><!-- 头像 --><div id="img_div" class="img_div"> </div><span class="hello_text">Hello</span><!-- 名称 --><div id="hello_text_name" class="hello_text_name"></div><!-- 欢迎语 --><div id="hello_text_welcome" class="hello_text_welcome"></div></div><!-- 飞书icon --><div class="icon"><img src="../public/svg/icon.svg"/></div>
</div><!-- 在html文档中引入本demo免登流程函数apiAuth()和用户信息展示函数showUser(res) -->
<script src="/public/index.js"></script>
<!-- 根据服务端传来的参数login_info判断走免登流程还是直接展示用户信息 -->
<script>const login_info = '{{ login_info }}';console.log("login info: ", login_info);if (login_info == "alreadyLogin") {const user_info = JSON.parse('{{ user_info | tojson | safe }}');console.log("user: ", user_info.name);$('document').ready(showUser(user_info))} else {$('document').ready(apiAuth())}
</script></body>
</html>

3.3 index.js ----- 前端主要交互代码(免登流程函数、用户信息展示函数)

服务器端Route:server.py
1.服务器端执行get_appid获取app_id
2.调用JSAPI tt.requestAuthCode 获取 authorization code,参数为app_id,存储在 res.code
3.服务器端fetch把code传递给接入方服务端Route: callback,并获得user_info
4.成功后showUser(res)

let lang = window.navigator.language;
console.log(lang);function apiAuth() {if (!window.h5sdk) {console.log('invalid h5sdk')alert('please open in feishu')return}// 通过服务端的Route: get_appid获取app_id// 服务端Route: get_appid的具体内容请参阅服务端模块server.py的get_appid()函数// 为了安全,app_id不应对外泄露,尤其不应在前端明文书写,因此此处从服务端获取fetch(`/get_appid`).then(response1 => response1.json().then(res1 => {console.log("get appid succeed: ", res1.appid);// 通过error接口处理API验证失败后的回调window.h5sdk.error(err => {throw('h5sdk error:', JSON.stringify(err));});// 通过ready接口确认环境准备就绪后才能调用APIwindow.h5sdk.ready(() => {console.log("window.h5sdk.ready");console.log("url:", window.location.href);// 调用JSAPI tt.requestAuthCode 获取 authorization codett.requestAuthCode({appId: res1.appid,// 获取成功后的回调success(res) {console.log("getAuthCode succeed");//authorization code 存储在 res.code// 此处通过fetch把code传递给接入方服务端Route: callback,并获得user_info// 服务端Route: callback的具体内容请参阅服务端模块server.py的callback()函数fetch(`/callback?code=${res.code}`).then(response2 => response2.json().then(res2 => {console.log("getUserInfo succeed");// 示例Demo中单独定义的函数showUser,用于将用户信息展示在前端页面上showUser(res2);})).catch(function (e) {console.error(e)})},// 获取失败后的回调fail(err) {console.log(`getAuthCode failed, err:`, JSON.stringify(err));}})})})).catch(function (e) { // 从服务端获取app_id失败后的处理console.error(e)})
}function showUser(res) {// 展示用户信息// 头像$('#img_div').html(`<img src="${res.avatar_url}" width="100%" height=""100%/>`);// 名称$('#hello_text_name').text(lang === "zh_CN" || lang === "zh-CN" ? `${res.name}` : `${res.en_name}`);// 欢迎语$('#hello_text_welcome').text(lang === "zh_CN" || lang === "zh-CN" ? "欢迎使用飞书" : "welcome to Feishu");
}

3.4 auth.py ----- 服务端免登流程类、错误信息处理类

1.获取authorize_app_access_token,参数为 “app_id” 和 “app_secret”,获取到USER_ACCESS_TOKEN_URI
2.获取authorize_user_access_token,url 通过self._gen_url(USER_ACCESS_TOKEN_URI)获得
3.authorize_user_access_token的headers中 :“app_access_token” 位于HTTP请求的请求头
4.response.json().get(“data”)获得user_access_token


def authorize_app_access_token(self):# 获取 app_access_token, 依托于飞书开放能力实现. # 文档链接: https://open.feishu.cn/document/ukTMukTMukTM/ukDNz4SO0MjL5QzM/auth-v3/auth/app_access_token_internalurl = self._gen_url(APP_ACCESS_TOKEN_URI)# "app_id" 和 "app_secret" 位于HTTP请求的请求体req_body = {"app_id": self.app_id, "app_secret": self.app_secret}response = requests.post(url, req_body)Auth._check_error_response(response)self._app_access_token = response.json().get("app_access_token")# 这里也可以拿到user_info
# 但是考虑到服务端许多API需要user_access_token,如文档:https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/document-docx/docx-overview
# 建议的最佳实践为先获取user_access_token,再获得user_info
# user_access_token后续刷新可以参阅文档:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/authen-v1/authen/refresh_access_token
def authorize_user_access_token(self, code):# 获取 user_access_token, 依托于飞书开放能力实现. # 文档链接: https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/authen-v1/authen/access_tokenself.authorize_app_access_token()url = self._gen_url(USER_ACCESS_TOKEN_URI)# “app_access_token” 位于HTTP请求的请求头headers = {"Content-Type": "application/json","Authorization": "Bearer " + self.app_access_token,}# 临时授权码 code 位于HTTP请求的请求体req_body = {"grant_type": "authorization_code", "code": code}response = requests.post(url=url, headers=headers, json=req_body)Auth._check_error_response(response)self._user_access_token = response.json().get("data").get("access_token")def get_user_info(self):# 获取 user info, 依托于飞书开放能力实现.  # 文档链接: https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/authen-v1/authen/user_infourl = self._gen_url(USER_INFO_URI)# “user_access_token” 位于HTTP请求的请求头headers = {"Authorization": "Bearer " + self.user_access_token,"Content-Type": "application/json",}response = requests.get(url=url, headers=headers)Auth._check_error_response(response)# 如需了解响应体字段说明与示例,请查询开放平台文档: # https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/authen-v1/authen/access_tokenreturn response.json().get("data")

3.5 server.py ----- 服务端核心业务代码

1.关键函数callback():
2.通过传递的code先获取 user_access_token
3. 再获取 user info
4. 将 user info 存入 session
5. get_home()查询USER_INFO_KEY 是否在session,在则免登,否则就登陆程序

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import loggingfrom auth import Auth
from dotenv import load_dotenv, find_dotenv
from flask import Flask, render_template, session, jsonify, request# 日志格式设置
LOG_FORMAT = "%(asctime)s - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S"
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT)# const
# 在session中存储用户信息 user info 所需要的对应 session key
USER_INFO_KEY = "UserInfo"
# secret_key 是使用 flask session 所必须有的
SECRET_KEY = "ThisIsSecretKey"# 从 .env 文件加载环境变量参数
load_dotenv(find_dotenv())# 初始化 flask 网页应用
app = Flask(__name__, static_url_path="/public", static_folder="./public")
app.secret_key = SECRET_KEY
app.debug = True# 获取环境变量值
APP_ID = os.getenv("APP_ID")
APP_SECRET = os.getenv("APP_SECRET")
FEISHU_HOST = os.getenv("FEISHU_HOST")# 用获取的环境变量初始化免登流程类Auth
auth = Auth(FEISHU_HOST, APP_ID, APP_SECRET)# 业务逻辑类
class Biz(object):@staticmethoddef home_handler():# 主页加载流程return Biz._show_user_info()@staticmethoddef login_handler():# 需要走免登流程return render_template("index.html", user_info={"name": "unknown"}, login_info="needLogin")@staticmethoddef login_failed_handler(err_info):# 出错后的页面加载流程return Biz._show_err_info(err_info)# Session in Flask has a concept very similar to that of a cookie, # i.e. data containing identifier to recognize the computer on the network, # except the fact that session data is stored in a server.@staticmethoddef _show_user_info():# 直接展示session中存储的用户信息return render_template("index.html", user_info=session[USER_INFO_KEY], login_info="alreadyLogin")@staticmethoddef _show_err_info(err_info):# 将错误信息展示在页面上return render_template("err_info.html", err_info=err_info)# 出错时走错误页面加载流程Biz.login_failed_handler(err_info)
@app.errorhandler(Exception)
def auth_error_handler(ex):return Biz.login_failed_handler(ex)# 默认的主页路径
@app.route("/", methods=["GET"])
def get_home():# 打开本网页应用会执行的第一个函数# 如果session当中没有存储user info,则走免登业务流程Biz.login_handler()if USER_INFO_KEY not in session:logging.info("need to get user information")return Biz.login_handler()else:# 如果session中已经有user info,则直接走主页加载流程Biz.home_handler()logging.info("already have user information")return Biz.home_handler()@app.route("/callback", methods=["GET"])
def callback():# 获取 user info# 拿到前端传来的临时授权码 Codecode = request.args.get("code")# 先获取 user_access_tokenauth.authorize_user_access_token(code)# 再获取 user infouser_info = auth.get_user_info()# 将 user info 存入 sessionsession[USER_INFO_KEY] = user_inforeturn jsonify(user_info)@app.route("/get_appid", methods=["GET"])
def get_appid():# 获取 appid# 为了安全,app_id不应对外泄露,尤其不应在前端明文书写,因此此处从服务端传递过去return jsonify({"appid": APP_ID})if __name__ == "__main__":# 以debug模式运行本网页应用# debug模式能检测服务端模块的代码变化,如果有修改会自动重启服务app.run(host="0.0.0.0", port=3000, debug=True)

以上,免登流程完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/144804.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IntelliJ IDEA 安装 GitHub Copilot插件 (最新)

注意&#xff1a; GitHub Copilot 插件对IDEA最低版本要求是2021.2&#xff0c;建议直接用2023.3&#xff0c;一次到位反正后续要升级的。 各个版本的依赖关系&#xff0c;请参照&#xff1a; ##在线安装&#xff1a; 打开 IntelliJ IDEA扩展商店&#xff0c;输入 "Git…

数据结构-二叉树力扣题

目录 1.相同的树 2.二叉树中查找值为x的节点 3.单值二叉树 4.对称二叉树 5.二叉树的前序遍历 6.另一颗树的子树 层序遍历&#xff1a; 7.二叉树遍历 8.判断二叉树是否是完全二叉树 一个特殊的性质&#xff1a; 1.相同的树 题目链接&#xff1a;力扣&#xff08;LeetC…

新版软考高项试题分析精选(四)

请点击↑关注、收藏&#xff0c;本博客免费为你获取精彩知识分享&#xff01;有惊喜哟&#xff01;&#xff01; 1、一般而言&#xff0c;大型软件系统中实现数据压缩功能&#xff0c;工作在OSI参考模型的&#xff08; &#xff09;。 A.应用层 B.表示层 C.会话层 D.网络层…

【验证码逆向专栏】百某网数字九宫格验证码逆向分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未…

vue中ref的用法

vue中ref的用法 在项目中使用ref时有时候直接取值,有时候返回的却是一个数组,不知其中缘由,后查了一下ref用法,所以总结一下. 1.绑定在dom元素上时&#xff0c;用起来与id差不多&#xff0c;通过this.$refs来调用: <div id"passCarEchart" ref"passCarEch…

[PyTorch][chapter 63][强化学习-QLearning]

前言&#xff1a; 这里结合走迷宫的例子,重点学习一下QLearning迭代更新算法 0,1,2,3,4 是房间&#xff0c;之间绿色的是代表可以走过去。 5为出口 可以用下图表示 目录&#xff1a; 策略评估 策略改进 迭代算法 走迷宫实现Python 一 策略评估 强化学习最终是为了…

构建Docker基础镜像(ubuntu20.04+python3.9.10+pytorch-gpu-cuda11.8)

文章目录 一、前置条件1.创建 ubuntu 镜像源文件【sources.list】2.下载 python 安装包【Python-3.9.10.tgz】 二、构建方法1.构建目录2.创建DockerFile3.打包镜像 一、前置条件 1.创建 ubuntu 镜像源文件【sources.list】 内容如下 deb http://mirrors.aliyun.com/ubuntu/ …

SQL编写规范【干货】

编写本文档的目的是保证在开发过程中产出高效、格式统一、易阅读、易维护的SQL代码。 1 编写目 2 SQL书写规范 3 SQL编写原则 获取所有软件开发资料&#xff1a;点我获取

【Java 进阶篇】JQuery 遍历:发现元素的魔法之旅

欢迎来到 JQuery 的奇妙世界&#xff0c;一个充满活力和灵感的地方。在这个世界里&#xff0c;我们将一起探讨 JQuery 的遍历功能&#xff0c;这是一个让你轻松发现和操作网页元素的神奇工具。无需太多前端经验&#xff0c;只要有一颗探险的心&#xff0c;你就能在 JQuery 遍历…

智能家居小程序

1、设备 系统板&#xff1a;STM32F103C8T6 温湿度传感器&#xff1a;DHT11 光照度传感器&#xff1a; BH1750 WIFI模块&#xff1a;ESP8266-015 蜂鸣器&#xff1a; 立创EDC画板子&#xff0c;然后微立创下单

【数据结构】希尔排序(最小增量排序)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;数据结构 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有帮助…

java springboot在当前测试类中添加临时属性 不影响application和其他范围

目前 我们的属性基本都写在 application.yml 里面了 但是 如果 我们只是想做一下临时变量的测试 有没有办法实现呢&#xff1f; 显然是有的 这里 我们还是先在application.yml中去写一个 test属性 下面加个prop 然后 我们尝试在测试类中 获取一下这个属性 直接用 Value 读取…

C语言——求1/1-1/2+1/3-......+1/99-1/100的值

#include<stdio.h> int main() {int i 1;double sum 0;int flage 1;for(i 1;i < 100; i){sumflage*1.0/i;flage -flage; //正负号}printf("%lf\n",sum);return 0; }

场景图形管理-多视图多窗口渲染示例(4)

多视图多窗口渲染示例的代码如程序清单8-6所示 // 多视图多窗口渲染示例 void compositeViewer_8_6(const string &strDataFolder) {// 创建一个CompositeViewer对象osg::ref_ptr<osgViewer::CompositeViewer> viewer new osgViewer::CompositeViewer();// 创建两个…

Outlook关闭过去事件的提醒

Outlook关闭过去事件的提醒 故障现象 最近Outlook中推出的新功能让我们可以选择自动关闭过去事件的提醒。 目前这个功能暂时只向当月通道的Office 365 订阅者发布。 这些用户升级到1810版本后&#xff0c;可以在不想收到已发生事件提醒的时候通过下面的步骤自动忽略过去事件…

day22_mysql

今日内容 零、 复习昨日 一、MySQL 一、约束 1.1 约束 是什么? 约束,即限制,就是通过设置约束,可以限制对数据表数据的插入,删除,更新 怎么做? 约束设置的语法,大部分是 create table 表名( 字段 数据类型(长度) 约束, 字段 数据类型(长度) 约束 );1.1 数据类型 其实数据类型…

在Spring Boot中使用进程内缓存和Cache注解

在Spring Boot中使用内缓存的时候需要预先知道什么是内缓存&#xff0c;使用内缓存的好处。 什么是内缓存 内缓存&#xff08;也称为进程内缓存或本地缓存&#xff09;是指将数据存储在应用程序的内存中&#xff0c;以便在需要时快速访问和检索数据&#xff0c;而无需每次都从…

Jmeter控制RPS

一、前言 ​ RPS (Request Per Second)一般用来衡量服务端的吞吐量&#xff0c;相比于并发模式&#xff0c;更适合用来摸底服务端的性能。我们可以通过使用 JMeter 的常数吞吐量定时器来限制每个线程的RPS。对于RPS&#xff0c;我们可以把他理解为我们的TPS&#xff0c;我们就不…

解决Github上的README无法显示图片

首先感谢博主的思路&#xff1a;思路 最近写了点东西提交到git 发现本地能查看md里的图片用的相对路径&#xff0c;提交到github就看不见&#xff0c;并且发现不只是我自己的仓库看不见&#xff0c;其他人的我也看不见。那就有问题了 解决&#xff1a;正常使用相对路径&…

使用Tauri开发桌面应用

本文是对视频 Tauri入门教程[1]的学习与记录 Tauri官网[2] 对 node版本有要求 创建项目及目录介绍: 项目的目录结构如下 可以安装推荐的插件 执行npm run tauri build出错,根据 https://github.com/tauri-apps/tauri/issues/7430 执行 yarn add -D tauri-apps/cli && y…