Session失效时间:
springboot配置session失效时间,只需要在application.properties里配置
#session超时时间,低于60秒按60秒
server.session.timeout = 60
如果想自己定义session失效的提示信息,需要配置:
@Configuration //这是一个配置 public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{ //版本二:可配置的登录页 @Override protected void configure(HttpSecurity http) throws Exception { //~~~-------------> 图片验证码过滤器 <------------------ ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter(); //验证码过滤器中使用自己的错误处理 validateCodeFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler); //配置的验证码过滤url validateCodeFilter.setSecurityProperties(securityProperties); validateCodeFilter.afterPropertiesSet(); //~~~-------------> 短信验证码过滤器 <------------------ SmsCodeFilter smsCodeFilter = new SmsCodeFilter(); //验证码过滤器中使用自己的错误处理 smsCodeFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler); //配置的验证码过滤url smsCodeFilter.setSecurityProperties(securityProperties); smsCodeFilter.afterPropertiesSet(); //实现需要认证的接口跳转表单登录,安全=认证+授权 //http.httpBasic() //这个就是默认的弹框认证 // http .addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class) // .apply(imoocSocialSecurityConfig)//社交登录 // .and() //把验证码过滤器加载登录过滤器前边 .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) //----------表单认证相关配置--------------- .formLogin() .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL) //处理用户认证BrowserSecurityController //登录过滤器UsernamePasswordAuthenticationFilter默认登录的url是"/login",在这能改 .loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM) .successHandler(imoocAuthenticationSuccessHandler)//自定义的认证后处理器 .failureHandler(imoocAuthenticationFailureHandler) //登录失败后的处理 .and() //------------记住我相关配置 ------------- .rememberMe() .tokenRepository(persistentTokenRepository())//TokenRepository,登录成功后往数据库存token的 .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())//记住我秒数 .userDetailsService(userDetailsService) //记住我成功后,调用userDetailsService查询用户信息 .and()//-----------session相关配置--------------- .sessionManagement() // .invalidSessionStrategy(invalidSessionStrategy) // .maximumSessions(securityProperties.getBrowser().getSession().getMaximumSessions()) .invalidSessionUrl(SecurityConstants.SESSION_INVALID_PAGE) //session失效跳转地址,如果简单的处理只要这一个就够了 // .maximumSessions(1) //一个用户只能登录一次,踢出前边登录用户 // .maxSessionsPreventsLogin(securityProperties.getBrowser().getSession().isMaxSessionsPreventsLogin())//阻止在登录 // .expiredSessionStrategy(sessionInformationExpiredStrategy) //session失效策略 .and() //?俩and为啥呢 // .and() //-----------授权相关的配置 --------------------- .authorizeRequests() // /authentication/require:处理登录,securityProperties.getBrowser().getLoginPage():用户配置的登录页 .antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL, securityProperties.getBrowser().getLoginPage(),//放过登录页不过滤,否则报错 SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, SecurityConstants.SESSION_INVALID_PAGE, SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*").permitAll() //验证码 .anyRequest() //任何请求 .authenticated() //都需要身份认证 .and() .csrf().disable() //关闭csrf防护 .apply(smsCodeAuthenticationSecurityConfig);//把短信验证码配置应用上 } }
public static final String SESSION_INVALID_PAGE = "/session/invalid";
@GetMapping("/session/invalid") @ResponseStatus(code = HttpStatus.UNAUTHORIZED) public SimpleResponse toSessionInvalidPage(){ String message = "session 失效!"; return new SimpleResponse(message); }
一个账户同时默认是可以在多处登录的,如果想要后边的登录踢出前边的登录,只要放开 .maximumSessions(1) 这句配置即可。
session并发控制:
也可以自定义session失效策略,自定义一个类ImoocExpiredSessionStrategy2:
package com.imooc.security.browser.session; import java.io.IOException; import javax.servlet.ServletException; import org.springframework.security.web.session.SessionInformationExpiredEvent; import org.springframework.security.web.session.SessionInformationExpiredStrategy; /** * session失效策略,简单版本 * ClassName: ImoocExpiredSessionStrategy * @Description: TODO * @author lihaoyang * @date 2018年3月8日 */ public class ImoocExpiredSessionStrategy2 implements SessionInformationExpiredStrategy{ /** * SessionInformationExpiredEvent:session失效事件,能拿到request、response */ @Override public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { //可以从event中拿到request中的信息 event.getResponse().setContentType("application/json;charset=UTF-8"); event.getResponse().getWriter().write("并发登录!"); } }
加上配置:
.sessionManagement() // .invalidSessionStrategy(invalidSessionStrategy) // .maximumSessions(securityProperties.getBrowser().getSession().getMaximumSessions()) .invalidSessionUrl(SecurityConstants.SESSION_INVALID_PAGE) //session失效跳转地址,如果简单的处理只要这一个就够了 .maximumSessions(1) //一个用户只能登录一次,踢出前边登录用户 .expiredSessionStrategy(new ImoocExpiredSessionStrategy2()) //简洁版session失效策略
此时如果第二次登录,第一个登录就会提示:
如果想阻止多处登录,可以加上这句配置:
.sessionManagement() // .invalidSessionStrategy(invalidSessionStrategy) // .maximumSessions(securityProperties.getBrowser().getSession().getMaximumSessions()) .invalidSessionUrl(SecurityConstants.SESSION_INVALID_PAGE) //session失效跳转地址,如果简单的处理只要这一个就够了 .maximumSessions(1) //一个用户只能登录一次,踢出前边登录用户 .expiredSessionStrategy(new ImoocExpiredSessionStrategy2()) //简洁版session失效策略 .maxSessionsPreventsLogin(true) //阻止并发登录
此时如果多出登录,后者就会提示
重构:将session失效策略自己实现
/** * */ package com.imooc.security.browser.session; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.web.session.InvalidSessionStrategy; /** * @author zhailiang * */ public class ImoocInvalidSessionStrategy extends AbstractSessionStrategy implements InvalidSessionStrategy { public ImoocInvalidSessionStrategy(String invalidSessionUrl) { super(invalidSessionUrl); } @Override public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { onSessionInvalid(request, response); } }
session过期策略:
package com.imooc.security.browser.session; import java.io.IOException; import javax.servlet.ServletException; import org.springframework.security.web.session.SessionInformationExpiredEvent; import org.springframework.security.web.session.SessionInformationExpiredStrategy; /** * session失效策略 * ClassName: ImoocExpiredSessionStrategy * @Description: TODO * @author lihaoyang * @date 2018年3月8日 */ public class ImoocExpiredSessionStrategy extends AbstractSessionStrategy implements SessionInformationExpiredStrategy { /** * SessionInformationExpiredEvent:session失效事件,能拿到request、response */ // @Override // public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { // event.getResponse().setContentType("application/json;charset=UTF-8"); // event.getResponse().getWriter().write("并发登录!"); // } public ImoocExpiredSessionStrategy(String invalidSessionUrl) { super(invalidSessionUrl); } /* (non-Javadoc) * @see org.springframework.security.web.session.SessionInformationExpiredStrategy#onExpiredSessionDetected(org.springframework.security.web.session.SessionInformationExpiredEvent) */ @Override public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { onSessionInvalid(event.getRequest(), event.getResponse()); } /* (non-Javadoc) * @see com.imooc.security.browser.session.AbstractSessionStrategy#isConcurrency() */ @Override protected boolean isConcurrency() { return true; } }
AbstractSessionStrategy:
/** * */ package com.imooc.security.browser.session; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.util.UrlUtils; import org.springframework.util.Assert; import com.fasterxml.jackson.databind.ObjectMapper; import com.imooc.security.browser.support.SimpleResponse; /** * @author zhailiang * */ public class AbstractSessionStrategy { private final Logger logger = LoggerFactory.getLogger(getClass()); /** * 跳转的url */ private String destinationUrl; /** * 重定向策略 */ private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); /** * 跳转前是否创建新的session */ private boolean createNewSession = true; private ObjectMapper objectMapper = new ObjectMapper(); /** * @param invalidSessionUrl * @param invalidSessionHtmlUrl */ public AbstractSessionStrategy(String invalidSessionUrl) { Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), "url must start with '/' or with 'http(s)'"); this.destinationUrl = invalidSessionUrl; } /* * (non-Javadoc) * * @see org.springframework.security.web.session.InvalidSessionStrategy# * onInvalidSessionDetected(javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse) */ protected void onSessionInvalid(HttpServletRequest request, HttpServletResponse response) throws IOException { if (createNewSession) { request.getSession(); } String sourceUrl = request.getRequestURI(); String targetUrl; if (StringUtils.endsWithIgnoreCase(sourceUrl, ".html")) { targetUrl = destinationUrl;//+".html"; logger.info("session失效,跳转到"+targetUrl); redirectStrategy.sendRedirect(request, response, targetUrl); }else{ String message = "session已失效"; if(isConcurrency()){ message = message + ",有可能是并发登录导致的"; } response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(message))); } } /** * session失效是否是并发导致的 * @return */ protected boolean isConcurrency() { return false; } /** * Determines whether a new session should be created before redirecting (to * avoid possible looping issues where the same session ID is sent with the * redirected request). Alternatively, ensure that the configured URL does * not pass through the {@code SessionManagementFilter}. * * @param createNewSession * defaults to {@code true}. */ public void setCreateNewSession(boolean createNewSession) { this.createNewSession = createNewSession; } }
配置:
.sessionManagement() //++++++=基本这样配置++++++++ // .invalidSessionUrl(SecurityConstants.SESSION_INVALID_PAGE) //session失效跳转地址,如果简单的处理只要这一个就够了 // .maximumSessions(1) //一个用户只能登录一次,踢出前边登录用户 // .expiredSessionStrategy(new ImoocExpiredSessionStrategy2()) //简洁版session失效策略 // .maxSessionsPreventsLogin(true) //阻止并发登录 // //++++++++重构后+++++++ .invalidSessionStrategy(invalidSessionStrategy) .maximumSessions(securityProperties.getBrowser().getSession().getMaximumSessions()) .maxSessionsPreventsLogin(securityProperties.getBrowser().getSession().isMaxSessionsPreventsLogin())//阻止在登录 .expiredSessionStrategy(sessionInformationExpiredStrategy) //session失效策略
而且变量都做成了可配置的,具体的代码放在了github: