前言
???????最近,说有可能要上只允许一个地方登录,还要配合信息推送,今天有空,就起个头,把登录超时、登录踢人下线一起做了。信息推送的,后面再说,留好口子就行。
一、背景
这里是spring security,其实这块已经很成熟了,加几个配置就行。
二、使用步骤
1.security配置增加
@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 {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
http.addFilterBefore(filter, CsrfFilter.class);
http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
http
.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()
.anyRequest().authenticated().and()
.formLogin()
.successHandler(linkappRestAuthenticationSuccessHandler)
.failureHandler(linkappRestAuthenticationFailureHandler).and()
.logout().logoutSuccessHandler(linkappRestLogoutSuccessHandler);
http.sessionManagement()
.invalidSessionStrategy(invalidSessionStrategy).maximumSessions(1)
.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
public class MyInvalidSessionStrategy implements InvalidSessionStrategy {
@Override
public void onInvalidSessionDetected(HttpServletRequest httpServletRequest, HttpServletResponse response) throws IOException {
throw new BusinessException("当前登录已失效!请重新登录");
}
}
BusinessException是封装的业务异常类。
3.MySessionInformationExpiredStrategy
@Slf4j
@Component
public class MySessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
@Autowired
private LinkappRestAuthenticationFailureHandler myAuthenticationFailureHandler;
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) {
UserDetails userDetails =
(UserDetails) event.getSessionInformation().getPrincipal();
AuthenticationException exception =
new AuthenticationServiceException(
String.format("[%s]用户在其他地方登录,您已被下线", userDetails.getUsername()));
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
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) {
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!!
|