본문 바로가기
Development/Java

[Spring Security] Max Session 적용하기

by 폴피드 2017. 11. 20.
728x90
반응형

기존 소스에 동시 로그인을 제어하기 위해서 Maxsession 설정을 넣어보았다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
public class ResourceSecurityConfiguration extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/""/login/**","/browser/**""/error/**").permitAll()
                .antMatchers("/private/**").authenticated()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .successHandler(new LoginSuccessHandler())
                .failureHandler(new LoginFailureHandler())
                .and()
                .logout().permitAll()
                .and()
                .sessionManagement()
                .maximumSessions(1)
                .maxSessionsPreventsLogin(true);
    }
}
cs


위 설정주에 보면 sessionManagement가 추가되어있다. 그리고 maximunSessions가 1로 설정되어있고 maxSessionPreventsLogin 이 true 로 설정되어있다. 

maximunSessions : Session 허용 개수

maxSessionPreventsLogin : true 일 경우 기존에 동일한 사용자가 로그인한 경우에는 login 이 안된다. false 일경우는 로그인이 되고 기존 접속된 사용자는 Session이 종료된다. false 가 기본이다.

이렇게 설정하고 나서 동시로그인을 해보았다.



처음 로그인 한 경우에는 잘 된다.



두번째 동일한 계정으로 접속을 할 경우에는 이런 메세지가 나온다. 

(물론 이 화면은 내가 LoginFailureHandler에서 페이지를 설정해놓았기 때문에 저런 화면이 나온다.)



실제로 Log를 보면 org.springframework.security.web.authentication.session.SessionAuthenticationException: Maximum sessions of 1 for this principal exceeded 라는 Exception 이 발생한 것을 볼수 있다.




실제로 어디에서 Exception이 발생하는지 궁금해서 Debug를 찍어보았다. 여러면 돌려보면서 확인 한 결과 AbstractAuthenticationProcessingFilter 에서 발생을 했다. 이 Filter에 보면 sessionStrategy.onAuthentication 이라는 메소드를 호출한다. 이 메소드를 따라가 보면 CompositeSessionAuthenticationStrategy 클래스에서 다시 ConcurrentSessionControlAuthenticationStrategy 클래스로 다시 Delegating 된다. ConcurrentSessionControlAuthenticationStrategy 의 onAuthentication 메소드는 아래와 같이 구현되어있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public void onAuthentication(Authentication authentication,
            HttpServletRequest request, HttpServletResponse response) {
 
        final List<SessionInformation> sessions = sessionRegistry.getAllSessions(
                authentication.getPrincipal(), false);
 
        int sessionCount = sessions.size();
        int allowedSessions = getMaximumSessionsForThisUser(authentication);
 
        if (sessionCount < allowedSessions) {
            // They haven't got too many login sessions running at present
            return;
        }
 
        if (allowedSessions == -1) {
            // We permit unlimited logins
            return;
        }
 
        if (sessionCount == allowedSessions) {
            HttpSession session = request.getSession(false);
 
            if (session != null) {
                // Only permit it though if this request is associated with one of the
                // already registered sessions
                for (SessionInformation si : sessions) {
                    if (si.getSessionId().equals(session.getId())) {
                        return;
                    }
                }
            }
            // If the session is null, a new one will be created by the parent class,
            // exceeding the allowed number
        }
 
        allowableSessionsExceeded(sessions, allowedSessions, sessionRegistry);
}
cs


4번 라인 : sessionRegistry 에서 현재 인증된 Principal과 동일한 session을 모두 가져오게 된다.  

7번 라인 : 먼저 로그인했던 session 이 있기 때문에 sessionCount 는 1이 된다.

8번라인 : allowedSessions 는 sessionManagement의 maxSession이 1로 설정했기 때문에 1이다. 


그래서 20번 라인의 if문을 통과해서 for문을 수행하게 되는데 sessionId가 다르기 때문에 그냥 빠져나오고 36 라인의 allowableSessionsExceeded가 실행된다.


allowableSessionsExceeded 메소드 안에는 아래와 같은 구문이 있는데 결론적으로 아래 조건문이 만족하게 되고 Exception을 throw 하게 된다.


1
2
3
4
5
6
if (exceptionIfMaximumExceeded || (sessions == null)) {
    throw new SessionAuthenticationException(messages.getMessage(
            "ConcurrentSessionControlAuthenticationStrategy.exceededAllowed",
            new Object[] { Integer.valueOf(allowableSessions) },
            "Maximum sessions of {0} for this principal exceeded"));
}
cs



728x90
반응형