vue---Jwt介绍与使用(vuex)

1 篇文章 0 订阅

Jwt介绍

什么是JWT

token进行用户身份验证的流程:

  • 客户端使用用户名和密码请求登录

  • 服务端收到请求,验证用户名和密码
  • 验证成功后,服务端 会签发一个token,再把这个token返回给客户端
  • 客户端收到token后可以把它存储起来,比如放到cookie中
  • 客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带
  • 服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据

JWT就是上述流程当中token的一种具体实现方式,其全称是JSON Web Token,官网地址:https://jwt.io/

token优点

  • 支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题
  • 无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力
  • 更适用CDN:可以通过内容分发网络请求服务端的所有资料
  • 更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多
  • 无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御

为什么要用JWT

session认证的缺点

  • 传统的session,随着用户的增多,服务器开销会明显增大
  • 对于非浏览器的客户端、手机移动端等不适用,因为session依赖于cookie,而移动端经常没有cookie
  • 因为session认证本质基于cookie,所以如果cookie被截获,用户很容易收到跨站请求伪造攻击。并且如果浏览器禁用了cookie,这种方式也会失效
  • 由于基于Cookie,而cookie无法跨域,所以session的认证也无法跨域,对单点登录不适用

JWT认证的优势

  • JWT的精髓在于:“去中心化”,数据是保存在客户端的。
  • 单点登录友好:使用Session进行身份认证的话,由于cookie无法跨域,难以实现单点登录。但是,使用token进行认证的话, token可以被保存在客户端的任意位置的内存中,不一定是cookie,所以不依赖cookie,不会存在这些问题
  • 适合移动端应用:使用Session进行身份认证的话,需要保存一份信息在服务器端,而且这种方式会依赖到Cookie(需要 Cookie 保存 SessionId),所以不适合移动端

JWT结构

JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串

Header

JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存

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

Payload

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

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

这些预定义的字段并不要求强制使用。除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中,如下例:

{"sub":"123","name":"Tom","admin":true}

根据JWT的标准,这些claims可以分为以下三种类型:

 1、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的唯一标识。 
2、 Public claims,略(不重要)

3、Private claims(私有)
              这个指的就是自定义的claim,比如前面那个示例中的admin和name都属于自定的claim。这些claim跟JWT标准规定的claim区别在于:JWT规定的claim,
              JWT的接收方在拿到JWT之后,都知道怎么对这些标准的claim进行验证;而private claims不会验证,除非明确告诉接收方要对这些claim进行验证以及规则才行

              按照JWT标准的说明:保留的claims都是可选的,在生成payload不强制用上面的那些claim,你可以完全按照自己的想法来定义payload的结构,不过这样搞根本没必要:
              第一是,如果把JWT用于认证, 那么JWT标准内规定的几个claim就足够用了,甚至只需要其中一两个就可以了,假如想往JWT里多存一些用户业务信息,
              比如角色和用户名等,这倒是用自定义的claim来添加;第二是,JWT标准里面针对它自己规定的claim都提供了有详细的验证规则描述,
              每个实现库都会参照这个描述来提供JWT的验证实现,所以如果是自定义的claim名称,那么你用到的实现库就不会主动去验证这些claim     
  

signature

签名是把header和payload对应的json结构进行base64url编码之后得到的两个串用英文句点号拼接起来,然后根据header里面alg指定的签名算法生成出来的。
       算法不同,签名结果不同。以alg: HS256为例来说明前面的签名如何来得到。
       按照前面alg可用值的说明,HS256其实包含的是两种算法:HMAC算法和SHA256算法,前者用于生成摘要,后者用于对摘要进行数字签名。这两个算法也可以用HMACSHA256来统称    

JWT的种类

  • JWS:经过签名的JWT
  • JWEpayload部分经过加密的JWT

jwt的签名算法有三种:

  • HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512
  • RSASSA【RSA签名算法(非对称)】(RS256/RS384/RS512)
  • ECDSA【椭圆曲线数据签名算法(非对称)】(ES256/ES384/ES512)

JWT的验证

首先引入依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

配置tomcat允许跨域访问  CorsFilter 设置headers的Jwt属性

package com.zking.vue.util;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 配置tomcat允许跨域访问
 * 
 * @author Administrator
 *
 */
public class CorsFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
			throws IOException, ServletException {
		HttpServletResponse resp = (HttpServletResponse) servletResponse;
		HttpServletRequest req = (HttpServletRequest) servletRequest;

		// Access-Control-Allow-Origin就是我们需要设置的域名
		// Access-Control-Allow-Headers跨域允许包含的头。
		// Access-Control-Allow-Methods是允许的请求方式
		resp.setHeader("Access-Control-Allow-Origin", "*");// *,任何域名
		resp.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE");
		
		// 允许客户端,发一个新的请求头jwt
		resp.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With, Content-Type, Accept, jwt");

		// 允许客户端,处理一个新的响应头jwt
		resp.setHeader("Access-Control-Expose-Headers", "jwt");
		

		if ("OPTIONS".equals(req.getMethod())) {// axios的ajax会发两次请求,第一次提交方式为:option,直接返回即可
			return;
		}
		filterChain.doFilter(servletRequest, servletResponse);
	}

	@Override
	public void destroy() {

	}
}

JWT验证过滤器  生成Jwt

package com.zking.vue.jwt;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import io.jsonwebtoken.Claims;

/**
 * * JWT验证过滤器,配置顺序 :CorsFilte-->JwtFilter-->struts2中央控制器
 * 
 * @author Administrator
 *
 */
public class JwtFilter implements Filter {

	// 排除的URL,一般为登陆的URL(请改成自己登陆的URL)
	private static String EXCLUDE = "^/userAction\\.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();
	}

	

}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>j2eeVue</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
   <!-- Vue之axios跨域调用过滤器 -->
  <filter>
  	<filter-name>corsFilter</filter-name>
  	<filter-class>com.zking.vue.util.CorsFilter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>corsFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping> 
  
  
  
  <!-- Jwt过滤器 -->
  <filter>
  	<filter-name>JwtFilter</filter-name>
  	<filter-class>com.zking.vue.jwt.JwtFilter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>JwtFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping> 
  
  
  <!-- 中文乱码过滤器 -->
  <filter>
  	<filter-name>encodingFilter</filter-name>
  	<filter-class>com.zking.vue.util.EncodingFilter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>encodingFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <!-- Servlet核心控制器 -->
  <servlet>
  	<servlet-name>ActionServlet</servlet-name>
  	<servlet-class>com.zking.mvc.framework.ActionServlet</servlet-class>
  	<init-param>
  		<param-name>config</param-name>
  		<param-value>/mvc.xml</param-value>
  	</init-param>
  </servlet>
  <servlet-mapping>
  	<servlet-name>ActionServlet</servlet-name>
  	<url-pattern>*.action</url-pattern>
  </servlet-mapping>
</web-app>

登录成功生成令牌,并设置到headr中

public String userLogin(HttpServletRequest req,HttpServletResponse resp) 
			throws ServletException,IOException{
		Map<String,Object> json=new HashMap<String,Object>();
		
		User selectUser = userdao.SelectUser(user);
		
		if(selectUser!=null) {
			json.put("code","1");
			json.put("msg", "用户登录成功");
			
			//生成JWT,并设置到response响应头中
			String jwt=JwtUtils.createJwt(json, JwtUtils.JWT_WEB_TTL);
			resp.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);
		}else {
			json.put("code", "-1");
			json.put("msg", "用户名或密码错误");
		}
		mapper.writeValue(resp.getOutputStream(),json);
		return null;
	}

前端Vue

vuex 中state.js定义jwt变量拿来保存令牌

export default{
     jwt:null
}

getters.js取值方法

export default{
  getjwt:(state)=>{
    return state.jwt;
  }
}

mutations.js中设置值的方法

export default{
    setJwt:(state,pyload)=>{
      state.jwt=pyload.jwt;
    }
}

main.js中定义全局Vue

window.vm=new Vue({
  el: '#app',
  router,
  store, //在main.js中导入store实例
  components: {
    App
  },
  template: '<App/>'
})

前端Jwt验证请求和响应拦截

// 请求拦截器
axios.interceptors.request.use(function(config) {
  //每一次向服务器发送请求时,先由请求拦截器在此处获取vuex中的令牌并存入到request请求头带入到后端(登录除外)

  //从vuex中获取jwt令牌
 let jwt=window.vm.$store.getters.getjwt;
  //除登录
    if(null!=jwt){
          config.headers['jwt']=jwt;
    }
  console.log(config);
	return config;
}, function(error) {
	return Promise.reject(error);
});

// 响应拦截器
axios.interceptors.response.use(function(response) {

  console.log(response.headers['jwt']);
   window.vm.$store.commit('setJwt',{jwt:response.headers['jwt']});
	return response;
}, function(error) {
	return Promise.reject(error);
});

headers中带有jwt属性以及令牌进行验证,每次向后台请求都会带有令牌请求验证,除了登录之外

vuex中的Index.js

import  Vue from 'vue'
import  Vuex from 'vuex'
//页面数据持久化
//第一种方式 保存到内存中 永久保存 只能手动清楚  Application
//import persistedstate from 'vuex-persistedstate'

import vuexalong from 'vuex-along'

Vue.use(Vuex)


//分别导入其他相应模块(state/action/getters/mutatios)
import state from './state.js'
import actions from './actions.js'
import mutations from './mutations.js'
import getters from './getters.js'



//新建vuex的store实例
//每一个Vuex应用的核心就是store(仓库),store基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。
const store = new Vuex.Store({
  state,  // 共同维护的一个状态,state里面可以是很多个全局状态
  getters,  // 获取数据并渲染
  actions,  // 数据的异步操作
  mutations,
  //plugins:[persistedstate()]
  plugins:[
        vuexalong({
          //设置保存的集合名称,避免在同一站点下多项目的数据
          name:'hellow-vue-along',
          session: {
            //保存模块 ma 中的a1 到sessionStorage
              list:['hotalname','collapsed','jwt']
          }
          /* local:{
            list:[""]   //将数据保存到localStoreage
          } */
        })
    ]
  }) // 处理数据的唯一途径,state的改变或赋值只能在这里


export default store

如果签名认证失败会抛出如下的异常:

 io.jsonwebtoken.SignatureException

JWT过期异常:

 io.jsonwebtoken.ExpiredJwtException

注意多次运行方法生成的token字符串内容是不一样的,尽管我们的payload信息没有变动。因为JWT中携带了超时时间,所以每次生成的token会不一样,我们利用base64解密工具可以发现payload确实携带了超时时间

代码链接 提取码:s6vu

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Ruoyi-vue-pro是一款便捷的前端Java框架,其官方开发指南PDF可在官网或相关技术博客上进行下载。 该开发指南PDF主要了Ruoyi-vue-pro的框架结构、设计规范、使用方法等内容,为开发人员提供了全面的参考资料。在其中可以学习到如何使用IDE工具进行项目开发、如何对接后端API接口、如何进行代码打包和发布、如何进行项目调试等技巧。 此外,该开发指南PDF还提供了大量代码示例,方便开发人员更好地理解和掌握框架使用。同时,对于想要深度定制Ruoyi-vue-pro框架的开发者,该开发指南PDF也提供了一些扩展和集成方案,方便自定义组件和功能的实现。 总之,Ruoyi-vue-pro开发指南PDF是一份十分实用和详尽的前端框架开发指南,对于想要使用该框架开发项目的开发者来说是不可多得的学习资料。建议各位开发者在开始使用Ruoyi-vue-pro前,先进行该指南的学习,以便更好地开展项目工作。 ### 回答2: ruoyi-vue-pro 是一个开源的后台管理系统,采用前后端分离的开发架构,前端采用 Vue.js 框架开发,后台采用 SpringBoot 框架开发。ruoyi-vue-pro 开发指南 pdf 是一份详细的开发文档,其中包括了系统的功能、搭建环境、项目结构、模块设计等内容。 在功能中,文档详细了系统的模块功能,包括登录管理、角色权限管理、用户管理、菜单管理、数据字典管理等。在搭建环境中,文档给出了详细的开发环境配置步骤,包括安装数据库、安装 Java JDK 等内容。在项目结构中,文档了整个项目的文件结构,包括前后端代码的目录结构和各个文件的作用。 在模块设计中,文档详细说明了各个模块的设计思路、实现方式和交互流程。其中,登录管理模块通过 JWT 鉴权实现用户登录,角色权限管理模块通过 Shiro 实现用户权限控制,数据字典管理模块通过缓存技术实现字典数据的高效查询。文档还提供了详细的代码示例和开发注意事项,方便开发者自行开发新的模块。 总之,ruoyi-vue-pro 开发指南 pdf 是一份非常详细的开发文档,无论是已经使用过该系统的开发者,还是新手开发者都能够轻松地了解系统的架构特点和开发流程,快速上手进行开发工作。 ### 回答3: ruoyi-vue-pro 开发指南 pdf 是一本非常实用的指南手册,旨在帮助开发人员更好地理解和利用该开源项目。该指南详细了 ruoyi-vue-pro 项目的核心功能和特点,以及如何在项目中运用这些功能来开发高效、易用的应用程序。 指南从项目的搭建和配置开始,包括项目依赖的环境、安装和配置开发工具的步骤,以及如何在项目中添加新的组件和插件等。接着,指南详细了项目的各种功能和构架,包括如何使用路由、Vuex、Mock等功能来实现应用程序的交互和通信。 该指南在每个功能和构架时,都提供了大量的代码示例和实用的技巧,使开发人员可以更加深入地了解和学习该项目的全部功能。此外,该指南还提供了一些常见问题的解答和调试技巧,帮助开发人员更加科学和高效地开发ruoyi-vue-pro项目。 总之,ruoyi-vue-pro 开发指南 pdf 是一本非常实用和详细的指南手册,对于希望学习和开发ruoyi-vue-pro项目的开发人员来说,是一本不可或缺的工具书。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值