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

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

一.上一例的问题修正

在上一例中,飞书登录查看网页的界面显示是有误的,看了代码,理论上登录成功之后,应该显示用户名等信息。
最后的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.rhkb.cn/news/194456.html

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

相关文章

Git目录不对,即当前文件夹不对应git仓库

报错信息是&#xff1a; fatal: not a git repository (or any of the parent directories): .git 如&#xff1a; 是当前文件夹不对应git仓库&#xff0c;一般在git clone之后&#xff0c;需要进入下一级文件夹才对应仓库。 在文件夹看&#xff0c;本层中没有.git文件夹&…

Unity开发之C#基础-集合(字典)(Dictionary)

前言 Hello 兄弟们 一转眼俩月又过去了&#xff08;失踪人口回归&#xff09; 不出意外的是出意外了 失踪了两个月 有点对不起我这为数不多的粉丝们 实不相瞒忙的焦头烂额 也没心情写博客 实在对不住各位 好了长话短说 今天这篇文章是讲解c#当中的新的一种集合的表现&#xff…

这7个“小毛病”项目经理必须克服

大家好&#xff0c;我是老原。 项目经理干项目可能不在行&#xff0c;但“踩坑”、“背锅”一定在行。 当上项目经理不容易&#xff0c;当好项目经理更不容易&#xff0c;有永远填不完的坑和背不完的锅。 如果要问项目经理都踩过哪些坑&#xff0c;那真的是太多了&#xff0…

《C++避坑神器·二十一》回调函数使用

1、不涉及类的回调函数使用&#xff1a; 注意&#xff1a;提供给别人使用的就是注册回调函数的那个接口 void registHeightcallback(CallbackFun callback, void* contex) 2、涉及类的回调函数使用&#xff1a; 一般提供给别人使用的都是注册回调函数的接口 在类中定义的回调函…

哈夫曼编码详细证明步骤

关于哈夫曼编码&#xff0c;想必大家都很清楚&#xff0c;这里来讲解一下他的详细证明方法。代码的话就不给了网上一大堆&#xff0c;我再给也没什么意思&#xff0c;这里主要讲明白正确性的证明。我将采取两种方法进行证明&#xff0c;一种常规的方法&#xff0c;还有一种采取…

langchain(1):使用LangChain 调用 openai 的 text/chat model

文章目录 重要参考OPENAI API调用 Text 模型调用 Chat 模型消息角色 Chat 模型 vs Text 模型 通过 LangChain 调用 Text 和 Chat 模型调用 text 模型调用 chat 模型 重要参考 langchain 中文网 langchain api openai api 文档 huggingface LangChain 是一个全方位的、基于大…

中国芯片金字塔成形,商业化拐点将至

其作始也简&#xff0c;其将毕也钜。 传说埃及用时30年建成左赛尔金字塔&#xff0c;成为亘古不灭的世界奇迹。在今天&#xff0c;中国芯片产业走过8年“国产替代”历程&#xff0c;国产芯片的“金字塔”体系业已初具雏形&#xff0c;展现出蓬勃的发展潜力。 2023年是补全自主…

c++ 经典服务器开源项目Tinywebserver如何运行

第一次直接按作者的指示&#xff0c;运行sh ./build.sh,再运行./server&#xff0c;发现不起作用&#xff0c;localhost:9006也是拒绝访问的状态&#xff0c;后来摸索成功了发现&#xff0c;运行./server之后&#xff0c;应该是启动状态&#xff0c;就是不会退出&#xff0c;而…

计算机视觉基础(8)——齐次坐标系与相机内外参

前言 本节我们将学习到透视投影、齐次坐标系等基础知识&#xff0c;在这些基础知识上&#xff0c;进一步了解到相机的参数&#xff0c;相机参数分为相机外参和相机内参&#xff0c;相机外参是从世界坐标系到相机坐标系&#xff0c;相机内参是从相机坐标系到图像坐标系。 一、透…

成功解决:com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl.

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 前言 使用Spring 整合 mybatis的时候 报错…

k8s_base

应用程序在服务器上部署方式的演变,互联网发展到现在为止 应用程序在服务器上部署方式 历经了3个时代1. 传统部署 优点简单 缺点就是操作系统的资源是有限制的&#xff0c;比如说操作系统的磁盘&#xff0c;内存 比如说我8G&#xff0c;部署了3个应用程序&#xff0c;当有一天…

Django测试环境搭建及ORM查询(创建外键|跨表查询|双下划线查询 )

文章目录 一、表查询数据准备及测试环境搭建模型层前期准备测试环境搭建代码演示 二、ORM操作相关方法三、ORM常见的查询关键字四、ORM底层SQL语句五、双下划线查询数据查询&#xff08;双下划线&#xff09;双下划线小训练Django ORM __双下划线细解 六、ORM外键字段创建基础表…

国内领先的五大API接口供应商

API&#xff08;Application Programming Interface&#xff09;接口&#xff0c;现在很多应用系统中常用的开放接口&#xff0c;对接相应的系统、软件功能&#xff0c;简化专业化的程序开发。作者用过的国内比较稳定的API接口供应商有如下几家&#xff0c;大家可以参考选择&am…

苍穹外卖-day11

苍穹外卖-day11 课程内容 Apache ECharts营业额统计用户统计订单统计销量排名Top10 功能实现&#xff1a;数据统计 数据统计效果图&#xff1a; 1. Apache ECharts 1.1 介绍 Apache ECharts 是一款基于 Javascript 的数据可视化图表库&#xff0c;提供直观&#xff0c;生…

调整Windows键盘上只能看到拼音而无法看到实际的文本以及关闭输入法悬浮窗方法

一、输入法设置 如果您在键盘上只能看到拼音而无法看到实际的文本&#xff0c;这可能是因为您的输入法设置为中文拼音输入法或其他仅显示拼音的输入法。 要解决这个问题&#xff0c;您可以尝试以下方法&#xff1a; 1. 切换输入法&#xff1a;按下 Shift Alt 组合键或 Wind…

轻量封装WebGPU渲染系统示例<27>- 浮点RTT纹理(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/FloatRTT.ts 当前示例运行效果: 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript源码如下: const floatRTT { diffuse: { uuid: "rtt0", rttTe…

2023年亚太杯数学建模思路 - 案例:ID3-决策树分类算法

文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模…

Backblaze 2023 Q3硬盘故障质量报告解读

作为一家在2021年在美国纳斯达克上市的云端备份公司&#xff0c;Backblaze一直保持着对外定期发布HDD和SSD的故障率稳定性质量报告&#xff0c;给大家提供了一份真实应用场景下的稳定性分析参考数据。2023年度之前发布的两次报告&#xff0c;请参考&#xff1a; Backblaze发布2…

qnx 工程目录创建工具 addvariant

文章目录 前言一、addvariant 是什么二、addvariant 使用实例1. variant names 参数说明2. 创建一个可执行文件工程3. 创建一个动态库工程 总结参考资料 前言 本文主要介绍如何在qnx 开发环境中创建工程目录及其相关的配置文件(common.mk, Makefile 文件等) 软件版本&#xff…

Java毕业设计心得体会

1. 开始准备选题、开题报告 大四上学期开学时开始准备论文的&#xff0c;首先是确定论文主题&#xff0c;看自己想做什么毕业设计&#xff0c;可以选取之前接触过的&#xff0c;做过的东西&#xff0c;这样快一些&#xff0c;我之前一直是学Java的&#xff0c;就打算直接用Jav…