Grails Spring Security 最大并发会话 [英] Grails Spring Security max concurrent session

查看:35
本文介绍了Grails Spring Security 最大并发会话的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有带有 spring 安全插件 (2.0-RC5) 的 grails 2.5.1 应用程序.我想阻止每个用户的当前会话数.我读过一些博客,但它不起作用.(http://www.block-consult.com/blog/2012/01/20/restricting-concurrent-user-sessions-in-grails-2-using-spring-security-core-plugin/)我的资源.groovy

I have grails 2.5.1 app with spring security plugin(2.0-RC5). I would like to block the number of current session per user. I have read some blog and it doesn't work.(http://www.block-consult.com/blog/2012/01/20/restricting-concurrent-user-sessions-in-grails-2-using-spring-security-core-plugin/) my resources.groovy

beans = {
  sessionRegistry(SessionRegistryImpl)

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

    }
}

在我的 boostrap.groovy 中

In my boostrap.groovy

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

和我的 config.groovy 我添加了这个:

and my config.groovy I have added this:

grails.plugin.springsecurity.useHttpSessionEventPublisher = true

谢谢..

推荐答案

首先,如果您决定继续我的解决方案,让我警告您.

To start with, let me warn you if you decided to continue with my solution.

  • SessionRegistryImpl 不可扩展.您需要根据您的扩​​展计划(例如数据网格)创建自己的可扩展实现.会话复制还不够.
  • 目前,默认注销处理程序没有正确删除 SessionRegistry.所以我创建了一个名为 CustomSessionLogoutHandler 的示例注销处理程序.
  • 您必须覆盖 grails spring-security-core 登录控制器来处理 SessionAuthenticationException.
  • 您可以将可以登录的用户数从 maximumSessions = 1 更改为 -1,以实现无限会话.
  • SessionRegistryImpl is not scalable. You need to create your own scalable implementation based on your scaling plan(e.g. data grid). Session Replication just not enough.
  • Currently, default logout handlers did not remove SessionRegistry properly. So i have created a sample Logout handler called CustomSessionLogoutHandler.
  • You have to override grails spring-security-core Login Controller to handle SessionAuthenticationException.
  • You can change number of users that can login maximumSessions = 1 to -1 for unlimited sessions.

首先,在 resources.groovy

first, in 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 之前:

in config.groovy make sure customSessionLogoutHandler is first before securityContextLogoutHandler:

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

ConcurrentSessionControlAuthenticationStrategy 使用这个 i18n.所以你可以用你的语言来提供它:

ConcurrentSessionControlAuthenticationStrategy uses this i18n. So you can have it in your language:

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

这是我的示例 CustomSessionLogoutHandler,您可以将其保存在 src/groovy/com/basic/CustomSessionLogoutHandler.groovy 中:

This is my sample CustomSessionLogoutHandler you can save it in src/groovy/com/basic/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());
    }
}

如果您也需要我的示例登录控制器(我从源代码中复制).只需在您的项目中另存为普通控制器,因为它将覆盖默认值.当我处理 SessionAuthenticationException 时,请参阅下面的第 115 行:

My Sample Login Controller (I copied from the source) if you need it too. Just save as normal controller in your project as it will override the default. See line 115 below as i handle the 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
    }
}

这篇关于Grails Spring Security 最大并发会话的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆