一、后端部分
(一)生成JWT工具类
public class JwtUtils {
private static String signKey = "MyPersonalTestKey"; // 这个字符串必须长!
private static Long expire = 43200000L; // 12个小时的有效时间
//private static long expire = 1000*5;//5秒
//private static long expire = 1000*60*60;//1小时
//生成JWT令牌
public static String generateJwt(Map<String,Object> claims){
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256,signKey)//签名算法
.setClaims(claims) // 自定义的内容(载荷)
.setExpiration(new Date(System.currentTimeMillis() + expire)) //设置有效时间
.compact();
return jwt;
}
//解析令牌
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
JWT工具类用于后端接收并验证登录信息正确后,生成JWT令牌用于前后端的身份验证
(二)请求拦截器
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor { //ctrl + O 调出继承
@Override //目标资源方法进行前运行,返回true:方向;返回false:不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
//返回谷歌浏览器的第一次预请求
if(req.getMethod().equals("OPTIONS")){
return true;
}
//1. 获取请求URL
String url = req.getRequestURL().toString();
log.info("请求的url : {}" ,url);
//2. 判断请求URL中是否包含login,如果包含,放行
if(url.contains("login") || url.contains("register")){
//*filterChain.doFilter(servletRequest,servletResponse);*//*
return true;
}
//3. 获取请求头中的令牌(token)
String jwt = req.getHeader("token");
log.info("传入的token为 {}" ,jwt);
//4. 判断令牌是否存在,如果不存在返回错误信息(未登录)
if(!StringUtils.hasLength(jwt)){
Result error = Result.error("NOT_LOGIN");
//转换对象 -》json
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return false;
}
//5. 解析令牌,如果解析失败,返回错误信息(未登录)
try {
Claims claims = JwtUtils.parseJWT(jwt);
log.info("解析结果为 : {}",claims);
log.info("这是账号信息 : {}",claims.get("id",String.class));
String loginId = claims.get("id",String.class);
CurrentUser.setLoginId(loginId);
log.info("这是账号信息 : {}",CurrentUser.getLoginId());
} catch (Exception e) {
e.printStackTrace();
Result error = Result.error("NOT_LOGIN");
//转换对象 -》json
String notLogin = JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return false;
}
//6. 放行
//*filterChain.doFilter(servletRequest,servletResponse);*//*
log.info("放行");
return true;
}
@Override //目标资源方法运行后运行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle....");
}
@Override //视图渲染完毕后运行,最后运行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion....");
}
}
前端的每一次请求,都要验证token的合法性和有效性,将解析到的用户信息,存放在一个自定义的静态类中,用于记录登录者的信息,以便后续的操作
(三)用户登录的Cotroller层代码
//用户登录
@PostMapping("/login")
public Result login(@RequestBody User user){
log.info("用户信息传进来的是:{}",user);
User u = userService.login(user);
if(u != null){
Map<String,Object> claims = new HashMap<>();
claims.put("id",u.getLoginId());
claims.put("money",u.getMoney());
claims.put("phone",u.getPhone());
String jwt = JwtUtils.generateJwt(claims); // jwt包含当前员工信息
return Result.success(jwt);
}
//登录失败,返回错误信息
return Result.error("用户名或密码错误");
}
如果账号密码正确,将用户的信息放入claims集合中,用于JWT自定义用户信息的载荷部分,也可以不弄 。并将token返回前端
二、前端部分
(一)接收设置token
Login() {
var vm = this;
console.log(this.loginForm);
this.axios({
method: "POST",
url: "http://localhost:3231/login",
data: {
loginId: this.loginForm.loginId,
password: this.loginForm.password
}
}).then(function(value) {
//value.data包含code(int),msg(String)data(Object)三部分
if (value.data.code === 1) {
console.info(value.data.data)
//设置token在sessionStrorage中
window.sessionStorage.setItem('token',value.data.data)
vm.$message({
//因为后端返回来的数据,再用this.$message,this已经不是原来的this了,可以在Login(),定义一个this
message: "登录成功!",
type: "success"
});
vm.$router.replace("/UserView");
} else {
vm.$message({
message: value.data.msg,
type: "error"
});
}
});
},
前端接收到后端转发来的token,保存在浏览器中(跟session差不多),但是比session安全
(二)全局守卫
//挂载全局路由导航守卫,【注意】,要在const router后面写,在export default router上面
// to是我们跳转的路径,from是来自的路径,next为放行的函数
router.beforeEach((to,from,next) => {
console.info(to.path)
//登录页面,直接放行
if(to.path === '/LoginView'|| to.path === '/AdmiLoginView' || to.path === '/RegisterView') // 放行用户登录,管理员登录,用户注册三个页面
{
return next();
}
//从sessionStrorage中获取token值
const token = window.sessionStorage.getItem("token");
//没有token返回登录界面
if(!token)
{
alert("访问权限不够,请先进行登录操作!")
return next("/LoginView");
}else{
//有token,放行
next();
}
})