前言
上一章节我们新增了用户登录的接口,因为JWT是无状态的,对应的用户登出接口需要我们增加对应的业务逻辑去控制,这里我们使用Map简单存储用户登录信息。
一、后端代码
这里我们使用
loginUsers
属性来保存用户登录信息,拦截器验证部分稍有改动。
1. LoginInceptor
package org.example.springboot3.bigevent.inceptors;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.springboot3.bigevent.entity.Result;
import org.example.springboot3.bigevent.login.LoginStorage;
import org.example.springboot3.bigevent.utils.JwtUtils;
import org.example.springboot3.bigevent.utils.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Map;
/**
* Create by zjg on 2024/5/26
*/
@Component
public class LoginInceptor implements HandlerInterceptor {
@Autowired
LoginStorage loginStorage;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if(token!=null&&token.contains("Bearer")){
String tokenStr = token.substring(token.indexOf("Bearer") + 7);
boolean verify = JwtUtils.verify(tokenStr);
if(verify){//此处解析loginUsers,验证用户已登录
Map<String, Object> claims = JwtUtils.getClaims(tokenStr);
if(tokenStr.equals(loginStorage.get(claims.get("userId").toString()))){
ThreadLocalUtil.set(claims);//用户信息放置ThreadLocal
return true;
};
}
}
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=UTF-8");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writerFor(Result.class);
String message = objectMapper.writeValueAsString(Result.error("token验证失败,请重新获取token后重试!"));
response.getWriter().println(message);
return false;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
ThreadLocalUtil.remove();
}
}
2. UserController1
登出接口需要携带token信息,因为只有登录了才能登出且必须和登录时保存的token信息一致,才能成功。
@Autowired
LoginStorage loginStorage;
/**
* 登出
* @param loginUser 登录用户信息
* @param token token
* @return Result
*/
@RequestMapping("logout")
public Result logout(@Valid User loginUser,@RequestHeader("Authorization") String token){
String message="用户名/密码不正确";
User user = userSerivce.findUserByName(loginUser.getUsername());
if(user!=null){//用户存在
if(token!=null&&token.contains("Bearer")){
String tokenStr = token.substring(token.indexOf("Bearer") + 7);
boolean verify = JwtUtils.verify(tokenStr);
if(verify&&tokenStr.equals(loginStorage.get(user.getId().toString()))){
loginStorage.remove(user.getId().toString());
return Result.success("登出成功");
}
}
}
return Result.error(message);
}
二、测试
1.登录
2.查询
3.登出
4.再次查询
总结
这种方式适合单机部署,集群部署会导致登录信息无法共享,可将Map值存储到redis中。