Spring-Boot并发会话管理

时间:2016-05-31 14:30:49

标签: spring-security spring-boot

我是Spring Boot和Spring Security的新手,我在Spring Boot和Spring Security的github上看样本。我想结合样本" spring-security-samples-concurrency-jc"使用示例" spring-boot-sample-web-method-security"。当我在SecurityConfig中设置sessionManagement时,扩展了WebSecurityConfigurerAdapter.It并不是所有的工作。我的问题是如何在Springboot中添加HttpSessionEventPublisher Listenier。 在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时,如何添加此Listener以使sessionManagement工作,换句话说,如何配置我的WebConfig.java以使同一用户一次只能登录一次,第二次登录是在第一次登录过期或注销时阻止了util.My 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);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

我的配置是正确的,它不起作用的原因是我定义了一个对象'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,则principalals.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);
    }
}
相关问题