Vapor 教程
用户认证:密码哈希与 Token
一旦你的应用有了「用户」,就得回答两个问题:你是谁?你能做什么?前者叫认证(Authentication),后者叫授权(Authorization)。这篇先搞定最要命的「密码怎么存才安全」。
一、认证 ≠ 授权
- 认证(Authentication):验明「你是谁」——通常是用户名 + 密码。
- 授权(Authorization):确认「你能干什么」——比如管理员能删帖、普通用户只能看。
先把认证做对,授权才有意义。而认证的第一铁律是:密码绝不能以明文存进数据库。
二、永远不要明文存密码
如果数据库泄露,明文密码就直接曝光了。正确做法是哈希:用 BCrypt 这类算法把密码变成一串不可逆的密文,而且它会自动「加盐」(每人的盐不同,相同密码也会得到不同哈希)。
Vapor 自带 Crypto 模块,一行就搞定:
import Crypto
func createHandler(_ req: Request) throws -> EventLoopFuture<User> {
let data = try req.content.decode(CreateUserData.self)
let user = User(
username: data.username,
password: try Bcrypt.hash(data.password)
)
return user.save(on: req.db).map { user }
}登录时,用 Bcrypt.verify(明文, against: 哈希) 比对,而不是把哈希「反解」出来——它本来就解不开。
三、User 模型与唯一约束
登录要靠「用户名 + 密码」唯一定位用户,所以用户名不能重复。在迁移里加唯一约束:
struct CreateUser: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
database.schema("users")
.id()
.field("username", .string, .required)
.field("password", .string, .required)
.unique(on: "username")
.create()
}
func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema("users").delete()
}
}改了表结构要重置:新增字段或约束后,需要重置一次数据库(或写新的迁移)让变动生效。在 Xcode 的 Scheme 运行参数里加上相应 flag 即可,否则会一直报「字段不存在」。
四、基本认证(Basic Auth)
最朴素的方案:客户端把 用户名:密码 用 Base64 塞进请求头 Authorization,服务端解出后查库、验密。
Authorization: Basic base64(用户名:密码)思路简单,但每次请求都带明文密码(即便 Base64 不是加密),通常只适合内部工具或配合 HTTPS。Vapor 提供了 BasicAuthorization 中间件帮你解析这个头。
五、Token 认证(更常见)
现代 API 更爱 Token:用户登录一次,服务端验证密码正确后发一个令牌(token),之后每次请求带上这个 token 即可,不用反复传密码。
// 登录:验密通过后签发 token
func loginHandler(_ req: Request) throws -> EventLoopFuture<Token> {
let data = try req.content.decode(LoginData.self)
return User.query(on: req.db)
.filter(\.$username == data.username)
.first()
.flatMap { user in
guard let user = user,
try Bcrypt.verify(data.password, against: user.password)
else { throw Abort(.unauthorized) }
let token = Token(value: [UInt8].random(count: 16).base64, userID: user.id!)
return token.save(on: req.db).map { token }
}
}后续请求在 Authorization: Bearer <token> 里带上令牌,服务端用 Token 中间件校验有效性。这样密码只在登录那一瞬间出现,日常调用更安全。