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

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

一.上一例的问题修正

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

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

相关文章

嵌入式养成计划-54----ARM--异常处理流程

一百三十五、异常处理流程 135.1 arm处理器工作模式 135.2 异常源和异常模式关系 135.2.1 异常源 异常源就是引发处理器进入相应异常模式 135.2.2 对应关系 异常模式异常源FIQ模式FIQ类型异常源引发处理器进入FIQ模式IRQ模式IRQ类型异常源引发处理器进入IRQ模式SVC模式上电…

工具及方法 - 手机扫条码工具: SCANDIT APP

一般扫个链接使用微信扫一扫即可。扫具体条码&#xff0c;可以在微信里搜索小程序&#xff0c;打开也能扫&#xff0c;得到条码内容。 还有其他方式&#xff0c;比如使用淘宝、百度等APP也可以直接扫码条码&#xff0c;还能得到更多的信息。 使用百度的话&#xff0c;不扫条码…

3D造型渲染软件DAZ Studio mac中文版介绍

DAZ Studio mac是一款3D造型和渲染软件&#xff0c;由 Daz 3D 公司开发。它允许用户创建、编辑、动画化并渲染精美的数字图像与动画。DAZ Studio 还提供了一个虚拟的3D艺术家工作室环境&#xff0c;让用户可以轻松地设置场景、布置角色和应用材质。 用户可以通过 DAZ Studio 中…

图像实时采集系统

本方案主要在于解决图像实时采集系统对算法校正的仿真实验&#xff0c;以及采集卡接收电路的验证。 由于图像实时跟踪处理系统需要大量的外场景实验&#xff0c;大部分时候只能通过采集的现场图像以在电脑软件中读取图片的形式来进行验证算法&#xff0c;而无法通过采集卡对接…

《网络协议》05. 网络通信安全 · 密码技术

title: 《网络协议》05. 网络通信安全 密码技术 date: 2022-09-10 15:16:15 updated: 2023-11-12 07:03:52 categories: 学习记录&#xff1a;网络协议 excerpt: 网络通信安全&#xff08;ARP 欺骗&#xff0c;DoS & DDoS&#xff0c;SYN 洪水攻击&#xff0c;LAND 攻击&a…

基于flask+bootstrap4实现的注重创作的轻博客系统项目源码

一个注重创作的轻博客系统 作为一名技术人员一定要有自己的博客&#xff0c;用来记录平时技术上遇到的问题&#xff0c;把技术分享出去就像滚雪球一样会越來越大&#xff0c;于是我在何三博客的基础上开发了[l4blog]&#xff0c;一个使用python开发的轻量博客系统&#xff0c;…

github镜像访问方法

https://ghproxy.com/ &#xff08;GitHub 文件 , Releases , archive , gist , raw.githubusercontent.com 文件代理加速下载服务&#xff09; https://mirror.ghproxy.com/ 在这个网址后面直接加上github的网址&#xff0c;如&#xff1a; https://mirror.ghproxy.com/htt…

ZYNQ实验--Petalinux--Linux C 编程入门

Linux C 编程入门 在 Windows 下我们可以使用各种各样的 IDE 进行编程&#xff0c;比如强大的 Visual Studio。Ubuntu 下也有一些可以进行编程的工具&#xff0c;但是大多都只是编辑器&#xff0c;也就是只能进行代码编辑&#xff0c;如果要编译的话就需要用到 GCC 编译器&…

Protobuf 语法

Protobuf语法 1.1.1. 基本规范 文件以.proto做为文件后缀&#xff0c;除结构定义外的语句以分号结尾 结构定义可以包含&#xff1a;message、service、enum rpc方法定义结尾的分号可有可无 Message命名采用驼峰命名方式&#xff0c;字段命名采用小写字母加下划线分隔方式 …

如何使用内网穿透实现远程公网访问windows node.js的服务端

使用Nodejs搭建简单的web网页并实现公网访问 前言 Node.js是建立在谷歌Chrome的JavaScript引擎(V8引擎)的Web应用程序框架。 Node.js自带运行时环境可在Javascript脚本的基础上可以解释和执行(这类似于JVM的Java字节码)。这个运行时允许在浏览器以外的任何机器上执行JavaScri…

轻量封装WebGPU渲染系统示例<31>- 若干线条对象(源码)

线条对象包括: AABB包围盒&#xff0c;OBB包围盒, 曲线&#xff0c;直线&#xff0c;圆&#xff0c;坐标轴&#xff0c;视锥体线框&#xff0c;方形网格等。 当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/LineOb…

go学习之接口知识

文章目录 接口1.接口案例代码展示2.基本介绍3.基本语法4.应用场景介绍5.注意事项和细节6.接口编程经典案例7.接口与继承之间的比较8.面向对象编程--多态1&#xff09;基本介绍2&#xff09;快速入门3&#xff09;接口体现多态的两种形式 9.类型断言1&#xff09;先看一个需求2&…