基本流程:
首先获得当前浏览器访问服务器的session,然后根据用户的信息(如id等)在redis中查找,如果找到,并且和查找对应的session不同,则可以判断已经有其他设备登录过了,这个时候就可以把redis中对应用户的session替换为当前的session,这个时候就代表其他设备的用户被强制下线了,当前设备成功登录。
下面是核心代码实现:
@Controller
public class UserController {
@Autowired
private 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 {
@Autowired
private 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