昨天我们做了一些基础配置,今天我们正式开始来写路由了,我们先来实现用户相关的内容
项目地址:
由于我是先实现完再写的这篇文章,如果有些地方无法运行,可以看下我项目里的代码。
目前我还在往全栈的方向学习,所以如果看的不顺眼,请多多包涵。如果觉得那里可以改进,麻烦评论区说下,谢谢~
我们的服务器在请求之前必须要先登录才行,那么就需要有一个字段即token
用于我们的验证。
在我们开始实现校验部分之前,我们需要了解token
的创建和解析。
这里我用的jsonwebtoken
[1]这个crate
简单的看下它的用法
你可以自定义插入到header
里的数据
其中exp
是强制要求的字段,即你自定义的struct
里必须要有一个exp
字段,用于表示token
的有效时间。
我们先在src
下创建一个auth
的mod
,然后在里面分别创建token.rs
和mod.rs
mod.rs
中我们创建一个UserToken
的struct
,它是我们插入到header
里的东西。
id
:用户的id
,为了确保安全,这个信息只会放在header
的_token
字段中,并且是加密过后的。exp
:这个则是用于校验token
是否过期的。token.rs
存放我们的生成/解析token
的方法
encode_token
就是用来生成token
,注意这里的Header
是jsonwebtoken
自己的Header
,而不是rocket
的Header
,而user_token
则是我们的数据,它有一个强制的字段要求exp
,表示过期时间。key
则是我们加密的key
,加密的方式我们就用它默认的即可。decode_token
自然就是解析set_token
:这个用于返回我们生成的token
信息,在这里我们用到了之前的MyConfig
。get_current_timestamp
[2]:获取当前时间的时间戳,加上我们在MyConfig
中配置的exp
时间间隔,这样就可以用来表示过期时间了。req.rocket().state::()
:我们可以通过这种方式获取全局的state
,注意这里必须用::
来声明指定的类型。由于我这定义了请求前必须要登入,任何接口都是。所以这个时候我们就需要给每个路由都加上对于token
的校验,根据我们学的知识,我们可以把这个放到全局的fairing
,相当于一个路由守卫,能参与一次请求的任意生命周期。
回到auth
文件夹,在mod.rs
文件夹中我们新建一个名为AuthMsg
的struct
这个struct
用于临时数据,等会会用到。
然后我们新建一个fairing.rs
的文件
实现Fairing
[3]必须实现info
这个method
,目的是告知rocket
哪些生命周期需要监听,这里我监听的是Request
和Response
。
在request
进入到路由之前就会触发这个on_request
函数,而response
准备发送给前端的时候即执行完处理器才会触发on_response
。
这里就用到了我们前面实现的decode_token
方法,用于获取request
的Header
里面的token
信息。
on_request
我通过判断是否存在token
,token
的exp
是否已经过期这两步来实现校验。
如果通过校验,那么请求的暂时缓存local_cache
[4]就会放一个AuthMsg
的数据,里面存放了一个标志位is_valid_token
,这个标志位用于response
的时候判断是否符合要求,不符合直接重写response
(这么做其实不太好,因为处理器里面的逻辑实际上还是执行了,浪费了很多性能,但是我找不到较好的方案。。。如果你有好的方案,请务必评论区里说下,谢谢!)
另外吐槽下这个local_cache
,它并不能改动数据,是的。。。也可能是我姿势不对,我尝试了一段时间后就放弃了。
虽然说每个路由都需要校验token
,但是实际上有些是不需要的,比如404
、500
、400
等。 所以这里搞了个白名单EXCEPT_LIST
由于过滤掉上面的几种错误场景
on_response
中重写数据时,我用到了前面我们实现的RtData
类型,但是如果直接用是会报错的,因为RtData
并不符合类型约束,没有实现Responder
[5] 。
那么我们回到type/mod.rs
中,给RtData
实现这个trait
。
给它实现Responder
,支持我们的类型作为response
。
这样就可以了。
另外提一下这个std::io::Cursor::new
,这个用来指向数据的起始位置
那么token
校验相关的就都完成了
最后我们在main.rs
中引入,不过在这之前,我们还需要在auth/mod.rs
中引入,包括前面提到的一些文件,不要忘了在mod
中引入以及pub
出去,如果只是文件夹里面跨文件使用,那么可以不需要pub
出去。
然后我们新建一个state
的文件夹,在里面新建mod.rs
文件。这个文件夹作为存放我们全局state
的地方。
最后我们在mian.rs
中引入
这样就行啦
下一章我们来开始编写user
相关的路由了。
另外有一点就是这个log,错误的收集,实际开发我们是需要把log都收集到.log文件中的,这样方便日志跟踪排错,我这里图省事就没这么做。我逛了一圈,发现有一个log4rs[6]的,显然是rust版本的log4j,但是我这里并没有采用,大家有需要可以自行引入。
当然你也可以自己实现,比如用channel收集错误信息,用队列存储,达到一定的量或者每隔一小时就批量写一次进文件中。。。
前面我们用的是fairing
监听on_request
和on_response
来限制反馈,但是这里有个大问题和bug
。
bug
是路由的function
实际上还是会执行,也就是说如果不在路由函数那边加上判断是否可执行和return
的逻辑,那么数据还是可以修改成功。
另外有些路由是没必要走这个校验逻辑的,所以要么我们加上白名单,要么我们最好就是换一个方案。。
user_msg
还记得我们在给路由参数实现的Request
等类型么?实际上我们可以把token
校验放在参数中,这个参数不需要前端传递,而是一个内部参数。这里命名为user_msg
,里面存放的是token
解密之后的参数id
,而另一个数据exp
就没必要了,仅做为token
校验用的。
代码很简单,和前面fairings
中做的一样,不同点在于这里校验失败直接通过Outcome::Failure
的方式直接去到error catcher
中,这里就不会继续往下执行路由的逻辑了。
那么该如何使用这个UserMsg
来限制一些借口呢?简单,如果有需要检验token
的地方,就在参数中加上这个UserMsg
即可。
比如:更新用户数据的,这里就必须要要有token
才行。
这样也不需要使用白名单的方式来过滤路由
编辑于 2023-09-04 21:03・IP 属地广东