2016-01-16 5 views
3

У меня есть приложение grail 2.5.1 с плагином для защиты от пружины (2.0-RC5). Я хотел бы заблокировать количество текущего сеанса для каждого пользователя. . Я прочитал некоторые блог и он не работает (http://www.block-consult.com/blog/2012/01/20/restricting-concurrent-user-sessions-in-grails-2-using-spring-security-core-plugin/) мой resources.groovyGrails Spring Security max одновременная сессия

beans = { 
    sessionRegistry(SessionRegistryImpl) 

    concurrencyFilter(ConcurrentSessionFilter,sessionRegistry,'/main/index'){ 
     logoutHandlers = [ref("rememberMeServices"), ref("securityContextLogoutHandler")] 
    } 
    concurrentSessionControlStrategy(ConcurrentSessionControlAuthenticationStrategy, sessionRegistry) { 
     exceptionIfMaximumExceeded = true 
     maximumSessions = 1 

    } 
} 

В моей boostrap.groovy

def init = { servletContext -> 
    SpringSecurityUtils.clientRegisterFilter('concurrencyFilter', SecurityFilterPosition.CONCURRENT_SESSION_FILTER) 
    } 

и мой Config.groovy Я добавил это:

grails.plugin.springsecurity.useHttpSessionEventPublisher = true 

Спасибо ..

ответ

1

Для начала, позвольте мне предупредить вас, если вы решили продолжить мое решение.

  • SessionRegistryImpl не является масштабируемым. Вам необходимо создать собственную масштабируемую реализацию на основе плана масштабирования (например, сетки данных). Сеанс репликации просто не достаточно.
  • В настоящее время обработчики выходных данных по умолчанию не удалили SessionRegistry должным образом. Итак, я создал образец обработчика выходных данных, который называется CustomSessionLogoutHandler.
  • Вам необходимо переопределить логический контроллер входа в систему весной Grails для управления SessionAuthenticationException.
  • Вы можете изменить количество пользователей, которые могут Войти максимумSessions = 1 в -1 для неограниченных сеансов.

первый в resources.groovy

import org.springframework.security.core.session.SessionRegistryImpl; 
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy; 
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy; 
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; 
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy; 
import com.basic.CustomSessionLogoutHandler 


// Place your Spring DSL code here 
beans = { 

sessionRegistry(SessionRegistryImpl) 

customSessionLogoutHandler(CustomSessionLogoutHandler,ref('sessionRegistry') ) 

concurrentSessionControlAuthenticationStrategy(ConcurrentSessionControlAuthenticationStrategy,ref('sessionRegistry')){ 
    exceptionIfMaximumExceeded = true 
    maximumSessions = 1 
} 

sessionFixationProtectionStrategy(SessionFixationProtectionStrategy){ 
    migrateSessionAttributes = true 
    alwaysCreateSession = true 
} 
registerSessionAuthenticationStrategy(RegisterSessionAuthenticationStrategy,ref('sessionRegistry')) 

sessionAuthenticationStrategy(CompositeSessionAuthenticationStrategy,[ref('concurrentSessionControlAuthenticationStrategy'),ref('sessionFixationProtectionStrategy'),ref('registerSessionAuthenticationStrategy')]) 

} 

в Config.groovy убедитесь, что customSessionLogoutHandler является первым, прежде чем securityContextLogoutHandler:

grails.plugin.springsecurity.logout.handlerNames = ['customSessionLogoutHandler','securityContextLogoutHandler'] 

ConcurrentSessionControlAuthenticationStrategy использует этот i18n. Таким образом, вы можете иметь его на вашем языке:

ConcurrentSessionControlAuthenticationStrategy.exceededAllowed = Maximum sessions for this principal exceeded. {0} 

Это мой образец CustomSessionLogoutHandler вы можете сохранить его в SRC/заводной/COM/основной/CustomSessionLogoutHandler.groovy:

/* 
* Copyright 2002-2013 the original author or authors. 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
*  http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/ 
package com.basic; 

import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

import org.springframework.security.core.Authentication; 
import org.springframework.security.web.authentication.logout.LogoutHandler; 
import org.springframework.util.Assert; 
import org.springframework.security.core.session.SessionRegistry; 

/** 
* {@link CustomSessionLogoutHandler} is in charge of removing the {@link SessionRegistry} upon logout. A 
* new {@link SessionRegistry} will then be generated by the framework upon the next request. 
* 
* @author Mohd Qusyairi 
* @since 0.1 
*/ 
public final class CustomSessionLogoutHandler implements LogoutHandler { 
    private final SessionRegistry sessionRegistry; 

    /** 
    * Creates a new instance 
    * @param sessionRegistry the {@link SessionRegistry} to use 
    */ 
    public CustomSessionLogoutHandler(SessionRegistry sessionRegistry) { 
     Assert.notNull(sessionRegistry, "sessionRegistry cannot be null"); 
     this.sessionRegistry = sessionRegistry; 
    } 

    /** 
    * Clears the {@link SessionRegistry} 
    * 
    * @see org.springframework.security.web.authentication.logout.LogoutHandler#logout(javax.servlet.http.HttpServletRequest, 
    * javax.servlet.http.HttpServletResponse, 
    * org.springframework.security.core.Authentication) 
    */ 
    public void logout(HttpServletRequest request, HttpServletResponse response, 
      Authentication authentication) { 
     this.sessionRegistry.removeSessionInformation(request.getSession().getId()); 
    } 
} 

Мой образец Войти Контроллер (я скопировал из источника), если вам это тоже нужно. Просто сохраните как обычный контроллер в своем проекте, так как он переопределит значение по умолчанию. См линии 115 ниже, как я обрабатывать SessionAuthenticationException:

/* Copyright 2013-2016 the original author or authors. 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
*  http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/ 
package com.basic 

import grails.converters.JSON 
import org.springframework.security.access.annotation.Secured 
import org.springframework.security.authentication.AccountExpiredException 
import org.springframework.security.authentication.AuthenticationTrustResolver 
import org.springframework.security.authentication.CredentialsExpiredException 
import org.springframework.security.authentication.DisabledException 
import org.springframework.security.authentication.LockedException 
import org.springframework.security.core.Authentication 
import org.springframework.security.core.context.SecurityContextHolder 
import org.springframework.security.web.WebAttributes 
import org.springframework.security.web.authentication.session.SessionAuthenticationException 
import javax.servlet.http.HttpServletResponse 
import grails.plugin.springsecurity.SpringSecurityUtils 

@Secured('permitAll') 
class LoginController { 

    /** Dependency injection for the authenticationTrustResolver. */ 
    AuthenticationTrustResolver authenticationTrustResolver 

    /** Dependency injection for the springSecurityService. */ 
    def springSecurityService 

    /** Default action; redirects to 'defaultTargetUrl' if logged in, /login/auth otherwise. */ 
    def index() { 
     if (springSecurityService.isLoggedIn()) { 
      redirect uri: conf.successHandler.defaultTargetUrl 
     } 
     else { 
      redirect action: 'auth', params: params 
     } 
    } 

    /** Show the login page. */ 
    def auth() { 

     def conf = getConf() 

     if (springSecurityService.isLoggedIn()) { 
      redirect uri: conf.successHandler.defaultTargetUrl 
      return 
     } 

     String postUrl = request.contextPath + conf.apf.filterProcessesUrl 
     render view: 'auth', model: [postUrl: postUrl, 
            rememberMeParameter: conf.rememberMe.parameter, 
            usernameParameter: conf.apf.usernameParameter, 
            passwordParameter: conf.apf.passwordParameter, 
            gspLayout: conf.gsp.layoutAuth] 
    } 

    /** The redirect action for Ajax requests. */ 
    def authAjax() { 
     response.setHeader 'Location', conf.auth.ajaxLoginFormUrl 
     render(status: HttpServletResponse.SC_UNAUTHORIZED, text: 'Unauthorized') 
    } 

    /** Show denied page. */ 
    def denied() { 
     if (springSecurityService.isLoggedIn() && authenticationTrustResolver.isRememberMe(authentication)) { 
      // have cookie but the page is guarded with IS_AUTHENTICATED_FULLY (or the equivalent expression) 
      redirect action: 'full', params: params 
      return 
     } 

     [gspLayout: conf.gsp.layoutDenied] 
    } 

    /** Login page for users with a remember-me cookie but accessing a IS_AUTHENTICATED_FULLY page. */ 
    def full() { 
     def conf = getConf() 
     render view: 'auth', params: params, 
       model: [hasCookie: authenticationTrustResolver.isRememberMe(authentication), 
         postUrl: request.contextPath + conf.apf.filterProcessesUrl, 
         rememberMeParameter: conf.rememberMe.parameter, 
         usernameParameter: conf.apf.usernameParameter, 
         passwordParameter: conf.apf.passwordParameter, 
         gspLayout: conf.gsp.layoutAuth] 
    } 

    /** Callback after a failed login. Redirects to the auth page with a warning message. */ 
    def authfail() { 

     String msg = '' 
     def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION] 
     if (exception) { 
      if (exception instanceof AccountExpiredException) { 
       msg = message(code: 'springSecurity.errors.login.expired') 
      } 
      else if (exception instanceof CredentialsExpiredException) { 
       msg = message(code: 'springSecurity.errors.login.passwordExpired') 
      } 
      else if (exception instanceof DisabledException) { 
       msg = message(code: 'springSecurity.errors.login.disabled') 
      } 
      else if (exception instanceof LockedException) { 
       msg = message(code: 'springSecurity.errors.login.locked') 
      } 
      else if (exception instanceof SessionAuthenticationException){ 
       msg = exception.getMessage() 
      } 
      else { 
       msg = message(code: 'springSecurity.errors.login.fail') 
      } 
     } 

     if (springSecurityService.isAjax(request)) { 
      render([error: msg] as JSON) 
     } 
     else { 
      flash.message = msg 
      redirect action: 'auth', params: params 
     } 
    } 

    /** The Ajax success redirect url. */ 
    def ajaxSuccess() { 
     render([success: true, username: authentication.name] as JSON) 
    } 

    /** The Ajax denied redirect url. */ 
    def ajaxDenied() { 
     render([error: 'access denied'] as JSON) 
    } 

    protected Authentication getAuthentication() { 
     SecurityContextHolder.context?.authentication 
    } 

    protected ConfigObject getConf() { 
     SpringSecurityUtils.securityConfig 
    } 
} 
+0

Где это springSecurityService взялось? У меня есть приложение Spring Boot/Security, которое не включает его. – sonoerin

+0

@Quchie в этом случае для отключения другого пользователя с тем же именем пользователя? как насчет отключения нескольких логинов? – akiong

+0

@akiong да, это будет использовать имя пользователя, чтобы заблокировать того же пользователя от входа в систему. – Quchie