前言
最近,说有可能要上只允许一个地方登录,还要配合信息推送,今天有空,就起个头,把登录超时、登录踢人下线一起做了。信息推送的,后面再说,留好口子就行。
一、背景
这里是spring security,其实这块已经很成熟了,加几个配置就行。
二、使用步骤
1.security配置增加
/*** 安全认证配置** @author zhengwen*/
@Configuration
@EnableWebSecurity
@Order(1)
public class LinkappSecurityConfiguration extends WebSecurityConfigurerAdapter {@Resourceprivate SessionInformationExpiredStrategy sessionInformationExpiredStrategy;@Resourceprivate InvalidSessionStrategy invalidSessionStrategy;@Overrideprotected 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();}
}
http.sessionManagement()开始就是本次分享内容。注意下面的2个bean,@Bean注解的。
2.MyInvalidSessionStrategy
/*** @author zhengwen*/
public class MyInvalidSessionStrategy implements InvalidSessionStrategy {@Overridepublic 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("当前登录已失效!请重新登录");}
}
BusinessException是封装的业务异常类。
3.MySessionInformationExpiredStrategy
/*** @author zhengwen*/
@Slf4j
@Component
public class MySessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {@Autowiredprivate LinkappRestAuthenticationFailureHandler myAuthenticationFailureHandler;@Overridepublic 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清除异常");}}
}
4.MyAuthenticationFailureHandler
/*** 认证失败处理者** @author zhengwen*/
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {@Overridepublic 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.先在postman登录成功。
2.再请求一个业务接口
3.然后在另外一个地方登录同一个账号,这里用FE的简易postman工具模拟
4.最后再重新请求下那个业务接口
多的废话以不想说了额,这一组图看的真真的。
总结
- spring security用户登录session登录管理真强大,虽然说很重,但是用起来确实方便。
- 现在spring security不像以前了,它也与时俱进,配置早springBoot里做的很友好了
- 你们猜.maximumSessions(2)会先踢哪个?
- 如果要加信息推送知道在那里加了吧?认真看TODO
好了,就到这吧,UPing!!