基本流程:
首先获得当前浏览器访问服务器的session,然后根据用户的信息(如id等)在redis中查找,如果找到,并且和查找对应的session不同,则可以判断已经有其他设备登录过了,这个时候就可以把redis中对应用户的session替换为当前的session,这个时候就代表其他设备的用户被强制下线了,当前设备成功登录。
下面是核心代码实现:
@Controller
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/")public String index(HttpServletRequest request){String sessionId= request.getSession().getId();User user;// 找到了sessionId,并且在对象里存储着,说明是同一设备if ((user=userService.isContainedInRedis(sessionId))!=null) {request.setAttribute("user",user);return "index.html";}return "login.html";}// 这里直接使用postman进行输入了@PostMapping("/login")public String login(@RequestParam("username") String username,HttpServletRequest request){User user=userService.isSameDeviceAndDownload(username, request.getSession().getId());request.setAttribute("user",user);return "index.html";}// 这里直接使用postman进行输入了@PostMapping("/register")public void register(@RequestParam("username") String username,@RequestParam("name") String name,HttpServletRequest request) throws JsonProcessingException {User user = new User(username,name, request.getSession().getId());userService.store(user);}
}
@Service
public class UserService {@Autowiredprivate RedisTemplate redisTemplate;// 判断sessionId是否存在于redis中,如果存在,说明登录过了,用户可以直接登录public User isContainedInRedis(String sessionId) {ObjectMapper objectMapper = new ObjectMapper();Map<Object,Object> users = redisTemplate.opsForHash().entries("user");System.out.println(users.size());for (Map.Entry<Object, Object> entry : users.entrySet()) {try {User user = objectMapper.readValue((String) entry.getValue(), User.class);if(user.getSessionId().equals(sessionId)){return user;}} catch (IOException e) {e.printStackTrace();}}return null;}// 存储user对象public void store(User user) throws JsonProcessingException {ObjectMapper objectMapper = new ObjectMapper();System.out.println(user);redisTemplate.opsForHash().put("user",user.getUsername(),objectMapper.writeValueAsString(user));}// 判断是否是同一设备,否则将其下线public User isSameDeviceAndDownload(String username,String sessionId) {System.out.println("username:"+username);Object user = redisTemplate.opsForHash().get("user", username);ObjectMapper objectMapper = new ObjectMapper();try {User user1 = objectMapper.readValue((String) user, User.class);//说明不是同一设备if(!user1.getSessionId().equals(sessionId)){user1.setSessionId(sessionId);redisTemplate.opsForHash().put("user",username,objectMapper.writeValueAsString(user1));}return user1;} catch (IOException e) {e.printStackTrace();}return null;}
}
如果当我们首次访问系统时,此时redis中是没有存放我们的sessionId值的
这时就会出现以下情况:
接下来我们先手动注册一个账户:
然后观察redis:
这时,我们用另一个设备进行(这里使用postman可以达到同样的效果)登录:
可以看到成功登录。
此时的sessionId也被改变了,另一个用户将会被强制下线
灵感来源:【实践】使用session实现单用户多端登录限制-腾讯云开发者社区-腾讯云
项目代码地址:https://github.com/hanxuyyds/SingleUserMultiLoginRestrictionApplication