springBoot+redis+拦截器简单实现互斥登录


一、互斥登录?

互斥登录,从字面上理解就是登录存在交叉影响,一个账号登录后,进行二次登录,在实际生活中,很多网站都做了多点登录互斥的操作,简单来说就是同一个账号,只能在一台电脑上登录,如果有人在其他地方登录,那么原来登录的地方就会自动下线,再进行操作就会弹出登录界面,需要登录的软件不管是手机端还是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了

在这里插入图片描述

测试成功

总结

此次只是单片机的一个程序,实际互斥登录多运用于多线程程序里,本文分享仅仅只是纯分享解决的一个思路,有问题还请大佬指出。

Spring Boot结合Redis实现登录功能通常涉及以下几个步骤: 1. **添加依赖**: 在`pom.xml`中添加Spring Data RedisSpring Security的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. **配置Redis**: 在`application.properties`中设置Redis连接信息: ``` spring.redis.host=localhost spring.redis.port=6379 ``` 3. **配置Security**: 创建`SecurityConfig`类,重写WebSecurityConfigurerAdapter,配置Redis作为用户认证存储: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Value("${spring.redis.authKey}") private String authKey; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .defaultSuccessUrl("/") .failureHandler(new CustomAuthenticationFailureHandler()) .and() .logout() .permitAll(); } @Bean public AuthenticationManager authenticationManager() throws Exception { RedisAuthenticationProvider provider = new RedisAuthenticationProvider(); provider.setConnectionFactory(connectionFactory()); provider.setUserDetailsMapper(userDetailsService); return provider; } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> template = new RedisTemplate<>(); JedisConnectionFactory factory = new JedisConnectionFactory(); factory.setHostName(authKey); //...其他属性配置 template.setConnectionFactory(factory); template.afterPropertiesSet(); return template; } } ``` 4. **用户服务** (`UserDetailsService`实现): 从Redis中读取用户的加密密码并验证: ```java @Service public class UserService implements UserDetailsService { private final JdbcTemplate jdbcTemplate; @Autowired public UserService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //从Redis获取用户信息,比如通过username和hash存储 Map<String, Object> userDetails = jedis.get(username); if (userDetails == null) throw new UsernameNotFoundException("Invalid username"); //解码密码并进一步处理... } } ``` 5. **登录和注销**: 使用Spring Security提供的表单登录功能,用户输入用户名和密码后会自动进行验证。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值