此包可以轻松处理涉及重置密码的流程。如何分发允许重置密码的令牌由消费者决定。
将 Reset
添加到包依赖项中 (在您的 Package.swift
文件中)
dependencies: [
...,
.package(url: "https://github.com/nodes-vapor/reset.git", from: "1.0.0")
]
以及您的目标 (例如 “App”)
targets: [
...
.target(
name: "App",
dependencies: [... "Reset" ...]
),
...
]
接下来,将 Resources/Views/Reset
文件夹复制/粘贴到您的项目中,以便能够使用提供的 Leaf 文件。这些文件可以按照指定响应部分中的说明进行更改,但建议无论如何都将此文件夹复制到您的项目中。这使您可以更轻松地跟踪更新,并且如果您稍后决定不使用您自己的自定义 leaf 文件,您的项目也将正常工作。
首先确保您已在所有需要的地方导入了 Reset
import Reset
Reset 附带一个轻量级的 provider,我们需要在 configure.swift
文件中的 configure
函数中注册它
try services.register(ResetProvider<User>(config: ResetConfig(
name: AppConfig.app.name,
baseURL: AppConfig.app.url,
signer: ExpireableJWTSigner(
expirationPeriod: 3600, // 1 hour
signer: .hs256(
key: env(EnvironmentKey.Reset.signerKey, "secret-reset"
).convertToData())
)
))
)
有关确认类型符合 PasswordResettable
的更多信息,请参阅制作 PasswordResettable
模型。
确保添加相关的 Reset 路由,例如在您的 configure.swift 或 routes.swift 中
services.register(Router.self) { container -> EngineRouter in
let router = EngineRouter.default()
try router.useResetRoutes(User.self, on: container)
return router
}
此包附带一个小的 Leaf 标签,用于将 Reset 相关信息(例如项目名称和项目 URL)传递给 Leaf。要将其添加到您的项目中,请执行以下操作
public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
services.register { _ -> LeafTagConfig in
var tags = LeafTagConfig.default()
tags.useResetLeafTags()
return tags
}
}
要使您的模型符合 PasswordResettable
,需要进行一些设置。以下示例基于您想要添加重置密码支持的 User
模型。
首先要定义的是请求重置密码流程所需的数据以及实际重置密码的数据。它可能看起来像这样
extension User: PasswordResettable {
// ...
public struct RequestReset: RequestCreatable, Decodable, HasReadableUsername {
static let readableUsernameKey = \RequestReset.username
public let username: String
}
public struct ResetPassword: RequestCreatable, Decodable, HasReadablePassword {
static let readablePasswordKey = \ResetPassword.password
public let password: String
}
// ..
}
基本上,需要用户名(也可以是电子邮件)来请求重置流程,并且需要新密码来提交密码更改。
请注意
RequestReset
如何符合HasReadableUsername
。这使 Reset 能够实现find
方法来自动查找用户。
一旦用户请求重置密码,将调用 sendPasswordReset
函数。该实现可以通过电子邮件发送 URL,或者只是在短信中包含令牌。如何分发此内容由实施者决定。
这是一个使用 Mailgun 包发送包含重置密码 URL 的电子邮件的示例
extension User: PasswordResettable {
// ...
public func sendPasswordReset(
url: String,
token: String,
expirationPeriod: TimeInterval,
context: ResetPasswordContext,
on req: Request
) throws -> Future<Void> {
let mailgun = try req.make(Mailgun.self)
let expire = Int(expirationPeriod / 60) // convert to minutes
return try req
.make(LeafRenderer.self)
.render(ViewPath.Reset.resetPasswordEmail, ["url": url, "expire": expire])
.map(to: String.self) { view in
String(bytes: view.data, encoding: .utf8) ?? ""
}
.map(to: Mailgun.Message.self) { html in
Mailgun.Message(
from: "donotreply@reset.com",
to: self.email,
subject: "Reset password",
text: "Please turn on html to view this email.",
html: html
)
}
.flatMap(to: Response.self) { message in
try mailgun.send(message, on: req)
}
.transform(to: ())
}
// ..
}
在某些情况下,您可能希望为多个不同的重置密码流程设置多个签名者。一个例子可能是处理常规的重置密码流程,以及在创建用户时自动重置密码。通过实现 signer
函数,您可以处理这种情况
extension User: PasswordResettable {
// ...
public enum MyResetPasswordContext: HasRequestResetPasswordContext {
case userRequestedToResetPassword
case newUserWithoutPassword
public static func requestResetPassword() -> MyResetPasswordContext {
return .userRequestedToResetPassword
}
}
public func signer(
for context: MyResetPasswordContext,
on container: Container
) throws -> ExpireableJWTSigner {
let resetConfig: ResetConfig<User> = try container.make() // The default signer
let myConfig: MyConfig = try container.make() // Some project specific config that holds the extra signer
switch context {
case .userRequestedToResetPassword: return resetConfig.signer
case .newUserWithoutPassword: return myConfig.newUserSetPasswordSigner
}
}
// ..
}
请注意,如果您想处理多个签名者,则需要实现您自己的
Context
。
Reset 使用的所有端点和响应都可以被覆盖。Reset 为以下情况提供响应
这是一个小例子,其中请求重置密码应仅通过 API 公开
let customResponse = ResetResponses(
resetPasswordRequestForm: { req in
return try HTTPResponse(status: .notFound).encode(for: req)
},
resetPasswordUserNotified: { req in
return try HTTPResponse(status: .noContent).encode(for: req)
},
resetPasswordForm: { req, user in
return try req
.make(LeafRenderer.self)
.render("MyPathForShowingResetForm")
.encode(for: req)
},
resetPasswordSuccess: { req, user in
return try req
.make(LeafRenderer.self)
.render("MyPathForShowingResetPasswordSuccess")
.encode(for: req)
}
)
然后可以在注册 provider 时使用此实例,如添加 Provider中所述。
或者,除了在 ResetConfig
中传入 ResetResponses
之外,还可以传入您自己实现的 ResetControllerType
以获得完全的自定义性。
此包由 Nodes 的 Vapor 团队开发和维护。
此包是根据 MIT 许可证获得许可的开源软件