无密码验证:服务器 | Linux 中国
无密码验证可以让你只输入一个 email 而无需输入密码即可登入系统。这是一种比传统的电子邮件/密码验证方式登入更安全的方法。-- Nicolás Parada
有用的原文链接请访问文末的“
原文链接
”获得可点击的文内链接、全尺寸原图和相关文章。致谢编译自 | http://nicolasparada.netlify.com/posts/passwordless-auth-server/
作者 | Nicolás Parada
译者 | qhwdw ?? ?? ?? ?? ?? 共计翻译:
117
篇 贡献时间:212 天无密码验证可以让你只输入一个 email 而无需输入密码即可登入系统。这是一种比传统的电子邮件/密码验证方式登入更安全的方法。
下面我将为你展示,如何在
Go
[1]
中实现一个 HTTP API 去提供这种服务。流程
? 用户输入他的电子邮件地址。? 服务器创建一个临时的一次性使用的代码(就像一个临时密码一样)关联到用户,然后给用户邮箱中发送一个“魔法链接”。? 用户点击魔法链接。? 服务器提取魔法链接中的代码,获取关联的用户,并且使用一个新的 JWT 重定向到客户端。? 在每次有新请求时,客户端使用 JWT 去验证用户。必需条件
? 数据库:我们为这个服务使用了一个叫CockroachDB
[2]
的 SQL 数据库。它非常像 postgres,但它是用 Go 写的。? SMTP 服务器:我们将使用一个第三方的邮件服务器去发送邮件。开发的时我们使用mailtrap
[3]
。Mailtrap 发送所有的邮件到它的收件箱,因此,你在测试时不需要创建多个假邮件帐户。从
Go 的主页
[4]
上安装它,然后使用 go version(1.10.1 atm)命令去检查它能否正常工作。从
CockroachDB 的主页
[5]
上下载它,展开它并添加到你的 PATH 变量中。使用 cockroach version(2.0 atm)命令检查它能否正常工作。数据库模式
现在,我们在 GOPATH 目录下为这个项目创建一个目录,然后使用 cockroach start 启动一个新的 CockroachDB 节点:
cockroach start
--
insecure
--
host
127.0
.
0.1
它会输出一些内容,找到 SQL 地址行,它将显示像 postgresql://root@127.0.0.1:26257?sslmode=disable 这样的内容。稍后我们将使用它去连接到数据库。
使用如下的内容去创建一个 schema.sql 文件。
DROP DATABASE IF EXISTS passwordless_demo CASCADE
;
CREATE DATABASE IF NOT EXISTS passwordless_demo
;
SET DATABASE
=
passwordless_demo
;
CREATE TABLE IF NOT EXISTS
users
(
id
UUID PRIMARY KEY DEFAULT gen_random_uuid
(),
email STRING UNIQUE
,
username STRING UNIQUE
);
CREATE TABLE IF NOT EXISTS verification_codes
(
id
UUID PRIMARY KEY DEFAULT gen_random_uuid
(),
user_id UUID NOT NULL REFERENCES
users
ON DELETE CASCADE
,
created_at TIMESTAMPTZ NOT NULL DEFAULT now
()
);
INSERT INTO
users
(
,
username
)
VALUES
(
"john@passwordless.local"
,
"john_doe"
);
这个脚本创建了一个名为 passwordless_demo 的数据库、两个名为 users 和 verification_codes 的表,以及为了稍后测试而插入的一些假用户。每个验证代码都与用户关联并保存创建时间,以用于去检查验证代码是否过期。
在另外的终端中使用 cockroach sql 命令去运行这个脚本:
cat
schema
.
sql
|
cockroach sql
--
insecure
环境配置
需要配置两个环境变量:SMTP_USERNAME 和 SMTP_PASSWORD,你可以从你的 mailtrap 帐户中获得它们。将在我们的程序中用到它们。
Go 依赖
我们需要下列的 Go 包:
?github.com/lib/pq
[6]
:它是 CockroachDB 使用的 postgres 驱动?github.com/matryer/way
[7]
: 路由器?github.com/dgrijalva/jwt-go
[8]
: JWT 实现go
get
-
u github
.
com
/
lib
/
pq
go
get
-
u github
.
com
/
matryer
/
way
go
get
-
u github
.
com
/
dgrijalva
/
jwt
-
go
代码
初始化函数
创建 main.go 并且通过 init 函数里的环境变量中取得一些配置来启动。
var
config
struct
{
port
int
appURL
*
url
.
URL
databaseURL string
jwtKey
[]
byte
smtpAddr string
smtpAuth smtp
.
Auth
}
func
init
()
{
config
.
port
,
_
=
strconv
.
Atoi
(
env
(
"PORT"
,
"80"
))
config
.
appURL
,
_
=
url
.
Parse
(
env
(
"APP_URL"
,
"http://localhost:"
+
strconv
.
Itoa
(
config
.
port
)+
"/"
))
config
.
databaseURL
=
env
(
"DATABASE_URL"
,
"postgresql://root@127.0.0.1:26257/passwordless_demo?sslmode=disable"
)
config
.
jwtKey
=
[]
byte
(
env
(
"JWT_KEY"
,
"super-duper-secret-key"
))
smtpHost
:=
env
(
"SMTP_HOST"
,
"smtp.mailtrap.io"
)
config
.
smtpAddr
=
net
.
JoinHostPort
(
smtpHost
,
env
(
"SMTP_PORT"
,
"25"
))
smtpUsername
,
ok
:=
os
.
LookupEnv
(
"SMTP_USERNAME"
)
if
!
ok
{
log
.
Fatalln
(
"could not find SMTP_USERNAME on environment variables"
)
}
smtpPassword
,
ok
:=
os
.
LookupEnv
(
"SMTP_PASSWORD"
)
if
!
ok
{
log
.
Fatalln
(
"could not find SMTP_PASSWORD on environment variables"
)
}
config
.
smtpAuth
=
smtp
.
PlainAuth
(
""
,
smtpUsername
,
smtpPassword
,
smtpHost
)
}
func
env
(
key
,
fallbackValue string
)
string
{
v
,
ok
:=
os
.
LookupEnv
(
key
)
if
!
ok
{
return
fallbackValue
}
return
v
}
env 函数允许我们去获得环境变量,不存在时返回一个回退值。
主函数
var
db
*
sql
.
DB
func main
()
{
var
err error
if
db
,
err
=
sql
.
Open
(
"postgres"
,
config
.
databaseURL
);
err
!=
nil
{
log
.
Fatalf
(
"could not open database connection: %v\n"
,
err
)
}
defer db
.
Close
()
if
err
=
db
.
Ping
();
err
!=
nil
{
log
.
Fatalf
(
"could not ping to database: %v\n"
,
err
)
}
router
:=
way
.
NewRouter
()
router
.
HandleFunc
(
"POST"
,
"/api/users"
,
jsonRequired
(
createUser
))
router
.
HandleFunc
(
"POST"
,
"/api/passwordless/start"
,
jsonRequired
(
passwordlessStart
))
router
.
HandleFunc
(
"GET"
,
"/api/passwordless/verify_redirect"
,
passwordlessVerifyRedirect
)
router
.
Handle
(
"GET"
,
"/api/auth_user"
,
authRequired
(
getAuthUser
))
addr
:=
fmt
.
Sprintf
(
":%d"
,
config
.
port
)
log
.
Printf
(
"starting server at %s
- 高血压有救了!半斤萝卜一瓶醋,让你的血压一降到底!已验证呢!
- 中科院国家高端智库论坛举办 解读中国经济发展密码
- 密码锁频频出故障 全家人难进难出
- 记者暗访"私房菜":藏身小区密码天天换 消费高人均过千
- 妻子去世,留下一张不知密码的银行卡,怎样才能取到钱?
- 速度修改密码!工信部称近10万个邮箱疑被黑客控制
- 普通人也想赚一个亿?欧神为你破译财富密码
- 免费试用|经 3 000 余篇世界文献验证的转染试剂
- 中方入侵希拉里的邮件服务器?外交部连举三实例驳斥
- 美官员称中方入侵希拉里邮件服务器 外交部回应