Vue 前端配合 Devise Token Auth 忘记密码重置操作

缘起

近来开发个活动报名站,后端使用 Ruby on Rails,前端使用 Vue。由于是SPA,传统的 cookie 法就不太适宜,使用流行的 JWT。因此选用 Devise Token Auth gem 来处理用户模型与 token 发行、验证等工作。

密码重置流程

其他模块使用正常且方便。而忘记密码重置涉及到邮件,reset link 发行等就显得麻烦。根据官方 wiki 记载 reset password flow

  • 在前端页面中填写邮箱地址输入框,有按钮可以发送重置邮件
  • POST /auth/password JSON: {email, redirect_url}
    这里 redirect_url 为前端页面,有填写密码框
  • 后端生成 reset_password_token 与链接填入邮件,形如 http://localhost:3000/api/v1/auth/password/edit?config=default
    &redirect_url=http%3A%2F%2Flocalhost%3A8080%2Freset
    &reset_password_token=L77a9yx2JHehPCgVoQ2_
  • 前端跳转 redirect url
  • 前端用 reset_password_token、client、uid 作 headers PUT /auth/password 完成重置

观察链接,发现其中包含reset_password_tokenredirect_url,点击跳转前端链接 http://localhost:8080/reset?

access-token=D3zacAdKrGwRPxcQ-9rnXQ&

client=CwF0zNBw9ovM3KmHaCmIIg&

client_id=CwF0zNBw9ovM3KmHaCmIIg&

config=default&expiry=1538140242&reset_password=true&

token=D3zacAdKrGwRPxcQ-9rnXQ&

uid=1%40e.com

可见 headers 信息都有包含在 URL

问题

网络中 devise token auth 重置密码相关的文章中使用 MVC,API的相对少,用 Vue 就更少。还得自己摸索。

问题1: hash 路径错位

最初在项目中 Vue-router 使用 hash 模式,故在请求 json 中填写 redirect_url 包含 #。而 rails 处理后忽略掉 # 造成路径错误,这里可以改用 history 模式解决。URL 中的参数可用 route.query 取出。

另有 router navigation guard 的应设定确保不跳转,不然链接变成 http://localhost:8080/reset?

access-token=D3zacAdKrGwRPxcQ-9rnXQ&

client=CwF0zNBw9ovM3KmHaCmIIg&

client_id=CwF0zNBw9ovM3KmHaCmIIg&

config=default&expiry=1538140242&reset_password=true&

token=D3zacAdKrGwRPxcQ-9rnXQ&uid=1%40e.com#/login

这里本人设定未登录跳转登录页,跳转信息与重置 URL 混杂造成混乱。

Vue 复用密码修改组件(若有登录后修改选项的话)可抽离 request 函数,通过判别是否有登录信息(如 Vuex state、sessionStorage)构造不同的 headers 发送请求。

问题2: 后端路径 host 变前端 host(password#edit)

原来 devise token anth 生成 reset password url 时取前端 host name,要是后端域名与之不同就出错。如前端 a.com,后端b.com,则邮件中生成 http://a.com/api/v1/auth/password/edit?...。看起来默认当作 MVC,是同域。

而在本地测试时同域端口不同没发现问题,可见靠同域不同端口区分。而部署到服务器用 80,443 端口就不区分了,怎么办呢?最后反代前端 /api 到 rails 解决。如 caddy 写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 前端
a.com {
root /var/www/xxx
}

# 将错误路径导向 api
a.com/api {
proxy / locahost:3000/api {
transparent
}
}

# 后端
b.com {
proxy / locahost:3000 {
transparent
}
}

Bonus: custom Devise Token Auth registration controller

设定用户删除账户时会同时删除创建的活动、与报名信息(人都跑路,活动显然没了,报名也吹了)防止出错(活动详细页获取不到创建人信息,报名信息有 null)

Devise Token Auth 中 用户注册、删除在 registration controller,默认删除操作只是完成用户删除不管别的。这时需要自定义。创建自定义类继承基类

1
2
3
4
5
6
7
class Users::RegistrationsController < DeviseTokenAuth::RegistrationsController
def destroy
@resource.events.each(&:destroy)
@resource.bookings.each(&:destroy)
super
end
end

由于在基类中已经获取到资源

1
2
3
module DeviseTokenAuth
class RegistrationsController < DeviseTokenAuth::ApplicationController
before_action :set_user_by_token, only: [:destroy, :update] # 这里 return @resource

因此可以直接使用,而不是向 super 加块处理。因为 super 的方法中块在 destroy 后

1
2
3
4
5
6
7
8
9
def destroy
if @resource
@resource.destroy
yield @resource if block_given? #destroy 后找不到用户相关联的活动
render_destroy_success
else
render_destroy_error
end
end

后记

其他问题好找,这个就没有了(估计第一篇Rails 5 API + Devise Token Auth + Vue 的密码重置流)。完成这个也算尾声了,现在将要部署,而 production 如何设定,dockerfile 如何写就一脸懵逼。若有运维可以赐教就感激不尽。


Vue 前端配合 Devise Token Auth 忘记密码重置操作
https://blog.ckyol.moe/2018/09/15/DTAresetPasswdWithVue/
作者
ϵ( 'Θ' )϶
发布于
2018年9月15日
许可协议