Jwt介绍和使用

JWT是什么

JWT(JSON Web Token)是一种用于进行身份验证和授权的开放标准,是基于JSON(JavaScript Object Notation)的一种轻量级的身份验证和授权机制。JWT使用一种紧凑且自包含的格式,将声明(claims)安全地传输于不同的应用之间。

JWT的结构

Header(头部)

  • 签名算法:声明签名所用的算法
  • 令牌类型:告诉接收方此令牌是JWT

Payload(负载)

1.注册声明(Registered Claims) 

一组预定义的声明,非强制的,但是推荐使用。包括:

  • iss(Issuer):签发人
  • exp(Expiration Time):过期时间
  • sub(Subject):主题
  • aud(Audience):受众
  • nbf(Not Before):生效时间
  • iat(Issued At):签发时间
  • jti(JWT ID):JWT的唯一标识

2.共有声明(Public Claims)

携带公共的数据,例如:用户状态、用户权限信息等

3.私有声明(Private Claims)

携带私有的数据,例如用户Id、临时授权码等

Signature(签名)

作用

  1. 防止篡改:签名可以确保JWT在传输过程中没有被篡改。如果攻击者试图修改JWT中的任何部分(包括头部、负载或签名本身),接收方在验证签名时将会发现不一致,从而拒绝该令牌。

  2. 验证发送者身份:当使用私钥进行签名时,只有持有对应公钥的一方才能验证签名的有效性。因此,签名可以用于验证发送者的身份。这是一种基于非对称加密的认证机制,确保只有持有私钥的合法发送者才能生成有效的JWT。

  3. 确保数据来源的可信性:签名提供了一种机制,使得接收方可以确信JWT是由一个可信的发送方生成的,而不是由一个恶意的第三方伪造的。

JWT使用

采用的Java 17,如果用的是Java 8或以下可以用 jjwt 依赖

Maven依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
</dependency>

具体实现

package org.example.demo.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.*;

@Component
public class TokenService {
    private static final Logger log = LoggerFactory.getLogger(TokenService.class);

    // 令牌自定义标识
    private final String header = "Authorization";

    // 令牌秘钥
    private final String secret = "ASO%#IB43hojas347-#^)&*^sabdjiDGgoui@#8^DF89sa68";

    // 令牌有效期(默认30分钟)
    private final int expireTime = 30;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

    private final String userId = "userId";

    // 加密算法
    String encodedSecret = Base64.getEncoder().encodeToString(secret.getBytes());


    /**
     * 生成令牌
     *
     * @param userId 用户Id
     * @return JWT 字符串
     */
    public String createToken(Long userId) {
        // 头部
        Map<String, Object> header = new HashMap<>();
        header.put("typ", "JWT");
        header.put("alg", "HS256");

        // 私有声明
        Map<String, Object> claims = new HashMap<>();
        claims.put(this.userId, userId);

        return Jwts.builder()
                .setHeader(header) // 设置头部数据
                .setIssuer("example.com") // 设置签发者(注册声明)
                .setExpiration(new Date(System.currentTimeMillis() + expireTime * MILLIS_MINUTE)) // 设置过期时间(注册声明)
                .setSubject("User Token") // 设置主题(注册声明)
                .setAudience("all") // 设置接收者(注册声明)
                .setNotBefore(new Date(System.currentTimeMillis())) // 设置生效时间(注册声明)
                .setIssuedAt(new Date()) // 设置签发时间(注册声明)
                .setId(UUID.randomUUID().toString()) // 设置JWT ID(注册声明)
                .setClaims(claims) // 设置私有声明
                .claim("https://baiodu.com/roles", "admin") // 添加公共声明
                .signWith(Keys.hmacShaKeyFor(encodedSecret.getBytes()), SignatureAlgorithm.HS256)
                .compact();
    }

    /**
     * 从token中解析UserId
     *
     * @return UserId
     */
    public Long getUserId() {
        String token = getToken();
        if (token == null) {
            throw new RuntimeException("未携带token!或头属性名不为:" + header);
        }
        final Claims claims = parseToken(token);
        try {
            return Long.parseLong(String.valueOf(claims.get(userId)));
        } catch (NumberFormatException e) {
            throw new RuntimeException("解析用户ID失败", e);
        }
    }

    /**
     * 解析 JWT
     *
     * @param token JWT字符串
     * @return 数据声明
     */
    public Claims parseToken(String token) {

        return Jwts.parserBuilder()
                .setSigningKey(encodedSecret.getBytes())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 从请求头中获取token
     *
     * @return token or null
     */
    public String getToken() {
        final ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            throw new RuntimeException("无法从当前线程获取请求属性!");
        }
        final HttpServletRequest request = attributes.getRequest();
        return request.getHeader(header);
    }

}

可能遇到的异常

解决方案

JWT报错 javax/xml/bind/DatatypeConverter解决办法_javax.xml.bind.datatypeconverter-CSDN博客

Django Rest Framework(DRF)是一种基于 Django 的 Web 应用程序开发框架,它提供了许多工具和库,使得开发 Web API 更加容易。JWT 是一种基于 JSON 的 Web Token,它用于在网络应用程序和服务器之间传递声明以使身份验证和授权更加安全。在 DRF 中使用 JWT 可以使得 API 更加安全。 JWT 由三个部分组成:头部、载荷和签名。头部通常包含算法和令牌类型的信息,载荷通常包含用户标识信息和令牌的过期时间,签名则是根据头部、载荷和密钥生成的。 DRF 支持 JWT 验证,可以使用第三方库 djangorestframework-jwt 来实现。实现方式如下: 1. 安装 djangorestframework-jwt ``` pip install djangorestframework-jwt ``` 2. 添加 JWT 相关配置到 settings.py 中 ``` REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ], } JWT_AUTH = { 'JWT_SECRET_KEY': SECRET_KEY, 'JWT_ALGORITHM': 'HS256', 'JWT_VERIFY_EXPIRATION': True, 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), 'JWT_ALLOW_REFRESH': True, 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=30), } ``` 3. 在 urls.py 中添加 JWT 相关路由 ``` from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token urlpatterns = [ url(r'^api-token-auth/', obtain_jwt_token), url(r'^api-token-refresh/', refresh_jwt_token), url(r'^api-token-verify/', verify_jwt_token), ] ``` 4. 在需要验证的视图中添加 @jwt_authentication_classes 装饰器 ``` from rest_framework.decorators import api_view, permission_classes, jwt_authentication_classes from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response @api_view(['GET']) @permission_classes([IsAuthenticated]) @jwt_authentication_classes def my_view(request): content = {'message': 'Hello, World!'} return Response(content) ``` 以上是 DRF 中使用 JWT 的简单介绍JWT 的原理是将用户标识信息和过期时间等信息进行编码,生成一个安全的 token,并将其传递给客户端。客户端在后续的请求中携带这个 token,在服务端进行验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值