后端交互
我知道洋葱模型:请求全部进来之后,出去时也会从这里走一段。
中间件的话,用我的理解,有点像是 Python 里面的 @ 装饰器。在所有函数被调用之前都会先走一遍这里。如果需要对每一个路由访问都进行一次的重复操作,那这些操作基本上都要放到中间件里。
关于 Token
一般称为 JWT。这东西主要是给后端用的,比如鉴权。你不能前端每次发请求过来都去查一遍数据库,那样效率太低也很麻烦,IO耗损会很大。
所以会有 Token 机制:
- 前端每次请求都会把 Token 发过来,后端通过它核查登录状态。
- 如果验证没有问题,用户就不会被跳转到登录界面。
- Token 中通常包含一些基本信息,比如: (a) 用户 ID (b) 权限信息 (c) Token 的过期时间
一般来说,对 Token 的校验都会放在中间件里,每次在路由跳转之前先校验权限,确认用户是否有权进入页面。而另一个东西就是 JSON 的返回数据,这是给前端用的(除非要求前端每次都很麻烦地去解析 Token)。前端会借助里面的数据,来决定是否要进行下一步渲染等等。
关于密码的处理
最好是在后端将其转换为哈希(Hash),否则会面临很尴尬的安全问题。如果哈希是在前端生成的,攻击者只需要修改一下 JS 代码,就可以直接绕过验证逻辑与数据库交互并完成登录,这会让加密行为变得根本没意义。所以各种安全验证最好都放在后端(服务端)来处理,因为后端运行在服务器上,即便有人在浏览器前端去下功夫破解,也没办法更改后端的逻辑。而后端对前端是基于一个零信任原则的。
关于 JWT Token 在前端的存储
通常有两个选择:直接存到 LocalStorage 里面、把它封装成 Cookie。与此对应,后端也需要进行相应处理。之所以将 Cookie 单独划分出来,主要原因是 Cookie 的安全系数更高。
如果只是把 Token 存在 LocalStorage 中,只要 JS 一行代码就能把它全部读取出来。在这种情况下,它就没办法防范 XSS 攻击,只要有一个网页端的 JS 代码注入,就能把所有的 Token 扒得一干二净,而 Cookie 就可以有效避免这个问题。
而且这里有一个重要的区分:F12 调试台确实可以直接获得 Cookie,但那是我作为浏览器使用者的的最高权限;而另一个则是直接在网页端运行的 JS 代码——这两者是完全不一样的。对于网页端运行的 JS 代码来说,设置了 HttpOnly 的 Cookie 它就是拿不到的,但是对于 LocalStorage 讲,网页端运行的 JS 也可以直接拿到,这个差别很大。
关于路由地址
这个地方遵循相对路径的逻辑(包括主路由、子路由)。
比如主路由是 /api,那么所有 /api 的请求都会被转发到这里,然后再根据后面的依赖关系转发到子路由。例如 /api/xx 就会被转发到 /xx。
关于子路由的写法:如果要写子路由的中间件,且该中间件已经挂载到子路由上了,路径直接写一个*即可,在子路由的模块里面,单独写其相对地址:
// dashboard.ts
const dashboardRoute = new Hono()
// 1. 保护这个子路由下的“所有”路径
dashboardRoute.use('/*', jwtMiddleware)
// 2. 定义具体路径 (注意这里不用写 /dashboard 了,只写相对路径)
dashboardRoute.get('/profile', (c) => c.text('我的资料'))
dashboardRoute.get('/settings', (c) => c.text('设置'))
关于这个星*的使用其实挺有意思的:如果是 /* 的话,那一般就是类似通配符的存在,在这个路径后面的内容会全部匹配;但如果只有一个星号的话,那指的就是这个路由里面的全部匹配。这里的坑在于,子路由这里一定要带 /*。
然后主路由如果只有一个星号的话,那就是主路由之下的全局匹配。如果是在挂载路由组的时候,那一定要把子路由的路径完整写下来,这样才能挂到主路由上,防止错配:
// app 是主路由,接子路由的完整路径
app.route('/api', authRoute)
app.route('/api/dashboard', dashboardRoute)
关于接口生成和调试
这方面有比较好的库可以一次性生成文档。接口搞好后,让AI写好注释,然后再接上Swagger这样的工具里,就可以自动产出接口文档了。而且可以在线调试,自动更新,非常好用。 当然还有另一个选择,就是一步到位,让 AI 全部处理。确实,这个工具在今天看来是可有可无的,但如果有一些调试上的需求,加上它可能会比较好。或者说,甚至连调试的单元测试也可以交给 AI 来写,这确实也没问题。