Python安全认证需要注意事项有哪些,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
python 生产实战 安全认证的那些事儿
系统安全可能往往是被大家所忽略的,我们的很多系统说是在互联网上"裸奔"一点都不夸张,很容易受到攻击,系统安全其实是一个复杂且庞大的话题,若要详细讲来估计用几本书的篇幅都讲不完,基于此本篇及下一篇会着重讲解在我们开发系统过程中遇到的一些安全校验机制,希望能起到抛砖引玉的作用,望各位在开发过程中多多思考不要只局限于功能实现上,共勉~
在系统安全、身份验证以及权限授权方面通常来说有各种各样的处理方式,但大多都比较复杂。在很多框架和系统里,涉及安全和身份验证的工作往往都比较繁琐,并且代码量也巨大,基于此也出现了一些相关的协议和相关库 我们今天就一起来了解一下相关的内容
1
常见认证规范/协议
1.1
OAuth3
OAuth3 是一种协议规范,定义了几种用来身份验证和权限授权的处理方式。它是一种可扩展的协议规范,涵盖了几种复杂的使用场景。并且包含了基于第三方身份验证的处理方法。我们常见的"使用微信登陆"、"使用 QQ 登陆"等第三方登陆方式的底层技术就是基于 OAuth3 实现的。
1.2
OpenID Connect
OpenIDConnect 是另一种基于 OAuth3 的协议规范。它扩展了 OAuth3 的部分功能,让以前相对模糊的功能变得可操作性更强。常见的 Google 登陆就是基于 OpenID Connect 实现的。
1.3
OpenAPI
OpenAPI 是一套构建 API 的开放标准。FastAPI 是基于 OpenAPI 构建而成。
OpenAPI 支持以下几种安全机制:
1.apiKey:应用指定的 key 来自于
(1) 查询参数
(2) header 信息
(3) cookie 信息
2.http:支持标准的 http 身份验证系统,包括:
bearer:头信息 Authorization 的内容中带有 Bearer 和 token 信息,继承自 OAuth3
HTTP 基本认证
HTTP 摘要认证
3.oauth3
4.openIdConnect
FastAPI 通过引入 fastapi.security 模块,可以支持以上所有安全机制,并且简化了使用方法。
2
JWT
2.1
JWT 的概念
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。
2.2
JWT 的组成
一个 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 实战
在写代码之前我们先来了解一下 OAuth3PasswordBearer 这个类的功能。OAuth3PasswordBearer 是接收 URL 作为参数的一个类:客户端会向该 URL 发送 username 和 password 参数,然后得到一个 Token 值。OAuth3PasswordBearer 并不会创建相应的 URL 路径操作,只是指明了客户端用来获取 Token 的目标 URL。
当请求到来的时候,FastAPI 会检查请求的 Authorization 头信息,如果没有找到 Authorization 头信息,或者头信息的内容不是 Bearer Token,它会返回 401 状态码(UNAUTHORIZED)。我们再从源码上认识一下:
class OAuth3PasswordBearer(OAuth3):
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 None
return param
为了完成我们接下来的功能,需要大家进行这两个模块的安装:
pip install pyjwtpip install python-multipart
简单解释一下:
pyjwt 是用来产生和校验 JWT token
python-multipart 是因为 OAuth3 需要通过表单数据来发送 username 和 password 信息
在生产实践过程中,获取 token 的代码:
from datetime import datetime, timedelta
from typing import Optional
from fastapi import Depends, FastAPI
from fastapi.security import OAuth3PasswordBearer, OAuth3PasswordRequestForm
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 = 30
class Token(BaseModel):
access_token: str
token_type: str
app = FastAPI()
# oauth3_scheme = OAuth3PasswordBearer(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_delta
else:
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: OAuth3PasswordRequestForm = 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"的解析结果为:
关于Python安全认证需要注意事项有哪些问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。