JWT技术--JSON Web Token

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 一、JWT简介JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

一、JWT简介

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。使用方式:服务端根据规范生成一个令牌(token),并且发放给客户端(保存在客户端)。此时客户端请求服务端的时候就可以携带者令牌,以令牌来证明自己的身份信息。


作用:类似session保持登录状态的办法,通过token来代表用户身份。


Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。

Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

二、为什么使用JWT

1、传统的Session认证

我们知道,http协议本身是一种无状态的协议,这意味着用户提供用户名和密码进行用户认证后,下一次请求还需要进行认证。因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让应用能识别是哪一个用户发出的请求,我们只能在服务器存储一份用户登录信息,这份信息会在响应时传递给客户端,告诉其保存为cookie(传输的是JsessionId而不是用户信息,不然用户修改cookie内容后可以获得其他权限),以便下次请求时发送给我们的应用,这样我们就知道请求应用的是哪个用户了。这就是传统的Session认证。


暴露的问题:


每个用户经过我们的应用认证后,我们的应用都要在服务端做一次记录,以便下次用户请求时的鉴别。通常而言session是保存在内存中的,随着认证用户的增多,服务端的开销会明显增大。


用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话, 以为这下次请求还必须在这台服务器上,才能拿到授权的资源。这样在分布式应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。


在前后端分离解耦后增加了部署的复杂性。通常用户一次请求要经过多次转发,如果使用session每次携带sessionId到服务器,服务器还要查询用户信息。同时如果用户很多,这些信息存储在服务器内存中,给服务器增加了负担。sessionId就是一个特征值,表达的信息不够丰富,不容易扩展。而且如果后端应用是多节点部署,就需要实现session共享机制,不方便集群应用。


因为是基于cookie来进行应用识别的,cookie如果被截获,用户就会很容易受到CSRF/XSRF(跨站请求伪造)


CSRF攻击的大致方式如下:某用户登录了A网站,认证信息保存在cookie中。当用户访问攻击者创建的B网站时,攻击者通过在B网站发送一个伪造的请求提交到A网站服务器上,让A网站服务器误以为请求来自于自己的网站,于是执行响应的操作,该用户的信息边遭到了篡改。总结起来就是,攻击者利用用户在浏览器中保存的认证信息,向对应的站点发送伪造请求。用户的认证是通过保存在cookie中的数据实现,在发送请求是,只要浏览器中保存了对应的cookie,服务器端就会认为用户已经处于登录状态,而攻击者正是利用了这一机制。

2、基于JWT认证

首先前端通过web表单将自己的用户名和密码发送给后端的接口。这个过程一般是是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。


后端核对用户名和密码成功后,将用户的id等其他用户信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT(token)。形成的JWT就是一个形同lll.zzz.xxx的字符串。Token:head.payload.signature


后端将JWT字符串作为登录成功的返回结果返回给前端,前端可以将返回结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。


前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)


后端检查是否存在,如存在验证JWT的有效性。例如检查签名是否正确,检查Token是否过期,检查Token的接收方是否为自己(可选)。


验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。

3、JWT优势

简洁:可以通过URL、POST参数或者在HTTP Header发送,因为数据量小,传输速度也很快

自包含:负载中包含了所有用户所需要的信息,避免了多次查询数据库

因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持

不需要在服务端保存会话信息,特别适合用于分布式微服务。

更适合用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时使用token认证方式会简单很多

单点登录友好:由于cookie无法跨域,难以实现单点登录。但是,使用token进行认证的话, token可以被保存在客户端的任意位置的内存中,不一定是cookie,所以不依赖cookie,不会存在这些问题

三、JWT的结构

1、令牌组成

  • 标头(Header)
  • 有效负荷(Payload)
  • 签名(Signature)

因此JWT通常为:xxxx.yyyy.zzzz,即Header.Payload.Signature

2、Header

标头通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法,例如HMAC、SHA256或RSA。它会使用Base64编码组成JWT结构的第一部分。


注意:Base64是一种编码,也就是说它是可以被翻译回原来的样子来的,它并不是一种加密过程


默认为:base64enc({“alg”:“HS256”,“typ”:“JWT”})

3、Payload

令牌的第二部分是有效负载,其中包含声明。声明是有关实体(通常是用户)和其他数据的声明。同样的,它会使用Base64编码组成JWT结构的第二部分

{
  "sub":"123456789”,
  "name”:"John Doe",
    "admin":true
}

4、Signature

前面两部分都是使用Base64进行编码的,即前端可以解开获取其中的内容。Signature需要使用编码后的header和payload以及我们提供的一个秘钥,然后使用header中指定的签名算法进行签名,签名的作用是保证JWT没有被篡改过


HMACSHA256(base64UrlEncode(header)+“.”+base64UrlEncode(payload),secret)


实际上是对头部信息和负载内容进行签名,防止内容被篡改,如果有人对头部以及负载内容解码后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT上附带的签名是不一样的。如果要对新的头部和负载进行签名,由于不知道服务器加密时使用的秘钥,得出来的结果也是不一样的。


信息安全问题


在JWT中不应该在负载中加入任何敏感的数据,用户ID被知道也是安全的,但是像密码这样的内容就不能放在JWT中了。

四、JWT实现

1、测试

public class JWTTest {
    public static void main(String[] args) {
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,1000);
        String sign = JWT.create()
                .withClaim("username", "cyh")//设置payload
                .withClaim("userId", 123)
                .withExpiresAt(instance.getTime())//设置令牌过期时间
                .sign(Algorithm.HMAC256("asd!de"));//签名
        System.out.println(sign);
    }
    @Test
    public void test(){
        //创建验证对象
        JWTVerifier build = JWT.require(Algorithm.HMAC256("asd!de")).build();
        DecodedJWT verify = build.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NDkxNzg1MTAsInVzZXJJZCI6MTIzLCJ1c2VybmFtZSI6ImN5aCJ9.J19zl01GobHB1XpnnvCe8N-cjqCY6SDyQ3eQGiYBE7M");
        System.out.println(verify.getClaims());
        System.out.println(verify.getClaim("username"));
        System.out.println(verify.getClaim("userId"));
        System.out.println(verify.getExpiresAt());
        System.out.println(verify.getHeaderClaim("alg"));
        System.out.println(verify.getHeaderClaim("typ"));
    }
}

2、封装

工具类

public class JWTUtils {
    private static final String SIGN="!ad#12~";
    //生成token
    public static String getToken(Map<String,String> map){
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE,7);//默认七天过期
        JWTCreator.Builder builder = JWT.create();
        map.forEach(builder::withClaim);
        return builder.withExpiresAt(instance.getTime())//设置令牌过期时间
                .sign(Algorithm.HMAC256(SIGN));//签名
    }
    //验证token合法性,获取token信息
    public static DecodedJWT verify(String token){
        return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
    }
}

拦截器

public class JWTInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String,Object> map = new HashMap<>();
        //获取请求头中的令牌
        String token = request.getHeader("token");
        try {
            JWTUtils.verify(token);//验证令牌
            return true;//放行请求
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            map.put("msg","token过期");
        } catch (AlgorithmMismatchException e) {
            e.printStackTrace();
            map.put("msg","token算法不一致");
        } catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg","无效签名");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("msg","token无效");
        }
        map.put("state",false);//设置状态
        //将map转为json
        String s = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(s);
        return false;
    }
}
//增加拦截器
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("user/test") //其他接口token验证
                .excludePathPatterns("user/login"); //放行用户操作
    }
}
相关文章
|
3天前
|
存储 JSON 安全
如何使用 JSON Web Tokens 进行身份验证?
总的来说,JWT 是一种强大而灵活的身份验证方式,通过正确使用和管理,可以为应用提供可靠的身份验证机制,同时提高系统的可扩展性和安全性。在实际应用中,需要根据具体的需求和场景,合理设计和实施 JWT 身份验证方案。
23 8
|
18天前
|
SQL 负载均衡 安全
安全至上:Web应用防火墙技术深度剖析与实战
【10月更文挑战第29天】在数字化时代,Web应用防火墙(WAF)成为保护Web应用免受攻击的关键技术。本文深入解析WAF的工作原理和核心组件,如Envoy和Coraza,并提供实战指南,涵盖动态加载规则、集成威胁情报、高可用性配置等内容,帮助开发者和安全专家构建更安全的Web环境。
36 1
|
21天前
|
负载均衡 监控 算法
论负载均衡技术在Web系统中的应用
【11月更文挑战第4天】在当今高并发的互联网环境中,负载均衡技术已经成为提升Web系统性能不可或缺的一环。通过有效地将请求分发到多个服务器上,负载均衡不仅能够提高系统的响应速度和处理能力,还能增强系统的可扩展性和稳定性。本文将结合我参与的一个实际软件项目,从项目概述、负载均衡算法原理以及实际应用三个方面,深入探讨负载均衡技术在Web系统中的应用。
48 2
|
1月前
|
人工智能 前端开发
2024 川渝 Web 前端开发技术交流会「互联」:等你来报名!
2024 川渝 Web 前端开发技术交流会「互联」:等你来报名!
2024 川渝 Web 前端开发技术交流会「互联」:等你来报名!
|
1月前
|
存储 安全 数据库
后端技术在现代Web开发中的实践与创新
【10月更文挑战第13天】 本文将深入探讨后端技术在现代Web开发中的重要性,通过实际案例分析展示如何利用先进的后端技术提升用户体验和系统性能。我们将从基础架构设计、数据库优化、安全性保障等方面展开讨论,为读者提供清晰的指导和实用的技巧。无论是新手开发者还是经验丰富的技术人员,都能从中获得启发和帮助。
35 2
|
1月前
|
机器学习/深度学习 移动开发 JavaScript
Web实时通信的学习之旅:SSE(Server-Sent Events)的技术详解及简单示例演示
Web实时通信的学习之旅:SSE(Server-Sent Events)的技术详解及简单示例演示
133 0
|
1月前
|
自然语言处理 Cloud Native 数据安全/隐私保护
后端技术在现代Web开发中的实践与创新
本文探讨了后端技术在现代Web开发中的重要性及其应用。通过分析当前流行的后端框架和开发模式,揭示了如何利用这些技术来构建高效、可扩展的Web应用程序。同时,文章也讨论了未来后端技术的发展趋势,为开发者提供了一些启示。
|
22天前
|
监控 前端开发 JavaScript
前端技术探索:构建高效、可维护的Web应用
【10月更文挑战第23天】前端技术探索:构建高效、可维护的Web应用
37 0
|
1月前
|
存储 前端开发 JavaScript
CSS:现代Web设计的不同技术
【10月更文挑战第11天】 CSS:现代Web设计的不同技术
|
1月前
|
移动开发 前端开发 JavaScript
HTML与现代Web开发的不同技术
【10月更文挑战第11天】HTML与现代Web开发的不同技术
22 0

热门文章

最新文章

下一篇
无影云桌面