zoukankan      html  css  js  c++  java
  • 在吗?认识一下JWT(JSON Web Token) ?

    点击上方“ 码农编程进阶笔记 ”,选择“置顶或者星标”
    文末有干货,每天定时与您相约!
    

    什么是JSON Web Token ?

    官网介绍:

    JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地将信息作为JSON对象传输。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公用/专用密钥对对JWT进行签名。

    尽管可以对JWT进行加密以在各方之间提供保密性,但我们将重点关注已签名的令牌。签名的令牌可以验证其中包含的声明的完整性,而加密的令牌则将这些声明隐藏在其他方的面前。当使用公钥/私钥对对令牌进行签名时,签名还证明只有持有私钥的一方才是对其进行签名的一方。

    emmmm.......balabala一堆文字,那么我们来简单总结下:

    JWT是一个JSON信息传输的开放标准,它可以使用密钥对信息进行数字签名,以确保信息是可验证和可信任的。

    JWT的结构是什么?

    JWT由三部分构成:header(头部)、payload(载荷)和signature(签名)。 以紧凑的形式由这三部分组成,由“.“分隔。

    因此,JWT通常如下所示。

    xxxxx.yyyyy.zzzzz

    让我们把这串奇奇怪怪的东西分解开来:

    header

    header通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法,例如HMAC SHA256或RSA等等。

    例如:

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

    显而易见,这货是一个json数据,然后这货会被Base64编码形成JWT的第一部分,也就是xxxxx.yyyyy.zzzzz中的xxxxxx。

    Payload

    这货是JWT的第二部分,叫载荷(负载),内容也是一个json对象,它是存放有效信息的地方,它可以存放JWT提供的现成字段 :

    • iss: 该JWT的签发者。

    • sub: 该JWT所面向的用户。

    • aud: 接收该JWT的一方。

    • exp(expires): 什么时候过期,这里是一个Unix时间戳。

    • iat(issued at): 在什么时候签发的。

    举个例子:

    {
      "iss": "www.baidu.com",
      "sub": "you",
      "aud": "me",
      "name": "456",
      "admin": true,
      "iat": 1584091337,
      "exp": 1784091337,
    }

    这货同样会被Base64编码,然后形成JWT的第二部分,也就是xxxxx.yyyyy.zzzzz中的yyyyyy。

    Signature

    这是JWT的第三部分,叫做签名,此部分用于防止JWT内容被篡改。将上面的两个编码后的字符串都用英文句号.连接在一起(头部在前),就形成了

    xxxxxx.yyyyyy
    

    然后再使用header中声明签名算法进行签名。 如果要使用HMAC SHA256算法,则将通过以下方式创建签名:

    HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

    当然,在加密的时候,我们还需要提供一个密钥(secret),我们可以自己随意指定。这样就形成了JWT的第三部分,也就是xxxxx.yyyyy.zzzzz中的zzzzzz。

    最后,我们把这三个部分拼在一起,就形成了一个完整的JWT。

    下面展示了一个完整的JWT,它先对header和payload进行编码,最后用一个密钥形成了签名。

    如果我们想试验一下的话,可以在JWT的官网进行debugger。贴一下官网:https://jwt.io/

    JSON Web Token认证流程

    什么时候应该使用JSON Web Token?

    以下是JSON Web Token 有用的一些情况:

    • 授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单一登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。

    • 信息交换:JSON Web Token是在各方之间安全地传输信息的好方法。因为可以对JWT进行签名(例如,使用公钥/私钥对),所以您可以确定发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否遭到篡改。

    那么,有人就会说了,道理我都懂,我应该怎样去实现呢?莫慌。。 

    如何实现?

    接下来我会用python实现JWT,不想拉仇恨,但是,python大法好啊。。。。

    在前后端分离的项目中,我们需要与前端约定一种身份认证机制。当用户登录的时候,后端会生成token,然后返回给前端,前端需要将token拿到并按照一定规则放到header中,在下一次请求的时候一并发送给后端,后端进行token身份校验。

    这里我们约定前端请求后端服务时需要添加头信息Authorization ,内容为token。

    我用的是fastapi web框架,搭建项目非常快。

    from datetime import timedelta, datetime
    
    import jwt
    from fastapi import FastAPI, HTTPException, Depends
    from starlette.status import HTTP_401_UNAUTHORIZED
    from starlette.requests import Request
    app = FastAPI()
    
    SECRET_KEY = "sdifhgsiasfjaofhslio" # JWY签名所使用的密钥,是私密的,只在服务端保存
    ALGORITHM = "HS256" # 加密算法,我这里使用的是HS256
    @app.get("/")
    async def root():
        return {"message": "Hello World"}
    
    @app.post("/create_token")
    def create_token(username,password):
        if username == "123" and password == "123":
            access_token_expires = timedelta(minutes=60)
            expire = datetime.utcnow() + access_token_expires
    
            payload = {
                        "sub": username,
                        "exp": expire
                         }
            # 生成Token,返回给前端
            access_token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
            return {"access_token": access_token, "token_type": "bearer"}
    
        else:
            raise HTTPException(
                status_code=HTTP_401_UNAUTHORIZED,
                detail="username or password are not true",
                headers={"WWW-Authenticate": "Bearer"}
            )
    
    
    def authorized_user(token):
        try:
            payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
            username: str = payload.get("sub")
            print(username)
            if username == "123":
                return username
        except jwt.PyJWTError:
            raise HTTPException(
            status_code=HTTP_401_UNAUTHORIZED,
            detail="认证失败,无权查看",
            headers={"WWW-Authenticate": "Bearer"},)
    
    
    
    @app.get("/app")
    def create_token(request: Request):
        print(request.headers.get("host"), request.headers.get("Authorization"))
        user = authorized_user(request.headers.get("Authorization")) # 验证Token
        if user:
            return {"username": user,"detail": "JWT通过,查询成功"}

    这里,由于现有的JWT库已经帮我们封装好了,我们可以使用JWT直接生成 token,不用手动base64加密和拼接。

    测试一下:

    启动项目之后,我们打开http://127.0.0.1:8000/docs# ,就会看到以下我们编写好的api:

    首先,我们先验证一下create_token接口

    当我们输入用户名,密码后,后端进行验证,验证成功后会返回给前端一个token,也就是JWT。当前端拿到这个token之后,下次在请求的时候就必须要带上这个token了,因为前后端已经约定好了。接下来我们试一下:

    认证失败???

    什么原因导致的呢??让我们点开检查抓一下包看看:

    恍然大悟,刚才我们说过,前后端事先约定好的,请求的header中一定要带上token,在Authorization ,内容token。我们现在这个请求的header中并没有带上token,那这种debug模式下又是改不了请求header信息的,我们可以使用接口测试工具进行测试,我主推Postman!!!,让我们来试一下:

    至此,JWT介绍以及使用梳理完毕。

    往日精选文章

    建议收藏 | JWT 超详细分析

    听说你sql写的挺溜的,你说一说查询sql的执行过程

    一次SQL查询优化原理分析(900W+数据,从17s到300ms)

    为什么 select count(*) from t,在 InnoDB 引擎中比 MyISAM 慢?

  • 相关阅读:
    悼念512汶川大地震遇难同胞——珍惜现在,感恩生活--hdu2191(多重背包模板)
    Gunner II--hdu5233(map&vector/二分)
    Geometric Progression---cf 567C(求组合方式,map离散)
    Guess Your Way Out! II---cf 558D (区间覆盖,c++STL map 的使用)
    Amr and Chemistry---cf558C(暴力,加技巧)
    汉诺塔IV---hdu2077
    Safe Or Unsafe--hdu2527(哈夫曼树求WPL)
    Divisibility by Eight---cf550C(被8整除 暴力)
    Charles使用技巧
    RabbitMQ-高级特性(六)
  • 原文地址:https://www.cnblogs.com/lxwphp/p/15452838.html
Copyright © 2011-2022 走看看