[SPRING] 동시 세션이있는 grails 3
SPRING동시 세션이있는 grails 3
iam이 내 프로젝트를 2.1.1에서 업그레이드하려고합니다. ~ 3.1.1
동시 세션에 몇 가지 문제가 있습니다. 예를 들면 ..
나는 브라우저 "chrome"에 사용자 이름 "AAA"로 로그인하고있다. 다른 사용자는 다른 브라우저에서 사용자 이름 "AAA"로 다시 로그인 한 다음 사용자 이름 "AAA"가 브라우저 "크롬"에서 자동으로 로그 아웃합니다
.. 내 코드
로그인 컨트롤러
package accounter
import com.vastpalaso.security.User
import grails.converters.JSON
import grails.plugin.springsecurity.SpringSecurityUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
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 grails.plugin.springsecurity.SpringSecurityUtils
import org.springframework.security.web.authentication.session.SessionAuthenticationException
import javax.servlet.http.HttpServletResponse
@Secured('permitAll')
class LoginController {
/** Dependency injection for the authenticationTrustResolver. */
AuthenticationTrustResolver authenticationTrustResolver
/** Dependency injection for the springSecurityService. */
def springSecurityService
def cifService
private static final Logger logger = LoggerFactory.getLogger(this)
/** 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() {
def username = session['SPRING_SECURITY_LAST_USERNAME']
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()
println "test"
}
else {
msg = message(code: 'springSecurity.errors.login.fail')
}
}
try {
boolean block = false;
block = cifService.addTryLogin(username)
if(session){
render([error: msg, block: block, reload: false] as JSON)
}
else{
render([error: msg, block: block, reload: true] as JSON)
}
}
//catch unknown RuntimeException, redirect to Error 500 server Error page
catch (RuntimeException e) {
logger.error(e.getMessage(), e)
redirect(controller: "error", action: "serverError")
return
}
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() {
def user = com.vastpalaso.security.User.findByUsername(springSecurityService.authentication.name)
def userDetails = com.vastpalaso.security.UserDetails.findByUser(user)
def cifUsergetCif
try{
cifUsergetCif = com.vastpalaso.app.cif.CifUser.findByUserDetails(userDetails)
session.setAttribute("company",cifUsergetCif.cif.corpName)
}
catch (Exception e){
println "e = "+e
println "You are loginning as admin!"
}
try {
println "params = "+params
def ipAddress = request.getHeader("Client-IP")
if (!ipAddress) {
ipAddress = request.getHeader("X-Forwarded-For")
}
if (!ipAddress) {
ipAddress = request.getRemoteAddr()
}
try{
cifService.resetTryLoginAddInfo(userDetails, ipAddress, session.id)
}catch (Exception e){
println "e = "+e
}
session.setAttribute("alias", userDetails.userAlias)
session.setAttribute("fullName", userDetails.firstName + " " + userDetails.lastName)
session.setAttribute("change", userDetails.forceChangePassword)
session.setAttribute("userType", userDetails.userType)
if(userDetails.language != null){
session[org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME] = new Locale(userDetails.language)
}
else{
session[org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME] = new Locale("id")
}
buildMenuList()
println "test = "
if (params.callback) {
render"${params.callback} (${[success: true,id: userDetails.id ,change: userDetails.forceChangePassword, username: springSecurityService.authentication.name, fullName: (userDetails.firstName + " " + userDetails.lastName)] as JSON})"
}
else {
render([success: true,id: userDetails.id, change: userDetails.forceChangePassword, username: springSecurityService.authentication.name, fullName: (userDetails.firstName + " " + userDetails.lastName)] as JSON)
}
}
//catch unknown RuntimeException, redirect to Error 500 server Error page
catch (RuntimeException e) {
logger.error(e.getMessage(), e)
redirect(controller: "error", action: "serverError")
return
}
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
}
def concurrentSession = {
def msg = "Your account is logged in from another browser or location."
if (springSecurityService.isAjax(request)) {
render([error: msg] as JSON)
}
else {
flash.message = msg
redirect action: 'auth', params: params
}
}
}
이것은 resources.groovy입니다.
// Place your Spring DSL code here
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.vastpalaso.helper.CustomSessionLogoutHandler
import org.springframework.security.web.session.ConcurrentSessionFilter
beans = {
sessionRegistry(SessionRegistryImpl)
customSessionLogoutHandler(CustomSessionLogoutHandler,ref('sessionRegistry'))
concurrencyFilter(ConcurrentSessionFilter) {
sessionRegistry = sessionRegistry
logoutHandlers = [ref("rememberMeServices"), ref("securityContextLogoutHandler")]
expiredUrl='/login/concurrentSession'
}
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')])
jmsConnectionFactory(org.apache.activemq.ActiveMQConnectionFactory) { brokerURL = "tcp://localhost:61616" }
}
이 코드는 내 페이지에서 복사합니다.
package com.vastpalaso.helper;
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());
}
}
해결법
-
==============================
1.이것은 현재 로그인 한 사용자 (동일한 사용자 이름과 암호)에 만료됩니다. 새 사용자는 아무런 문제없이 계속 로그인 할 수 있습니다.
이것은 현재 로그인 한 사용자 (동일한 사용자 이름과 암호)에 만료됩니다. 새 사용자는 아무런 문제없이 계속 로그인 할 수 있습니다.
이것을 src 폴더에 추가하여 SessionAuthenticationStrategy의 새로운 구현을 생성하십시오. grails 3 (src / main / groovy) in grails (2.x src / groovy). 이 이름을 ConcurrentSingleSessionAuthenticationStrategy.groovy로 저장하려는 달성하려는 이름을 기반으로 사용자 정의 이름이라고합니다.
package com.myapp.test; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.web.session.HttpSessionEventPublisher; import org.springframework.util.Assert; import org.springframework.security.core.session.SessionRegistry; import grails.plugin.springsecurity.SpringSecurityUtils; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; /** * Strategy used to register a user with the {@link SessionRegistry} after successful * {@link Authentication}. * * <p> * {@link RegisterSessionAuthenticationStrategy} is typically used in combination with * {@link CompositeSessionAuthenticationStrategy} and * {@link ConcurrentSessionControlAuthenticationStrategy}, but can be used on its own if * tracking of sessions is desired but no need to control concurrency. * * <p> * NOTE: When using a {@link SessionRegistry} it is important that all sessions (including * timed out sessions) are removed. This is typically done by adding * {@link HttpSessionEventPublisher}. * * @see CompositeSessionAuthenticationStrategy * * @author Luke Taylor * @author Rob Winch * @since 3.2 */ public class ConcurrentSingleSessionAuthenticationStrategy implements SessionAuthenticationStrategy { private SessionRegistry sessionRegistry; /** * @param sessionRegistry the session registry which should be updated when the * authenticated session is changed. */ public ConcurrentSingleSessionAuthenticationStrategy(SessionRegistry sessionRegistry) { Assert.notNull(sessionRegistry, "SessionRegistry cannot be null"); this.sessionRegistry = sessionRegistry; } /** * In addition to the steps from the superclass, the sessionRegistry will be removing * with the new session information. */ public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) { def sessions = sessionRegistry.getAllSessions( authentication.getPrincipal(), false); def principals = sessionRegistry.getAllPrincipals() sessions.each{ if(it.principal == authentication.getPrincipal()){ it.expireNow() } } } }
resources.groovy에서 :
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 org.springframework.security.core.session.SessionRegistryImpl; import com.myapp.test.ConcurrentSingleSessionAuthenticationStrategy; import org.springframework.security.web.session.ConcurrentSessionFilter // Place your Spring DSL code here beans = { sessionRegistry(SessionRegistryImpl) //I see you did not have this. Very dangerous! sessionFixationProtectionStrategy(SessionFixationProtectionStrategy){ migrateSessionAttributes = true alwaysCreateSession = true } //Initiate the bean concurrentSingleSessionAuthenticationStrategy(ConcurrentSingleSessionAuthenticationStrategy,ref('sessionRegistry')) registerSessionAuthenticationStrategy(RegisterSessionAuthenticationStrategy,ref('sessionRegistry')) sessionAuthenticationStrategy(CompositeSessionAuthenticationStrategy,[ref('concurrentSingleSessionAuthenticationStrategy'),ref('sessionFixationProtectionStrategy'),ref('registerSessionAuthenticationStrategy')]) concurrentSessionFilter(ConcurrentSessionFilter,ref('sessionRegistry')) }
config에서 마지막으로 다음 행을 추가하십시오.
grails.plugin.springsecurity.filterChain.filterNames = [ 'securityContextPersistenceFilter', 'logoutFilter', 'concurrentSessionFilter', 'rememberMeAuthenticationFilter', 'anonymousAuthenticationFilter', 'exceptionTranslationFilter', 'filterInvocationInterceptor' ]
희망이 도움이됩니다.
from https://stackoverflow.com/questions/39741426/grails-3-with-concurrent-session by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] 봄부터 컨트롤러에서 프런트 엔드로 데이터를 전달하는 방법 (0) | 2019.04.15 |
---|---|
[SPRING] 스프링 프로파일로 util : properties를로드하면 ID가 여러 번 발생합니다 (0) | 2019.04.15 |
[SPRING] 최대 절전 모드 컨텍스트 세션을 사용하여 일반 DAO 클래스를 만드는 방법 (0) | 2019.04.15 |
[SPRING] java.lang.NoClassDefFoundError : javax / 지속성 / SharedCacheMode (0) | 2019.04.15 |
[SPRING] SpringBeanAutowiringSupport가 jUnit 테스트에서 빈을 삽입하지 않습니다. (0) | 2019.04.15 |