SpringBoot + jwt + vue实现双token下的用户登录状态控制

SpringBoot + jwt + vue实现双token下的用户登录状态控制

业务流程图

1.总体概述

用户登录后,后台springboot工程通过jwt技术生成scessToken和freshToken双令牌,并且freshToken的有效时间是scessToken的两倍,如分别设置我半个小时和一个小时,前端vue将scessToken和freshToken存储至token,在业务请求的时候携带scessToken,在scessToken验证失败后,发送刷新token的请求,请求成功会返回新的scessToken和freshToken,前端携带新的scessToken重新发送请求,若失败则退出登录。

2.前端代码实现

vi ~/src/request/index.js
"use strict";
import axios from "axios";
import store from "../store";
// create an axios instance

const request = axios.create({
  //baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
baseURL: "./", 
 //baseURL: "http://127.0.0.1:8083/", 
  timeout: 60000 // request timeout
});
// request interceptor
request.interceptors.request.use(
  config => {
    // Do something before request is sent
	
    console.info("请求拦截");
	let activeUser = store.getters.getActiveUser();
	if(activeUser !=null){
		 config.headers["userName"] = store.getters.getActiveUser().userName;
		  config.headers["accessToken"] = store.getters.getActiveUser().accessToken;
	}
    return config;
  },
  error => {
    //message.error(error.message)
    return Promise.reject(error);
  }
);

// response interceptor
request.interceptors.response.use(
  response => {
    if (response.data.code == "4001") {
		return  Promise.reject(response);
    }else{
		return response;
	}
    
  },
  error => {
	  
	  return Promise.reject(error);
  }
);



request.interceptors.response.use(
  response => {
	return response;
    
  },
  error => {
	  let response = error;
	 // console.info(error)
	  try{
		  return  new Promise((resolve,reject)=>{
			  store.dispatch("tokenRefresh",store.getters.getActiveUser()).then(() => {
				  
				  
			  	let config = response.config;
			  	let activeUser = store.getters.getActiveUser();
			  	if(activeUser !=null){
			  		 config.headers["userName"] = store.getters.getActiveUser().userName;
			  		  config.headers["accessToken"] = store.getters.getActiveUser().accessToken;
			  	}
			  	return resolve(axios(config));
			  }).catch(err=>{
				  store.dispatch('LogOut').then(()=>{
				     location.reload()
				      //this.$router.push({path:'/login'})
				  });
			  });
		  });
	
		  }catch(e){
		  	 return Promise.reject(error);
		  }
  }
);

export default request;

vi ~/store/index.js
"use strict";
import Vue from "vue";
import Vuex from "vuex";
import cookie from "js-cookie";
import { selectAllUser } from "@/api/user.js";
Vue.use(Vuex);
const store = new Vuex.Store({
  state: {
	user:cookie.get("user")
  },
  getters: {

  	  getUser:(state,getters)=>(userName)=>{
	  		   let user =   state.userList.filter(item=>{
	  			  return (item.userName == userName);
	  		  })
			  if(user.length>0){
				   return user[0];
			  }else{
				  return {};
			  }
	  }
  },
  mutations: {
	
	SET_USER: (state, user) => {
	  state.user =  JSON.stringify(user);
	   cookie.set("user", JSON.stringify(user));
	},
		CLEAR_USER: (state) => {
	   state.user =  "";
	   cookie.set("user", "");
	},
  },
  actions: {
		},
    Login({ commit }, data) {
      return new Promise((resolve, reject) => {
        login(data).then(response => {
          if (response.data.code == "0000") {
            commit("SET_USER", response.data.extend);
            resolve();
          } else {
            reject(response.data.message);
          }
        }).catch((error)=>{
			 reject(error.message);
		});
      });
    },
	
	tokenRefresh({commit},data){
		return new Promise((resolve, reject) => {
		  tokenRefresh(data).then(response => {
		    if (response.data.code == "0000") {
		      commit("SET_USER", response.data.extend);
		      resolve();
		    } else {
		      reject(response.data.message);
		    }
		  }).catch((error)=>{
					 reject(error.message);
		  });
		});
	},
    LogOut({ commit }) {
      commit("CLEAR_USER");
    },
  }
});

export default store;

vi ~/api/system.js
//请求后台的方法
import request from "@/request";
//登录
export function login(data) {
  return request({
    url: "/user/login",
    method: "post",
    data
  });
}
//刷新缓存
export function tokenRefresh(data) {
  return request({
    url: "/user/tokenRefresh",
    method: "post",
    data
  });
}

3.后端代码实现

3.1 添加jwt的maven依赖

vi pom.xml
	<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.2.0</version>
	</dependency>

3.2 java代码

vi com/git/smp/filter/LoginInterceptor.java
package com.git.smp.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.HandlerInterceptor;

import com.git.smp.controller.SmpUserController;
import com.git.smp.utils.JWTUtil;

@Service
public class LoginInterceptor implements HandlerInterceptor {
	Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
	
	
	@Value("${jwt.password}")
	private String jwtPassword;
	
	@Value("${jwt.version}")
	private String version;

	/*
	 * @Override public void doFilter(ServletRequest request, ServletResponse
	 * response, FilterChain chain) throws IOException, ServletException {
	 * //logger.info(request.getLocalAddr());
	 * 
	 * HttpServletRequest httpRequest = (HttpServletRequest)request;
	 * logger.info(httpRequest.getRequestURI());
	 * 
	 * if("/index.html".equals(httpRequest.getRequestURI())||"/favicon.ico".equals(
	 * httpRequest.getRequestURI())||"/".equals(httpRequest.getRequestURI())||
	 * "/user/login".equals(httpRequest.getRequestURI())) { chain.doFilter(request,
	 * response); return; }
	 * 
	 * String accessToken = httpRequest.getHeader("accessToken");
	 * 
	 * if(JWTUtil.verify(accessToken, "smp@ahrcu")) { chain.doFilter(request,
	 * response); }else { //登录验证失败 //throw new SmpException("401", "令牌无效");
	 * response.getWriter().write("{\"code\":\"4001\",\"message\":\"\"}"); }
	 * 
	 * logger.info("登录拦截");
	 * 
	 * }
	 */

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		HttpServletRequest httpRequest = (HttpServletRequest) request;
		logger.info(httpRequest.getRequestURI());
		String accessToken = httpRequest.getHeader("accessToken");

		if (JWTUtil.verify(accessToken,jwtPassword,version)) {
			return true;
		} else {
			// 登录验证失败 //throw new SmpException("401", "令牌无效");
			response.getWriter().write("{\"code\":\"4001\",\"message\":\"\"}");
			return false;
		}
	}
}
vi com/git/smp/controller/system.java
package com.git.smp.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties.Jwt;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.git.smp.core.PageRequest;
import com.git.smp.core.ResponseMessage;
import com.git.smp.dao.UserMapper;
import com.git.smp.dao.UserRoleMapper;
import com.git.smp.entity.User;
import com.git.smp.entity.UserExtend;
import com.git.smp.entity.UserRole;
import com.git.smp.utils.JWTUtil;
import com.git.smp.utils.ObjectUtil;
@RestController
@RequestMapping("/user")
public class SmpUserController {

	Logger logger = LoggerFactory.getLogger(SmpUserController.class);

	@Autowired
	UserMapper UserMapper;
	
	@Autowired
	UserRoleMapper UserRoleMapper;
	

	@Value("${jwt.password}")
	private String jwtPassword;
	
	@Value("${jwt.version}")
	private String version;
	

	@Autowired
	private HttpServletRequest request;
	
	
	/**
	 * 
	 * @param pageRequest
	 * @return
	 */
	@RequestMapping("/login")
	public ResponseMessage<UserExtend> login(@RequestBody User user) {
		ResponseMessage<UserExtend> responseMessage = new ResponseMessage<UserExtend>();
		responseMessage.setCode("0000");
		responseMessage.setMessage("查询成功!");
		
		User userRsult = UserMapper.selectByPrimaryKey(user.getUserName());
		if (userRsult == null) {
			responseMessage.setCode("0001");
			
			responseMessage.setMessage("用户不存在");
		}else if(!userRsult.getUserPasswd().equals(user.getUserPasswd())) {
			responseMessage.setCode("0001");
			responseMessage.setMessage("密码错误");
		}else {
			String accessToken = JWTUtil.sign(userRsult,  30*60*1000, jwtPassword,version);
			String freshToken = JWTUtil.sign(userRsult,  60*60*1000,jwtPassword,version);
			
			
			UserExtend UserExtend = new UserExtend();
			
			ObjectUtil.copyBeans(userRsult, smpUserExtend);
			
			UserExtend.setAccessToken(accessToken);
			UserExtend.setFreshToken(freshToken);
			UserExtend.setUserPasswd("");
			responseMessage.setExtend(UserExtend);
		}
		logger.info(responseMessage.getMessage());
		return responseMessage;
	}
	
	/**
	 * 刷新token
	 * @param pageRequest
	 * @return
	 */
	@RequestMapping("/tokenRefresh")
	public ResponseMessage<UserExtend> login(@RequestBody UserExtend user) {
		logger.info("刷新缓存");
		ResponseMessage<UserExtend> responseMessage = new ResponseMessage<UserExtend>();
		responseMessage.setCode("0000");
		responseMessage.setMessage("查询成功!");
		if(JWTUtil.verify(user.getFreshToken(), jwtPassword,version)) {
			String accessToken = JWTUtil.sign(user,  30*60*1000, jwtPassword,version);
			String freshToken = JWTUtil.sign(user,  60*60*1000,jwtPassword,version);
			user.setAccessToken(accessToken);
			user.setFreshToken(freshToken);
			responseMessage.setExtend(user);
		}else {
			responseMessage.setCode("0001");
			responseMessage.setMessage("token已过期");
		}
		
		
		return responseMessage;
	}
}

vi  com/git/smp/utils.java
package com.git.smp.utils;

import java.io.UnsupportedEncodingException;
import java.util.Date;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.git.smp.entity.SmpUser;

public class JWTUtil {

    // 过期时间2分钟
    private static final long EXPIRE_TIME = 24*60*60*1000;

    /**
     * 校验token是否正确
     * @param token 密钥
     * @param secret 用户的密码
     * @return 是否正确
     */
    public static boolean verify(String token, String secret,String version) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret+version);
            JWTVerifier verifier = JWT.require(algorithm).build();
            @SuppressWarnings("unused")
			DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception exception) {
            return false;
        }
    }
    


    /**
     * 获得token中的信息无需secret解密也能获得
     * @return token中包含的用户名
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("userName").asString();
           
        } catch (JWTDecodeException e) {
            return null;
        }
    }
    

    /**
     * 生成签名,5min后过期
     * @param userName 用户名
     * @param secret 用户的密码
     * @return 加密的token
     */
    public static String sign(SmpUser smpUser,long expireTime,String secret,String version) {
        try {
            Date date = new Date(System.currentTimeMillis()+expireTime);
            Algorithm algorithm = Algorithm.HMAC256(secret+version);
            // 附带username信息
            return JWT.create()
            		.withClaim("userName", smpUser.getUserName())
                    .withClaim("userChineseName", smpUser.getUserChineseName())
                    .withClaim("passWord", smpUser.getUserPasswd())
                    .withClaim("version", version)
                    .withExpiresAt(date)
                    .sign(algorithm);
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }
}

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring BootVue.js是两个非常流行的技术栈,可以非常好地实现后端分离的开发模式。Security和JWT是两个很好的工具,可以帮助我们实现安全的登录和授权机制。 以下是实现Spring BootVue.js后端分离的步骤: 1.创建Spring Boot工程 首先,我们需要创建一个Spring Boot工程,可以使用Spring Initializr来生成一个基本的Maven项目,添加所需的依赖项,包括Spring Security和JWT。 2.配置Spring Security 在Spring Security中,我们需要定义一个安全配置类,该类将定义我们的安全策略和JWT的配置。在这里,我们可以使用注解来定义我们的安全策略,如@PreAuthorize和@Secured。 3.实现JWT JWT是一种基于令牌的身份验证机制,它使用JSON Web Token来传递安全信息。在我们的应用程序中,我们需要实现JWT的生成和验证机制,以便我们可以安全地登录和授权。 4.配置Vue.jsVue.js中,我们需要创建一个Vue.js项目,并使用Vue CLI来安装和配置我们的项目。我们需要使用Vue Router来定义我们的路由,并使用Axios来发送HTTP请求。 5.实现登录和授权 最后,我们需要实现登录和授权机制,以便用户可以安全地登录和访问我们的应用程序。在Vue.js中,我们可以使用Vue Router和Axios来发送HTTP请求,并在Spring Boot中使用JWT来验证用户身份。 总结 以上是实现Spring BootVue.js后端分离的步骤,我们可以使用Security和JWT实现安全的登录和授权机制。这种开发模式可以让我们更好地实现后端分离,提高我们的开发效率和应用程序的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值