django
# 面试
# 谈谈对django的认识
django 是python的一个web框架,采用了mvt的软件架构设计模式
django的特点:
- 功能齐全,几乎涵盖了服务后端的所有功能
- 强大的数据功能,通过继承的方式,可以很快的写出动态操作数据库的api
- 强大的后台功能,轻松管理内容
- 实现了认证,权限,节流,分页等功能,我们使用时,可以直接用,也可以通过继承的方式进行自定义
# 什么是restfull
retful是一种软件设计风格不是一种技术,是说我们在开发API过程中应当遵循的守则
遵循的规则:
尽量使用https
尽量部署在专有域名下
有版本提示
路径使用名词,资源集合通常用集合的复数形式
依据对资源对象的不同操作,使用不同的请求
过滤信息(filter, 查询参数)
limit =10 : 指定返回记录的数量
offset=10 : 指定返回结果的起始位置
page=1&pagesize=20 : 指定第几页以及每页数量
sortby=name&order=asc : 指定排序属性,以及排序顺序
id=10 : 指定筛选条件
# django 的生命周期
- 浏览器发送请求到负载均衡服务器,然后通过nginx, 通过uwsgi协议与uWSGI服务器进行连接,然后uWSGI服务器会通过wsgi协议与Django进行交互,请求首先会经过中间件, 然后经过总的url,通过路由分发,进入子应用路由,匹配响应路由后,进入试图,在试图中处理业务逻辑,然后通过respon进行返回
# django 的各种mixin以及视图有哪些
- 两个基本试图类
- APIView
- GenericAPIView(通用试图类)
- 五个扩展类
- ListModelMixin
- CreateModelMixin
- RetrieveModelMixin
- UpdateModelMixin
- DestoryModelMixin
- GenericAPIView的视图子类
- CreateAPIView
- ListAPIView
- RetrieveAPIView
- DestoryAPIView
- UpdateAPIView
- ListCreateAPIView
- RetrieveDestroyAPIView
- RetrieveUpdateAPIView
- RetrieveUpdateDestoryAPIView
- 视图集ViewSet
- 常用视图集
- ViewSet
- GenericViewSet
- ModelViewSet
- 视图集中定义附加action动作
- action属性
- 常用视图集
# 有读过python源码吗
# Django中请求方式执行不同方法的源码
- django的试图有两类,一类是CBV, 一类是FBV
- 在CBV中我们需要根据request.http_method_name判断来执行不同的方法
- 那么什么我们在CBV中就不需要呢
- 我看了下源码,在url中,比如说通过
re_path(r"^(?P<version>[v1|v2]+)/order/", views.OrderView.as_view()),
里的as_view
进去,会发现重写了def view方法,紧接着会执行dispatch
方法,然后会通过反射getter(self, request.http_method_name) 获取对用方法的内存地址,然后执行。这样实现的好处就是不用我们自己去通过if判断了
# Django中的认证的源码
- 这个是在项目中需要自定义自己的认证方法时去读的源码
- 首先继承自APIView,然后点到源码里看到他是继承自
django
的view
, 然后重写了dispatch方法 - 在这个方法里,会有一个
self.request=request_initlialize
方法。 - 点进去之后呢,会发现它会返回一个drf的
Requet
对象,这个对象呢其实师对原本的django request
进行了包装,将原有的request
保存在self.requst._reqeust
中新增了一些功能,,在新增的功能中,有一个authenticators=self.get_authenticators()
然后self.get_authenticators()
其实就是循环获取authenticate_class
中的认证对象,因为是有继承的关系,他会先找你在view里面自定义的,如果没有,就会找配置文件里的,这是获取认证对象的过程 - 然后就要针对不同的请求进行判断是否通过认证了
- 还是
dispatch
里,在执行完上面提到的request_initlialize
之后会执行一个initial方法,在这个方法里有一个self.perform_authentication(request)
, - 这个方法里面实质执行request.user。
- 然后返回到Request中可以看到,这个类里面的.user其实是一个方法,通过
perporty
装饰器进行装饰,将方法变为属性,在这个方法里会执行self._authenticate()
,然后通过循环最开始获取到的认证对象集合,调用authenciate方法进行具体的认证 - 所以在进行自定义认证的时候,我们可以通过重写authenticate方法进行认证,包括django里自定义的一些认证类,我们可以继承这些类,重写具体的authencicate来实现自定义认证功能。
- 通过阅读这些源码,一个是提高了编码能力,另一个是对类的分装继承,有了更深入的体会
# cookie和session认识
- 我们直到http是无状态的,没有办法直接通过http来检查用户身份,所以我们的思路是方法令牌后端通过令牌来检查用户,这就是cookie, session的使用场景
# 两者区别
- cookie是保存在客户端的, session是保存在服务器端
- cookie由于保存在客户端,所以安全性比sessin差
- cookie大小有限制,session没有大小限制
# token认证
Token,也称为“令牌”,是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码
为什么使用Token
传统验证方式的弊端, cookie不安全,有大小限制,会有跨域以及跨站请求伪造问题,session会存在消耗服务器性能的问题等,扩展性差,在分布式应用中扩展性差
相对的使用token 满足了扩展性安全性的问题,且不会有cookie的跨域问题
而且只要token设计的足够复杂,除非用户泄露,否则几乎没有被破解的可能,加上token是有时效的,在有限的时间加上有限的算力,更是无懈可击,这也类似于加密资产比如比特币钱包对应的私钥,安全性极高。
另外Token可以有效减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
# jwt
# 什么是jwt
- jwt是客户端和服务端用来传递身份信息的,一般用于分布式站点的单点登录
# JWT的构成
- 第一部分为头部(header)
- 第二部分为载荷(payload)
- 第三部分为签证(signature)
# 头部(header)
jwt的头部承载两部分信息
申明类型
加密算法
{ 'typ': 'JWT', 'alg': 'HS256' }
1
2
3
4将头部进行base64加密(该加密是对称加密可以解密),构成了第一部分类似
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
1
# 载荷(payload)
- 存放有效信息的地方,可以将部分不敏感信息放在这里
- 然后将这部分信息进行base64加密得到jwt的第二部分
# 签证信息(signature)
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
1.var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); 2.var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
1
2
3注意
secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
如何应用
- 一般是在请求头里加入
Authorization,并加上Bearer标注
'Authorization': 'Bearer ' + token
- 一般是在请求头里加入
# jwt的加密方式
- 项目里用的是
SHA256
# 项目里jwt认证具体实现
- 首先是继承了jwt认证的token认证类,重写了authenticatef方法,然后再配置文件里指定认证类
- 再用户登录的试图里,通过手机号和验证码登录验证后,会将用户信息保存在缓存里,然后生成token,并返回给前端
- 前端拿到之后,会在http的请求头里添加
Authorization
字段,并将返回的token作为值,这样每次请求在头信息里就会携带token,然后服务端通过验证这个token是否合法,是否过期等判断用户的登录状态
# Token 如何刷新
# 双token方法
- 登录成功以后,后端返回 access_token 和 refresh_token,客户端缓存此两种token;
- 使用 access_token 请求接口资源,成功则调用成功;如果token超时,客户端携带 refresh_token 调用token刷新接口获取新的 access_token;
- 后端接受刷新token的请求后,检查 refresh_token 是否过期。如果过期,拒绝刷新,客户端收到该状态后,跳转到登录页;如果未过期,生成新的 access_token 返回给客户端。
- 客户端携带新的 access_token 重新调用上面的资源接口。
- 客户端退出登录或修改密码后,注销旧的token,使 access_token 和 refresh_token 失效,同时清空客户端的 access_token 和 refresh_toke。
# reids
- 后端实现token过期还可以利用Redis来存储token,设置redis的键值对的过期时间。如果发现redis中不存在token的记录,说明token已经过期了
# Django查询集有哪些方法
- userObjs = User.objects.filter(age__get=18)
- 后面可以接
.values
,获取指定字段.count
, 获取条数- .first, 获取第一条
- .last, 获取最后一条
- .order_by, 排序
- .aggreate 对查询结果进行聚合
- .annotate分组后进行聚合
# 一对多查询
正向:属性名
反向:小写类名加 _set