文章目录
一、互斥登录?
互斥登录,从字面上理解就是登录存在交叉影响,一个账号登录后,进行二次登录,在实际生活中,很多网站都做了多点登录互斥的操作,简单来说就是同一个账号,只能在一台电脑上登录,如果有人在其他地方登录,那么原来登录的地方就会自动下线,再进行操作就会弹出登录界面,需要登录的软件不管是手机端还是PC端都是这样,比如企鹅,微信,
二、创建流程与编码
1. 正常创建SpringBoot项目
pom文件依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
前端模板是thymeleaf,缓存数据redis,我们创建项目时需要勾选Thymeleaf&&Redis
2. 项目结构
3.1 pojo(实体类)
@Data
public class User implements Serializable {
private Integer id;
private String password;
//版本
private Integer version;
}
3.2 config(配置类)
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Resource
private RedisTemplate<Object,Object> redisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
Interceptor interceptor=new Interceptor(redisTemplate);
registry.addInterceptor(interceptor)
.excludePathPatterns("/toLogin","/inLogin");
}
@EventListener({ApplicationReadyEvent.class})
void applicationReadyEvent() {
// 这里需要注url:端口号+方法名
String url = "http://localhost:8081/toLogin";
Runtime runtime = Runtime.getRuntime();
try {
runtime.exec("rundll32 url.dll,FileProtocolHandler " + url);
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里需要在配置类中装配好redis再添加到拦截器内,拦截器内需要重写一个无参构造把redis带入
3.3 Interceptor(拦截器)
public class Interceptor implements HandlerInterceptor {
private RedisTemplate<Object,Object>redisTemplate;
public Interceptor(RedisTemplate<Object,Object> redisTemplate) {
this.redisTemplate=redisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Integer id = Integer.parseInt(request.getParameter("id"));
String password = request.getParameter("password");
Integer version = Integer.parseInt(request.getParameter("version"));
User user =(User) redisTemplate.opsForValue().get("user");
//判断是否是二次登录的用户
if (id.equals(user.getId())){
//再判断身份是否过期
if (!version.equals(user.getVersion())){
response.setStatus(444);
System.out.println("拦截,下线...");
return false;
}
}else {
return true;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
3.4 controller(控制层)
@Controller
public class UserController {
@Resource
private RedisTemplate<Object,Object>redisTemplate;
/**
* 进入登录页
* @return
*/
@RequestMapping("/toLogin")
public String login(){
return "login";
}
/**
* 执行登录
* @param id
* @param password
* @param model
* @return
*/
@PostMapping("/inLogin")
public String inLogin(Integer id, String password, Model model){
//由于是单线程项目 所以直接从redis获取用户信息判断
User user=(User) redisTemplate.opsForValue().get("user");
//无此用户,新增
if (user==null){
user.setId(id);
user.setPassword(password);
user.setVersion(1);
} else {
//存在此用户,版本号+1
user.setVersion(user.getVersion()+1);
}
model.addAttribute("user",user);
//存入redis缓存供之后请求拦截使用
redisTemplate.opsForValue().set("user",user);
return "index";
}
/**
* 测试
* @return
*/
@RequestMapping("/text")
public String text(){
return "success";
}
}
3.5 index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<form th:action="@{/text}">
<input type="hidden" name="id" th:value="${user.id}">
<input type="hidden" name="password" th:value="${user.password}">
<input type="hidden" name="version" th:value="${user.version}">
<input type="submit" value="测试">
</form>
</body>
</html>
三、执行流程
打开两个路径都指向登录的页面
两个页面前后登录一样的id与pwd
登录完成后我们看第一个页面测试,也就是第一个用户登录,现在它的账号已被第二个页面登录,省份状态已过期,不能直接执行其他操作,我们直接给出状态码444
点击测试按钮,看dug
id一致,是存在二次登录的用户…下一步
再看版本号不一致肯定直接退出了,拦截器返回false
前端状态码444
那么互斥登录成不成功就看第二个页面是否能测试成功,拦截器是否能返回true
点击测试看dug
版本号一致肯定返回true了
测试成功
总结
此次只是单片机的一个程序,实际互斥登录多运用于多线程程序里,本文分享仅仅只是纯分享解决的一个思路,有问题还请大佬指出。