关于Restful风格的一个具体逻辑问题请教

发布于 2022-09-03 13:58:25 字数 3143 浏览 12 评论 0

自学萌新正在用python+Tornado建立一个前后端完全分离restful风格的网站,关于接口设计参考过许多文章,但由于也是闭门造车,在auth验证方面具体有一点设计还是不能确定是否合乎规范纠结良久,特意请教:

在auth方面我采用的是access token方式,类似于oauth2,只是token由自己服务器发出。
这里就存在一个token保存的问题,token保存有很多说法,有的人直接保存在cookie中,有的人则是保存在local storage中(我没尝试过)等等。

我选择保存在cookie中(我咨询过这是合乎规范的,但之前学习Flask搭建api时也有人说过对于不支持cookie的情况下这api就不好了),但保存方法方面有一点疑问,那就是我利用Tornado框架自带的set_secure_cookie方法进行保存的(一个简单的测试例子):

class LoginHandler(BaseHandler):
    def post(self, token=None):
        username = self.get_argument('username')
        password = self.get_argument('password')
        user = self.db.query(User).filter_by(username=username).first()
        if not user:
            response = {"error" : "This user doesn't exist."}
        if user:
            if user.verify_password(password):
                token = user.generate_auth_token(3600)
                response = {
                    "message":"You have log in.",
                    "access_token":str(token),
                }
                self.set_secure_cookie("access_token", token)
            else:
                response = {"error": "Incorrect password"}
        self.write(response)

然后我在所有接口的类都要继承的父类BaseHandler定义了一个方法来获取对方身份,让我可以在其他接口时调用:

# Base class - setring db and gain auth info
class BaseHandler(tornado.web.RequestHandler):
    @property
    def db(self):
        return self.application.db

    def get_current_user(self):
        access_token = self.get_secure_cookie("access_token")
        if not access_token: return None
        return User.verify_auth_token(access_token)
        

然后就像这样调用:

class IndexHandler(BaseHandler):
    def get(self):
        user = self.get_current_user()
        if user:
            response = {"user": user.username}
            self.write(response)
        else:
            self.write("You haven't logined")
            

因为restful风格的api设计强调stateless,所以我对自己在接口会主动去寻找对方的浏览器token感觉不大对劲——或许我应该在BaseHandler里面的get_current_user里修改不仅自动寻找cookie里的token值,还能把token传参给api也能识别才符合规范?或者说这已经违反“一切皆资源”(如果是,那我就应该返回一个json数据让前端进行判断是否要从客户端获取用户的token了,那样前端工作量已经瞬间变大不少,而后端能轻松解决,但耦合更低),我不应该尝试去设置行为,因为获取token后再把token传给接口已经是前端的任务了,那样我就和前端的工作重合了,就不是真正的前后端分离了。

所以我目前打算增加token接受的接口通过传递token也能登录,因此总结以上得出以下问题

1)我后端是否应该取消主动增加cookie(对应登录行为,把token放到用户的cookie里)、获取cookie(对应验证行为,把用户cookie里的token进行解码没问题返回相关资源)和删除cookie的行为(对应登出行为,token保存在cookie里,我清除掉cookie后token也没了,用户必须重新账号密码登录获取token后才能访问有权限保护的资源)?

2)另外接口我是否应该加上版本号?在这方面我看到了不同的案例,略纠结。

3)我是否应该保证用户在不用token的情况下,只要愿意每个接口都传入账号密码 or token我就让他看到资源?还是说,看到资源的唯一方式就是让他获取token,然后用token接受验证(然而我这里还规定它必须要存在cookie里),有就返回资源,不通过cookie渠道那就算知道账号密码也都得吃闭门羹。在我上面的代码中,账号密码的唯一作用就是获取token,而token也是资源唯一的通行证,有点霸道?毕竟我不是搞第三方授权,我完全可以支持每个接口都接受账号密码,只要用户也愿意那么折腾我不应该有什么抱怨?)。

4)就算token是唯一的通行证,只允许把token放在cookie里是不是有点过分?我是否应该在前端那里尝试把token存在多个地方?然后向接口发送请求时逐个扫描,要是某种意外“token们”不一样我就说只要有一个是有效的我就把资源给你,但那样是不是多此一举?甚至不安全(虽然说理论上都用SSL也不会不安全,但有多份通行证放在多个地方随便拿到其中一个就能乱来也是有点不安全)?造成JS无谓的膨胀,因为需要额外的代码进行包装?

求教指点,另外如果看到代码有其他问题也请提出,非常感谢!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

注定孤独终老 2022-09-10 13:58:25

问题1)
我觉得吧,tokencookie是有问题的。
比如A是善良的某网站W的管理员,B是坏人。
A浏览器打开了后台页面,并且没有关闭,并且token存储在cookie中。
坏人B做了钓鱼页面,诱导A访问,页面看起来很正常并且可以吸引A停留。页面存在一个长宽为0的iframeiframe去请求A的后台的新增用户的接口或其他接口。假定接口坏人B通过一定手段猜出来,并且知道添加用户填的基本参数,用户名一些,或者不知道接口,B可以暴力破解吧,常用的后台添加用户的接口列表放文件,自己网站js读取文件进行暴力尝试如果A网站没做过滤,A作为合法用户请求网站W时,浏览器判断网站W的域名下有这个cookie,会请求带上。GG。
我是把token放在localstorage中,localstorage中的存储虽然不能设置过期等等,但在登出或者服务端验证token失效,进行移除localstroage中的token即可。
可以看一下Json Web Token,一个RFC 7519标准。网站下拉有python的版本。
问题2)
接口添加版本问题可以参考RESTful API 设计指南 - 阮一峰github 查看仓库不同tag时版本是在url
问题3)
token作为令牌应该作为唯一认证,访问接口又用账号密码进行验证,首先你得保证你的接口是https的,所有通信加密,访问数据不会明文暴露在网络上,很容易被嗅探检测到。其次,你想接口访问带上账号密码,它怎么来,登录的时候认证通过客户端存本地然后获取?不要吧,这种念头以后不要有。
问题4)
允许一个用户对应多个客户端,每个客户端只允许存在一个token,过期drop和update,且token不用存储,就像你说的stateless

如有不对,请及时指出

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文