SpringBoot整合SpringSecurity,SESSION 并发管理,同账号只允许登录一次

摘要:
privatebooleansentingChainBeforeSuccessfulAuthentication=false;privateAuthenticationFailureHandlerfailureHandler=newSimpleUrlAuthenticationFailtureHandler();p>

重写了UsernamePasswordAuthenticationFilter,里面继承AbstractAuthenticationProcessingFilter,这个类里面的session认证策略,是一个空方法,貌似RememberMe也是.

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {
    protected ApplicationEventPublisher eventPublisher;
    protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
    private AuthenticationManager authenticationManager;
    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private RememberMeServices rememberMeServices = new NullRememberMeServices();
    private RequestMatcher requiresAuthenticationRequestMatcher;
    private boolean continueChainBeforeSuccessfulAuthentication = false;
    private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
    private boolean allowSessionCreation = true;
    private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
    private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
NullAuthenticatedSessionStrategy 源码
public final class NullAuthenticatedSessionStrategy implements SessionAuthenticationStrategy {
    public NullAuthenticatedSessionStrategy() {
    }

    public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
    }
}

所以必须自己配置一个session验证策略,以及配置并发控制.红字为关键

WebSecurityConfigurerAdapter 
/**
 * Created by ZhenWeiLai on on 2016-10-16.
 * <p>
 * 三种方法级权限控制
 * <p>
 * 1.securedEnabled: Spring Security’s native annotation
 * 2.jsr250Enabled: standards-based and allow simple role-based constraints
 * 3.prePostEnabled: expression-based
 */
@EnableWebSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private UserDetailsService userDetailsService;

    @Resource
    private FilterInvocationSecurityMetadataSource securityMetadataSource;

    @Resource
    private SessionRegistry sessionRegistry;

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/assets/**");
        web.ignoring().antMatchers("/components/**");
        web.ignoring().antMatchers("/css/**");
        web.ignoring().antMatchers("/images/**");
        web.ignoring().antMatchers("/js/**");
        web.ignoring().antMatchers("/mustache/**");
        web.ignoring().antMatchers("/favicon.ico");

        //注册地址不拦截
//        web.ignoring().antMatchers("/base/invoice/userinfo/u/reg");

//        web.ignoring().antMatchers("/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //解决不允许显示在iframe的问题
        http.headers().frameOptions().disable();

        http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
       //session并发控制过滤器
        http.addFilterAt(new ConcurrentSessionFilter(sessionRegistry,sessionInformationExpiredStrategy()),ConcurrentSessionFilter.class);
        //自定义过滤器
        //在适当的地方加入
        http.addFilterAt(filterSecurityInterceptor(securityMetadataSource, accessDecisionManager(), authenticationManagerBean()), FilterSecurityInterceptor.class);

        http.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")).and().logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll().and().exceptionHandling().accessDeniedPage("/accessDenied");


        http.authorizeRequests().anyRequest().fullyAuthenticated();

        // 关闭csrf
        http.csrf().disable();


        /**
         * 以下配置无效
         */
        //session管理
        //session失效后跳转
//        http.sessionManagement().invalidSessionUrl("/login");
//        //只允许一个用户登录,如果同一个账户两次登录,那么第一个账户将被踢下线,跳转到登录页面
//        http.sessionManagement().maximumSessions(1).expiredUrl("/login");
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        // 自定义UserDetailsService,设置加密算法
        auth.userDetailsService(userDetailsService);
//.passwordEncoder(passwordEncoder())
        //不删除凭据,以便记住用户
        auth.eraseCredentials(false);
    }

    //session失效跳转
    private SessionInformationExpiredStrategy sessionInformationExpiredStrategy() {
        return new SimpleRedirectSessionInformationExpiredStrategy("/login");
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    //SpringSecurity内置的session监听器
    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }

    private UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
        UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new CuzUsernamePasswordAuthenticationFilter();
        usernamePasswordAuthenticationFilter.setPostOnly(true);
        usernamePasswordAuthenticationFilter.setAuthenticationManager(this.authenticationManager());
        usernamePasswordAuthenticationFilter.setUsernameParameter("name_key");
        usernamePasswordAuthenticationFilter.setPasswordParameter("pwd_key");
        usernamePasswordAuthenticationFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/checkLogin", "POST"));
        usernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler());
        usernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
        //session并发控制,因为默认的并发控制方法是空方法.这里必须自己配置一个
        usernamePasswordAuthenticationFilter.setSessionAuthenticationStrategy(new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry));
        return usernamePasswordAuthenticationFilter;
    }

//    @Bean
//    public LoggerListener loggerListener() {
//        System.out.println("org.springframework.security.authentication.event.LoggerListener");
//        return new LoggerListener();
//    }
//
//    @Bean
//    public org.springframework.security.access.event.LoggerListener eventLoggerListener() {
//        System.out.println("org.springframework.security.access.event.LoggerListener");
//        return new org.springframework.security.access.event.LoggerListener();
//    }

    /**
     * 投票器
     */
    private AbstractAccessDecisionManager accessDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList();
        decisionVoters.add(new AuthenticatedVoter());
        decisionVoters.add(new RoleVoter());//角色投票器,默认前缀为ROLE_
        RoleVoter AuthVoter = new RoleVoter();
        AuthVoter.setRolePrefix("AUTH_");//特殊权限投票器,修改前缀为AUTH_
        decisionVoters.add(AuthVoter);
        AbstractAccessDecisionManager accessDecisionManager = new AffirmativeBased(decisionVoters);
        return accessDecisionManager;
    }

    @Override
    public AuthenticationManager authenticationManagerBean() {
        AuthenticationManager authenticationManager = null;
        try {
            authenticationManager = super.authenticationManagerBean();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return authenticationManager;
    }


    /**
     * 验证异常处理器
     *
     * @return
     */
    private SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() {
        return new SimpleUrlAuthenticationFailureHandler("/getLoginError");
    }


//    /**
//     * 表达式控制器
//     *
//     * @return
//     */
//    private DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() {
//        DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
//        return webSecurityExpressionHandler;
//    }

//    /**
//     * 表达式投票器
//     *
//     * @return
//     */
//    private WebExpressionVoter webExpressionVoter() {
//        WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
//        webExpressionVoter.setExpressionHandler(webSecurityExpressionHandler());
//        return webExpressionVoter;
//    }

    // Code5  官方推荐加密算法
//    @Bean("passwordEncoder")
//    public BCryptPasswordEncoder passwordEncoder() {
//        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
//        return bCryptPasswordEncoder;
//    }

//    // Code3----------------------------------------------


    /**
     * 登录成功后跳转
     * 如果需要根据不同的角色做不同的跳转处理,那么继承AuthenticationSuccessHandler重写方法
     *
     * @return
     */
    private SimpleUrlAuthenticationSuccessHandler authenticationSuccessHandler() {
        return new SimpleUrlAuthenticationSuccessHandler("/loginSuccess");
    }


    /**
     * Created by ZhenWeiLai on on 2016-10-16.
     */

    private FilterSecurityInterceptor filterSecurityInterceptor(FilterInvocationSecurityMetadataSource securityMetadataSource, AccessDecisionManager accessDecisionManager, AuthenticationManager authenticationManager) {
        FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
        filterSecurityInterceptor.setSecurityMetadataSource(securityMetadataSource);
        filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager);
        filterSecurityInterceptor.setAuthenticationManager(authenticationManager);
        return filterSecurityInterceptor;
    }


}

继承UsernamePasswordAuthenticationFilter ,注入SessionRegistry ,当用户登录验证成功后注册session

/**
 * Created by ZhenWeiLai on 2017/6/2.
 */
public class CuzUsernamePasswordAuthenticationFilter  extends UsernamePasswordAuthenticationFilter {

    private boolean postOnly = true;

    @Resource
    private SessionRegistry sessionRegistry;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if(this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);
            String password = this.obtainPassword(request);
            if(username == null) {
                username = "";
            }

            if(password == null) {
                password = "";
            }

            username = username.trim();
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            //用户名密码验证通过后,注册session
            sessionRegistry.registerNewSession(request.getSession().getId(),authRequest.getPrincipal());
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }
}

重写用户实体类的比较方法.

    /**
     * 重写比较方法,SpringSecurity根据用户名来比较是否同一个用户
     */
    @Override
    public boolean equals(Object o){
        if(o.toString().equals(this.username))
            return true;
        return false;
    }

    @Override
    public int hashCode(){
        return username.hashCode();
    }


    @Override
    public String toString() {
        return this.username;
    }

免责声明:文章转载自《SpringBoot整合SpringSecurity,SESSION 并发管理,同账号只允许登录一次》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇神奇的position:stickyVC++的dll中接收消息下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

springboot设置session超时和session监听

                               2.0版本以下设置session超时时间 1.  springboot 2.0版本以下配置session超时      1.1 application.properties配置文件: spring.session.store-type=none      1.2 引入 spring-boo...

iOS系统原生 二维码的生成、扫描和读取(高清、彩色)

由于近期工作中遇到了个需求:需要将一些固定的字段 在多个移动端进行相互传输,所以就想到了 二维码 这个神奇的东东! 现在的大街上、连个摊煎饼的大妈 都有自己的二维码来让大家进行扫码支付。可见现在的二维码使用率多高,不光如此,在很多的社交类的APP 基本都有扫一扫加好友这个功能吧,因此决定学一学这个神奇的东西。 查找了一些资料博客啊发现,iOS7之前 对于开...

会话、进程组与僵死进程

  1.   终端       在Linux系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(Controlling Terminal),Shell进程启动的其他进程的控制终端也是这个终端。默认情况下(没有重定向),每个进程的标准输入、标准输出和标准错误输出都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进...

SpringBoot配置属性之Server参数

server配置server.address指定server绑定的地址 server.compression.enabled是否开启压缩,默认为false. server.compression.excluded-user-agents指定不压缩的user-agent,多个以逗号分隔,默认值为:text/html,text/xml,text/plain,t...

Java之MyBatis

MyBatis中文文档:https://mybatis.org/mybatis-3/zh/index.htmlMyBatis是一款优秀的持久化框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动配置参数以及结果集。MyBatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain...

转载【微信支付】jsapi支付之传参问题(使用微信官方SDK之PHP版本) V3之WxpayPubHelper 亲测有效,V3WxpayAPI_php_v3.zip版未测试,理论上也是一样的。

本文转载至:http://blog.csdn.net/geeklx/article/details/51146151 (微信支付现在分为v2版和v3版,2014年9月10号之前申请的为v2版,之后申请的为v3版。V3版的微信支付没有paySignKey参数。) 写在最前面:V3 WxpayPubHelper 亲测有效,最新版本(WxpayAPI_php_v...