为了账号安全,请及时绑定邮箱和手机立即绑定

JWT教程:轻松入门与实践

标签:
安全 API
概述

JWT教程介绍了JWT的基本概念、构成和工作原理,详细解释了JWT的生成与验证过程,并提供了在实际项目中的应用案例和常见问题解决方案。

JWT简介

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用之间安全地传输声明(声明是JSON对象)。JWT设计为紧凑、自包含的令牌,非常适合用于基于Web的身份验证和信息交换。JWT通常包含一个JSON对象,该对象包含有关用户的信息和相关的元数据。

JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。这些部分用.分隔,形成一个紧凑的、URL安全的字符串。头部包含了令牌的类型及签名算法;载荷包含了必要的声明信息;签名用于确保令牌未被篡改,且由令牌持有者生成并验证。

什么是JWT

JWT是一种用于在网络应用之间传输声明(声明是JSON对象)的标准方式。JWT设计为紧凑且自包含的,非常适合用于基于Web的身份验证和信息交换。JWT通常包含一个JSON对象,该对象包含有关用户的信息和相关的元数据。

JWT的工作原理可以分为以下几个步骤:

  1. 令牌生成:客户端请求一个JWT,服务器验证客户端信息后生成一个JWT。JWT包含三个部分:头部、载荷和签名。头部描述了令牌的类型,载荷包含有用的数据,而签名则用于验证令牌的完整性。

  2. 编码:使用Base64对头部和载荷进行编码。然后使用一个哈希函数和私钥对头部和载荷的编码结果进行签名。生成的签名将被附加到JWT的末尾,作为令牌的第三部分。

  3. 验证与解析:当客户端使用JWT请求资源时,服务器会进行验证。服务器首先验证签名是否正确,如果签名验证失败,说明令牌可能已被篡改或不是来自可信源。如果签名验证通过,则服务器会解析载荷中的数据,进行进一步的验证或权限控制。

JWT的优点和应用场景

JWT具有以下优点:

  • 安全性:JWT是无状态的,服务器不需要存储令牌,从而减轻了服务器的负担。另外,JWT还支持加密签名,可以防止数据被篡改。
  • 无状态性:由于JWT是无状态的,服务器不需要在内存中保存用户的会话信息,减轻了服务器的存储负担。
  • 跨域支持:JWT可以在不同域名之间共享,非常适合前后端分离的应用架构。
  • 易集成:JWT可以轻松集成到Web应用、移动应用、桌面应用等各种场景中。

JWT在以下几个场景中被广泛使用:

  • 用户身份验证:用户登录后,服务器生成并发送JWT,客户端可以将JWT存储在本地(如LocalStorage或Cookie),用于后续请求的身份验证。
  • 信息传输:JWT可以安全地传输数据,比如用户信息、权限信息等。
  • 第三方服务集成:JWT可以用于整合不同的服务和应用,确保数据的一致性和安全性。

JWT的基本构成

JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。这三部分通过.分隔形成一个紧凑的、URL安全的字符串。

头部(Header)

头部包含两种信息:声明类型和所用签名算法。JWT的头部就是一个普通的JSON对象,即一个键值对的集合(一个关联数组)。

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg:表示签名使用的算法。例如,HS256表示使用HMAC算法,密钥为256位。
  • typ:表示令牌的类型,JWT的类型永远是JWT

头部用Base64编码后,形成JWT的第一部分。

载荷(Payload)

载荷是JWT的主体,包含声明(Claim)的数据。声明是JWT的主体部分,包含了一系列的键值对。声明分成了三种类型:注册声明(Registered Claims)、公共声明(Public Claims)和私有声明(Private Claims)。

  • 注册声明:注册声明是一些预定义的声明,它们用于描述JWT的某些属性,如iss(Issuer)、exp(Expiration Time)、sub(Subject)等。这些声明是可选的,但它们的含义是预定义的,如果使用这些声明,建议严格遵循它们的定义。
  • 公共声明:公共声明是标准中定义的其他声明,它们不是JWT的标准组成部分,但对于某些应用可能会有特殊意义。
  • 私有声明:私有声明可以自定义,但通常会遵循JWT的结构,以便于其他系统集成。

载荷的结构如下:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022
}
  • sub:主题,指用户的唯一身份标识。
  • name:用户名。
  • admin:用户权限信息。
  • iat:签发日期,通常表示签发时间,即该JWT何时被签发。

载荷用Base64编码后,形成JWT的第二部分。

签名(Signature)

签名部分用于验证消息是否被篡改,以及该JWT是由谁发出的。签名部分的算法是使用头部指定的算法,基于头部与载荷的字符串编码,以及一个密钥。签名算法可以是HS256HS384HS512等。

签名生成的过程如下:

  1. 用Base64编码头部和载荷,形成两部分字符串。
  2. 将这两部分字符串拼接,形成一个字符串。
  3. 使用密钥和指定的哈希算法来生成签名。

例如,使用HS256算法,假设密钥为secret

import base64
import hmac
import json

def create_signature(header, payload, secret):
    header_encoded = base64.b64encode(json.dumps(header).encode('utf-8')).decode('utf-8')
    payload_encoded = base64.b64encode(json.dumps(payload).encode('utf-8')).decode('utf-8')
    signature_input = f"{header_encoded}.{payload_encoded}"
    signature = hmac.new(secret.encode('utf-8'), signature_input.encode('utf-8'), digestmod='sha256').hexdigest()
    return signature

header = {"alg": "HS256", "typ": "JWT"}
payload = {"sub": "1234567890", "name": "John Doe", "admin": True, "iat": 1516239022}
secret = "secret"
signature = create_signature(header, payload, secret)
print(signature)

最终,签名部分会附加到JWT的末尾,形成完整的JWT字符串。

JWT的生成与验证

如何生成JWT

生成JWT的过程包括创建头部、载荷,然后通过指定的密钥和算法生成签名。生成的JWT有三部分组成,分别是头部、载荷和签名,它们通过.分隔形成一个完整的JWT字符串。

  1. 创建头部:头部是一个JSON对象,包含JWT的类型和签名算法。

  2. 创建载荷:载荷也是JSON对象,包含各种声明信息。

  3. 生成签名:通过头部和载荷的信息,以及预定义的密钥和签名算法,生成签名。

下面是一个使用Python的示例代码:

import base64
import hmac
import json

def encode_base64(input_str):
    return base64.urlsafe_b64encode(input_str.encode('utf-8')).decode('utf-8')

def decode_base64(input_str):
    return base64.urlsafe_b64decode(input_str.encode('utf-8')).decode('utf-8')

def generate_jwt(header, payload, secret):
    header_encoded = encode_base64(json.dumps(header))
    payload_encoded = encode_base64(json.dumps(payload))
    signature_input = f"{header_encoded}.{payload_encoded}"
    signature = hmac.new(secret.encode('utf-8'), signature_input.encode('utf-8'), digestmod='sha256').hexdigest()
    jwt = f"{header_encoded}.{payload_encoded}.{signature}"
    return jwt

header = {"alg": "HS256", "typ": "JWT"}
payload = {"sub": "1234567890", "name": "John Doe", "admin": True, "iat": 1516239022}
secret = "secret"
jwt = generate_jwt(header, payload, secret)
print(jwt)

下面是一个使用Java的示例代码:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtUtil {
    public static String generateJwt(String subject, String name, boolean admin, long iat, String secret) {
        return Jwts.builder()
                .setSubject(subject)
                .claim("name", name)
                .claim("admin", admin)
                .setIssuedAt(new java.util.Date(iat * 1000))
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }
}

public class Main {
    public static void main(String[] args) {
        String subject = "1234567890";
        String name = "John Doe";
        boolean admin = true;
        long iat = 1516239022;
        String secret = "secret";
        String token = JwtUtil.generateJwt(subject, name, admin, iat, secret);
        System.out.println(token);
    }
}

如何验证JWT

验证JWT的过程包括解码头部和载荷,生成新的签名,然后将新生成的签名与JWT中的签名进行比较。如果签名一致,则验证通过。

  1. 解码头部和载荷:通过Base64解码JWT的头部和载荷部分。

  2. 生成签名:使用相同的密钥和算法生成新的签名。

  3. 比较签名:将生成的新签名与JWT中的签名进行比较,如果一致,表示JWT未被篡改,验证通过。

下面是一个使用Python的示例代码:

def verify_jwt(jwt, secret):
    parts = jwt.split('.')
    header_encoded = parts[0]
    payload_encoded = parts[1]
    signature = parts[2]

    header = json.loads(decode_base64(header_encoded))
    payload = json.loads(decode_base64(payload_encoded))

    signature_input = f"{header_encoded}.{payload_encoded}"
    new_signature = hmac.new(secret.encode('utf-8'), signature_input.encode('utf-8'), digestmod='sha256').hexdigest()

    if signature == new_signature:
        return True
    else:
        return False

jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.sflKxwRJSMeKKF2QTfRdv7lh7c342928"
secret = "secret"
print(verify_jwt(jwt, secret))

下面是一个使用Java的示例代码:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

public class JwtUtil {
    public static boolean verifyJwt(String token, String secret) {
        try {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.sflKxwRJSMeKKF2QTfRdv7lh7c342928";
        String secret = "secret";
        System.out.println(JwtUtil.verifyJwt(token, secret));
    }
}

JWT的常见库介绍

JWT有多种库可供使用,这里介绍一些常用的库:

  • PythonPyJWT库是最常用的Python库,用于生成和验证JWT。
  • JavaScriptjsonwebtoken库是JavaScript中最常用的库,用于生成和验证JWT。
  • Javajjwt库是Java中最常用的库,用于生成和验证JWT。

下面分别介绍这些库的使用方法:

Python

使用PyJWT库生成JWT:

import jwt

def generate_jwt(payload, secret):
    token = jwt.encode(payload, secret, algorithm='HS256')
    return token

payload = {"sub": "1234567890", "name": "John Doe", "admin": True, "iat": 1516239022}
secret = "secret"
token = generate_jwt(payload, secret)
print(token)

使用PyJWT库验证JWT:

def verify_jwt(token, secret):
    try:
        decoded = jwt.decode(token, secret, algorithms=['HS256'])
        return True
    except jwt.exceptions.InvalidTokenError:
        return False

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.sflKxwRJSMeKKF2QTfRdv7lh7c342928"
secret = "secret"
print(verify_jwt(token, secret))

JavaScript

使用jsonwebtoken库生成JWT:

const jwt = require('jsonwebtoken');

function generateJwt(payload, secret) {
    const token = jwt.sign(payload, secret, { algorithm: 'HS256' });
    return token;
}

const payload = { sub: "1234567890", name: "John Doe", admin: true, iat: 1516239022 };
const secret = "secret";
const token = generateJwt(payload, secret);
console.log(token);

使用jsonwebtoken库验证JWT:

function verifyJwt(token, secret) {
    try {
        const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] });
        return true;
    } catch (error) {
        return false;
    }
}

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.sflKxwRJSMeKKF2QTfRdv7lh7c342928";
const secret = "secret";
console.log(verifyJwt(token, secret));

Java

使用jjwt库生成JWT:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtUtil {
    public static String generateJwt(String subject, String name, boolean admin, long iat, String secret) {
        return Jwts.builder()
                .setSubject(subject)
                .claim("name", name)
                .claim("admin", admin)
                .setIssuedAt(new java.util.Date(iat * 1000))
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }
}

public class Main {
    public static void main(String[] args) {
        String subject = "1234567890";
        String name = "John Doe";
        boolean admin = true;
        long iat = 1516239022;
        String secret = "secret";
        String token = JwtUtil.generateJwt(subject, name, admin, iat, secret);
        System.out.println(token);
    }
}

使用jjwt库验证JWT:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

public class JwtUtil {
    public static boolean verifyJwt(String token, String secret) {
        try {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.sflKxwRJSMeKKF2QTfRdv7lh7c342928";
        String secret = "secret";
        System.out.println(JwtUtil.verifyJwt(token, secret));
    }
}

JWT在项目中的应用

JWT在项目中的应用非常广泛,特别是在基于Web的身份验证、信息传输和第三方服务集成等方面。以下是JWT在项目中的一些具体应用案例。

用户身份验证

JWT常被用于用户身份验证,尤其是在前后端分离的系统中。用户登录后,服务端会生成一个JWT,并将其返回给客户端。客户端可以将这个JWT存储在本地(如LocalStorage或Cookie),并在后续请求中携带这个JWT。服务端通过验证JWT来确认用户的身份信息。

示例代码

  • 服务端生成JWT并返回给客户端
import jwt
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
    username = request.json.get('username')
    password = request.json.get('password')
    # 模拟用户验证过程
    if username == 'admin' and password == '12345':
        payload = {
            "sub": "admin",
            "name": "Admin User",
            "iat": int(time.time())
        }
        token = jwt.encode(payload, 'secret', algorithm='HS256')
        return jsonify({'token': token})
    else:
        return jsonify({'error': 'Invalid credentials'}), 401

if __name__ == '__main__':
    app.run(debug=True)
``

下面是一个使用Java的示例代码:

```java
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class JwtUtil {
    public static String generateJwt(HttpServletRequest request, String secret) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        // 模拟用户验证过程
        if ("admin".equals(username) && "12345".equals(password)) {
            return Jwts.builder()
                    .setSubject(username)
                    .claim("name", "Admin User")
                    .setIssuedAt(new Date())
                    .signWith(SignatureAlgorithm.HS256, secret)
                    .compact();
        } else {
            HttpServletResponse response = (HttpServletResponse) request.getAttribute("javax.servlet.error.response");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return null;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        HttpServletRequest request = null;
        String secret = "secret";
        String token = JwtUtil.generateJwt(request, secret);
        System.out.println(token);
    }
}
  • 客户端存储并使用JWT
fetch('/login', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        username: 'admin',
        password: '12345'
    })
})
.then(response => response.json())
.then(data => {
    localStorage.setItem('token', data.token);
})
.catch(error => console.error('Error:', error));
``

- **服务端验证JWT**

```python
@app.route('/protected', methods=['GET'])
def protected():
    token = request.headers.get('Authorization')
    if not token:
        return jsonify({'error': 'Missing token'}), 401

    try:
        decoded = jwt.decode(token, 'secret', algorithms=['HS256'])
        return jsonify({'message': 'Access granted', 'user': decoded['name']})
    except jwt.exceptions.InvalidTokenError:
        return jsonify({'error': 'Invalid token'}), 401

if __name__ == '__main__':
    app.run(debug=True)

下面是一个使用Java的示例代码:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class JwtUtil {
    public static boolean verifyJwt(HttpServletRequest request, String secret) {
        String token = request.getHeader("Authorization");
        if (token == null) {
            HttpServletResponse response = (HttpServletResponse) request.getAttribute("javax.servlet.error.response");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }

        try {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        HttpServletRequest request = null;
        String secret = "secret";
        boolean result = JwtUtil.verifyJwt(request, secret);
        System.out.println(result);
    }
}

信息传输

JWT可以用于安全地传输信息,例如用户信息、权限信息等。JWT的无状态性使得它非常适合用于跨域服务之间的信息传输。

示例代码

  • 服务端生成JWT并返回用户信息
@app.route('/get-user', methods=['GET'])
def get_user():
    token = request.headers.get('Authorization')
    if not token:
        return jsonify({'error': 'Missing token'}), 401

    try:
        decoded = jwt.decode(token, 'secret', algorithms=['HS256'])
        return jsonify({
            'user_id': decoded['sub'],
            'name': decoded['name'],
            'is_admin': decoded.get('admin', False)
        })
    except jwt.exceptions.InvalidTokenError:
        return jsonify({'error': 'Invalid token'}), 401

if __name__ == '__main__':
    app.run(debug=True)

下面是一个使用Java的示例代码:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class JwtUtil {
    public static String getUserInfo(HttpServletRequest request, String secret) {
        String token = request.getHeader("Authorization");
        if (token == null) {
            HttpServletResponse response = (HttpServletResponse) request.getAttribute("javax.servlet.error.response");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return null;
        }

        try {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            return String.format("User ID: %s, Name: %s, Is Admin: %s", claims.getSubject(), claims.get("name"), claims.get("admin"));
        } catch (Exception e) {
            return null;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        HttpServletRequest request = null;
        String secret = "secret";
        String userInfo = JwtUtil.getUserInfo(request, secret);
        System.out.println(userInfo);
    }
}
  • 客户端获取并显示用户信息
fetch('/get-user', {
    headers: {
        'Authorization': localStorage.getItem('token')
    }
})
.then(response => response.json())
.then(data => {
    console.log(data);
})
.catch(error => console.error('Error:', error));

第三方服务集成

JWT也可以用于第三方服务的集成,例如OAuth2或OpenID Connect。通过JWT,可以安全地在不同的服务之间传递用户信息和权限信息。

示例代码

  • 服务端生成JWT并携带用户信息
@app.route('/get-token', methods=['GET'])
def get_token():
    user_id = 'user123'
    name = 'John Doe'
    token = jwt.encode({
        'sub': user_id,
        'name': name,
        'iat': int(time.time())
    }, 'secret', algorithm='HS256')
    return token

if __name__ == '__main__':
    app.run(debug=True)

下面是一个使用Java的示例代码:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtUtil {
    public static String generateJwt(String subject, String name, long iat, String secret) {
        return Jwts.builder()
                .setSubject(subject)
                .claim("name", name)
                .setIssuedAt(new java.util.Date(iat * 1000))
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }
}

public class Main {
    public static void main(String[] args) {
        String subject = "user123";
        String name = "John Doe";
        long iat = System.currentTimeMillis() / 1000;
        String secret = "secret";
        String token = JwtUtil.generateJwt(subject, name, iat, secret);
        System.out.println(token);
    }
}
  • 客户端获取JWT并传递给第三方服务
fetch('/get-token')
.then(response => response.text())
.then(token => {
    fetch('https://thirdparty.com/api', {
        headers: {
            'Authorization': `Bearer ${token}`
        }
    })
    .then(response => console.log(response))
    .catch(error => console.error('Error:', error));
})
.catch(error => console.error('Error:', error));

JWT的常见问题与解决方案

JWT虽然在身份验证和信息传输方面具有很多优势,但在实际使用过程中也可能会遇到一些问题,例如JWT过期、安全问题和性能问题等。以下是一些常见的JWT问题及解决方案。

JWT过期问题

过期问题是指JWT有固定的有效时间,超过这个时间,JWT就会失效。对于这个问题,可以考虑以下几种解决方案:

  1. 刷新机制:客户端可以实现一个刷新机制,当JWT即将过期时,通过刷新接口请求一个新的JWT。刷新机制需要客户端在请求新JWT时携带一些额外的信息,例如旧的JWT或其他一些安全信息,以确保请求的合法性。

  2. 动态调整过期时间:根据业务场景动态调整过期时间,例如,对于某些敏感操作,可以设置较短的过期时间;而对于一些常规操作,则可以设置较长的过期时间。

  3. 长寿命令牌:在某些情况下,可以使用长寿命令牌(长期令牌)代替短寿命令牌。但是,长期令牌的安全性较低,使用时需要特别注意。

示例代码

  • 刷新机制实现
@app.route('/refresh', methods=['GET'])
def refresh():
    token = request.headers.get('Authorization')
    if not token:
        return jsonify({'error': 'Missing token'}), 401

    try:
        decoded = jwt.decode(token, 'secret', algorithms=['HS256'])
        new_token = jwt.encode({
            'sub': decoded['sub'],
            'name': decoded['name'],
            'iat': int(time.time())
        }, 'secret', algorithm='HS256')
        return jsonify({'token': new_token})
    except jwt.exceptions.InvalidTokenError:
        return jsonify({'error': 'Invalid token'}), 401

if __name__ == '__main__':
    app.run(debug=True)

下面是一个使用Java的示例代码:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class JwtUtil {
    public static String refreshJwt(HttpServletRequest request, String secret) {
        String token = request.getHeader("Authorization");
        if (token == null) {
            HttpServletResponse response = (HttpServletResponse) request.getAttribute("javax.servlet.error.response");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return null;
        }

        try {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            return Jwts.builder()
                    .setSubject(claims.getSubject())
                    .claim("name", claims.get("name"))
                    .setIssuedAt(new Date())
                    .signWith(io.jsonwebtoken.SignatureAlgorithm.HS256, secret)
                    .compact();
        } catch (Exception e) {
            return null;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        HttpServletRequest request = null;
        String secret = "secret";
        String newToken = JwtUtil.refreshJwt(request, secret);
        System.out.println(newToken);
    }
}
  • 客户端刷新JWT
function refreshToken() {
    fetch('/refresh', {
        headers: {
            'Authorization': localStorage.getItem('token')
        }
    })
    .then(response => response.json())
    .then(data => {
        localStorage.setItem('token', data.token);
    })
    .catch(error => console.error('Error:', error));
}

// 调用函数定时刷新
setInterval(refreshToken, 300000); // 每5分钟刷新一次

JWT的安全问题

JWT的安全性问题主要集中在密钥管理、篡改防范和传输安全等方面。为了确保JWT的安全性,可以采取以下几种措施:

  1. 密钥管理:密钥是JWT安全性的重要保障,需要妥善保管。使用强密钥,并定期更换密钥。
  2. 签名算法选择:选择合适的签名算法,如HS256,确保JWT的完整性。
  3. HTTPS传输:使用HTTPS传输JWT,确保JWT在传输过程中不被截取。
  4. 防止XSS攻击:防止JWT被窃取,通过设置HttpOnly和Secure属性来保护Cookie中的JWT。

示例代码

  • 确保密钥安全
import os

SECRET_KEY = os.getenv('JWT_SECRET_KEY', 'default_secret_key')
  • 使用HTTPS
from flask import Flask, request

app = Flask(__name__)
app.config['ENV'] = 'production'

@app.route('/login', methods=['POST'])
def login():
    # ...
    token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
    return jsonify({'token': token})

如何优化JWT性能

JWT的性能优化主要集中在减少不必要的计算和存储上。以下是一些优化JWT性能的建议:

  1. 减少载荷大小:减少载荷中的数据量,只包含必要的声明信息。
  2. 缓存JWT:对于频繁访问的资源,可以缓存JWT,减少重复验证的开销。
  3. 异步验证:使用异步验证JWT,避免阻塞主线程。
  4. 使用短寿命令牌:短寿命令牌可以减少存储和验证的成本,但需要定期刷新JWT。

示例代码

  • 减少载荷大小
payload = {
    "sub": "1234567890",
    "name": "John Doe",
    "iat": int(time.time())
}
token = jwt.encode(payload, 'secret', algorithm='HS256')
  • 异步验证JWT
import asyncio

async def verify_jwt_async(token, secret):
    result = await asyncio.to_thread(jwt.decode, token, secret, algorithms=['HS256'])
    return result

async def main():
    token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.sflKxwRJSMeKKF2QTfRdv7lh7c342928"
    secret = "secret"
    decoded = await verify_jwt_async(token, secret)
    print(decoded)

if __name__ == '__main__':
    asyncio.run(main())

JWT实战案例

JWT实战案例展示了JWT在实际项目中的应用,包括用户登录并返回JWT、使用JWT验证用户权限、JWT在前后端分离项目中的应用等。

实现用户登录并返回JWT

用户登录时,服务端验证用户名和密码等信息,如果验证通过,则生成一个JWT并返回给客户端。客户端将JWT存储起来,用于后续的身份验证。

示例代码

  • 服务端实现用户登录
import jwt
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
    username = request.json.get('username')
    password = request.json.get('password')
    # 模拟用户验证过程
    if username == 'admin' and password == '12345':
        payload = {
            "sub": "admin",
            "name": "Admin User",
            "iat": int(time.time())
        }
        token = jwt.encode(payload, 'secret', algorithm='HS256')
        return jsonify({'token': token})
    else:
        return jsonify({'error': 'Invalid credentials'}), 401

if __name__ == '__main__':
    app.run(debug=True)

下面是一个使用Java的示例代码:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class JwtUtil {
    public static String generateJwt(HttpServletRequest request, String secret) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        // 模拟用户验证过程
        if ("admin".equals(username) && "12345".equals(password)) {
            return Jwts.builder()
                    .setSubject(username)
                    .claim("name", "Admin User")
                    .setIssuedAt(new Date())
                    .signWith(SignatureAlgorithm.HS256, secret)
                    .compact();
        } else {
            HttpServletResponse response = (HttpServletResponse) request.getAttribute("javax.servlet.error.response");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return null;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        HttpServletRequest request = null;
        String secret = "secret";
        String token = JwtUtil.generateJwt(request, secret);
        System.out.println(token);
    }
}
  • 客户端获取JWT并存储
fetch('/login', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        username: 'admin',
        password: '12345'
    })
})
.then(response => response.json())
.then(data => {
    localStorage.setItem('token', data.token);
})
.catch(error => console.error('Error:', error));

使用JWT验证用户权限

服务端在接收到请求时,会对请求头中的JWT进行验证,验证通过后,根据JWT中的声明信息(如用户角色)来决定是否允许访问资源。

示例代码

  • 服务端验证JWT并检查权限
@app.route('/protected', methods=['GET'])
def protected():
    token = request.headers.get('Authorization')
    if not token:
        return jsonify({'error': 'Missing token'}), 401

    try:
        decoded = jwt.decode(token, 'secret', algorithms=['HS256'])
        if decoded.get('admin', False):
            return jsonify({'message': 'Access granted', 'user': decoded['name']})
        else:
            return jsonify({'error': 'Insufficient permissions'}), 403
    except jwt.exceptions.InvalidTokenError:
        return jsonify({'error': 'Invalid token'}), 401

if __name__ == '__main__':
    app.run(debug=True)

下面是一个使用Java的示例代码:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class JwtUtil {
    public static boolean verifyJwt(HttpServletRequest request, String secret) {
        String token = request.getHeader("Authorization");
        if (token == null) {
            HttpServletResponse response = (HttpServletResponse) request.getAttribute("javax.servlet.error.response");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }

        try {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            return claims.get("admin", Boolean.class);
        } catch (Exception e) {
            return false;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        HttpServletRequest request = null;
        String secret = "secret";
        boolean result = JwtUtil.verifyJwt(request, secret);
        System.out.println(result);
    }
}
  • 客户端访问受保护资源
fetch('/protected', {
    headers: {
        'Authorization': `Bearer ${localStorage.getItem('token')}`
    }
})
.then(response => response.json())
.then(data => {
    console.log(data);
})
.catch(error => console.error('Error:', error));

JWT在前后端分离项目中的应用

前后端分离项目中,前端和后端是独立的,JWT非常适合用于这种架构。前端通过JWT向后端请求资源,后端验证JWT并提供相应的资源。

示例代码

  • 后端提供资源
@app.route('/api/resource', methods=['GET'])
def get_resource():
    token = request.headers.get('Authorization')
    if not token:
        return jsonify({'error': 'Missing token'}), 401

    try:
        decoded = jwt.decode(token, 'secret', algorithms=['HS256'])
        return jsonify({'resource': 'This is a protected resource', 'user': decoded['name']})
    except jwt.exceptions.InvalidTokenError:
        return jsonify({'error': 'Invalid token'}), 401

if __name__ == '__main__':
    app.run(debug=True)

下面是一个使用Java的示例代码:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class JwtUtil {
    public static boolean verifyJwt(HttpServletRequest request, String secret) {
        String token = request.getHeader("Authorization");
        if (token == null) {
            HttpServletResponse response = (HttpServletResponse) request.getAttribute("javax.servlet.error.response");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }

        try {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        HttpServletRequest request = null;
        String secret = "secret";
        boolean result = JwtUtil.verifyJwt(request, secret);
        System.out.println(result);
    }
}
  • 前端获取资源
fetch('/api/resource', {
    headers: {
        'Authorization': `Bearer ${localStorage.getItem('token')}`
    }
})
.then(response => response.json())
.then(data => {
    console.log(data);
})
.catch(error => console.error('Error:', error));
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
8
获赞与收藏
25

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消