Spring Security登出Session失效

一、配置Session和自定义登出

package com.sf.browser;

import com.sf.config.MySessionExpiredStrategy;
import com.sf.filter.ValidateCodeFilter;
import com.sf.handler.MyAuthenticationFailureHandler;
import com.sf.handler.MyAuthenticationSucessHandler;
import com.sf.handler.MyLogOutSuccessHandler;
import com.sf.service.impl.UserDetailService;
import com.sf.smsvalidate.SmsAuthenticationConfig;
import com.sf.smsvalidate.SmsCodeFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

@Configuration
@EnableWebSecurity
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationSucessHandler authenticationSucessHandler;

    @Autowired
    private MyAuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private ValidateCodeFilter validateCodeFilter;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailService userDetailService;


    @Autowired
    private SmsCodeFilter smsCodeFilter;

    @Autowired
    private SmsAuthenticationConfig smsAuthenticationConfig;

    @Autowired
    private MySessionExpiredStrategy mySessionExpiredStrategy;

    @Autowired
    private MyLogOutSuccessHandler logOutSuccessHandler;


    @Autowired
    SessionRegistry sessionRegistry;

    /**
     * .loginPage("/login.html")指定了跳转到登录页面的请求URL,
     * .loginProcessingUrl("/login")对应登录页面form表单的action="/login",
     * .antMatchers("/login.html").permitAll()表示跳转到登录页面的请求不被拦截
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器
                .addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器
                .formLogin() // 表单登录
                // http.httpBasic() // HTTP Basic
                .loginPage("/authentication/require") // 登录跳转 URL, 进行验证或者登陆页 跳转
                .loginProcessingUrl("/login") // 处理表单登录 URL
                .successHandler(authenticationSucessHandler) // 处理登录成功
                .failureHandler(authenticationFailureHandler) // 处理登录失败
                .and()
                .rememberMe()
                .tokenRepository(persistentTokenRepository()) // 配置 token 持久化仓库
                .tokenValiditySeconds(3600) // remember 过期时间,单为秒
                .userDetailsService(userDetailService) // 处理自动登录逻辑
                .and()
                .authorizeRequests() // 授权配置
                .antMatchers("/authentication/require", "/login.html", "/code/image",
                        "/code/sms", "/session/invalid", "/css/*", "/js/*", "/login1.html", "/statics/**", "/signout/success").permitAll() // 登录跳转 URL 无需认证 (含登录页的静态资源)
                .anyRequest()  // 所有请求
                .authenticated() // 都需要认证
                .and()
                .sessionManagement() // 添加 Session管理器
                .invalidSessionUrl("/session/invalid") // Session失效后跳转到这个链接
                .maximumSessions(1) //最大session并发数量,超过定义数量前一个session就会失效
                .maxSessionsPreventsLogin(true) //Session达到最大有效数的时候,不再允许相同的账户登录。
                .expiredSessionStrategy(mySessionExpiredStrategy)//配置了Session在并发下失效后的处理策略;
                .sessionRegistry(sessionRegistry)//添加 session 注册
                .and()
                .and()
                .logout()//自定义退出登录
                .logoutUrl("/signout")//退出登录的URL
                .invalidateHttpSession(true)
                //.logoutSuccessUrl("/signout/success")//退出成功后跳转的URL
                .deleteCookies("JSESSIONID")//退出成功后删除名称为JSESSIONID的cookie, 删除不成功???
                .logoutSuccessHandler(logOutSuccessHandler) //logoutSuccessHandler指定退出成功处理器来处理退出成功后的逻辑:
                .and()
                .csrf().disable()// 关闭 CSRF攻击防御关了
                .apply(smsAuthenticationConfig);// 将短信验证码认证配置加到 Spring Security 中
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置token持久化对象
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();//默认表
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(false);
        return jdbcTokenRepository;
    }
    
    
    /**
     * 用户登录以后security 会把生成的session 放到 SessionRegistry 里面。那么我门想让session 失效只用找到对应的session,然后将它从SessionRegistry 中剔除出去就好了。
     * 但是查看了源码发现SessionRegistry 是 final 。。。。。这个可怎么办呢?
     * 自己new 一个对象吧,所以我就new SessionRegistry(),并用我自己的SessionRegistry管理session 咯
     */
    @Bean
    public SessionRegistry getSessionRegistry(){
        SessionRegistry sessionRegistry=new SessionRegistryImpl();
        return sessionRegistry;
    }
}

二、自定义登出SuccessHandler,在里面出来登录用户的session,不然虽然登出但是session还有效,下次登录会累加session次数,超出上面配置的maximumSessions,会提示 "Maximum sessions of {n} for this principal exceeded"

package com.sf.handler;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;


@Component
public class MyLogOutSuccessHandler implements LogoutSuccessHandler {

	@Autowired
	private SessionRegistry sessionRegistry;

	@Override
	public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
		System.out.println(authentication);
		System.out.println(authentication.getName());
		List<Object> o= sessionRegistry.getAllPrincipals();
		//退出成功后删除当前用户session
		for (Object principal : o) {
			if (principal instanceof User) {
				final User loggedUser = (User) principal;
				if (authentication.getName().equals(loggedUser.getUsername())) {
					List<SessionInformation> sessionsInfo = sessionRegistry.getAllSessions(principal, false);
					if (null != sessionsInfo && sessionsInfo.size() > 0) {
						for (SessionInformation sessionInformation : sessionsInfo) {
							sessionInformation.expireNow();
						}
					}
				}
			}
		}

		httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
		httpServletResponse.setContentType("application/json;charset=utf-8");
		httpServletResponse.getWriter().write("退出成功,请重新登录");
	}
}

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值