1 ,后端JJWT类
package com.utils; import com.entity.User; import io.jsonwebtoken.*; import java.util.Date; public class JwtUtils { /** * 签名需要的私有密钥 */ private static final String SECRET = "myScrect"; /** * 重载createJwt,通过用户信息和生成Jwt * @param user 用户信息 * @return token */ public static String createJwt(User user) { return createJwt(user,1000*60*60); } /** * 通过用户信息和过期时间生成Jwt * @param user 用户信息 * @param ttlMillis 过期时间 * @return token */ public static String createJwt(User user, long ttlMillis) { // 签名算法 HS256,即jjwt已经封装header中需要的算法名称 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 生成Jwt的时间,即签发时间 long nowMillis = System.currentTimeMillis(); //构建Jwt JwtBuilder builder = Jwts.builder() //jwt的唯一标识 .setId(String.valueOf(user.getId())) //jwt面向用户 .setSubject(user.getUname()) //jwt的签发者 .setIssuer("aaa") //jwt的签发时间 .setIssuedAt(new Date(nowMillis)) //设置签名使用的签名算法和签名使用的秘钥 .signWith(signatureAlgorithm,SECRET); //自定义payload的claim信息 builder.claim("root", "root"); // 设置过期时间,需要大于签发时间 if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; builder.setExpiration(new Date(expMillis)); } String token = builder.compact(); return token; } //token过期则抛出异常 public static void parseJwt(String jwt){ //解析jwt JwtParser parser = Jwts.parser(); Claims claims; //获取解析后的对象 claims = Jwts.parser() //设置签名秘钥,和生成的签名的秘钥一模一样 .setSigningKey(SECRET) //设置需要解析的jwt .parseClaimsJws(jwt) .getBody(); } }
2,后端过滤器类
import utils.JwtUtils; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; import java.util.List; public class LoginFilter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } // 白名单 private List<String> whiteList= Arrays.asList( "/", "/user" ); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request=(HttpServletRequest) servletRequest; HttpServletResponse response=(HttpServletResponse) servletResponse; // 获取请求路径 String currentUrl=request.getServletPath(); // 白名单放行,遍历白名单 for (String url:whiteList){ if (url.contains(currentUrl)){ filterChain.doFilter(servletRequest, servletResponse); return; } } // 获取token String token=request.getHeader("Authorization"); try { JwtUtils.parseJwt(token); // 捕获token异常 } catch (Exception e) { // token过期,重定向到一个error处理页面 response.sendRedirect("/user/error"); } // token有效,放行 filterChain.doFilter(servletRequest, servletResponse); } }
3,前端axios文件夹下index.js(作用为设置请求头)
import store from "@/store";
import Axios from "axios";
// 重写axios请求基地址
const axios = Axios.create({
baseURL: "http://localhost:8080/demo_war_exploded/",
timeout: 60000, //改成60s
});
// axios请求拦截器。当请求地址不为/user且vuex中已存入token,设置请求头,存入token给后端验证
axios.interceptors.request.use(function (config) {
if (config.url !== "user" && store.getters.getToken) {
config.headers.Authorization = store.getters.getToken;
}
// 请求地址为/user或vuex内未存token,直接返回config对象,后端进行判断处理
return config;
});
export default axios;
4,前端store文件夹下的index.js(定义及存取token)
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
token: "",
},
getters: {
setToken(state, res) {
state.token = res;
},
getToken(state) {
return state.token || null;
},
},
mutations: {},
actions: {},
modules: {},
});
5,登录页面发送的axios get请求(记得引入store到登录页面)
doGet("user", {
uname: this.user.uname,
pwd: this.user.pwd,
}).then((res) => {
// 存入token至vuex
store.state.token = res.data.data.token;
this.$router.push({ path: "/city" });
});
6,main.js文件(在头部视项目情况引入对应文件)
// element-ui的引入
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
// 这里我引入了阿里妈妈的图标库
import "./static/css/iconfont.css";
// 引入store
import store from "./store";
Vue.config.productionTip = false;
Vue.use(ElementUI);
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
// 前置导航守卫,如果页面不是跳转去登录页面并且没有存入token。则跳转回首页
router.beforeEach((to, from, next) => {
if (store.getters.getToken || to.path.startsWith("/login")) {
next();
} else {
next("/");
}
});
7,附加内容:
7-1,多次点击登录的报错解决
import CityView from "@/views/CityView.vue";
import Vue from "vue";
import VueRouter from "vue-router";
import LoginView from "../views/LoginView.vue";
Vue.use(VueRouter);
const routes = [
// 登陆路由
{
path: "/",
name: "login",
component: LoginView,
},
// city数据展示路由
{
path: "/city",
component: CityView,
},
];
const router = new VueRouter({
routes,
});
// 防止连续点击多次路由报错
let routerPush = router.push;
let routerReplace = router.replace;
// push方法的处理
router.push = function push(location) {
return routerPush.call(this, location).catch((err) => err);
};
// replace方法的处理
router.replace = function push(location) {
return routerReplace.call(this, location).catch((err) => err);
};
export default router;
7-2 User实体类
import lombok.Data; import java.io.Serializable; @Data public class User implements Serializable { private Integer id; private String uname; private String pwd; }
7-3 UserVo类(继承自user类)
import lombok.Data; @Data public class UserVo extends User{ private String token; }