Skip to content

Commit

Permalink
完成限制用户多设备登录
Browse files Browse the repository at this point in the history
  • Loading branch information
LiuYuYang01 committed Jan 26, 2025
1 parent 6c9ffc5 commit 1ec32c1
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,40 +1,37 @@
package liuyuyang.net.interceptor;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.jsonwebtoken.Claims;
import liuyuyang.net.annotation.NoTokenRequired;
import liuyuyang.net.execption.CustomException;
import liuyuyang.net.mapper.UserTokenMapper;
import liuyuyang.net.model.UserToken;
import liuyuyang.net.properties.JwtProperties;
import liuyuyang.net.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.List;

/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {

@Autowired
@Resource
private JwtProperties jwtProperties;
@Resource
private UserTokenMapper userTokenMapper;

/**
* 校验jwt
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
// 从请求头中获取令牌
String token = request.getHeader(jwtProperties.getTokenName());

Expand All @@ -61,23 +58,30 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
if (token != null) {
if (token.startsWith("Bearer ")) token = token.substring(7);
JwtUtils.parseJWT(jwtProperties.getSecretKey(), token);
return true;
} else {
return true;
}
return true;
}

// 处理Authorization的Bearer
if (token.startsWith("Bearer ")) token = token.substring(7);
Claims claims = JwtUtils.parseJWT(jwtProperties.getSecretKey(), token);

// 放行
return true;
LambdaQueryWrapper<UserToken> userTokenLambdaQueryWrapper = new LambdaQueryWrapper<>();
userTokenLambdaQueryWrapper.eq(UserToken::getToken, token);
List<UserToken> userTokens = userTokenMapper.selectList(userTokenLambdaQueryWrapper);

// 如果跟之前的token相匹配则进一步判断token是否有效
if (userTokens != null && !userTokens.isEmpty()) {
Claims claims = JwtUtils.parseJWT(jwtProperties.getSecretKey(), token);
return true;
} else {
throw new CustomException(401, "该账号已在另一台设备登录");
}
} catch (Exception ex) {
System.out.println("校验失败:" + ex);
// 校验失败,响应401状态码
response.setStatus(401);
throw new CustomException(401, "身份验证失败:无效或过期的token");
String message = ex.getMessage() != null ? ex.getMessage() : "无效或过期的token";
throw new CustomException(401, message);
}
}
}
10 changes: 10 additions & 0 deletions blog/src/main/java/liuyuyang/net/mapper/UserTokenMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package liuyuyang.net.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import liuyuyang.net.model.UserToken;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserTokenMapper extends BaseMapper<UserToken> {

}
41 changes: 24 additions & 17 deletions blog/src/main/java/liuyuyang/net/service/impl/UserServiceImpl.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package liuyuyang.net.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
Expand All @@ -10,6 +11,7 @@
import liuyuyang.net.execption.CustomException;
import liuyuyang.net.mapper.RoleMapper;
import liuyuyang.net.mapper.UserMapper;
import liuyuyang.net.mapper.UserTokenMapper;
import liuyuyang.net.model.*;
import liuyuyang.net.properties.JwtProperties;
import liuyuyang.net.service.UserService;
Expand Down Expand Up @@ -43,6 +45,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
private UserMapper userMapper;
@Resource
private RoleMapper roleMapper;
@Resource
private UserTokenMapper userTokenMapper;

@Override
public void add(UserDTO user) {
Expand Down Expand Up @@ -122,28 +126,32 @@ public Page<User> paging(UserFilterVo filterVo, PageVo pageVo) {
}

@Override
public Map<String, Object> login(UserLoginDTO user) {
public Map<String, Object> login(UserLoginDTO userDTO) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", user.getUsername());
queryWrapper.eq("password", DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));

User data = userMapper.selectOne(queryWrapper);

if (data == null) throw new CustomException(400, "用户名或密码错误");

data.setPassword("只有聪明的人才能看到密码");
queryWrapper.eq("username", userDTO.getUsername());
queryWrapper.eq("password", DigestUtils.md5DigestAsHex(userDTO.getPassword().getBytes()));

Role role = roleMapper.selectById(data.getRoleId());
User user = userMapper.selectOne(queryWrapper);
if (user == null) throw new CustomException(400, "用户名或密码错误");
user.setPassword("只有聪明的人才能看到密码");

Map<String, Object> claims = new HashMap<>();
claims.put("user", data);
claims.put("role", role);
String token = JwtUtils.createJWT(jwtProperties.getSecretKey(), jwtProperties.getTtl(), claims);
Role role = roleMapper.selectById(user.getRoleId());

Map<String, Object> result = new HashMap<>();
result.put("token", token);
result.put("user", data);
result.put("user", user);
result.put("role", role);
String token = JwtUtils.createJWT(jwtProperties.getSecretKey(), jwtProperties.getTtl(), result);
result.put("token", token);

// 先删除用户的token
LambdaQueryWrapper<UserToken> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
userLambdaQueryWrapper.eq(UserToken::getUid,user.getId());
userTokenMapper.delete(userLambdaQueryWrapper);
// 再存储用户的token
UserToken userToken = new UserToken();
userToken.setUid(user.getId());
userToken.setToken(token);
userTokenMapper.insert(userToken);

return result;
}
Expand All @@ -166,7 +174,6 @@ public void editPass(EditPassDTO data) {

@Override
public void check(String token) {
// boolean isCheck = yuYangUtils.isAdmin(token);
boolean isCheck = yuYangUtils.check(token);

if (!isCheck) {
Expand Down
21 changes: 21 additions & 0 deletions model/src/main/java/liuyuyang/net/model/UserToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package liuyuyang.net.model;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@TableName("user_token")
public class UserToken {
@TableId(type = IdType.AUTO)
@ApiModelProperty(value = "ID")
private Integer id;

@ApiModelProperty(value = "用户ID", example = "1", required = true)
private Integer uid;

@ApiModelProperty(value = "用户token", example = "dadaffasfefewfwf.wefwfwfwwzfwe.zfwfwefZFfw", required = true)
private String token;
}

0 comments on commit 1ec32c1

Please sign in to comment.