【问题标题】:Spring-Boot concurrency sessionManagementSpring-Boot 并发会话管理
【发布时间】:2016-09-29 15:11:10
【问题描述】:

我是 Spring Boot 和 Spring Security 的新手,我在 github 中观看了 Spring Boot 和 Spring Security 的示例。我想将示例“spring-security-samples-concurrency-jc”与示例“spring-boot-sample-web-method-security”结合起来。当我在我的 SecurityConfig 中更改 sessionManagement 的设置时,扩展了 WebSecurityConfigurerAdapter。它根本不起作用。我的问题是如何在 Springboot 中添加一个 HttpSessionEventPublisher 监听器。 在 web.xml 中

<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>

在javaconfig中

public class MessageSecurityWebApplicationInitializer extends
        AbstractSecurityWebApplicationInitializer {

    @Override
    protected boolean enableHttpSessionEventPublisher() {
        return true;
    }
}

但是当我使用 Spring-Boot 时,如何添加此侦听器以使 sessionManagement 工作,换句话说,如何配置我的 WebConfig.java 以使同一用户一次只能登录一次,第二次登录是在第一次登录过期或注销时阻止了 util。我的 SecurityConfig.java 如下所示,对不起我的游泳池英语:)

package com.eexcel.branch.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.data.repository.query.SecurityEvaluationContextExtension;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.eexcel.common.domain.SpringSecurityAuditorAware;
import com.eexcel.common.service.distributor.DistributorService;

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
    }
    @Bean
    public AuditorAware<String> auditor() {
        return new SpringSecurityAuditorAware();
    }
    @Bean
    public SecurityEvaluationContextExtension expressionEvaluationContextProvider() {
        return new SecurityEvaluationContextExtension();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Bean
    public ApplicationSecurity applicationSecurity() {
        return new ApplicationSecurity();
    }
    @Bean
    public static SessionRegistry sessionRegistry() {
        SessionRegistry sessionRegistry = new SessionRegistryImpl();
        return sessionRegistry;
    }
    @Bean
    public static ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
        return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(
                new HttpSessionEventPublisher());
    }
    @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
    protected static class ApplicationSecurity extends
            WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().fullyAuthenticated().and()
                    .formLogin().loginPage("/login").failureUrl("/login?error")
                    .permitAll().and().logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                    .logoutSuccessUrl("/login?logout").permitAll().and()
                    .rememberMe().and().sessionManagement().maximumSessions(1)
                    .maxSessionsPreventsLogin(true)
                    .expiredUrl("/login?expired")
                    .sessionRegistry(sessionRegistry());
        }
    }
    @Order(Ordered.HIGHEST_PRECEDENCE)
    @Configuration
    protected static class AuthenticationManagerConfiguration extends
            GlobalAuthenticationConfigurerAdapter {
        @Autowired
        private DataSource dataSource;
        @Autowired
        private PasswordEncoder passwordEncoder;
        @Autowired
        private DistributorService userDetailsService;

        @Override
        public void init(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService).passwordEncoder(
                    passwordEncoder);
        }
    }
}

【问题讨论】:

    标签: spring-security spring-boot


    【解决方案1】:

    我的配置是对的,它不起作用的原因是我定义了一个对象'CustomUserDetails'实现了springsecurity的接口'UserDetails',而SessionRegistryImpl中的方法

    public List<SessionInformation> getAllSessions(Object principal,
                boolean includeExpiredSessions) {
            final Set<String> sessionsUsedByPrincipal = principals.get(principal);
    
            if (sessionsUsedByPrincipal == null) {
                return Collections.emptyList();
            }
    
            List<SessionInformation> list = new ArrayList<SessionInformation>(
                    sessionsUsedByPrincipal.size());
    
            for (String sessionId : sessionsUsedByPrincipal) {
                SessionInformation sessionInformation = getSessionInformation(sessionId);
    
                if (sessionInformation == null) {
                    continue;
                }
    
                if (includeExpiredSessions || !sessionInformation.isExpired()) {
                    list.add(sessionInformation);
                }
            }
    
            return list;
        }
    

    校长是

    private final ConcurrentMap<Object, Set<String>> principals = new ConcurrentHashMap<Object, Set<String>>();
    

    所以它得到principal的实例是CustomUserDetails,如果不覆盖EqualsAndHashCode,principal.get(principal)总是为null。 最后我覆盖了我的 CustomUserDetails 并且它起作用了。

    @EqualsAndHashCode(callSuper = true, exclude = { "authorities", "sysOperator" })
    public class CustomUserDetails extends SysOperator implements UserDetails {
        private static final long serialVersionUID = 1L;
        private List<String> authorities;
        private SysOperator sysOperator;
    
        public CustomUserDetails(SysOperator sysOperator,
                final List<String> authorities) {
            super(sysOperator);
            this.authorities = authorities;
            this.sysOperator = sysOperator;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            String[] tmp = authorities.toArray(new String[authorities.size()]);
            return AuthorityUtils.createAuthorityList(tmp);
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            return sysOperator.getEnable();
        }
    
        @Override
        public String getUsername() {
            return sysOperator.getLoginName();
        }
    }
    
    @Entity
    @Table(name = "sys_operator")
    @EqualsAndHashCode(callSuper = false, exclude = { "realName", "password",
            "mobile", "email", "enable", "remark", "roleIds", "branchNo",
            "birthday" })
    public class SysOperator extends IdEntity {
    
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        @Column(nullable = false, unique = true, length = 32)
        private String loginName;
        private String realName;
        @Column(nullable = false)
        private String password;
        private String mobile;
        @Column(nullable = false, unique = true, length = 32)
        private String email;
        @Column(nullable = false)
        private Boolean enable;
        private String remark;
        @Column(nullable = true)
        private String roleIds;
    
        private String branchNo;
    
        @Past
        @Temporal(TemporalType.DATE)
        private Date birthday;
    
        public SysOperator(SysOperator sysOperator) {
            BeanUtils.copyProperties(sysOperator, this);
        }
    
        public SysOperator() {
            super();
        }
    
        @Transient
        public boolean isAdmin() {
            return (AuthConsts.ADMIN.equalsIgnoreCase(this.loginName) || Arrays
                    .asList(AuthConsts.ADMIN_GROUP).contains(this.loginName));
        }
    
        public String getLoginName() {
            return loginName;
        }
    
        public void setLoginName(String loginName) {
            this.loginName = loginName;
        }
    
        public String getRealName() {
            return realName;
        }
    
        public void setRealName(String realName) {
            this.realName = realName;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getMobile() {
            return mobile;
        }
    
        public void setMobile(String mobile) {
            this.mobile = mobile;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    
        public String getRemark() {
            return remark;
        }
    
        public void setRemark(String remark) {
            this.remark = remark;
        }
    
        public String getRoleIds() {
            return roleIds;
        }
    
        public void setRoleIds(String roleIds) {
            this.roleIds = roleIds;
        }
    
        public Boolean getEnable() {
            return enable;
        }
    
        public void setEnable(Boolean enable) {
            this.enable = enable;
        }
    
        public String getBranchNo() {
            return branchNo;
        }
    
        public void setBranchNo(String branchNo) {
            this.branchNo = branchNo;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-25
      • 2018-12-05
      • 2016-10-26
      • 2020-12-16
      • 2011-09-19
      • 2012-04-30
      • 2016-05-31
      • 2016-05-07
      相关资源
      最近更新 更多