본문 바로가기

카테고리 없음

[Spring/maven]프로젝트 회고 2. 로그인(유효성 체크 & 쿠키를 이용한 로그인 유지)

로그인 로직

 

1. 회원가입시 DB에 입력된 정보(id,password)와 대조하여 로그인 유효성체크를 하고 웹사이트의 접근을 허용한다.

2. 로그인을 한 유저들은 자신의 정보와 권한 만큼의 사이트의 기능들을 사용 할 수 있다. 로그인을 하지 않은 유저들의 사이트 이용을 막는것 또한 로그인 기능의 역할이다. 세션을 통해 로그인한 유저와 하지 않은 유저간의 사이트의 기능이용 제한에 차이를 둔다. 

3. 브라우저를 종료하면 사라지는 세션과는 달리 local저장소에 저장 될 수 있는 쿠키를 이용하여 로그인 유지를 구현 할 수 있다.

4. 쿠키만으로 로그인을 구현하면 보안에 굉장히 취약해지므로 세션과 쿠키를 같이 사용해 보안을 강화한다.

 

 

 

Oracle

ALTER table users ADD sessionkey varchar2(50) DEFAULT 'none' NOT NULL ;
alter table users add  sessionlimit timestamp;

로그인 유지기능을 사용하기 위해서 기존의 유저테이블에 sessionkey 와 sessionlimit를 추가해줍니다.

 

users.xml

	<!-- 로그인 -->
	<select id="loginById" parameterType="com.kosta.petner.bean.Users" resultType="com.kosta.petner.bean.Users">
	<![CDATA[
		select *
		from users 
		where id=#{id} 
	]]>
	</select>
	  
    <!-- 로그인된 경우 해당 세션id와 유효시간을 사용자 테이블에 세팅한다.-->
    <update id="keepLogin">
    <![CDATA[
        update users set sessionkey = #{sessionId}, sessionlimit = #{next} where id=#{id}
        ]]>
    </update>
     
    <!--유효기간이 남아 있으면서 해당 sessionId를 가지는 사용자 정보를 꺼내오는 부분-->
    <select id="checkUserWithSessionKey" resultType="com.kosta.petner.bean.Users" parameterType="String">
    <![CDATA[
        select * from users where sessionkey = #{sessionId} and sessionlimit > sysdate
        ]]>
    </select>

로그인 로그인유지, 로그인정보 조회  쿼리문을 각각 작성해줍니다.

 

UsersDAO

// 아이디+비번으로 로그인하기
Users loginById(Users users) throws Exception;
	
// 자동로그인 체크한 경우에 사용자 테이블에 세션과 유효시간을 저장하기 위한 메서드
public void keepLogin(String uid, String sessionId, Date next);
     
// 이전에 로그인한 적이 있는지, 즉 유효시간이 넘지 않은 세션을 가지고 있는지 체크한다.
public Users checkUserWithSessionKey(String sessionId);

 

 

UsersDAOImpl

	//로그인
	@Override
	public Users loginById(Users users) {
		Users vo = sqlSession.selectOne("mapper.users.loginById", users);
		return vo;
	}
	
	// 자동로그인 체크한 경우에 사용자 테이블에 세션과 유효시간을 저장하기 위한 메서드
	@Override
	public void keepLogin(String uid, String sessionId, Date next) {
		   Map<String, Object> map = new HashMap<String,Object>();
	        map.put("id", uid);
	        map.put("sessionId", sessionId);
	        map.put("next", next);
	        sqlSession.update("mapper.users.keepLogin",map);
		
	}
	
	// 이전에 로그인한 적이 있는지, 즉 유효시간이 넘지 않은 세션을 가지고 있는지 체크한다.
	@Override
	public Users checkUserWithSessionKey(String sessionId) {
		 return sqlSession.selectOne("mapper.users.checkUserWithSessionKey",sessionId);
	}

 

 

UsersService

//로그인
public Users login(Users users) throws Exception;
	
 // 자동로그인 체크한 경우에 사용자 테이블에 세션과 유효시간을 저장하기 위한 메서드
 public void keepLogin(String uid, String sessionId, Date next);
     
  // 이전에 로그인한 적이 있는지, 즉 유효시간이 넘지 않은 세션을 가지고 있는지 체크한다.
  public Users checkUserWithSessionKey(String sessionId);

 

UsersServiceImpl

//로그인
@Override
	public Users login(Users users) throws Exception{
		return usersDAO.loginById(users);
			}

//로그인유지
@Override
	public void keepLogin(String uid, String sessionId, Date next) {
		usersDAO.keepLogin(uid, sessionId, next);
		
	}

//로그인유지 체크
@Override
	public Users checkUserWithSessionKey(String sessionId) {
		return usersDAO.checkUserWithSessionKey(sessionId);
	}

 

UsersController

//로그인
@RequestMapping(value="/login", method=RequestMethod.POST)
    public String login(Users users, Model model, HttpServletResponse response) throws Exception {
        String returnURL = "";

        if ( session.getAttribute("authUser") != null ){
            // 기존에 login이란 세션 값이 존재한다면
            session.removeAttribute("authUser"); // 기존값을 제거해 준다.
        }

        //ID 비밀번호와 대조해서 로그인성공 (암호화 된 비밀번호랑 대조)
        Users authUser = usersService.login(users);

        if(authUser != null && bcryptPasswordEncoder.matches(users.getPassword(), authUser.getPassword())) {
            //System.out.println(authUser);
            session.setAttribute("authUser", authUser);
            session.setAttribute("uid", users.getId());

            returnURL = "redirect:/";

            // 1. 로그인이 성공하면, 그 다음으로 로그인 폼에서 쿠키가 체크된 상태로 로그인 요청이 왔는지를 확인한다.
            if(users.isUseCookie() ){ // users 클래스 안에 useCookie 항목에 폼에서 넘어온 쿠키사용 여부(true/false)가 들어있을 것임
                // 쿠키 사용한다는게 체크되어 있으면...
                // 쿠키를 생성하고 현재 로그인되어 있을 때 생성되었던 세션의 id를 쿠키에 저장한다.
                Cookie cookie = new Cookie("loginCookie", session.getId());
                // 쿠키를 찾을 경로를 컨텍스트 경로로 변경해 주고...
                cookie.setPath("/");
                int amount = 60 * 60 * 24 * 7;
                cookie.setMaxAge(amount); // 단위는 (초)임으로 7일정도로 유효시간을 설정해 준다.
                // 쿠키를 적용해 준다.
                response.addCookie(cookie); 

                // currentTimeMills()가 1/1000초 단위임으로 1000곱해서 더해야함 
                Date sessionlimit = new Date(System.currentTimeMillis() + (1000*amount));
                // 현재 세션 id와 유효시간을 사용자 테이블에 저장한다.
                usersService.keepLogin(authUser.getId(), session.getId(), sessionlimit);

                System.out.println("Cookie:" +"Cookie 있음, "+ "로그인 유지시간:" +sessionlimit);
            }

        }else { // 로그인에 실패한 경우
            model.addAttribute("check", 1);
            model.addAttribute("message", "아이디와 비밀번호를 확인해주세요.");
            model.addAttribute("page", "users/login/loginForm");
            returnURL = "/layout/main"; // 로그인 폼으로 다시 가도록 함
        }

        return returnURL; // 위에서 설정한 returnURL 을 반환해서 이동시킴
    }

//로그아웃
@RequestMapping(value="/logout",method = RequestMethod.GET)
public String logout(HttpSession session, HttpServletRequest request, HttpServletResponse response) {

    Object obj = session.getAttribute("authUser");
    if ( obj != null ){
        Users users = (Users)obj;
        // null이 아닐 경우 제거
        session.removeAttribute("authUser");
        session.invalidate(); // 세션 전체를 날려버림
        //쿠키를 가져와보고
        Cookie loginCookie = WebUtils.getCookie(request, "loginCookie");
        if ( loginCookie != null ){
            // null이 아니면 
            loginCookie.setPath("/");
            // 쿠키는 없앨 때 유효시간을 0으로 설정하는 것 !!! invalidate같은거 없음.
            loginCookie.setMaxAge(0);
            // 쿠키 설정을 적용한다.
            response.addCookie(loginCookie);

            // 사용자 테이블에서도 유효기간을 현재시간으로 다시 세팅해줘야함.
            Date date = new Date(System.currentTimeMillis());
            usersService.keepLogin(users.getId(), session.getId(), date);
        }
    }
    return "redirect:/"; // 로그아웃 후 메인으로 이동하도록...함
}

authUser라는 변수에 세션을 담고 세션 값이 있는지 없는지를 체크한다.

if / else 를 통해 세션이 있을 때(로그인성공) 와 없을 때(로그인 실패)로 분기를 나눕니다. 

로그인 성공시: 세션, 쿠키 값을 줌

로그인 실패시: 변수 check, message에 value값을 담아  jsp에 넘김

 

체크박스에 로그인유지를 클릭시 쿠키를 생성하고 쿠키의 유지기간을 7일로 설정한 뒤 

sessionkey에 update 해줍니다.

 

loginForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="cssPath" value="${pageContext.request.contextPath}/resources/css"/>
<c:set var="imgPath" value="${pageContext.request.contextPath}/resources/images"/>



<style>
  .login_wr .logo{text-align: center; padding:30px 0;}
  .login_form{margin-bottom:12px;}
  .login_form .f_row{border:1px solid #e4e4e4; padding-bottom: 0; margin-bottom:28px; border-radius: 5px; overflow: hidden;}
  .login_form input{width:100%; height:52px; border:none; border-radius: 0;;}
  .login_form input:focus{border:none; outline:none;}
  .login_form input:nth-child(1){border-bottom: 1px solid #e4e4e4;}
  .login_form .login_btn{background-color: #e4e4e4; color:var(--black); height:52px; }  
  
  .login_wr .info_are label.fcCbox1 > span:before{color:var(--fcc-font01) !important}
  .login_wr .info_area label.fcCbox1 span{color:#e4e4e4}
  .login_wr p:nth-child(1){
    display:flex; 
    justify-content: space-between; 
    padding-bottom: 10px;
    color:#e4e4e4;
  }
  .login_wr p:nth-child(3){text-align: center; text-decoration: underline; padding-top:20px;}
  .kakao_btn{background-color:#FAE100; color:#3B1E1E; font-size:1.4rem; height:52px;}
  #close_btn{position: height:20px; width:50px;}
  .login_option{
    padding-top: 30px;
    border-top: 1px solid #e4e4e4;
    margin-top: 30px;
  }
  
  .login_optionA{
    display: flex;
    justify-content: space-between;
    margin-top: 15px;
    color:gray;
   }
   
       .modal {
        position: absolute;
        top: 0;
        left: 0;
		
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0,.5);
      }

      .modal.show {
        display: block;
      }

      .modal_body {
        position: absolute;
        top: 30%;
        left: 50%;

        width: 280px;
        height: 100px;

        padding: 40px;

       

        background-color: rgb(255, 255, 255);
        border-radius: 10px;
        box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);

        transform: translateX(-50%) translateY(-50%);
      }
      
       .modal_btn {
        position: absolute;
        top: 50%;
        left: 70%;
		
        width: 280px;
        height: 100px;

        padding: 40px;
        

       } 

</style>


<script>
		$(document).ready(function(){
			$("#close_btn").click(function(){
				$(".modal").hide();
			})
			$("#x_button").click(function(){
				$(".modal").hide();
			})

			
		})

		//뒤로가기 모달창 뜨지마라
	window.history.pushState({page: 1}, "", "");	
	window.onpopstate = function(event) {
	  // "event" object seems to contain value only when the back button is clicked
	  // and if the pop state event fires due to clicks on a button
	  // or a link it comes up as "undefined" 
	
	  if(event){
		  $(".modal").hide();  }
	 
	}

	
	

	
	function login() {
		loginForm.submit();
		$("input:checkbox[id='useCookie']").prop("checked",true);
		$("input:checkbox[id='useCookie']").prop("checked",false);
			
	
	}
	

</script>

<div class="w45 login_wr">      
  <div class="logo"><img src="./images/logo3.svg" alt=""></div>
  <form action="./login" method="POST" id="loginForm" class="login_form">
  	  <div class="f_row">
      <input type="text" placeholder="ID" name="id" id="id" >
      <input type="password" placeholder="비밀번호" name="password" id="password">
    </div>
     <input type ="submit" class="pet_btn login_btn transition02" value= "로그인" onsubmit="login()"/>
    <div class="login_optionA">
    	<label class="fcCbox1">
	      <input type="checkbox" id="useCookie" name="useCookie" >
	      <span>로그인 유지하기</span>
	    </label>
	    <span><a href="./findId" >아이디</a>&nbsp;/&nbsp;<a href="./findPass">비밀번호찾기</a></span>
   </div>
  </form>

	<div class="form-label-group">
		<c:if test="${check == 1}">
		   <div class="modal">
				<div class="modal_body">
						<button class="" id="x_button" style= "color: black; cursor:pointer; background-color:white; margin: -35px 50px 0px 300px; position:absolute ">❌</button>
							<p style="color: #FF9614; ">Petner &#128062</p>
							<p style="text-decoration: none;">	&#128531 ${message} </p>
							
						<div class="modal_btn">
							<button class="pet_btn" id="close_btn" style= "background-color:#FF9614;">닫기</button>
						</div>
				</div>				
   		  </div>
		</c:if>
	</div>
	<div class="info_area">
	    <p class="login_option"><a href="./join">펫트너회원가입</a></p>
	 </div>
</div>

모달창을 만들고(구글링 열심히해서) 로그인 실패시 모달창이 뜨도록 설정한다.

( 로그인이 실패 하고 모달창이 뜬 뒤 다른 페이지로 넘어가도 모달창이 떠있는상태로 되어있는 현상이 발생해 중간에 모달창이 안뜨게 해주었다)

 

로그인 성공시에는 useCookie 체크박스에 따라서  true false 값을 주어 로그인 유지를 설정하였다.

 

 

 

구현화면 

 

로그인 유지 클릭시,

 

sessionlimit 와 sessionkey 칼럼에 현재날짜+7일의 값과 고유의 세션키가 적용되어 자동으로 로그인이 가능하다.

 

로그아웃 클릭 시,

SessionLimit값이 현재시각으로 바뀌어 더이상 쿠키가 유지되지 않으며 자동로그인을 막는다.