• 登录超时提示+踢人下线实现(spring security)


    前言

           最近,说有可能要上只允许一个地方登录,还要配合信息推送,今天有空,就起个头,把登录超时、登录踢人下线一起做了。信息推送的,后面再说,留好口子就行。


    一、背景

    这里是spring security,其实这块已经很成熟了,加几个配置就行。

    二、使用步骤

    1.security配置增加

    
    /**
     * 安全认证配置
     *
     * @author zhengwen
     */
    @Configuration
    @EnableWebSecurity
    @Order(1)
    public class LinkappSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
      @Resource
      private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;
    
      @Resource
      private InvalidSessionStrategy invalidSessionStrategy;
     
    
      @Override
      protected void configure(HttpSecurity http) throws Exception {
    
    //		解决 spring security 对于开放接口返回乱码的解决
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        filter.setForceEncoding(true);
        http.addFilterBefore(filter, CsrfFilter.class);
        http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        http
            // 禁止匿名用户
            // .anonymous().disable()
            // 禁止csrfz
    
            .csrf().disable()
            // 认证失败处理
            .exceptionHandling()
            .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
            .and()
            // 白名单
            .authorizeRequests()
            .antMatchers("/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/v2/api-docs/**",           "/enterpriseEditionBi/**","/oss/download/**","/download/**","/config/getEduUrl")
            .permitAll()
            // 接口调试阶段 目前不校验接口 modify by tongjie
            .anyRequest().authenticated().and()
            // 表单登录配置
            .formLogin()
            // 登录成功处理
            .successHandler(linkappRestAuthenticationSuccessHandler)
            // 登录失败处理
            .failureHandler(linkappRestAuthenticationFailureHandler).and()
            // 登出成功处理
            .logout().logoutSuccessHandler(linkappRestLogoutSuccessHandler);
          //一个账号只允许一个地方登录,
          http.sessionManagement()
                  //session失效,调用此方法
                  .invalidSessionStrategy(invalidSessionStrategy).maximumSessions(1)
                  // 当用户达到最大session数后,则调用此处的实现
                  .expiredSessionStrategy(sessionInformationExpiredStrategy);
      }
    
        @Bean
        @ConditionalOnMissingBean(InvalidSessionStrategy.class)
        public InvalidSessionStrategy invalidSessionStrategy() {
            return new MyInvalidSessionStrategy();
        }
        @Bean
        @ConditionalOnMissingBean(SessionInformationExpiredStrategy.class)
        public SessionInformationExpiredStrategy informationExpiredStrategy() {
            return new MySessionInformationExpiredStrategy();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    http.sessionManagement()开始就是本次分享内容。注意下面的2个bean,@Bean注解的。

    2.MyInvalidSessionStrategy

    
    /**
     * @author zhengwen
     */
    public class MyInvalidSessionStrategy implements InvalidSessionStrategy {
        @Override
        public void onInvalidSessionDetected(HttpServletRequest httpServletRequest, HttpServletResponse response) throws IOException {
            /* 接口请求没有页面,给统一的友好提示,可以直接利用封装的的业务异常
            response.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write("当前登录已失效!请重新登录");
            */
            throw new BusinessException("当前登录已失效!请重新登录");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    BusinessException是封装的业务异常类

    3.MySessionInformationExpiredStrategy

    
    /**
     * @author zhengwen
     */
    @Slf4j
    @Component
    public class MySessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
    
        @Autowired
        private LinkappRestAuthenticationFailureHandler myAuthenticationFailureHandler;
    
        @Override
        public void onExpiredSessionDetected(SessionInformationExpiredEvent event) {
            // 1. 获取用户名
            UserDetails userDetails =
                    (UserDetails) event.getSessionInformation().getPrincipal();
    
            AuthenticationException exception =
                    new AuthenticationServiceException(
                            String.format("[%s]用户在其他地方登录,您已被下线", userDetails.getUsername()));
            //TODO 这里可以增加根据用户名找到手机号信息,进行推送信息,或者在myAuthenticationFailureHandler里去处理
            try {
                // 当用户在另外终端登录后,交给失败处理器回到认证页面
                event.getRequest().setAttribute("toAuthentication", true);
                myAuthenticationFailureHandler
                        .onAuthenticationFailure(event.getRequest(), event.getResponse(), exception);
            } catch (Exception e) {
                log.error("--登录失效session清除异常,原因:{}", e.getMessage());
                throw new BusinessException("超时登录session清除异常");
            }
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    4.MyAuthenticationFailureHandler

    
    
    /**
     * 认证失败处理者
     *
     * @author zhengwen
     */
    @Component
    public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
     
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) {
    
            //TODO 自己的异常处理逻辑
    
            RestMessage message = RestBuilders.failureBuilder().code("login.failure")
                .message(exception.getMessage()).build();
    
            Responses.standard(response).respond(message);
    
        }
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    三、看效果

    1.先在postman登录成功。

    在这里插入图片描述

    2.再请求一个业务接口

    在这里插入图片描述

    3.然后在另外一个地方登录同一个账号,这里用FE的简易postman工具模拟

    在这里插入图片描述

    4.最后再重新请求下那个业务接口

    在这里插入图片描述
    多的废话以不想说了额,这一组图看的真真的。


    总结

    • spring security用户登录session登录管理真强大,虽然说很重,但是用起来确实方便。
    • 现在spring security不像以前了,它也与时俱进,配置早springBoot里做的很友好了
    • 你们猜.maximumSessions(2)会先踢哪个?
    • 如果要加信息推送知道在那里加了吧?认真看TODO
      好了,就到这吧,UPing!!
  • 相关阅读:
    k8s~istio的安装与核心组件
    ElasticSearch之Kibana安装及使用
    浅析ES6六种声明变量的方法
    Android系统启动流程
    使用frp+nginx内网穿透并配置https
    算法复杂度分析笔记
    【自动化营销】跨境电商高效进行WhatsApp营销技巧!
    深度学习中的偏差、方差、正则化
    ESP8266-Arduino编程实例-RPI-1031倾斜传感器驱动
    在Linux中使用shell指令完成文件打包、压缩、解压缩
  • 原文地址:https://blog.csdn.net/zwrlj527/article/details/127655337