赞
踩
目标:
①、jwt出现的原因及工作原理
②、jwt工具类介绍,三种场景
③、jwt与vuex配合在SPA项目中的应用
JSON Web Token (JWT),它是目前最流行的跨域身份验证解决方案
JWT的精髓在于:“去中心化”,数据是保存在客户端的。
是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,示例如下: {"UserName": "Chongchong","Role": "Admin","Expire": "2018-08-08 20:15:56"}
之后,当用户与服务器通信时,客户在请求中发回JSON对象
为了防止用户篡改数据,服务器将在生成对象时添加签名,并对发回的数据进行验证
一个JWT实际上就是一个字符串,它由三部分组成:头部(Header)、载荷(Payload)与签名(signature) JWT结构原理图:见资料“JWT的数据结构.jpg” JWT实际结构:eyJhbGciOiJIUzI1NiJ9. eyJzdWIiOiJ7fSIsImlzcyI6InpraW5nIiwiZXhwIjoxNTYyODUwMjM3LCJpYXQiOjE1NjI4NDg0MzcsImp0aSI6ImM5OWEyMzRmMDc4NzQyZWE4YjlmYThlYmYzY2VhNjBlIiwidXNlcm5hbWUiOiJ6c3MifQ. WUfqhFTeGzUZCpCfz5eeEpBXBZ8-lYg1htp-t7wD3I4
它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。 写成一行,就是下面的样子:Header.Payload.Signature
<!-- 解决jwt问题的过滤器 -->
<filter>
<filter-name>jwtFilter</filter-name>
<filter-class>com.zking.vue.util.JwtFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>jwtFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
将true改为false,开启
private boolean OFF = false;// true关闭jwt令牌验证功能
- package com.zking.vue.util;
-
- 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 = "^/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();
- }
-
- // public static void main(String[] args) {
- // String path = "/sys/userAction_doLogin.action?username=zs&password=123";
- // Matcher matcher = PATTERN.matcher(path);
- // boolean b = matcher.matches();
- // System.out.println(b);
- // }
-
- }
将下面代码取消注释
Map<String, Object> claims = new HashMap<String, Object>();
claims.put("uname",user.getUname());
claims.put("pwd", user.getPwd());
String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL);
response.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);
jsonData = new JsonData(1, "登录成功", u);
- package com.zking.vue.web;
-
- import java.util.HashMap;
- import java.util.Map;
-
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.opensymphony.xwork2.ModelDriven;
- import com.zking.base.web.BaseAction;
- import com.zking.vue.biz.UserBiz;
- import com.zking.vue.entity.User;
- import com.zking.vue.util.JsonData;
- import com.zking.vue.util.JwtUtils;
- import com.zking.vue.util.PageBean;
- import com.zking.vue.util.ResponseUtil;
- import com.zking.vue.util.StringUtils;
-
- public class UserAction extends BaseAction implements ModelDriven<User>{
-
- private UserBiz userBiz;
- private User user = new User();
-
- public UserBiz getUserBiz() {
- return userBiz;
- }
-
- public void setUserBiz(UserBiz userBiz) {
- this.userBiz = userBiz;
- }
-
- public String login() {
- ObjectMapper om = new ObjectMapper();
- JsonData jsonData = null;
- try {
- if(StringUtils.isBlank(user.getUname()) || StringUtils.isBlank(user.getPwd())) {
- jsonData = new JsonData(0, "用户或者密码为空", user);
- }else {
- User u = this.userBiz.login(user);
- Map<String, Object> claims = new HashMap<String, Object>();
- claims.put("uname",user.getUname());
- claims.put("pwd", user.getPwd());
- String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL);
- response.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);
- jsonData = new JsonData(1, "登录成功", u);
- }
- } catch (Exception e) {
- e.printStackTrace();
- jsonData = new JsonData(0, "用户或者密码错误", user);
- }finally {
- try {
- ResponseUtil.write(response, om.writeValueAsString(jsonData));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- return null;
- }
-
- public String getAsyncData() {
- ObjectMapper om = new ObjectMapper();
- try {
- Thread.sleep(6000);
- ResponseUtil.write(response, om.writeValueAsString("http://www.javaxl.com"));
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- @Override
- public User getModel() {
- return user;
- }
- }
产生原因:
1、id被封,被拉入黑名单
2、没有登录凭证
// 响应拦截器
axios.interceptors.response.use(function(response) {
debugger;
var jwt = response.headers['jwt'];
if(jwt){
window.vm.$store.commit('setJwt',{jwt:jwt});
}
return response;
}, function(error) {
return Promise.reject(error);
});
将jwt的值放入vuex中
增加红色部分
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
//开发环境下才会引入mockjs
//开发环境:true && require('@/mock')会执行后面的代码,也就是说mock.js会被导入到当前环境中
//生产环境:false && require('@/mock')不会执行后面代码,也就是说mock.js不会引人到当前环境中
// '/'相对的是src路径
// process.env.MOCK && require('@/mock')
import 'element-ui/lib/theme-chalk/index.css' //新添加2,避免后期打包样式不同,要放在import App from './App';之前
import App from './App'
import router from './router'
import ElementUI from 'element-ui' //新添加1import axios from '@/api/http' // #vue项目对axios的全局配置
import VueAxios from 'vue-axios'
import store from './store'Vue.use(ElementUI) //新添加3
Vue.use(VueAxios, axios)
Vue.config.productionTip = false/* eslint-disable no-new */
window.vm= new Vue({
el: '#app',
data() {
return {
Bus: new Vue({})
}
},
router,
store,
components: {
App
},
template: '<App/>'
})
- export default{
- resturantName:'飞歌餐馆',
- jwt:''
- }
setJwt:(state,payload)=>{
state.jwt=payload.jwt;
},
- export default{
- setResturantName:(state,payload)=>{
- state.resturantName=payload.resturantName;
- },
- setJwt:(state,payload)=>{
- state.jwt=payload.jwt;
- },
- doAjax:(state,payload)=>{
- // 需求:想在当前的文件中与后台服务器做数据交互
- let _this=payload._this;
- let url = _this.axios.urls.SYSTEM_MENU_TREE;
- // 箭头函数解决了this 指针污染的问题
- _this.axios.post(url, {}).then((resp) => {
- console.log(resp);
- _this.menus=resp.data.result;
- }).catch(function(error) {
- console.log(error);
- });
-
- _this.$root.Bus.$on("collapsed-aside",(v)=>{
- _this.collapsed=v;
- });
- }
-
- }
getJwt:(state)=>{
return state.jwt;
}
- export default{
- getResturantName:(state)=>{
- return state.resturantName;
- },
- getJwt:(state)=>{
- return state.jwt;
- }
- }
// 请求拦截器
axios.interceptors.request.use(function(config) {
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) {
debugger;
var jwt = response.headers['jwt'];
if(jwt){
window.vm.$store.commit('setJwt',{jwt:jwt});
}
return response;
}, function(error) {
return Promise.reject(error);
});
三种场景及结果展示
用户信息都获得了,签发过期时间都出现了
以下错误就是令牌过期:
令牌过期时间为30分钟,假如一直在使用,30分钟后就要过期(不合理),
因此,需要设置最后使用时间为签发时间,再过30分钟为过期时间
解决方法:copy jwt令牌,从新设置签发过期时间
代码:
- package com.zking.vue.test;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
- import org.junit.Test;
- import com.zking.vue.util.JwtUtils;
- import io.jsonwebtoken.Claims;
- public class JwtDemo {
- 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));
- }
- @Test
- public void test2() {// 解析oldJwt
- // String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI5MDMzNjAsImlhdCI6MTU2MjkwMTU2MCwiYWdlIjoxOCwianRpIjoiZDVjMzE4Njg0MDcyNDgyZDg1MDE5ODVmMDY3OGQ4NjkiLCJ1c2VybmFtZSI6InpzcyJ9.XDDDRRq5jYq5EdEBHtPm7GcuBz4S0VhDTS1amRCdf48";
- String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjM1MjU5MjMsImlhdCI6MTU2MzUyNDEyMywiYWdlIjoxOCwianRpIjoiOTAzNmMwY2Q3NGIwNDBjMzgzMDAxYzdiNmZkMzYzZmIiLCJ1c2VybmFtZSI6InpzcyJ9.sgV9fr4fgmmahDFRJnsfazA6R3H-gNMVcg2ucA227n4";
- 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));
- }
- @Test
- public void test3() {// 复制jwt,并延时30秒
- String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI5MDMzNjAsImlhdCI6MTU2MjkwMTU2MCwiYWdlIjoxOCwianRpIjoiZDVjMzE4Njg0MDcyNDgyZDg1MDE5ODVmMDY3OGQ4NjkiLCJ1c2VybmFtZSI6InpzcyJ9.XDDDRRq5jYq5EdEBHtPm7GcuBz4S0VhDTS1amRCdf48";
- 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));
- }
- @Test
- public void test4() {// 测试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));
- }
- @Test
- public void test5() {// 三秒后再解析上面过期时间只有三秒的令牌,因为过期则会报错io.jsonwebtoken.ExpiredJwtException
- String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI4NTMzMzAsImlhdCI6MTU2Mjg1MzMyNywidXNlcm5hbWUiOiJ6c3MifQ.e098Vj9KBlZfC12QSDhI5lUGRLbNwb27lrYYSL6JwrQ";
- Claims parseJwt = JwtUtils.parseJwt(oldJwt);
- // 过期后解析就报错了,下面代码根本不会执行
- Date d1 = parseJwt.getIssuedAt();
- Date d2 = parseJwt.getExpiration();
- System.out.println("令牌签发时间:" + sdf.format(d1));
- System.out.println("令牌过期时间:" + sdf.format(d2));
- }
-----------------没有了------------------------------------------------
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。