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:

https://github.com/lhy1234/spring-security