Teo 0.2.16 发布!用户登录只需 5 行代码,高度可扩展!


Teo是以结构为核心的声明式网络后端开发框架,支持Node.js、Python和Rust语言,支持手动编写和AI编写。它能够节省80%的开发时间,大大缩短开发时间和开发成本,符合当下的技术环境和社会环境,是新一代的网络开发框架。

在0.2.16版本中,我们新增了用户登录、token验证等功能。多的不说,请看演示。

Teo schema

声明一个服务器,包含增删改查分组聚合是非常简单的,甚至不需要编程代码,使用编程代码,开发者可以访问ORM API和定义自定义的路由,编写自定义的路由先进后出中间件。

connector {provider: .sqlite,url: "sqlite:./database.sqlite"} server {bind: ("0.0.0.0", 5052)}model User {@id @autoIncrement @readonlyid: Int@unique @onSet($if($presents, $isEmail))email: String@writeonly @onSet($presents.bcrypt.salt)  password: String}

如上所示,声明一个含有邮箱和密码的用户表,并产生增删改查分组聚合等API,是非常容易的。在schema中,我们也指定了服务器监听的端口和连接的数据库。Teo采用连接池来管理数据库连接,非常的高效和性能。使用命令`teo serve`即可启动服务器。

用户登录

为这个用户表增加登录功能,非常的简单,请看更新后的代码。

connector {provider: .sqlite,url: "sqlite:./database.sqlite"} server {bind: ("0.0.0.0", 5052)} @identity.tokenIssuer($identity.jwt(expired: 3600 * 24 * 365))@identity.jwtSecret(ENV["JWT_SECRET"]!)model User {@id @autoIncrement @readonlyid: Int@unique @onSet($if($presents, $isEmail)) @identity.idemail: String@writeonly @onSet($presents.bcrypt.salt)@identity.checker($get(.value).presents.bcrypt.verify($self.get(.password).presents))password: String include handler identity.signIninclude handler identity.identity} middlewares [identity.identityFromJwt(secret: ENV["JWT_SECRET"]!)]

我们增加了一些修饰符,指定了token的encode方式,这里采用业内最流行的JWT token,并指定了过期时间。我们标记了采用邮箱登录,采用密码验证,并且增加了两个由模版handler构成的新的route handler。这两个route handler完全不需要手动代码编写。在文件内增加中间件,这个中间件会验证token并且设置当前的登录者。这段代码,总共比之前的没有登录功能的时候,增加了5行代码。

辅助验证信息

在实际开发中,我们在登录的时候,经常要用户点击图片验证码或输入一些来自图片中的文字。这些辅助的验证信息,也可以通过Teo的API进行传递,话不多说,上例子。

connector {provider: .sqlite,url: "sqlite:./database.sqlite"} server {bind: ("0.0.0.0", 5052)} @identity.tokenIssuer($identity.jwt(expired: 3600 * 24 * 365))@identity.jwtSecret(ENV["JWT_SECRET"]!)model User {@id @autoIncrement @readonlyid: Int@unique @onSet($if($presents, $isEmail)) @identity.idemail: String@writeonly @onSet($presents.bcrypt.salt)@identity.checker($do($get(.value).presents.bcrypt.verify($self.get(.password).presents)).do($get(.companions).presents.get(.imageAuthToken).presents))password: String@virtual @writeonly @identity.companionimageAuthToken: String? include handler identity.signIninclude handler identity.identity} middlewares [identity.identityFromJwt(secret: ENV["JWT_SECRET"]!)]

我们又增加了四行代码。imageAuthToken是一个虚拟字段,它不会在数据库中出现,但是却会在请求中出现,这样的字段是配合API功能的。比如,输入旧密码更改当前密码,这个功能,就需要定义一个oldPassword这样的虚拟字段。在这个例子中,我们只是检查imageAuthToken是否存在,而实际应用中,我们通过编写pipeline item跟第三方接口验证验证码即可。

动态的token过期时间

有一些论坛网站,token过期时间是用户自己选择的,现在有这个需求的产品很少,但是Teo同样支持。

connector {provider: .sqlite,url: "sqlite:./database.sqlite"} server {bind: ("0.0.0.0", 5052)} @identity.tokenIssuer($identity.jwt(expired: $get(.expired).presents))@identity.jwtSecret(ENV["JWT_SECRET"]!)model User {@id @autoIncrement @readonlyid: Int@unique @onSet($if($presents, $isEmail)) @identity.idemail: String@writeonly @onSet($presents.bcrypt.salt)@identity.checker($do($get(.value).presents.bcrypt.verify($self.get(.password).presents)).do($get(.companions).presents.get(.imageAuthToken).presents))password: String@virtual @writeonly @identity.companionimageAuthToken: String?@virtual @writeonly @identity.companionexpired: Int64? include handler identity.signIninclude handler identity.identity} middlewares [identity.identityFromJwt(secret: ENV["JWT_SECRET"]!)]

把JWT的expire参数改为动态获取,就可以得到用户传来的expire参数了。如果expire参数填一个很低的秒数,这个token很快就会过期。

账号封禁

实现封禁账号是很简单的,利用声明式的优势。告诉Teo,什么样的账号判定为被封禁即可。

connector {provider: .sqlite,url: "sqlite:./database.sqlite"} server {bind: ("0.0.0.0", 5052)} @identity.tokenIssuer($identity.jwt(expired: $get(.expired).presents))@identity.jwtSecret(ENV["JWT_SECRET"]!)@identity.validateAccount($get(.enabled).presents.eq(true))model User {@id @autoIncrement @readonlyid: Int@unique @onSet($if($presents, $isEmail)) @identity.idemail: String@writeonly @onSet($presents.bcrypt.salt)@identity.checker($do($get(.value).presents.bcrypt.verify($self.get(.password).presents)).do($get(.companions).presents.get(.imageAuthToken).presents))password: String@virtual @writeonly @identity.companionimageAuthToken: String?@virtual @writeonly @identity.companionexpired: Int64?@migration(default: true)enabled: Bool include handler identity.signIninclude handler identity.identity} middlewares [identity.identityFromJwt(secret: ENV["JWT_SECRET"]!)]

在这里更新的例子中,通过新增的一行模型声明,Teo知道了,enabled字段为false的账号即是封禁账号。封禁的账号不能进行登录,其token也会失效。

第三方身份集成

实际开发中,我们经常要做微信登录,支付宝登录等平台身份登录。具体的前端跳转逻辑,我们不做演示。在后端,绑定账号就是更新字段,我们演示如何使用三方token进行登录。

connector {provider: .sqlite,url: "sqlite:./database.sqlite"} server {bind: ("0.0.0.0", 5052)} @identity.tokenIssuer($identity.jwt(expired: $get(.expired).presents))@identity.jwtSecret(ENV["JWT_SECRET"]!)@identity.validateAccount($message($get(.enabled).presents.eq(true), "this account is blocked"))model User {@id @autoIncrement @readonlyid: Int@unique @onSet($if($presents, $isEmail)) @identity.idemail: String@writeonly @onSet($presents.bcrypt.salt)@identity.checker($do($get(.value).presents.bcrypt.verify($self.get(.password).presents)).do($get(.companions).presents.get(.imageAuthToken).presents))password: String@virtual @writeonly @identity.companionimageAuthToken: String?@virtual @writeonly @identity.companionexpired: Int64?@migration(default: true) @default(true)enabled: Bool@identity.id @uniquethirdPartyId: String?@virtual @writeonly @identity.checker($get(.value).presents.valid)thirdPartyToken: String? include handler identity.signIninclude handler identity.identity} middlewares [identity.identityFromJwt(secret: ENV["JWT_SECRET"]!)]

在表中新增三方id和三方token,使用自定义的pipeline item与三方沟通获得token是否有效。一旦有效,就给用户在我们系统中登录,获得我们的token。这时,进行signIn请求,我们不用输入邮箱和密码,输入三方id和token即可。

手机验证码/邮箱验证码/手机密码/邮箱密码

Teo支持任意的身份字段和验证方式相匹配。我们来看一个验证码验证的案例。这一次需要编写编程代码。

connector {provider: .sqlite,url: "sqlite:./database.sqlite"} server {bind: ("0.0.0.0", 5052)} entity {provider: .rust,dest: "./src/entities"} declare pipeline item validateAuthCode<T>: T -> String @identity.tokenIssuer($identity.jwt(expired: $get(.expired).presents))@identity.jwtSecret(ENV["JWT_SECRET"]!)@identity.validateAccount($message($get(.enabled).presents.eq(true), "this account is blocked"))model User {@id @autoIncrement @readonlyid: Int@unique @onSet($if($presents, $isEmail)) @identity.id@presentWithout(.phoneNumber)email: String?@writeonly @onSet($presents.bcrypt.salt)@identity.checker($do($get(.value).presents.bcrypt.verify($self.get(.password).presents)).do($get(.companions).presents.get(.imageAuthToken).presents))password: String?@virtual @writeonly @identity.companionimageAuthToken: String?@virtual @writeonly @identity.companionexpired: Int64?@migration(default: true) @default(true)enabled: Bool@identity.id @uniquethirdPartyId: String?@virtual @writeonly @identity.checker($get(.value).presents.valid)thirdPartyToken: String?@onSet($if($presents, $regexMatch(/\\+?[0-9]+/))) @identity.id@presentWithout(.email) @uniquephoneNumber: String?@virtual @writeonly @identity.checker($validateAuthCode)authCode: String? include handler identity.signIninclude handler identity.identity} model AuthCode {@id @autoIncrement @readonlyid: Int@presentWithout(.phoneNumber) @uniqueemail: String?@presentWithout(.email) @uniquephoneNumber: String?@onSave($randomDigits(4))code: String} middlewares [identity.identityFromJwt(secret: ENV["JWT_SECRET"]!)]

我们新增了一张AuthCode表,用来保存用户收到的验证码。我们用编程代码访问Teo的ORM API来进行验证码验证。代码如下,有三个语言版本,选择你采用的语言即可。

Node.js版本

import { App } from '@teocloud/teo'import { AuthCodeWhereUniqueInput, Teo, User } from './entities' const app = new App() app.mainNamespace().defineValidatorPipelineItem("validateAuthCode", async (checkArgs: any, _, user: User, teo: Teo) => {const finder: AuthCodeWhereUniqueInput = {}if (checkArgs.ids.email) {finder.email = user.email!}if (checkArgs.ids.phoneNumber) {finder.phoneNumber = user.phoneNumber!}const authCode = await teo.authCode.findUnique({where: finder})if (!authCode) {return "auth code not found"}if (authCode.code !== checkArgs.value) {return "auth code is wrong"}}) app.run()

Python版本

from __future__ import annotationsfrom typing import Anyfrom asyncio import runfrom teo import Appfrom entities import Teo, User async def main():app = App()async def validate_auth_code(checker_args: dict[str, Any], _, user: User, teo: Teo):finder = {}if checker_args['ids'].get('email') is not None:finder['email'] = user.emailif checker_args['ids'].get('phoneNumber') is not None:finder['phoneNumber'] = user.phone_numberauth_code = await teo.auth_code.find_unique({"where": finder})if auth_code is None:return "auth code not found"if auth_code.code != checker_args.value:return "auth code is wrong" app.main_namespace().define_validator_pipeline_item("validateAuthCode", validate_auth_code) run(main())

Rust版本

pub mod entities; use entities::{Teo, User};use indexmap::indexmap;use tokio::main;use teo::prelude::{pipeline::item::validator::Validity, App, Result, Value, Error}; #[main]async fn main() -> Result<()> {let app = App::new()?;app.main_namespace_mut().define_validator_pipeline_item("validateAuthCode", move |check_args: Value, user: User, teo: Teo| async move {let mut finder = Value::Dictionary(indexmap!{});let check_args = check_args.as_dictionary().unwrap();let ids = check_args.get("ids").unwrap().as_dictionary().unwrap();if ids.contains_key("email") {finder.as_dictionary_mut().unwrap().insert("email".to_owned(), ids.get("email").unwrap().clone());}if ids.contains_key("phoneNumber") {finder.as_dictionary_mut().unwrap().insert("phoneNumber".to_owned(), ids.get("phoneNumber").unwrap().clone());}let auth_code = teo.auth_code().find_unique(finder).await?;match auth_code {Some(auth_code) => if auth_code.code().as_str() == check_args.get("value").unwrap().as_str().unwrap() {Ok::<Validity, Error>(Validity::Valid)} else {Ok(Validity::Invalid("auth code is wrong".to_owned()))},None => Ok(Validity::Invalid("auth code not found".to_owned()))}});app.run().await}

这三组代码做的事情是一样的,创建一个叫作"validateAuthCode"的pipeline item。在schema中,我们看到了这个pipeline item的位置,我们声明并使用了它。这个方法内部,我们根据用户输入的电话号码或邮箱地址,来查找一个验证码,一旦输入正确,我们给予用户登录,否则给予用户错误信息:验证码不存在或验证码错误。

举一反三

在刚刚展示的例子中,我们列举了常常容易被想到的token用法。如果实现一个人最多允许几个token,和invalid某个token,则需要单独建表并编写自定义的验证代码,像上面的验证码验证那样。其他的各种验证和token方式,也可以通过Teo强大的生命方式来实现。

声明式的魅力

在这逐步几次更新的schema中,我们看到了声明式编程的魅力。一切都那么简洁、易读、可描述。不再有难以看得懂的代码。因为声明式很紧凑,逻辑混乱的bug也很难出现。这样的代码,易于编写,易于部署。我们的开发者用户已经开始采用AI编程的方式来编写Teo服务器代码了。

开发文档

我们的官网内写有丰富的开发文档,支持白天模式和夜间模式的阅读。安装流程,快速开始指南,教程,概念,专题指南和API文档一应俱有。

官网:https://teocloud.io

支持我们

我们从2022年编写Teo至今,已经接近两年了,它从最初的仅支持Rust和MongoDB的框架,到现在支持三种语言,支持主流SQL和MongoDB,支持自定义handler和中间件,支持生成前端请求客户端,一路过来真的很辛苦。我们不断科研创新、探索研究,克服重重技术难题,把它做到现在。它是完全开源免费的框架。我们做它的目的,是让开发者能过上不卷、没有太大压力的好日子,让创业者和企业能够节省成本,更容易不被技术所负累,更容易成功。

我们十分需要您的支持,请关注我们的公众号,在Gitee为我们点一颗星。

Gitee: https://gitee.com/teocloud/teo

联系我们

在使用过程中,如果遇到任何困难,想要与我们沟通,或是提出功能需求,是非常容易的。带着点赞截图和公众号关注截图,添加我们的微信群群管微信caofz007,即可加入我们的用户群。

加入我们团队

目前我们都是以志愿者形式参与的开发,我是项目的发起者和主要编写者,框架核心,语言绑定,编辑器插件,网站都是我全职编写的。我们需要核心开发,前端开发,编辑器插件开发等人手,来把它做得更好。

我们现在有清晰的开发和推广,和商业化路线。在Teo具有一定规模和可见的良好未来的时候,即在具有一定底牌、资格、合适的时候,我们会积极努力寻找融资,把它做大。也会给予我们的项目贡献者,符合贡献价值的奖励。添加我们的伙伴caofz007这个微信,加入我们,成为我们的一部分。


相關推薦

2024-05-05

我们会在未来的0.3.0版本,随同其他核心功能升级,一同发布Admin Dashboard的正式版。正式版功能会包括: 可以配置的数据统计仪表盘 完善的数据筛选、滚动加载和组件类型配置 更强大的表单和富文本编辑 其它更强

2023-11-15

用参数 支持 支持 通知公告 系统通知公告信息发布维护 支持 支持 操作日志 系统正常操作日志记录和查询 系统异常信息日志记录和查询 支持 支持 登录日志 系统登录日志记录查询包含登录异常 支

2023-11-16

用参数 支持 支持 通知公告 系统通知公告信息发布维护 支持 支持 操作日志 系统正常操作日志记录和查询 系统异常信息日志记录和查询 支持 支持 登录日志 系统登录日志记录查询包含登录异常 支

2023-11-28

用参数 支持 支持 通知公告 系统通知公告信息发布维护 支持 支持 操作日志 系统正常操作日志记录和查询 系统异常信息日志记录和查询 支持 支持 登录日志 系统登录日志记录查询包含登录异常 支

2023-09-26

用参数 支持 支持 通知公告 系统通知公告信息发布维护 支持 支持 操作日志 系统正常操作日志记录和查询 系统异常信息日志记录和查询 支持 支持 登录日志 系统登录日志记录查询包含登录异常 支

2023-11-28

用参数 支持 支持 通知公告 系统通知公告信息发布维护 支持 支持 操作日志 系统正常操作日志记录和查询 系统异常信息日志记录和查询 支持 支持 登录日志 系统登录日志记录查询包含登录异常 支

2022-10-08

通过安装扩展来获得更多语言和功能的支持。 近日微软发布了 1.72 版本,更新内容如下: 工具栏自定义:隐藏/显示工具栏操作 用户现在可以从工具栏上隐藏操作。右键单击工具栏中的任何操作,并选择其隐藏命令或任何

2023-09-09

行优化配置,可编译成独立的组件,单独以组件形式进行发布。 代码以共享模块的方式进行单独维护开发,降低现有工程代码复杂度,便于后续功能的扩展和代码的复用。 [3]界面预览 Dromara 开源社区 一、社区愿景

2023-04-18

深度操作系统(deepin)是一款致力于为全球用户提供美观易用、安全稳定服务的Linux发行版,也是全球排名领先的来自中国团队研发的Linux发行版。点击查看deepin全球排名:https://distrowatch.com/table.php?distribution=deepin deepin20.9版本

2023-04-01

合性案例,助力开发人员对新兴技术的学习和掌握。 发布背景 自11月24日,Spring Boot 3.0 以及 Spring Cloud 2022.0.0、Spring Cloud Tencent 1.8.2 等全新版本发布,整个Java 社区也步入的 Java 17 和 Spring Boot 3 的新时代。紧跟 Java 技术和 Spring

2022-11-13

Dante Cloud 是一款企业级微服务架构和服务能力开发平台。首个全面拥抱 Spring Authorization Server 的版本,基于Spring Boot 2.7.5、Spring Cloud 2021.0.5、Spring Cloud Alibaba 2021.0.4.0、 Spring Authorization Server 0.3.1、Nacos 2.1.2 等最新版本开发的多租户

2023-11-03

这是一个开发良好体验的开始! userId : 当前发起请求的 用户 id。 参数 : 通常是游戏前端传入的值。 响应: 通常是业务方法返回的值 ,业务框架会把这个返回值推送到游戏前端。 时间: 执行业务方法总耗时,我们可根据业务

2022-10-24

Dante Cloud 是一款企业级微服务架构和服务能力开发平台。首个全面拥抱 Spring Authorization Server 的版本,基于Spring Boot 2.7.5、Spring Cloud 2021.0.4、Spring Cloud Alibaba 2021.0.4.0、 Spring Authorization Server 0.3.1、Nacos 2.1.2 等最新版本开发的多租户

2024-02-22

master Spring Boot 3.2 和 Spring Cloud 2023.0.0 JDK 17 主要发布分支 可使用,但 Spring Cloud Alibaba、Tencent 等生态并为完全适配,部分功能受限 develop Spring Boot 3.2 和 Spring Cloud 2023.0.0 JDK 17 Development 分支 新功能、ISSUE 均以