马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
目录
一、JWT
1、什么是Jwt
2、为什么要使用Jwt
3、应用场景
4.Jwt的组成
4.1、Header
4.2、Payload
4.3、signature
二、Jwt验证过程
1、生成Jwt令牌
2、解析旧的Jwt
3、复制Jwt
4、Jwt有用时间测试
三、Jwt令牌刷新思路
1、配置JwtFilter过滤器
2、登录生成Jwt令牌
3、配置CorsFilter过滤器
4、Vuex存储Jwt令牌
5、修改main.js
6、配置axios请求响应头
前言:
随着前后端分离的发展,以及数据中心的创建,越来越多的公司会创建一个中心服务器,服务于各种产物线。而这些产物线上的产物,它们可能有着各种终端设备,包罗但不但限于浏览器、桌面应用、移动端应用、平板应用、甚至智能家居。
这些设备与中心服务器之间会进行http通信,一般来说,中心服务器至少负担着认证和授权的功能。比方登录:各种设备发送消息到中心服务器,然后中心服务器响应一个身份令牌,当这种结构出现后,就出现一个题目:它们之间还能使用传统的cookie方式传递令牌信息吗?
其实,也是可以的,由于cookie在传输中无非是一个消息头而已,只不过浏览器对这个消息头有特殊处理罢了。但浏览器之外的设备肯定不喜好cookie,由于浏览器有着对cookie完善的管理机制,但是在其他设备上,就需要开发者本身手动处理了。
JWT 的出现就是为了办理这个题目!!!
一、JWT
1、什么是Jwt
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包罗的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,由于它是数字签名的。是目前最盛行的跨域身份验证办理方案;
RFC 7519:RFC 7519 - JSON Web Token (JWT)
2、为什么要使用Jwt
它要办理的题目,就是为多种终端设备,提供统一的、安全的令牌格式。因此,jwt只是一个令牌格式而已,你可以把它存储到cookie,也可以存储到localstorage,没有任何限定!同样的,对于传输,你可以使用任何传输方式来传输jwt,一般来说,我们会使用消息头header来传输它。
比如:当登录成功后,服务器可以给客户端响应一个Jwt。
- HTTP/1.1 200 OK
- ...
- set-cookie:token=jwt令牌
- authorization:jwt令牌
- ...
- {..., token:jwt令牌}
复制代码 可以看到,jwt令牌可以出现在响应的任何一个地方,客户端和服务器自行约定即可。
固然,它也可以出现在响应的多个地方,比如为了充分使用浏览器的 cookie,同时为了照顾其他设备,也可以让 jwt 出现在 set-cookie 和 authorization 或 body 中,只管这会增加额外的传输量。
总之,Jwt的精髓在于去中心化,数据是保存在客户端的。
3、应用场景
- 认证:认证是JWT的最常用场景。只要用户完成登录,其随后的请求都会包罗JWT,以答应用户访问经由当前JWT授权的路由、服务大概是资源。由于开销小且能够被简单应用在跨域访问上,JWT在分布式站点上所支持的单点登录(SSO)已经是当前它被广泛应用的一个特性。
- 信息互换:JWT是一种在各参与方之间安全传递信息的良好方法。由于JWT可以被签名(例:使用公钥/秘钥对),因而可用于确认发送者自称的身份。除此之外,由于signature使用header和payload进行盘算,也可以验证内容没有被篡改。
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
4.Jwt的组成
一个Jwt实际上就是一个字符串,它由三部分组成:
- Header(头部)
- Payload(载荷)
- Signature(签名)
- eyJhbGciOiJIUzI1NiJ9.
- eyJzdWIiOiJ7fSIsImlzcyI6InpraW5nIiwiZXhwIjoxNTYyODUwMjM3LCJpYXQiOjE1NjI4NDg0MzcsImp0aSI6ImM5OWEyMzRmMDc4NzQyZWE4YjlmYThlYmYzY2VhNjBlIiwidXNlcm5hbWUiOiJ6c3MifQ.
- WUfqhFTeGzUZCpCfz5eeEpBXBZ8-lYg1htp-t7wD3I4
复制代码 它是一个很长的字符串因此,中心用点(.)分隔成三个部分。
注意,Jwt 内部是没有换行的,这里只是为了便于展示,将它写成了几行。
4.1、Header
典范的JWT header包罗两个部分:
- typ(type)属性,用来标识整个token字符串是一个JWT字符串,即JWT。
- alg(algorithm)属性,用来阐明这个JWT签发的时间所使用的签名和择要算法,比如HMAC SHA256大概RSA。
- {
- "alg": "HS256",
- "typ": "JWT"
- }
复制代码 4.2、Payload
JWT token的第二部分是包罗了声明的payload,声明(要求)是一个实体的表述加上额外信息,一共有三种情势的声明:注册、公有和私有;
它的json结构实际上是对JWT要传递的数据的一组声明,这些声明被JWT标准称为claims,它的一个属性值对其实就是一个claim(要求),每一个claim的都代表特定的寄义和作用。
- {
- "iss":"发行者",
- "iat":"发布时间",
- "exp":"到期时间",
- "sub":"主题",
- "aud":"听众",
- "nbf":"在此之前不可用",
- "jti":"JWT ID"
- }
复制代码 属性阐明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的唯一标识
公有声明可以加入任何信息,一般会添加用户相关信息大概业务需要的信息,但不发起添加敏感信息,由于该部分会在客户端解密。
私有声明是提供者和使用者所共同定义的声明,一般不发起存放敏感信息,由于base64是对称解密,基本等同于明文信息。
如果把JWT用于认证, 那么JWT标准内规定的几个claim就足够用了,甚至只需要其中一两个就可以了,假如想往JWT里多存一些用户业务信息,比如角色和用户名等,这倒是可以用私有声明来添加。
4.3、signature
JWT的第三部分是签名信息,Signature由三部分组成:
- header(base64加密后)
- payload(base64加密后)
- secret (盐)
签名需要将base64加密后的header和payload使用.连接,然后通过header所使用的加密方式进行加盐(secret)组合加密,产生了jwt的第三部分。以使用HMAC SHA256算法为例;
按照前面alg可用值的阐明,HS256其实包罗的是两种算法:HMAC算法和SHA256算法,前者用于生成择要,后者用于对择要进行数字签名。这两个算法也可以用HMAC SHA256来统称;
- HMACSHA256(
- base64UrlEncode(header) + "." +
- base64UrlEncode(payload),
- secret)
复制代码 签名是用于验证消息在传递过程中有没有被更改,而且,对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。
算法差别,签名结果差别。
二、Jwt验证过程
可将绑定的资源中src(ElementUI之Jwt)目录下的JwtUtils.java和JwtDemo.java导入到项目中的util包下:
1、生成Jwt令牌
- //定义私有声明
- Map<String, Object> claims = new HashMap<String, Object>();
- claims.put("username", "zss");
- claims.put("age", 18);
- //根据私有声明和JWT的token有效时间生成JWT
- String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL);
- //获取JWT中的声明信息,包括:私有声明和标准声明
- Claims parseJwt = JwtUtils.parseJwt(jwt);
- for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
- System.out.println(entry.getKey() + "=" + entry.getValue());
- }
- //获取token令牌的签发时间
- Date d1 = parseJwt.getIssuedAt();
- //获取token令牌的过期时间
- Date d2 = parseJwt.getExpiration();
- System.out.println("令牌签发时间:" + sdf.format(d1));
- System.out.println("令牌过期时间:" + sdf.format(d2));
复制代码 2、解析旧的Jwt
解析旧的JWT。如果凌驾JWT本身定义的逾期时间,则直接抛出ExpiredJwtException
- //旧的JWT令牌
- String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjM2NzY0NDQsImlhdCI6MTU2MzY3NDY0NCwiYWdlIjoxOCwianRpIjoiMzE5MmYxOTg4NzFkNGVkZWIyMzU0MmY3NWVhMWI5NDciLCJ1c2VybmFtZSI6InpzcyJ92.4VnkdvGNNe8U1EiKaLz7h6bOJkVGSGtfqojcP_y-0Cc";
- Claims parseJwt = JwtUtils.parseJwt(oldJwt);
- 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));
复制代码 3、复制Jwt
模拟Session功能(Tomcat默认session逾期时间:30分钟)。当有访问的情况下,自动延伸jwt中的token令牌逾期时间,在原偶然间上+30秒;
- String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjM2NzUzMDAsImlhdCI6MTU2MzY3MzUwMCwiYWdlIjoxOCwianRpIjoiYjY5MmE5ZjZkMGZhNDMyN2I1YWY2NTI5OGMzMjQ5MTYiLCJ1c2VybmFtZSI6InpzcyJ9.asHGMPFKURMLnooK29abAuKEdLOHBycAuTovNuGQio0";
- String jwt = JwtUtils.copyJwt(oldJwt, JwtUtils.JWT_WEB_TTL);
- 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));
复制代码 4、Jwt有用时间测试
生成新的Jwt令牌,设置令牌有用时间3秒钟,在3秒钟后再次解析Jwt令牌提示令牌已逾期。
- Map<String, Object> claims = new HashMap<String, Object>();
- claims.put("username", "zss");
- String jwt = JwtUtils.createJwt(claims, 3 * 1000L);
- System.out.println(jwt);
- Claims parseJwt = JwtUtils.parseJwt(jwt);
- Date d1 = parseJwt.getIssuedAt();
- Date d2 = parseJwt.getExpiration();
- System.out.println("令牌签发时间:" + sdf.format(d1));
- System.out.println("令牌过期时间:" + sdf.format(d2));
复制代码 三、Jwt令牌刷新思路
使用ssm+vue+elementui实现jwt验证。
1、配置JwtFilter过滤器
导入JwtFilter.java到utils包下,并注意配置顺序题目:CorsFilter > JwtFilter > SpringMVC焦点控制器
- <!-- JwtFilter -->
- <filter>
- <filter-name>jwtFilter</filter-name>
- <filter-class>com.wsl.ssm.jwt.JwtFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>jwtFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
复制代码 2、登录生成Jwt令牌
在登录成功后,使用JwtUtil工具类生成JWT,并将Jwt令牌添加到响应头(response)中带到前端页面。在这里可以将用户对应用户基本信息和用户角色信息保存到私有claim中。
- if(userVo.getUsername().equals("admin")&&userVo.getPassword().equals("123")){
- //私有要求claim
- Map<String,Object> json=new HashMap<String,Object>();
- json.put("username", userVo.getUsername());
- //生成JWT,并设置到response响应头中
- String jwt=JwtUtils.createJwt(json, JwtUtils.JWT_WEB_TTL);
- response.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);
- return new JsonResponseBody<>("用户登陆成功!",true,0,null);
- }else{
- return new JsonResponseBody<>("用户名或密码错误!",false,0,null);
- }
复制代码 3、配置CorsFilter过滤器
浏览器只能访问默认的响应头,比方:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma等,如果想让浏览器能访问到其他的响应头的话,需要在服务器上设置。在CorsFilter跨域过滤器的doFilter方法中配置其他响应头的支持。
- //允许客户端发一个新的请求头jwt
- httpResponse.setHeader("Access-Control-Allow-Headers","Origin,X-Requested-With, Content-Type, Accept, jwt");
- //允许客户端处理一个新的响应头jwt
- httpResponse.setHeader("Access-Control-Expose-Headers", "jwt");
复制代码 4、Vuex存储Jwt令牌
使用vuex保存后端服务器提交过来的Jwt,分别在state、getters、mutation中定义信息。
- state.js
- export default {
- jwt:null
- }
复制代码 - getters.js
- export default {
- getJwt:(state)=>{
- return state.jwt;
- }
- }
复制代码
- mutation.js
- export default {
- setJwt:function (state, payload){
- state.jwt = payload.jwt;
- }
- }
复制代码 5、修改main.js
办理axios从响应头得到jwt令牌并保存到vuex,通过修改main.js中的配置window.vm=new Vue({});获取Vue根实例对象。
- window.vm = new Vue({
- el: '#app',
- router,
- store,
- components: { App },
- template: '<App/>'
- })
复制代码 6、配置axios请求响应头
在vue的http.js中配置axios的请求响应拦截器,用于存取Jwt,并将Jwt信息保存到Vuex中。
- // 请求拦截器
- axios.interceptors.request.use(function(config) {
- let jwt=window.vm.$store.getters.jwt;
- //console.log(jwt);
- if(jwt)
- config.headers['jwt']=jwt;
- return config;
- }, function(error) {
- return Promise.reject(error);
- });
- // 响应拦截器
- axios.interceptors.response.use(function(response) {
- let jwt=response.headers['jwt'];
- if(jwt)
- window.vm.$store.commit('setJwt',{jwt:jwt});
- return response;
- }, function(error) {
- return Promise.reject(error);
- });
复制代码 注:可通过使用vuex-along办理刷新state初始化题目。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |