프로젝트/Salle(살래) 중고거래 웹

Error - 비정상적인 LoginSession 점유

klyhyeon 2021. 1. 12. 23:24
728x90

메인 화면의 로그인/회원가입 탭은 LoginSession의 유무에 따라 사용자 ${nickName}님 으로 탭 화면이 전환됩니다. 그런데 Login 과정에서 비밀번호나 이메일이 틀린경우에도 LoginSession이 채워져 메인 화면에 ${공란}님 으로 뜨는 오류가 있었습니다. 몇가지 가설을 세우고 차례대로 해결해가면서 문제를 파악하려 했습니다.

 

가설 1. 로그인이나 회원가입에 실패해도 Model의 Member 객체에 입력값이 들어있다.

LoginInterceptor 클래스에서 session.setAttribute(name, value) name을 "member"로 처리하고, LoginController나 RegisterController 입력값도 "member" name으로 model에 담아줬습니다. 그리고 "member" model이 들어있는지에 따라 로그인 된 사용자로 판단하기 때문에 model member에 남아있는 값을 로그인으로 잘못 인식하는 오류가 있다고 생각했습니다.

이에 Login 객체를 따로 만들어 로그인과 회원가입, 회원관리를 구분해주었습니다. 또한 LoginController에서 로그인이 비정상적으로 이루어졌을 경우 return하기 전에 HttpSession.removeAttribute()로 session 값을 비우는 작업을 실행시켰습니다.하지만 오류는 해결되지 않았습니다. 

 

가설 2. LoginInterceptor가 동작하는 URI path에 "/login" 경로를 포함시켜줬기 때문이다.

WebServiceConfig 는 Interceptor가 언제 동작할 지 설정해주는 WebMvcConfiguer을 implements 하는 클래스 입니다. 로그인 한 사용자가 웹사이트 내 여러 페이지들로 요청을 전송받으며 옮겨 다녀도 loginSession이 유지될 수 있도록 모든 경로를 addPathPatterns로 지정해주었습니다. 그러다보니 login이 틀렸을 경우에 Controller에서 loginSession을 removeAttribute로 삭제했지만, PostHandle은 Controller에서 View로 가는 경로에 실행되기 때문에 modelAndView의 getModelMap 메서드로 인해 남아있던 "login" attribute를 재호출 하게되는 것 같습니다. session에 저장은 되지 않지만, "login"이 model로부터 재호출 되므로 JSP의 EL ${login}은 null이 아니라고 인식하고 login.getNickName() 값을 null로 return 해줍니다. 

 

따라서, 해결책은 WebServiceConfig의 LoginInterceptor excludePathPatterns()에 "/login/**"을 추가해주는 것이었습니다.

 

LoginInterceptor 코드

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
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.example.demo.application;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.SessionScope;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
import com.example.demo.domain.Login;
 
public class LoginInterceptor extends HandlerInterceptorAdapter {
    
    private static final String login = "login";
    //사용자 상호작용 logger에 LoginInterceptor 클래스를 저장
    private static final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 
        return true;
    }
 
    //View로 가기 전 실행되는 Interceptor
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
 
        //recreate session if session doesn't exist, if exist, retrieve the session
        HttpSession session = request.getSession(true);
        //get model by HashMap if it exists
        ModelMap modelMap = modelAndView.getModelMap();
        //get value Object key "login" from modelMap and modelAttributename "login" is added in Controller
        Object loginInter = modelMap.get("login");
                
        if (loginInter != null) {
            logger.info("새로운 로그인 성공");
            session.setAttribute("login", loginInter);
        }
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
 
    }
 
}
cs

 

WebserviceConfig 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.demo.domain;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
import com.example.demo.application.LoginInterceptor;
 
@Configuration
public class WebserviceConfig implements WebMvcConfigurer {
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/register/**""/login");
       }
}
 
cs

 

home. JSP 코드

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
    <c:choose>
                    <c:when test="${login == null}">
                        <a class="login" href="<c:url value="/login"/>"> 
                            로그인/회원가입
                        </a>
                    </c:when>
                    <c:otherwise>
                        <button id="link_logininfo" >  
                            ${login.getNickName()}님
                        </button>
                    <div class="member-loggedin">
                        <ul id="link_logintoggle">
                            <li>
                                <a href="/logout">
                                    로그아웃
                                </a>
                            </li>
                            <li>
                                <a href="<c:url value="/profile/${login.getNickName()}"/>">
                                    프로필
                                </a>
                            </li>                    
                        </ul>
                            <script>
                                    $("#link_logininfo").click(function() {
                                        $("#link_logintoggle").toggle();
                                    });
                            </script>
                        </div>
                    </c:otherwise>
                </c:choose>
cs