系统安全可能往往是被大家所忽略的,我们的很多系统说是在互联网上"裸奔"一点都不夸张,很容易受到攻击,系统安全其实是一个复杂且庞大的话题,若要详细讲来估计用几本书的篇幅都讲不完,基于此本篇及下一篇会着重讲解在我们开发系统过程中遇到的一些安全校验机制,希望能起到抛砖引玉的作用,望各位在开发过程中多多思考不要只局限于功能实现上,共勉~

在系统安全、身份验证以及权限授权方面通常来说有各种各样的处理方式,但大多都比较复杂。在很多框架和系统里,涉及安全和身份验证的工作往往都比较繁琐,并且代码量也巨大,基于此也出现了一些相关的协议和相关库 我们今天就一起来了解一下相关的内容

1.常见认证规范/协议

1.1OAuth2

OAuth2 是一种协议规范,定义了几种用来身份验证和权限授权的处理方式。它是一种可扩展的协议规范,涵盖了几种复杂的使用场景。并且包含了基于第三方身份验证的处理方法。我们常见的"使用微信登陆"、"使用 QQ 登陆"等第三方登陆方式的底层技术就是基于 OAuth2 实现的。

1.2OpenID Connect

OpenIDConnect 是另一种基于 OAuth2 的协议规范。它扩展了 OAuth2 的部分功能,让以前相对模糊的功能变得可操作性更强。常见的 Google 登陆就是基于 OpenID Connect 实现的。

1.3OpenAPI

OpenAPI 是一套构建 API 的开放标准。FastAPI 是基于 OpenAPI 构建而成。

OpenAPI 支持以下几种安全机制:

1.apiKey:应用指定的 key 来自于

(1) 查询参数
(2) header 信息
(3) cookie 信息

2.http:支持标准的 http 身份验证系统,包括:

bearer:头信息 Authorization 的内容中带有 Bearer 和 token 信息,继承自 OAuth2
HTTP 基本认证
HTTP 摘要认证

3.oauth2

4.openIdConnect

FastAPI 通过引入 fastapi.security 模块,可以支持以上所有安全机制,并且简化了使用方法。

2.JWT

2.1JWT 的概念

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。

2.2JWT 的组成

一个 JWT 实际上就是一个字符串,它由三部分组成:头部、载荷与签名。将这三段信息文本用.链接一起就构成了 Jwt 字符串。就像这样:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

头部(Header)

JWT 的头部承载两部分信息:
1、声明类型,这里是 jwt
2、声明加密的算法,通常直接使用 HMAC SHA256
我们使用 base64 解析一下 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 这个子串 可以得到:

{"typ": "JWT","alg": "HS256"
}

可以看出: 在头部指明了签名算法是 HS256 算法。

2.3载荷(Payload)

载荷就是存放有效信息的地方。这些有效信息包含三个部分
1、标准中注册的声明
2、公共的声明
3、私有的声明

标准中注册的声明 (建议但不强制使用) :
1、iss(Issuer): 签发人
2、sub(Subject): 主题
3、aud(Audience): 受众
4、exp(Expiration Time): 过期时间,这个过期时间必须要大于签发时间
5、nbf(Not Before): 生效时间
6、iat(Issued At): 签发时间
7、jti(JWT ID) : JWT 的唯一身份标识,主要用来作为一次性 token,从而回避重放攻击。

公共的声明:
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息。但不建议添加敏感信息,因为该部分在客户端可解密。

私有的声明:
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为 base64 是对称解密的,意味着该部分信息可以归类为明文信息。

我们使用 base64 解析一下 eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 就可以得到 之前定义的一个 payload:

{"sub": "1234567890","name": "John Doe","admin": true
}

2.4签名(Signature)

JWT 的第三部分是一个签证信息,这个签证信息由三部分组成:
1、header (base64 后的)
2、payload (base64 后的)
3、secret

这个部分需要 base64 加密后的 header 和 base64 加密后的 payload 使用.连接组成的字符串,然后通过 header 中声明的加密方式进行加盐 secret 组合加密,然后就构成了 JWT 的第三部分。

Signature = HMACSHA256(base64UrlEncode(header) + "." +  base64UrlEncode(payload), secret);
Token = base64(头部).base64(载荷).Signature

注意:secret 是保存在服务器端的,在任何场景都不应该流露出去。

2.5使用

在请求头 headers 中加入 Authorization,并加上 Bearer 标注
headers = {…‘Authorization’: 'Bearer ’ + token…}

3.基于 JWT 的 Token 的认证过程

3.1登陆认证过程

1.第一次认证:第一次登录,用户从浏览器输入用户名/密码,提交后到服务器的登录处理的 Action 层(Login Action)
2.Login Action 调用认证服务进行用户名密码认证,如果认证通过,LoginAction 层调用用户信息服务获取用户信息(包括完整的用户信息及对应权限信息)
3.返回用户信息后,Login Action 从配置文件中获取 Token 签名生成的秘钥信息,进行 Token 的生成
4.生成 Token 的过程中可以调用第三方的 JWT Lib 生成签名后的 JWT 数据
5.完成 JWT 数据签名后,将其设置到 COOKIE 对象中,并重定向到首页,完成登录过程

我们再通过完整的图来看一下登陆的整个认证过程:

3.2请求认证

1.基于 Token 的认证机制会在每一次请求中都带上完成签名的 Token 信息,这个 Token 信息可能在 COOKIE 中,也可能在 HTTP 的 Authorization 头中
2.客户端(APP 客户端或浏览器)通过 GET 或 POST 请求访问资源(页面或调用 API)
3.认证服务作为一个 Middleware HOOK 对请求进行拦截,首先在 COOKIE 中查找 Token 信息,如果没有找到,则在 HTTP Authorization Head 中查找
4.如果找到 Token 信息,则根据配置文件中的签名加密秘钥,调用 JWT Lib 对 Token 信息进行解密和解码
5.完成解码并验证签名通过后,对 Token 中的 exp、nbf、aud 等信息进行验证
6.全部通过后,根据获取的用户的角色权限信息,进行对请求的资源的权限逻辑判断
如果权限逻辑判断通过则通过 Response 对象返回;否则则返回 HTTP 401

我们再通过完整的图来看一下登陆的整个请求认证过程:

3.3基于 JWT 的 Token 认证的几点总结:

1.一个 Token 就是一些信息的集合,是一个字符串信息
2.在 Token 中包含足够多的信息,以便在后续请求中减少查询数据库的几率
3.服务端需要对 COOKIE 和 HTTP Authrorization Header 进行 Token 信息的检查
4.基于上一点,可以用一套 Token 认证代码来面对浏览器类客户端和非浏览器类客户端
5.因为 Token 是被签名的,所以我们可以认为一个可以解码认证通过的 Token 是由我们系统发放的,其中带的信息是合法有效的

4. 获取 Token 实战

在写代码之前我们先来了解一下 OAuth2PasswordBearer 这个类的功能。OAuth2PasswordBearer 是接收 URL 作为参数的一个类:客户端会向该 URL 发送 username 和 password 参数,然后得到一个 Token 值。OAuth2PasswordBearer 并不会创建相应的 URL 路径操作,只是指明了客户端用来获取 Token 的目标 URL。

当请求到来的时候,FastAPI 会检查请求的 Authorization 头信息,如果没有找到 Authorization 头信息,或者头信息的内容不是 Bearer Token,它会返回 401 状态码(UNAUTHORIZED)。我们再从源码上认识一下:

class OAuth2PasswordBearer(OAuth2):def __init__(self,tokenUrl: str,scheme_name: str = None,scopes: dict = None,auto_error: bool = True,):if not scopes:scopes = {}flows = OAuthFlowsModel(password={"tokenUrl": tokenUrl, "scopes": scopes})super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error)async def __call__(self, request: Request) -> Optional[str]:authorization: str = request.headers.get("Authorization")scheme, param = get_authorization_scheme_param(authorization)if not authorization or scheme.lower() != "bearer":if self.auto_error:raise HTTPException(status_code=HTTP_401_UNAUTHORIZED,detail="Not authenticated",headers={"WWW-Authenticate": "Bearer"},)else:return Nonereturn param

为了完成我们接下来的功能,需要大家进行这两个模块的安装:

pip install pyjwt
pip install python-multipart

简单解释一下:
pyjwt 是用来产生和校验 JWT token
python-multipart 是因为 OAuth2 需要通过表单数据来发送 username 和 password 信息

在生产实践过程中,获取 token 的代码:

from datetime import datetime, timedelta
from typing import Optionalfrom fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
import jwt
from pydantic import BaseModel# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30class Token(BaseModel):access_token: strtoken_type: strapp = FastAPI()# oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")# 生成token
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):to_encode = data.copy()if expires_delta:expire = datetime.utcnow() + expires_deltaelse:expire = datetime.utcnow() + timedelta(minutes=15)to_encode.update({"exp": expire})encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)return encoded_jwt# 请求接口
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)access_token = create_access_token(data={"sub": "test"}, expires_delta=access_token_expires)
return {"access_token": access_token, "token_type": "bearer"}

我们运行一下代码可以看一下效果:

我们对 access_token 进行一下解析来看一下每一部分的组成:

  1. 子串"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"的解析结果为:


2. 子串"eyJzdWIiOiJ0ZXN0IiwiZXhwIjoxNjE4MTExNTA4fQ"的解析结果为:
3.子串"pwoiwQmQZbIFVvmdlmkSPXdoHrtZyoNNTRhoWAZWU9o"的解析结果为:

本期总结

1.介绍了常见的 认证规范/协议
2.对 JWT 进行了深入的研究和分析
3.在实际生产过程中如何产生一个有效的 Token 在代码层面进行落地
4.本篇不仅可以让"守"方清楚了如何有效的制作一个 Token 来进行防御,另一方面若是做逆向的"攻"方也了解了如何进行破防

15分钟详解 Python 安全认证的那些事儿~相关推荐

  1. 全程15分钟 详解如何为MacBook Pro 15寸 加装固态硬

    本文导航 第1页:详解如何为MBP15加装固态硬 第2页:用时约3分钟:查看硬盘和光驱接口信息 第3页:用时约2分钟:拆解机身底盖螺丝 第4页:用时约3分钟:拆解光驱和清理风扇 第5页:用时约5分钟: ...

  2. python权限管理系统_Django 自定义权限管理系统详解(通过中间件认证)

    1. 创建工程文件, 修改setting.py文件 django-admin.py startproject project_name 特别是在 windows 上,如果报错,尝试用 django-a ...

  3. 数学建模——一维、二维插值模型详解Python代码

    数学建模--一维.二维插值模型详解Python代码 一.一维插值 # -*-coding:utf-8 -*- import numpy as np from scipy import interpol ...

  4. python动态显示进度条_实例详解python如何轻松实现动态进度条

    本次的这篇文章主要是和大家分享了关于实例详解python如何轻松实现动态进度条,有需要的小伙伴可以看一下 思路及讲解 假设有一个1000000数据的列表(为了让进度条可以显示出来,很大的数据),我们每 ...

  5. [转载] python中for语句用法_详解Python中for循环的使用_python

    参考链接: 在Python中将else条件语句与for循环一起使用 这篇文章主要介绍了Python中for循环的使用,来自于IBM官方网站技术文档,需要的朋友可以参考下 for 循环 本系列前面 &q ...

  6. python装饰器由浅入深_详解Python装饰器由浅入深

    装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们 ...

  7. python操作符op_详解python中的 is 操作符

    大家可以与Java中的 == 操作符相互印证一下,加深一下对引用和对象的理解.原问题: Python为什么直接运行和在命令行运行同样语句但结果却不同,他们的缓存机制不同吗? 其实,高票答案已经说得很详 ...

  8. python xlrd安装_详解python中xlrd包的安装与处理Excel表格

    一.安装xlrd 地址 下载后,使用 pip install .whl安装即好. 查看帮助: >>> import xlrd >>> help(xlrd) Help ...

  9. python repair修复功能_详解Python修复遥感影像条带的两种方式

    GDAL修复Landsat ETM+影像条带 Landsat7 ETM+卫星影像由于卫星传感器故障,导致此后获取的影像出现了条带.如下图所示, 影像中均匀的布满条带. 使用GDAL修复影像条带的代码如 ...

最新文章

  1. java使用线程求素数和1000个0~0.9随机数_求素数(多线程练习题)
  2. 区分真实模式的两个标准
  3. 参加51CTO培训,PMP考试通过啦
  4. gdb 调试core 文件
  5. 【学术相关】供参考:刚刚,2021 世界大学学术排名发布!
  6. 号称最强“抓取”工具,没有搞不到的资源!
  7. 数据科学家编程能力需要多好_我们不需要这么多的数据科学家
  8. 无意间发现的java的相关资料
  9. 转一个高内存定位的文章
  10. 阿里成立智能搜索业务部;任天堂:共有30万账号被黑客入侵;TiDB 3.1.2 发布| 极客头条...
  11. ASP.NET 4新增功能(一) ASP.NET核心服务
  12. java调用geth_php调用以太坊geth API说明
  13. 游戏契合度提示音_产品/市场契合度
  14. HTML+CSS+Bootstrap+ECMAScript+DOM
  15. 域名被劫持应该如何处理
  16. [MTK]LCD 调试总结
  17. 程序员怎样写出搞垮公司的代码?
  18. 你能为公司带来什么?
  19. 【Warshall算法】
  20. 财经小知识:美国国债

热门文章

  1. 干货分享!华为模拟器Web配置防火墙
  2. [数据压缩作业1]利用Audacity分析浊音、清音、爆破音|RGB文件三通道分量的熵计算
  3. 锂电池充放电曲线、设置充电电流、检测TP4054的3种充电状态
  4. 053试题 334/682 - crosscheck
  5. kettle的调度监控平台
  6. dns劫持是什么意思?常见的劫持有哪些?
  7. 怎样记账家庭成员收支,搜索查看明细
  8. 怎样批量查询顺心捷达单号信息并分析是否延误
  9. 基于Java的旅行管理系统的设计与实现
  10. 夜曲歌词 拼音_矢野真纪《夜曲》罗马拼音歌词