注册登录案例
客户端 UI 实现
包含五个页面
- 闪屏页
- 登录页
- 内容主页
- 设置页
- 主页
- 发现页
使用Form控件实现表单提交,并处理返回键拦截。
服务端实现
原理
理解两个概念
- Authentication:用户认证,一般是通过用户名密码来确认用户是否为系统中合法主体
- Authorization:用户授权,一般是给系统中合法主体授予相关资源访问权限
OAuth 2.0
OAuth2是一个关于授权的开放标准,核心思路是通过各类认证手段(具体什么手段OAuth2不关心)认证用户身份,并颁发token(令牌),使得第三方应用可以使用该token(令牌)在限定时间、限定范围访问指定资源。
关于OAuth2.0 的中文解释,可以阅读这篇博客 理解OAuth 2.0

这里主要包含四个角色
- Client:第三方应用,获取资源服务器提供的资源
- Resource Owner:拥有被访问资源的用户
- Authorization Server:授权服务器,提供授权许可code、令牌token等
- Resource Server:资源服务器,拥有被访问资源的服务器,需要通过token来确定是否有权限访问
在较小的业务实现中,资源服务器和认证服务器是一个服务器,但是较大规模的部署中通常将其构建为单独的组件。
OAuth 2.0 规定了四种授权方式
- 授权码(authorization-code)
- 隐藏式(implicit)
- 密码式(password):
- 客户端凭证(client credentials)
其中授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌,这种方式是最常用的授权方式。各大应用内的QQ,微信,微博登录等是使用的这种方式。
本案例使用的是密码式。如果你高度信任某个应用,也允许把用户名和密码直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为密码式(password)。
JWT机制及其与OAuth 2.0的区别
JWT全称为Json Web Token,号称新一代的认证技术。OAuth2中使用token验证用户登录合法性,但token最大的问题是不携带用户信息,资源服务器无法在本地进行验证,每次对于资源的访问,资源服务器都需要向认证服务器发起请求。普通的OAuth2颁发的就是一串随机hash字符串,本身无意义,而JWT使用一种特殊格式的token,token是有特定含义的,分为三部分:Header、Payload和Signature。这三部分均用base64进行编码,并使用.进行分隔。一个典型的JWT格式的token类似xxxxx.yyyyy.zzzzz
简单说,JWT是基于token的认证协议的实现,而OAuth2是一种授权框架,提供了一套详细的授权协议标准。JWT通常适合一次性的身份认证、API的鉴权等场景,这些场景能充分发挥JWT无状态以及分布式验证的优势。当然,OAuth 2.0也可以和JWT结合起来使用。
案例实现
只需要明确,当用户使用用户名和密码进行登录时,服务端会返回访问令牌access_token、刷新令牌refreshToken、访问令牌过期时间给客户端,客户端把令牌保存下来,下次访问服务器只需带上访问令牌证明已经登录,当令牌过期时,我们需要使用刷新令牌,请求新的访问令牌即可
环境准备
检查aqueduct框架的版本
pub global run aqueduct --version
去官网查看版本号,并更新版本
pub upgrade
pub global activate aqueduct 3.3.0+1
可以使用如下命令查看模板,并创建支持auth的模板
# 查看支持的模板
pub global run aqueduct create list-templates
# -t 指定模板,并创建名为login_service的工程
pub global run aqueduct create -t db_and_auth login_service
进入工程,修改config.yaml文件,配置数据库(提前创建好指定的数据库dart_db)
database:
username: postgres
password: '123456'
host: localhost
port: 5432
databaseName: dart_db
生成相应表,执行数据库迁移(详情查看 aqueduct 中文文档)
pub global run aqueduct db generate
注意,执行迁移时有两种方式
命令行指定(需在命令行中指定数据库的用户名、密码、端口等信息)
pub global run aqueduct db upgrade --connect postgres://username:password@localhost:5432/my_application配置文件指定(推荐)
在项目根路径下创建配置文件
database.yaml,添加配置项。需注意,这里与config.yaml配置文件的结构是不同的。username: postgres password: '123456' host: localhost port: 5432 databaseName: dart_db然后执行命令
pub global run aqueduct db upgrade
用户模型定义
打开工程下model/user.dart文件,我们可以修改用户的定义,增加更多字段
如下示例
class User extends ManagedObject<_User>
implements _User, ManagedAuthResourceOwner<_User> {
@Serialize(input: true, output: false)
String password;
}
class _User extends ResourceOwnerTableDefinition {
// 用户昵称
@Column(nullable: true)
String nickName;
// 头像
@Column(nullable: true)
String avatar;
// 创建时间
DateTime createTime;
}
生成API文档
可查看中文文档
执行命令生成 Swagger 规范
pub global run aqueduct document > swagger.json
打开 swagger 工具 ,导入生成的json文件,可视化查看接口
设置客户端标识符
pub global run aqueduct auth add-client --id com.arcticfox.flutterregisterlogin
启动服务器
pub global run aqueduct serve
验证码处理
使用Dart 的 image库 生成验证码图像。
修改路由,添加验证码获取接口。
常见的媒体格式类型如下:
text/html: HTML格式text/plain:纯文本格式text/xml: XML格式image/gif:gif图片格式image/jpeg:jpg图片格式image/png:png图片格式
总结
通常使用HTTPS协议,解决传输过程中Token被截获的安全问题。在Dart服务器之前部署反向代理服务器,推荐使用Nginx。
移动端的验证码方案应考虑手机短信验证
客户端和服务端都应该做验证码校验,防止爬虫绕过客户端发起请求。
aqueduct框架默认创建的access_token有效期为24小时,可以在AuthController中修改有效时间if (grantType == "password") { final token = await authServer.authenticate( username, password, basicRecord.username, basicRecord.password, requestedScopes: scopes); return AuthController.tokenResponse(token); } /// 设置 authenticate 方法中的 expiration 参数 /// Future<AuthToken> authenticate( /// String username, String password, String clientID, String clientSecret, /// {Duration expiration = const Duration(hours: 24), /// List<AuthScope> requestedScopes})
公众号“编程之路从0到1”
