• Spring Security构建Rest服务-1100-单机Session管理


    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:

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

  • 相关阅读:
    C#中一行代码实现18位数字时间戳转换为DateTime
    Java,Python,前端,Linux,公众号等5T编程资源整理免费下载
    Winform中使用FastReport的DesignReport时怎样给通过代码Table添加数据
    一、渐变边框
    一、Dev单元格
    一、Dev
    一、
    三、数据-1
    三、接口数据格式-2
    二、GitLab使用
  • 原文地址:https://www.cnblogs.com/lihaoyang/p/8529561.html
Copyright © 2020-2023  润新知