JWT详细介绍

一、what

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的

二、when

下列场景中使用JSON Web Token是很有用的:

  • Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。
  • 单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
  • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

三、结构

JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:

  • Header
  • Payload
  • Signature

因此,一个典型的JWT看起来是这个样子的:

xxxxx.yyyyy.zzzzz

四、Header

Header header典型的由两部分组成:

  1. token的类型(“JWT”)
  2. 算法名称(比如:HMAC SHA256或者RSA等等)

例如:

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

然后,用Base64对这个JSON编码就得到JWT的第一部分Header

五、Payload

Payload JWT的第二部分是payload,它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public private

  1. Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
  2. Public claims : 可以随意定义。
  3. Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。

例如:

{
    "sub": '1234567890',
    "name": 'john',
    "admin":true
}

Reserved claims(保留),它的含义就像是编程语言的保留字一样,属于JWT标准里面规定的一些claim。JWT标准里面定好的claim有:

  • iss(Issuser):代表这个JWT的签发主体;
  • sub(Subject):代表这个JWT的主体,即它的所有人;
  • aud(Audience):代表这个JWT的接收对象;
  • exp(Expiration time):是一个时间戳,代表这个JWT的过期时间;
  • nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的;
  • iat(Issued at):是一个时间戳,代表这个JWT的签发时间;
  • jti(JWT ID):是JWT的唯一标识。

对payload进行Base64编码就得到JWT的第二部分

注意:不要在JWT的payload或header中放置敏感信息,除非它们是加密的。

六、Signature

为了得到签名部分,你必须有:

  1. 编码过的header
  2. 编码过的payload
  3. 一个秘钥

签名算法是header中指定的那个,然后它们签名即可。

如:

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

签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。

七、JWT用户认证流程

基于Token的身份认证是如何工作的 基于Token的身份认证是无状态的,服务器或者Session中不会存储任何用户信息。

没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器,而不必担心用户登录的位置。

虽然这一实现可能会有所不同,但其主要流程如下:

  1. 用户携带用户名和密码请求访问
  2. 服务器校验用户凭据
  3. 应用提供一个token给客户端
  4. 客户端存储token,并且在随后的每一次请求中都带着它
  5. 服务器校验token并返回数据

注意:

  • 每一次请求都需要token
  • Token应该放在请求header中
  • 我们还需要将服务器设置为接受来自所有域的请求,用Access-Control-Allow-Origin:

八、使用Token的好处

1. 无状态和可扩展性:Tokens存储在客户端。完全无状态,可扩展。我们的负载均衡器可以将用户传递到任意服务器,因为在任何地方都没有状态或会话信息。
2. 安全:Token不是Cookie。(The token, not a cookie.)每次请求的时候Token都会被发送。而且,由于没有Cookie被发送,还有助于防止CSRF攻击。即使在你的实现中将token存储到客户端的Cookie中,这个Cookie也只是一种存储机制,而非身份认证机制。没有基于会话的信息可以操作,因为我们没有会话!
3. token在一段时间以后会过期,这个时候用户需要重新登录。这有助于我们保持安全。还有一个概念叫token撤销,它允许我们根据相同的授权许可使特定的token甚至一组token无效。

九、与OAuth2的区别

  1. OAuth2是一种授权框架 ,JWT是一种认证协议
  2. 无论使用哪种方式切记用HTTPS来保证数据的安全性
  3. OAuth2用在使用第三方账号登录的情况(比如使用weibo, qq, github登录某个app),而JWT是用在前后端分离, 需要简单的对后台API进行保护时使用。

十、JWT的框架JJWT

1. JJWT是什么

JJWT是一个提供端到端的JWT创建和验证的Java库。永远免费和开源(Apache License,版本2.0),JJWT很容易使用和理解。它被设计成一个以建筑为中心的流畅界面,隐藏了它的大部分复杂性。

2. JJWT的好处
  • JJWT的目标是最容易使用和理解用于在JVM上创建和验证JSON Web令牌(JWTs)的库。
  • JJWT是基于JWT、JWS、JWE、JWK和JWA RFC规范的Java实现。
  • JJWT还添加了一些不属于规范的便利扩展,比如JWT压缩和索赔强制。
3. 规范兼容
  • 创建和解析明文压缩JWTs

  • 创建、解析和验证所有标准JWS算法的数字签名紧凑JWTs(又称JWSs):

  • HS256: HMAC using SHA-256

  • HS384: HMAC using SHA-384

  • HS512: HMAC using SHA-512

  • RS256: RSASSA-PKCS-v1_5 using SHA-256

  • RS384: RSASSA-PKCS-v1_5 using SHA-384

  • RS512: RSASSA-PKCS-v1_5 using SHA-512

  • PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256

  • PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384

  • PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512

4. JWTUtil
/**
 * 解析jwt
 */
public static Claims parseJWT(String jsonWebToken) {
    try {
        Claims claims = Jwts.parser()
                .setSigningKey(DatatypeConverter.parseBase64Binary(base64Secret))
                .parseClaimsJws(jsonWebToken).getBody();
        return claims;
    } catch (Exception ex) {
    //            ex.printStackTrace();
            return null;
        }
    }
/**
 * 构建jwt,将参数信息加密
 */
public static String createJWT(TokenInfoModel tokenInfoModel) {
    SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    //生成签名密钥
    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Secret);
    Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

    //Token头部信息
    Map<String, Object> map = new HashMap<>();
    map.put("typ", "JWT");
    map.put("alg", "HS256");

    //添加构成JWT的参数
    JwtBuilder builder = Jwts.builder().setHeader(map)

            .claim("accountId", tokenInfoModel.getAccountId())//用户ID
            .claim("roleIds", tokenInfoModel.getRoleIds())//角色
            .claim("username", tokenInfoModel.getUsername())//用户名
            .setIssuer(issuer)//JWT签发者
            .signWith(signatureAlgorithm, signingKey);
    //添加Token过期时间
    if (expiresSecond >= 0) {
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        long expMillis = nowMillis + expiresSecond;
        Date exp = new Date(expMillis);
        builder.setExpiration(exp).setNotBefore(now);
    }

    //生成JWT
    return builder.compact();
}

/**
 * 获取头文件中的Token,根据Token取得用户信息,返回model
 *
 * @param Token
 * @return
 */
public static TokenInfoModel geTokenInfo(String Token) {
    //String Token= headers.get("authorization").get(0).substring(7);
    Claims claims = JwtUtil.parseJWT(Token);

    TokenInfoModel tokenInfoModel = new TokenInfoModel();
    try {
        if (claims != null) {
            tokenInfoModel.setAccountId(Long.valueOf(String.valueOf(claims.get("accountId"))));
            List<Long> roleIds = new ArrayList<>();
            for (Integer o : (List<Integer>) claims.get("roleIds")) {
                roleIds.add(Long.valueOf(o));
            }
            tokenInfoModel.setRoleIds(roleIds);
            tokenInfoModel.setUsername((String)claims.get("username"));
        }
    }catch (Exception e){
        tokenInfoModel = new TokenInfoModel();
    }
    return tokenInfoModel;
}

public static String eccrypt(String info) throws NoSuchAlgorithmException{
    MessageDigest messageDigest;
    String encodeStr = "";
    try {
        messageDigest = MessageDigest.getInstance("SHA-256");
        messageDigest.update(info.getBytes("UTF-8"));
        encodeStr = byte2Hex(messageDigest.digest());
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return encodeStr;
}

/**
 * 将byte转为16进制
 * @param bytes
 * @return
 */
private static String byte2Hex(byte[] bytes){
    StringBuffer stringBuffer = new StringBuffer();
    String temp = null;
    for (int i=0;i<bytes.length;i++){
        temp = Integer.toHexString(bytes[i] & 0xFF);
        if (temp.length()==1){
            //1得到一位的进行补0操作
            stringBuffer.append("0");
        }
        stringBuffer.append(temp);
    }
    return stringBuffer.toString();
}
5. JWTFilter
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>(
            Arrays.asList(
                    "/api/auth/login",
                   
            )));


    /**
     * Reserved claims(保留),它的含义就像是编程语言的保留字一样,属于JWT标准里面规定的一些claim。JWT标准里面定好的claim有:
     * <p>
     * iss(Issuser):代表这个JWT的签发主体;
     * sub(Subject):代表这个JWT的主体,即它的所有人;
     * aud(Audience):代表这个JWT的接收对象;
     * exp(Expiration time):是一个时间戳,代表这个JWT的过期时间;
     * nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的;
     * iat(Issued at):是一个时间戳,代表这个JWT的签发时间;
     * jti(JWT ID):是JWT的唯一标识。
     */
    @Override
    public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");


        String path = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("[/]+$", "");
        boolean allowedPath = ALLOWED_PATHS.contains(path);
        logger.info(path);
        if (allowedPath) {
            chain.doFilter(req, res);
        } else {
            //处理token过滤
            //等到请求头信息authorization信息
            final String token = request.getHeader("authorization");
            PrintWriter out = null;
            try {
                final Claims claims = JwtUtil.parseJWT(token);
                if (claims == null) {
                    response.setStatus(HttpServletResponse.SC_OK);
                    JSONObject responseJSONObject = new JSONObject();
                    responseJSONObject.put("error_code","401");
                    responseJSONObject.put("error_msg","身份验证已过期");
                    out = response.getWriter();
                    out.append(responseJSONObject.toString());

                    return ;
                }

            } catch (final Exception e) {
                e.printStackTrace();
                response.setStatus(HttpServletResponse.SC_OK);
                JSONObject responseJSONObject = new JSONObject();
                responseJSONObject.put("error_code","401");
                responseJSONObject.put("error_msg","身份验证异常");
                out = response.getWriter();
                out.append(responseJSONObject.toString());
                return ;
            }
            System.out.println("123123"+request.getRequestURI());
            TokenInfoModel tokenInfo = JwtUtil.geTokenInfo(token);
           // operateLogService.save(path,tokenInfo);

            //将请求转发给过滤器链上下一个对象
            chain.doFilter(req, res);
        }

    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Session、Cookie、Token、JWT都是在Web开发中用于管理用户身份认证和会话管理的工具。 1. Session:Session指的是服务器端保存的用户信息。当用户登录成功后,服务器会创建一个session,为该用户分配一个session ID,并将该ID保存到cookie中,发送给客户端。客户端浏览器保存了这个cookie,以后每次请求都会带上这个cookie,服务器通过这个cookie就可以识别出用户身份,从而进行相应的操作。Session机制存在一定的风险,比如会话劫持、会话固定等问题,需要注意安全性。 2. Cookie:Cookie是一种客户端保存用户信息的机制,它是由服务器在响应HTTP请求时通过Set-Cookie头部字段发给客户端浏览器的一小段文本信息。浏览器将这些信息保存在客户端,以后每次向服务器发送请求时都会自动带上这些cookie,从而实现身份认证和会话管理。但是Cookie也存在一些安全问题,比如会话劫持、跨站脚本攻击等问题。 3. Token:Token是一种用户身份认证和授权的机制,通常由服务器生成,以便在客户端和服务端之间进行身份认证和授权。客户端在登录成功后,服务器会为该用户生成一个Token,并将Token发送给客户端浏览器。客户端在之后的请求中需要携带该Token,服务器验证Token的有效性后,就可以识别出用户身份,并进行相应的操作。Token相比Session和Cookie,具有更高的安全性和可扩展性。 4. JWTJWT是一种基于Token的身份认证和授权机制。JWT包含三部分,分别是头部、载荷和签名。头部包含加密算法和类型等信息;载荷包含用户的身份信息和相关的元数据等信息;签名则是对头部和载荷进行签名生成的一段密文,用于验证Token的有效性。JWT具有无状态、可扩展、跨域等特点,被广泛用于Web开发中的用户身份认证和授权。 ### 回答2: Session、Cookie、Token和JWT都是常见的身份验证和会话管理方法。下面我会分别介绍它们。 Session:Session是服务器端记录用户状态的一种机制。当用户通过用户名和密码登录后,服务器会为该用户创建一个唯一的Session。之后,用户再发送请求时,服务器会根据Session来识别用户并获取用户的状态信息。 Cookie:Cookie是一种在客户端存储的小型文本文件。在用户通过用户名和密码登录成功后,服务器可以将一个包含Session信息的Cookie发送给客户端,客户端会将该Cookie保存下来。之后,客户端发送请求时,会自动附带上该Cookie,用于向服务器证明用户的身份。 Token:Token是一种代表用户身份的令牌。与Cookie不同,Token是在客户端保存的,并且不需要服务器端存储用户状态。当用户登录成功后,服务器会生成一个Token并发送给客户端,客户端将其保存起来。之后,客户端发送请求时,会将Token作为请求头信息的一部分发送给服务器,服务器根据Token验证用户身份。 JWTJWT(JSON Web Token)是一种基于JSON的开放标准,用于在各方之间安全传输信息。它由三部分组成:Header、Payload和Signature。其中,Header用于描述JWT的元数据,Payload用于存储实际传输的数据,Signature用于验证JWT的合法性。在使用JWT进行身份验证时,服务器会生成一个JWT并发送给客户端,客户端将其保存起来。之后,客户端发送请求时,会将JWT作为请求头信息的一部分发送给服务器,服务器根据JWT验证用户身份的合法性。 总结:Session、Cookie、Token和JWT都是常用于身份验证和会话管理的方法,它们各有优劣和适用场景。Session和Cookie依赖于服务器端存储用户状态,而Token和JWT则可以减轻服务器的负担,并且适用于分布式系统。其中,JWT由于其自包含性和可扩展性,在分布式系统和前后端分离的架构中得到了广泛应用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值