java - 无法阻止用户使用带有自定义 UserDetail 和 AuthenticationProvider 的 spring security 多次登录

标签 java xml spring spring-mvc spring-security

我已经实现了自定义 UserDetail 和 AuthenticationProvider 以与需要超过 1 个参数的自定义基本登录表单一起使用。一切正常,但并发控制除外,它不会阻止用户多次登录或使用多个设备。

这是我正在使用的配置:

网络.xml

<web-app 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"
  version="3.0"> 
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
    <listener-class>kh.com.gfam.rsos.listener.InitializeApplicationListner</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>
    <multipart-config>
        <max-file-size>10485760</max-file-size>
        <max-request-size>104857600</max-request-size>
        <file-size-threshold>20971520</file-size-threshold>
    </multipart-config>
</servlet>
<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>rsos.root</param-value>

<listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet>
    <description></description>
    <display-name>GetImageController</display-name>
    <servlet-name>GetImageController</servlet-name>
    <servlet-class>kh.com.gfam.rsos.presentation.controller.GetImage.GetImageController</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>GetImageController</servlet-name>
    <url-pattern>/GetImageController</url-pattern>
</servlet-mapping>
<welcome-file-list>
    <welcome-file>home</welcome-file>
</welcome-file-list>
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-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>

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

spring-security.xml

    <http auto-config="false" use-expressions="true"
        entry-point-ref="loginUrlAuthenticationEntryPoint">  
        <!-- omitted -->
        <intercept-url pattern="/Admin/**" access="hasRole('ROLE_ADMIN')" />
        <intercept-url pattern="/Concierge/**" access="hasRole('ROLE_USER')" />
        <intercept-url pattern="/Login" access="permitAll" />
        <intercept-url pattern="/**" access="permitAll" />
        <custom-filter position="FORM_LOGIN_FILTER"
            ref="companyIdUsernamePasswordAuthenticationFilter" />  
        <custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />

        <logout logout-url="/Logout" delete-cookies="true"
            invalidate-session="true" success-handler-ref="RsosLogoutSuccessHandler" />
        <csrf disabled="true" />
        <session-management invalid-session-url="/Login"
            session-authentication-strategy-ref="sas"/>

    </http>

    <global-method-security secured-annotations="enabled"/>

    <authentication-manager alias="authenticationManager">
        <authentication-provider
            ref="companyIdUsernamePasswordAuthenticationProvider" />  
    </authentication-manager>

spring-config.xml

<bean id="companyIdUsernamePasswordAuthenticationProvider"
        class="kh.com.gfam.rsos.common.security.RsosAuthenticationProvider" />

    <bean id="companyIdUsernamePasswordAuthenticationFilter"
        class="kh.com.gfam.rsos.common.security.RsosUsernamePasswordAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager" />
        <property name="sessionAuthenticationStrategy" ref="sas" />
        <property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
        <property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
        <property name="filterProcessesUrl" value="/Authenticate" />
    </bean>

    <bean id="loginUrlAuthenticationEntryPoint"
        class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <constructor-arg value="/Login" />
    </bean>

    <bean id="authenticationFailureHandler"
        class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
        <property name="defaultFailureUrl" value="/Login/defaultError" />
        <property name="exceptionMappings">
            <props>
                <prop
                    key="org.springframework.security.authentication.BadCredentialsException">
                    /Login/badCredentials
                </prop>
                <prop
                    key="org.springframework.security.core.userdetails.UsernameNotFoundException">
                    /Login/usernameNotFound
                </prop>
                <prop
                    key="org.springframework.security.authentication.DisabledException">
                    /Login/disabled
                </prop>
                <prop
                    key="org.springframework.security.authentication.ProviderNotFoundException">
                    /Login/providerNotFound
                </prop>
                <prop
                    key="org.springframework.security.authentication.AuthenticationServiceException">
                    /Login/authenticationService
                </prop>
            </props>
        </property>
    </bean>

    <bean id="authenticationSuccessHandler"
        class="kh.com.gfam.rsos.common.security.RsosAuthenticationSuccessHandler">
    </bean>

    <bean id="RsosLogoutSuccessHandler"
        class="kh.com.gfam.rsos.common.security.RsosLogoutSucessHandler"></bean>

    <bean id="concurrencyFilter"
        class="org.springframework.security.web.session.ConcurrentSessionFilter">
        <constructor-arg name="sessionRegistry" ref="sessionRegistry" />
        <constructor-arg name="expiredUrl" value="/session-expired.jsp" />
    </bean>

    <bean id="sas"
        class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
        <constructor-arg name="sessionRegistry" ref="sessionRegistry" />
        <property name="maximumSessions" value="1" />
        <property name="exceptionIfMaximumExceeded" value="true" />
    </bean>

    <bean id="sessionRegistry"
        class="org.springframework.security.core.session.SessionRegistryImpl"/>

根上下文.xml

        <import resource="classpath:springConfig.xml" />
        <import resource="appServlet/servlet-context.xml" />
        <import resource="appServlet/spring-security.xml" />

        <bean id="InitializationService"
            class="kh.com.gfam.rsos.businesslogic.initialization.impl.InitializationServiceImpl">
        </bean>

自定义 AuthenticationProvider 类

public class RsosAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    LoginService service;

    @Override
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        RsosUsernamePasswordAuthenticationToken auth = (RsosUsernamePasswordAuthenticationToken) authentication;
        String user_id = String.valueOf(auth.getName());
        String password = String.valueOf(auth.getCredentials());
        int hotel_code = auth.getHotel_code();
        int user_type = auth.getUser_type();

        UserDTO user = null;
        user = service.authenicate(hotel_code, user_id, password, user_type);

        if (user.getUser_type() == 1) {
            return new UsernamePasswordAuthenticationToken(user, authentication.getCredentials(),
                    Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
        } else {
            return new UsernamePasswordAuthenticationToken(user, authentication.getCredentials(),
                    Collections.singletonList(new SimpleGrantedAuthority("ROLE_ADMIN")));
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return RsosUsernamePasswordAuthenticationToken.class
                .equals(authentication);
    }
}

自定义用户名密码AuthenticationToken

public class RsosUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
    private static final long serialVersionUID = 9103166337373681531L;
    private final int hotel_code;
    private final int user_type;

    public RsosUsernamePasswordAuthenticationToken(Object principal, Object credentials,
            int hotel_code, int user_type) {
        super(principal, credentials);
        this.hotel_code = hotel_code;
        this.user_type = user_type;
    }

    public RsosUsernamePasswordAuthenticationToken(Object principal, Object credentials,
            int hotel_code, int user_type,
            Collection<? extends GrantedAuthority> authorities) {
        super(principal, credentials, authorities);
        this.hotel_code = hotel_code;
        this.user_type = user_type;
    }

    public int getHotel_code() {
        return hotel_code;
    }

    public int getUser_type() {
        return user_type;
    }
}

自定义用户名密码身份验证过滤器

public class RsosUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException {

        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported:"
                    + request.getMethod());
        }

        String username = super.obtainUsername(request);
        String password = super.obtainPassword(request);
        int hotel_code = Integer.parseInt(ObtainHotelCode(request));
        int user_type = Integer.parseInt(ObtainUserType(request));

        if (!StringUtils.hasText(hotel_code + "")) {
            throw new AuthenticationServiceException("Hotel Code is require");
        }

        RsosUsernamePasswordAuthenticationToken authrequest =
                new RsosUsernamePasswordAuthenticationToken(username, password, hotel_code, user_type); // (1)

        setDetails(request, authrequest);

        return  this.getAuthenticationManager().authenticate(authrequest);
    }

    protected String ObtainHotelCode(HttpServletRequest request) {
        return request.getParameter("hotel_code");
    }

    protected String ObtainUserType(HttpServletRequest request) {
        return request.getParameter("user_type");
    }
}

自定义 AuthenticationSuccessHandler

public class RsosAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {

        UserDTO authUser = (UserDTO) SecurityContextHolder.getContext().getAuthentication()
                .getPrincipal();
        request.getSession().setAttribute("userData", authUser);

        response.setStatus(HttpServletResponse.SC_OK);

        if (authUser.getUser_type() == 1) {
            response.sendRedirect(request.getContextPath() + "/Concierge/New_Arrival");
        } else {
            response.sendRedirect(request.getContextPath() + "/Admin/Main_Info");
        }
    }

}

自定义 LogoutSucessHandler

public class RsosLogoutSucessHandler implements LogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest request,
            HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        if (authentication != null && authentication.getDetails() != null) {
            try {
                request.getSession().invalidate();
            } catch (Exception e) {
                e.printStackTrace();
                e = null;
            }
        }

        response.setStatus(HttpServletResponse.SC_OK);

        response.sendRedirect(request.getContextPath() + "/Login");

    }
}

自定义 UserDetail 类

public class UserDTO implements UserDetails {

    /**  */
    private static final long serialVersionUID = -2228367483835088451L;

    /** Hotel Code */
    private int hotel_code;
    /** UserID */
    // @Size(min=4, max=4) @Pattern(regexp = "[0-9]")
    private String user_id;
    /** User Name */
    private String user_name;
    /** Password */
    @NotNull
    @Size(min = 8, max = 30)
    private String password;
    /** User Type */
    @NotNull
    private int user_type;

    //set() .... get()//

    public void setUser_name(String user_name) {
        this.user_name = user_name;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getUsername() {
        return user_id;
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return false;
    }

    @Override
    public boolean equals(Object rhs) {
        if (rhs instanceof UserDTO) {
            return (user_id.equals(((UserDTO) rhs).user_id));
        }
        return false;
    }

    /**
     * Returns the hashcode of the {@code username}.
     */
    @Override
    public int hashCode() {
        return user_id.hashCode();
    }
}

这是登录表单的样子

enter image description here

我是 spring security 的新手,所以这里可能出了什么问题?有人可以指出错误吗? 谢谢。

最佳答案

在阅读有关如何从 Spring 3.2 迁移到 4.x 的 Spring 文档后,我已经解决了这个问题 http://docs.spring.io/spring-security/site/migrate/current/3-to-4/html5/migrate-3-to-4-xml.html#m3to4-deprecations-web-cscs

我从

<bean id="sas"
    class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
    <constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <property name="maximumSessions" value="1" />
    <property name="exceptionIfMaximumExceeded" value="true" />
</bean>

<bean id="sas"
    class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
    <constructor-arg>
        <list>
            <bean
                class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
                <constructor-arg ref="sessionRegistry" />
                <property name="maximumSessions" value="1" />
                <property name="exceptionIfMaximumExceeded" value="true" />
            </bean>
            <bean
                class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
            <bean
                class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
                <constructor-arg ref="sessionRegistry" />
            </bean>
        </list>
    </constructor-arg>
</bean>

使用Spring 4.x遇到同样问题的 friend ,你可以尝试阅读上面链接中的全文,应该也能解决你的问题。

关于java - 无法阻止用户使用带有自定义 UserDetail 和 AuthenticationProvider 的 spring security 多次登录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31022909/

相关文章:

java - 无法加载属性

java - 为什么背景颜色拒绝填满整个屏幕

xml - 使用 lift 将 xml 转换为 Json 的行为很奇怪

spring - 我什么时候使用 XA 数据源和 2 阶段提交

java - 找不到 applicationContext 类路径

java - GLES30.glGetBufferSubData 在哪里?如何读取转换反馈数据opengles 3.0

java - 获取mongodb中按年份和月份的所有文档

java.lang.UnsatisfiedLinkError : TestJni. print(LA;)V 错误在 Ubuntu 上

java - @GetMapping 和 @GetMapping ("/{id}")始终返回 @GetMapping ("/{id}")

java - Xml 架构接口(interface)