什么是jwt?

JSON Web Token (JWT),它是目前最流行的跨域身份验证解决方案

JWT的工作原理

1. 是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,示例如下:
{"UserName": "Chongchong","Role": "Admin","Expire": "2018-08-08 20:15:56"}

2. 之后,当用户与服务器通信时,客户在请求中发回JSON对象
3. 为了防止用户篡改数据,服务器将在生成对象时添加签名,并对发回的数据进行验证

为什么使用JWT

1、jwt基于json,非常方便解析。
2、可以在令牌中自定义丰富的内容,易扩展。
3、通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
4、资源服务使用JWT可不依赖认证服务即可完成授权。

我们可以来做个对比:

一般传统开发模式是依赖session的,进行操作时都要去访问一下session;

众所周知,session是跟着tomcat走的,也就是说,浏览器访问网址跟前端挂钩,那么就存在一个确定缺陷,浏览器可以随意访问任何组件;

对系统的保护的很不利的,所以这个时候jwt出现了!

图解:

后端:

前端:

JWT的组成

它是针对于传统开发的模式所出现的问题进行维护的

一个JWT实际上就是一个字符串,它由三部分组成:

  • 头部(Header)
  • 载荷(Payload)
  • 签名(signature)

jwt的机制见图解:

jwt它实际长啥样呢

eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjYwMTQ2MzgsImlhdCI6MTU2NjAxMjgzOCwiYWdlIjoxOCwianRpIjoiZGFjOTlmNDQ3MGVhNDc3ODhiMzAyNTM4YjdmNmE5ZWEiLCJ1c2VybmFtZSI6InpzcyJ9.9kFQ2c5nEbQRoyQXTc_2goW7r0D5oAjLrXDX3ae-4bA

对没错,就这个亚子;它其实时根据(.)去把这段指令分为三部分

  • Header

这个json中的typ属性,用来标识整个token字符串是一个JWT字符串;它的alg属性,用来说明这个JWT签发的时候所使用的签名和摘要算法
typ跟alg属性的全称其实是type跟algorithm,分别是类型跟算法的意思。之所以都用三个字母来表示,也是基于JWT最终字串大小的考虑,
同时也是跟JWT这个名称保持一致,这样就都是三个字符了…typ跟alg是JWT中标准中规定的属性名称

  • Payload

payload用来承载要传递的数据,它的json结构实际上是对JWT要传递的数据的一组声明,这些声明被JWT标准称为claims,
它的一个“属性值对”其实就是一个claim(要求),
每一个claim的都代表特定的含义和作用。

  • signature

签名是把header和payload对应的json结构进行base64url编码之后得到的两个串用英文句点号拼接起来,

然后根据header里面alg指定的签名算法生成出来的。

算法不同,签名结果不同。以alg: HS256为例来说明前面的签名如何来得到。

JWT的验证

  • 在pom.xml中导入相关依赖

     <dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.</version>
</dependency>

JwtUtil工具类

public class JwtUtils {
/**
* JWT_WEB_TTL:WEBAPP应用中token的有效时间,默认30分钟
*/
public static final long JWT_WEB_TTL = 30 * 60 * 1000; /**
* 将jwt令牌保存到header中的key
*/
public static final String JWT_HEADER_KEY = "jwt"; // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
private static final String JWT_SECRET = "f356cdce935c42328ad2001d7e9552a3";// JWT密匙
private static final SecretKey JWT_KEY;// 使用JWT密匙生成的加密key static {
byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);
JWT_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
} private JwtUtils() {
} /**
* 解密jwt,获得所有声明(包括标准和私有声明)
*
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJwt(String jwt) {
Claims claims = Jwts.parser().setSigningKey(JWT_KEY).parseClaimsJws(jwt).getBody();
return claims;
} /**
* 创建JWT令牌,签发时间为当前时间
*
* @param claims
* 创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
* @param ttlMillis
* JWT的有效时间(单位毫秒),当前时间+有效时间=过期时间
* @return jwt令牌
*/
public static String createJwt(Map<String, Object> claims, long ttlMillis) {
// 生成JWT的时间,即签发时间
long nowMillis = System.currentTimeMillis(); // 下面就是在为payload添加各种标准声明和私有声明了
// 这里其实就是new一个JwtBuilder,设置jwt的body
JwtBuilder builder = Jwts.builder()
// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setClaims(claims)
// 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
// 可以在未登陆前作为身份标识使用
.setId(UUID.randomUUID().toString().replace("-", ""))
// iss(Issuser)签发者,写死
// .setIssuer("zking")
// iat: jwt的签发时间
.setIssuedAt(new Date(nowMillis))
// 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
// .setSubject("{}")
// 设置签名使用的签名算法和签名使用的秘钥
.signWith(SIGNATURE_ALGORITHM, JWT_KEY)
// 设置JWT的过期时间
.setExpiration(new Date(nowMillis + ttlMillis)); return builder.compact();
} /**
* 复制jwt,并重新设置签发时间(为当前时间)和失效时间
*
* @param jwt
* 被复制的jwt令牌
* @param ttlMillis
* jwt的有效时间(单位毫秒),当前时间+有效时间=过期时间
* @return
*/
public static String copyJwt(String jwt, Long ttlMillis) {
Claims claims = parseJwt(jwt); // 生成JWT的时间,即签发时间
long nowMillis = System.currentTimeMillis(); // 下面就是在为payload添加各种标准声明和私有声明了
// 这里其实就是new一个JwtBuilder,设置jwt的body
JwtBuilder builder = Jwts.builder()
// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setClaims(claims)
// 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
// 可以在未登陆前作为身份标识使用
//.setId(UUID.randomUUID().toString().replace("-", ""))
// iss(Issuser)签发者,写死
// .setIssuer("zking")
// iat: jwt的签发时间
.setIssuedAt(new Date(nowMillis))
// 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
// .setSubject("{}")
// 设置签名使用的签名算法和签名使用的秘钥
.signWith(SIGNATURE_ALGORITHM, JWT_KEY)
// 设置JWT的过期时间
.setExpiration(new Date(nowMillis + ttlMillis));
return builder.compact();
}
}

JwtDome测试类

我们现在后台测试看是否能生成令牌并且运行对应语句

private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    @Test
public void test1() {// 生成JWT
Map<String, Object> claims = new HashMap<String, Object>();
claims.put("username", "zss");
claims.put("age", 18); String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL);
System.out.println(jwt); Claims parseJwt = JwtUtils.parseJwt(jwt);
for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
Date d1 = parseJwt.getIssuedAt();
Date d2 = parseJwt.getExpiration();
System.out.println("令牌签发时间:" + sdf.format(d1));
System.out.println("令牌过期时间:" + sdf.format(d2));
}

看控制台输出是否成功:

注1:在验证一个JWT的时候,签名认证是每个实现库都会自动做的,但是payload的认证是由使用者来决定的。因为JWT里面可能会包含一个自定义claim,
所以它不会自动去验证这些claim,以jjwt-0.7.0.jar为例:
A 如果签名认证失败会抛出如下的异常:
io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
即签名错误,JWT的签名与本地计算机的签名不匹配
B JWT过期异常(403)
io.jsonwebtoken.ExpiredJwtException: JWT expired at 2017-06-13T11:55:56Z. Current time: 2017-06-13T11:55:57Z, a difference of 1608 milliseconds. Allowed

现在我们来连接前端玩玩jwt

首先:在后台打开jwt令牌验证功能

JwtFilter

public class JwtFilter implements Filter {

    // 排除的URL,一般为登陆的URL(请改成自己登陆的URL)
private static String EXCLUDE = "^/vue/userAction_login\\.action?.*$"; private static Pattern PATTERN = Pattern.compile(EXCLUDE); private boolean OFF = false;// true关闭jwt令牌验证功能 @Override
public void init(FilterConfig filterConfig) throws ServletException {
} @Override
public void destroy() {
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String path = req.getServletPath();
if (OFF || isExcludeUrl(path)) {// 登陆直接放行
chain.doFilter(request, response);
return;
} // 从客户端请求头中获得令牌并验证
String jwt = req.getHeader(JwtUtils.JWT_HEADER_KEY);
Claims claims = this.validateJwtToken(jwt);
if (null == claims) {
// resp.setCharacterEncoding("UTF-8");
resp.sendError(403, "JWT令牌已过期或已失效");
return;
} else {
String newJwt = JwtUtils.copyJwt(jwt, JwtUtils.JWT_WEB_TTL);
resp.setHeader(JwtUtils.JWT_HEADER_KEY, newJwt);
chain.doFilter(request, response);
}
} /**
* 验证jwt令牌,验证通过返回声明(包括公有和私有),返回null则表示验证失败
*/
private Claims validateJwtToken(String jwt) {
Claims claims = null;
try {
if (null != jwt) {
claims = JwtUtils.parseJwt(jwt);
}
} catch (Exception e) {
e.printStackTrace();
}
return claims;
}
/**
* 是否为排除的URL
*
* @param path
* @return
*/
private boolean isExcludeUrl(String path) {
Matcher matcher = PATTERN.matcher(path);
return matcher.matches();
} }

记得在useraction中加入jwt

现在我们来布置前端嘛,让它体现出价值来

首先,咱们先在state.js中给他来个jwt变量嘛

然后分别在getters.js和mutations.js中定义getjwt和setjwt方法

在http.js中塞入jwt

在http.js的请求、响应拦截器中放进Jwt令牌

// 请求拦截器
axios.interceptors.request.use(function(config) {
// 将jwt令牌放进request Headers里面
var jwt = window.vm.$store.getters.getJwt;
config.headers['jwt'] = jwt;
return config;
}, function(error) {
return Promise.reject(error);
}); // 响应拦截器
axios.interceptors.response.use(function(response) {
// 将jwt令牌放进response Headers里面
// debugger;
var jwt = response.headers['jwt'];
if(jwt){
window.vm.$store.commit('setJwt',{jwt:jwt});
}
return response;
}, function(error) {
return Promise.reject(error);
});
export default axios;

最后一步在main.js中加入全局变量

运行效果:

你从登陆进去毫无问题,除了会报403的错误(兄弟,你的令牌过期咯)以外,基本上不会报错啦

如果还报了其他错,自己检查代码去我只能

这个是安安分分从登陆进来没毛病的例子,如果手欠在当前页面刷新咯那只能吃瘪,啥数据也拿不到。

这就是jwt在其中发挥了作用,提高了系统的维护,不能随意进入其他组件;

建议做项目时不用加入jwt后期维护时再加入,你懂的搞代码的都很“和蔼”

谢谢观看!

SPA中使用jwt的更多相关文章

  1. sau交流学习社区--songEagle开发系列:Vue.js + Koa.js项目中使用JWT认证

    一.前言 JWT(JSON Web Token),是为了在网络环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519). JWT不是一个新鲜的东西,网上相关的介绍已经非常多了.不是很了解的 ...

  2. 如何在SpringBoot中集成JWT(JSON Web Token)鉴权

    这篇博客主要是简单介绍了一下什么是JWT,以及如何在Spring Boot项目中使用JWT(JSON Web Token). 1.关于JWT 1.1 什么是JWT 老生常谈的开头,我们要用这样一种工具 ...

  3. Laravel 中使用 JWT 认证的 Restful API

    Laravel 中使用 JWT 认证的 Restful API 5天前/  678 /  3 / 更新于 3天前     在此文章中,我们将学习如何使用 JWT 身份验证在 Laravel 中构建 r ...

  4. 如何简单的在 ASP.NET Core 中集成 JWT 认证?

    前情提要:ASP.NET Core 使用 JWT 搭建分布式无状态身份验证系统 文章超长预警(1万字以上),不想看全部实现过程的同学可以直接跳转到末尾查看成果或者一键安装相关的 nuget 包 自上一 ...

  5. ASP.NET Core WebAPI中使用JWT Bearer认证和授权

    目录 为什么是 JWT Bearer 什么是 JWT JWT 的优缺点 在 WebAPI 中使用 JWT 认证 刷新 Token 使用授权 简单授权 基于固定角色的授权 基于策略的授权 自定义策略授权 ...

  6. 在SpringBoot中使用JWT

    JWT简介 简介 JSON Web token简称JWT, 是用于对应用程序上的用户进行身份验证的标记.也就是说, 使用 JWTS 的应用程序不再需要保存有关其用户的 cookie 或其他sessio ...

  7. ASP.Net Core 3.0 中使用JWT认证

    JWT认证简单介绍     关于Jwt的介绍网上很多,此处不在赘述,我们主要看看jwt的结构.     JWT主要由三部分组成,如下: HEADER.PAYLOAD.SIGNATURE HEADER包 ...

  8. SPA中前端路由基本原理与实现方式

    SPA 前端路由原理与实现方式 通常 SPA 中前端路由有2中实现方式,本文会简单快速总结这两种方法及其实现: 修改 url 中 Hash 利用 H5 中的 history Hash 我们都知道 ur ...

  9. 个人博客开发系列:Vue.js + Koa.js项目中使用JWT认证

    前言 JWT(JSON Web Token),是为了在网络环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519). 更多的介绍和说明,以及各种原理,我在此就不多赘诉了.JWT不是一个新鲜 ...

随机推荐

  1. 机器不学习:CNN入门讲解-为什么要有最后一层全连接

    哈哈哈,又到了讲段子的时间 准备好了吗? 今天要说的是CNN最后一层了,CNN入门就要讲完啦..... 先来一段官方的语言介绍全连接层(Fully Connected Layer) 全连接层常简称为 ...

  2. 应急响应系列之OA被入侵挖矿分析报告

    一 基本情况 1.1  简要 此事件是去年应急处置时完成的报告,距今有半年时间了.一直存在电脑里,最近准备完善应急响应中遇到的各类安全事件,这篇文章作为这一系列的开端. 对于 Linux 安全检查,个 ...

  3. Cesium原理篇:6 Renderer模块(1: Buffer)【转】

    https://www.bbsmax.com/A/n2d9P1Q5Dv/ 刚刚结束完地球切片的渲染调度后,打算介绍一下目前大家都很关注的3D Tiles方面的内容,但发现要讲3D Tiles,或者充分 ...

  4. keepalived非争抢机制不生效【原创】

    故障现象:俩台服务器设置了keepalived非争抢机制,但是发现优先级高的服务器还是会优先获取vip 利用tcpdump抓包发现只有其中一台服务器,没有另一台 tcpdump -i eth0 vrr ...

  5. Nutch、Scrapy、Lucene、Heritrix、Solr、Sphinx

    Nutch.Scrapy.Lucene.Heritrix.Solr.Sphinx

  6. kafka笔记博客

    大数据数据流组件选择: https://www.cnblogs.com/yinzhengjie/articles/11155051.html 初识Apache Kafka 核心概念: https:// ...

  7. C++main函数命令行选项——学习笔记

    atoi字符串的数转化为整数 atof转化为小数

  8. centos7安装python3.6独立的virtualenv环境

    centos7安装python3.6独立的virtualenv环境 1.编译安装python3.6环境# 安装依赖yum -y install zlib-devel bzip2-devel opens ...

  9. 008-SpringBoot发布WAR启动报错:Error assembling WAR: webxml attribute is required

    一.Spring Boot发布war包流程: 1.修改web model的pom.xml <packaging>war</packaging> SpringBoot默认发布的都 ...

  10. osg include lib -computer1

    E:\\OpenSceneGraph\\OpenSceneGraph_3_6_install\\lib E:\\OpenSceneGraph\\3rdParty\\v140-x64\\lib Open ...