Для начала, позвольте мне предупредить вас, если вы решили продолжить мое решение.
- 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
}
}
Где это springSecurityService взялось? У меня есть приложение Spring Boot/Security, которое не включает его. – sonoerin
@Quchie в этом случае для отключения другого пользователя с тем же именем пользователя? как насчет отключения нескольких логинов? – akiong
@akiong да, это будет использовать имя пользователя, чтобы заблокировать того же пользователя от входа в систему. – Quchie