春季安全并发会话不起作用

Posted

技术标签:

【中文标题】春季安全并发会话不起作用【英文标题】:spring security concurrent session not working 【发布时间】:2016-11-06 05:18:24 【问题描述】:

我正在使用 Spring 4 和 Hibernate 4 我已经实现了 Spring 安全性并且它工作正常,但是我不想允许使用相同的凭据进行并发登录。 1.我在web.xml中添加了监听器“HttpSessionEventPublisher”,并在spring security中使用了“会话管理”标签来实现并发控制,但它不起作用以下是完整的代码:

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

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

<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern> 
</filter-mapping>

spring-security.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans.xsd    
                    http://www.springframework.org/schema/security
                    http://www.springframework.org/schema/security/spring-security.xsd">


<http auto-config="true" use-expressions="true">
    <intercept-url pattern="/login" access="permitAll()" />
    <intercept-url pattern="/loginError" access="isAnonymous()" />
    <intercept-url pattern="/sessionTimeout" access="isAnonymous()" />
    <intercept-url pattern="/forgotPassword" access="isAnonymous()" />
    <intercept-url pattern="/requestNewPassword" access="isAnonymous()" />
    <intercept-url pattern="/assets/**" access="permitAll()" />

    <intercept-url pattern="/sessionExpired" access="isAnonymous()" />
    <intercept-url pattern="/error" access="isAnonymous()" />


    <form-login  login-page="/login"
                 username-parameter="userId"
                 password-parameter="password"
                 authentication-success-handler-ref="cdatSuccessHandler"
                 authentication-failure-url="/loginError" />

    <!-- <session-management session-fixation-protection="newSession" invalid-session-url="/sessionTimeout">
    </session-management> -->

    <session-management>
        <concurrency-control max-sessions="1" expired-url="/sessionTimeout" />
    </session-management>

    <intercept-url pattern="/**" access="isAuthenticated()"/>

    <csrf/>

    <!-- <access-denied-handler error-page="/sessionExpired"/>  -->

    <headers>
        <xss-protection enabled="true" block="true"/>
    </headers>

</http>

<authentication-manager erase-credentials="true">
    <authentication-provider ref="cdatAuthenticationProvider"> </authentication-provider>
</authentication-manager>

身份验证提供者类

package com.component.cdat.security.configuration;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import     org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import com.component.cdat.project.bean.MappProjectUser;
import com.component.cdat.user.bean.User;
import com.component.cdat.user.services.UserService;

@Component("cdatAuthenticationProvider")
public class CDATAuthenticationProvider implements AuthenticationProvider

@Autowired
UserService userService;

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException 

    String loginId = authentication.getName().trim();
    String password = (String) authentication.getCredentials();

    if(loginId == null || password == null || loginId.isEmpty() || password.isEmpty())
        // throw exception
        System.out.println("username or password is empty!!");
        throw new NullPointerException();
    

    User user = userService.getUserByUserName(loginId);

    if(user == null || !loginId.equalsIgnoreCase(user.getUserName()))
        System.out.println("User Not Found!!");
        throw new NullPointerException();
    

    if(!password.equalsIgnoreCase(user.getPassword()))
        System.out.println("Pasword is incorrect!!");
        throw new NullPointerException();
    

    Collection<? extends GrantedAuthority> authorities = getAuthorities(user);

    return new UsernamePasswordAuthenticationToken(user, password, authorities);


private Collection<? extends GrantedAuthority> getAuthorities(User user)

    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    List<MappProjectUser> userAuthorityList = userService.getUserRole(user.getUserId());

    for(MappProjectUser userAuthority : userAuthorityList)
        authorities.add(new SimpleGrantedAuthority("ROLE_" + userAuthority.getUserType().getShortDesc()));
    
    return authorities;


@Override
public boolean supports(Class<?> arg0) 
    return true;


【问题讨论】:

password.equalsIgnoreCase(user.getPassword()) 从安全的角度来看可能不是一个好主意。您如何进行测试并得出结论它不起作用? (例如,同一浏览器中的多个选项卡通常对给定站点使用相同的会话)。 不...我使用了不同的浏览器,还尝试以隐身模式打开链接,但它允许我使用相同的凭据登录。我们应该使用什么来比较密码以获得更好的安全性? 对于密码,不要忽略大小写。 【参考方案1】:

看看reference documentation(这是针对spring security 3.1):

  <http>
      ...
      <session-management>
          <concurrency-control max-sessions="1" />
      </session-management>
  </http>

这将阻止用户多次登录 - 第二次登录将导致第一次无效。通常您希望阻止第二次登录,在这种情况下您可以使用

<http>
    ...
    <session-management>
        <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
    </session-management>
</http>

尝试将error-if-maximum-exceeded="true" 添加到concurrency-control 标签,看看它是否有效。请注意这种并发控制的缺点:如果用户在没有注销的情况下关闭浏览器,他将无法在会话超时之前登录(通常是 30 分钟......所以他将无法获得在接下来的 30 分钟内访问该网站)。

【讨论】:

我已经浏览了 spring 安全参考文档,并且仅在此文档的帮助下实现了此并发控制,并且我尝试了上述 error-if-maximum-exceeded="true" 但它没有工作。

以上是关于春季安全并发会话不起作用的主要内容,如果未能解决你的问题,请参考以下文章

春季安全。授权在jsp上不起作用

春季请求验证不起作用

春季启动@Transactional 不起作用

春季数据 findByStartDateBetween 不起作用

春季启动验证不起作用

春季侦探行李传播不起作用