본문 바로가기
개발 공부 Today I Learned

[국비 62일차 TIL] 로그인 확인, 횟수 제한

by 개발자신입 2024. 2. 21.
반응형

 

-----------------------------------------------------------------------------
오늘의 교훈 : 프로젝트는 와이어 프레임 짜고 생각하자...!
-----------------------------------------------------------------------------

 

Util.java

	// 2024-02-21 psd
	public HttpServletRequest req() { // 리퀘스트를 받아주는
		ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
		HttpServletRequest request = sra.getRequest();
		return request;
	}
}

 

더 짧게 쓰기 위해서 util에서 받아오도록 getSession() 생성

	public HttpSession getSession() { 세션을 주는 -> 코드가 중복되므로 중복을 없애보자
		ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
		HttpServletRequest request = sra.getRequest();
		HttpSession session = request.getSession();
		return session;
	}
}

 

-> 위의 코드를 이렇게 중복 제거

	public HttpServletRequest req() { // 리퀘스트를 받아주는
		ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
		HttpServletRequest request = sra.getRequest();
		return request;
	}
	
	public HttpSession getSession() { // 세션을 주는 -> 윗 부분과 코드가 중복되므로 중복을 없애보자
		HttpSession session = req().getSession();
		return session;
	}

 

-> BoardService에서 util을 이용해 중복을 줄였으니 컨트롤러에서도 삭제해줘야함.

BoardController.java

@PostMapping("/write")에서 if문 수정
result의 write 변수에서 request삭제, dto만 남김.

		// 2024-02-16 글쓰기 + 02-20 
		@PostMapping("/write") // 내용,제목 -> DB에 저장 -> 게시판으로 
		public String write(WriteDTO dto, HttpServletRequest request) { 
			
			if(util.getSession().getAttribute("mid") != null) {
				int result = boardService.write(dto); // dto, request
				if(result == 1) {
					return "redirect:/detail?no="+dto.getBoard_no();
				} else {
					return "redirect:/error";
				}
		} else {
			return "redirect:/login";
		}
       
        
        @PostMapping("/commentWrite")
		public String commentWrite(CommentDTO comment) {				
			int result = boardService.commentWrite(comment); // boardService 실행 시 dto를 가지고 날아감
			return "redirect:/detail?no="+comment.getNo(); // 글 번호를 받아서 글 번호로 페이지 이동 
			
		}

BoardService.java

	public int write(WriteDTO dto) {
		HttpServletRequest request = util.req();
		HttpSession session = request.getSession();
		
		dto.setMid((String) session.getAttribute("mid"));
		return boardDAO.write(dto);
	}

 

-> 짧게 줄여 중복을 없애보자 wirte, commentWrite

 

util.getSession().getAttribute("mid"));

	public int write(WriteDTO dto) {		
		dto.setMid((String) util.getSession().getAttribute("mid"));
		return boardDAO.write(dto);
	}

	public int commentWrite(CommentDTO comment) {
		comment.setMid((String) util.getSession().getAttribute("mid"));
		return boardDAO.commentWrite(comment); // DAO에게 일 시키기
	}

 

-> 중복을 줄인 부분은 컨트롤러에서도 수정해줘야 함.

BoardService.java

BoardService 의 comment부분 코드 줄여주기

	public int commentWrite(CommentDTO comment) { 
		// 가져오는 것 : 댓글 내용, 글 번호 + mid
		comment.setMid((String) util.getSession().getAttribute("mid"));
		return boardDAO.commentWrite(comment); // DAO에게 일 시키기
	}

 

-> mapper 내용 수정해야함.

 

board-mapper.xml

  • board_no의 변수명 수정 #{board_no}
  • AND mno=() 쿼리문 추가

 

CDATA는 Character Data의 약어로, XML 또는 HTML 문서에서 문자 데이터를 포함하는 데 사용됩니다. CDATA 섹션은 특별한 문자나 태그로부터 내용을 보호하고, 해당 내용이 파서에 의해 해석되지 않도록 합니다.

	<update id="postDel" parameterType="WriteDTO">
		<![CDATA[
		UPDATE board SET board_del='0' WHERE board_no=#{board_no} 
		AND mno=(SELECT mno FROM member WHERE mid=#{mid}))
		]]>
	</update>

 

 

BoardDTO.java

DTO에서 board_write를 mname으로 변경하기 -> 그에 따라 다른 파일들에서도 변경해줘야 한다.

 

// 게시판, 톺아보기, 글 삭제 등.. 변경해야함


// board-mapper의 boardList에서 변경 : board_write as mname
// detail 부분에 m.mname as board_write를 m.mname으로 변경 / m.mid 추가 

// board.jsp : board_write를 mname으로 변경<td>${row.mname }</td>

import lombok.Data;

@Data
public class BoardDTO {
	private int board_no, board_count, comment;
	private String board_title, board_content, mname, mid, board_date, board_ip;
}

 

 

detail.jsp

 

+ 댓글 출력창에 수정, 삭제 아이콘 이미지 추가하기

 

<c:if test="${detail.mid eq sessionScope.mid }">
    <img alt="edit" src="./assets/img/pencil.png">
    <img alt="delete" src="./assets/img/deletey.png" title="글 삭제" onclick="deletePost(${detail.board_no})">
</c:if>

 

 

-> 게시판 디테일에 같은 아이디인지 확인하기

 

${detail.mname } 
<c:if test="${detail.mid eq sessionScope.mid }">
    같은 아이디입니다.
</c:if>

// 게시글 디테일 부분 코드

<h2 class="section-heading text-uppercase">게시글 디테일(톺아보기)</h2>
			</div>
			<div class="card mb-4" style="min-height: 500px;">
				<div class="card-body">
					<div class="h2">${detail.board_title }</div>
					<div class="row p-2 bg-secondary">
						<div class="col align-middle text-start">
							${detail.mname }
							<c:if test="${detail.mid eq sessionScope.mid }">
								<img alt="edit" src="./assets/img/pencil.png">
								<img alt="delete" src="./assets/img/deletey.png" title="글 삭제" onclick="deletePost(${detail.board_no})">
							</c:if>
						</div>
						<div class="col align-middle text-end">${detail.board_date }</div>
					</div>
					<div class="mt-4 vh -75">${detail.board_content }</div>
				</div>
			</div>
			<button class="btn btn-warning" onclick="history.back()">게시판으로</button>
			<button class="btn btn-warning" onclick="history.go(-1)">게시판으로</button>

 

-> 댓글 출력창도 로그인한 아이디와 같은지 확인 코드 

 

<c:if test="${c.mid eq sessionScope.mid }">
    <img alt="edit" src="./assets/img/pencil.png">
    <img alt="delete" src="./assets/img/deletey.png" title="댓글 삭제" onclick="deleteComment(${c.no })">
</c:if>

 

			<!-- 댓글 출력창 20240219 -->
			<div class="mt-2">
				<c:forEach items="${commentsList}" var="c">
					<div class="card mb-3">
						<div
							class="card-header bg-success text-white d-flex justify-content-between align-items-center">
							<!-- 댓글 헤더 좌측 -->
							<div>
								${c.mname } / ${c.mid }
								<c:if test="${c.mid eq sessionScope.mid }">
									<img alt="edit" src="./assets/img/pencil.png">
									<img alt="delete" src="./assets/img/deletey.png" title="댓글 삭제" onclick="deleteComment(${c.no })">
								</c:if>
							</div>
							<!-- 댓글 정보 우측 -->
							<div class="text-end">
								<div>${c.cip } | ${c.cdate } | ${c.clike }</div>
							</div>
						</div>
						<div class="card-body">
							<p class="card-text">${c.comment}</p>
						</div>
					</div>
				</c:forEach>
			</div>
			<!-- 댓글 끝 -->

 

detail.jsp

2024-02-21 댓글 삭제 버튼 jQuery function으로 추가

function deleteComment(no){
//	Swal.fire("댓글 삭제", no+"번 댓글을 삭제합니다.","warning");

	if(confirm("댓글을 삭제할까요?")){
		location.href="./deleteComment?no=${board.board_no}&cno="+no; // get방식
	}
}
</script>

 

 

댓글 삭제가 안되는 상태?

 

해결방법 :  

${board.board_no} 를 ${detail.board_no}로 변경.

board아니라 detail임

// 2024-02-21 댓글 삭제 버튼
function deleteComment(no){
	if(confirm("댓글을 삭제할까요?")){
		location.href="./deleteComment?no=${detail.board_no}&cno="+no;
	}
}

BoardController.java

deleteComment() 

		@GetMapping("/deleteComment")
		public String deleteComment(@RequestParam("no") int no, @RequestParam("cno") int cno) {
			System.out.println("no : " + no);
			System.out.println("cno : " + cno);
			return "redirect:/detail?no="+no; // 댓글 삭제 후에 원래의 게시물 페이지로 다시 돌아가기 위함
		}

 

-> CommentDTO에 담아서

BoardService.java

	// 2024-02-21 
	public int deleteComment(int no, int cno) {
		CommentDTO dto = new CommentDTO();
		dto.setNo(cno);
		dto.setBoard_no(no);
		dto.setMid((String) util.getSession().getAttribute("mid"));
		
		return boardDAO.deleteComment(dto);
	}

 

-> BoardDAO에 deleteComment 생성

BaordDAO.java

	public int deleteComment(CommentDTO dto) {
		return sqlSession.update("board.deleteComment", dto);
	}

 

-> board-mapper에 추가

board-mapper.xml

	<!-- 2024-02-21 본인 글만 삭제하도록 -->
	<update id="deleteComment" parameterType="commetDTO">
		UPDATE comment SET cdel='0' 
		WHERE cno=#{no}	AND board_no=#{board_no} AND mno=(SELECT mno FROM member WHERE mid=#{mid})
	</update>

Util.java

util에 ip 추가

	// ip
	public String getIP() { // 유효객체 만들어서 사용
		HttpServletRequest request = req();
		String ip = request.getHeader("X-FORWARDED-FOR");
        if(ip == null) {
           ip = request.getHeader("Proxy-Client-IP");
        }
        if(ip == null) {
           ip = request.getHeader("WL-Proxy-Client-IP");   
        }
        if(ip == null) {
           ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if(ip == null) {
           ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if(ip == null) {
           ip = request.getRemoteAddr();
        }
        return ip;
     }

BoardDTO.java

boardDTO와 writeDTO에 String 타입으로 ip 추가

  • BoardDTO : board_ip
  • WriteDTO : ip
@Data
public class BoardDTO {
	private int board_no, board_count, comment;
	private String board_title, board_content, mname, mid, board_date, board_ip;
}
@Data
public class WriteDTO {
	private int board_no;
	private String title, content, mid, ip;
}

BoardService.java

public int write(WriteDTO dto) {
		dto.setMid((String) util.getSession().getAttribute("mid"));
		dto.setIp(util.getIP()); // 아까 util에서 생성한 ip 부분
		
		return boardDAO.write(dto);
	}

 

 

board-mapper.xml

write의 sql 문 : board_ip와 #{ip} 추가하기

	<insert id="write" parameterType="writeDTO">
		INSERT INTO board (board_title, board_content, mno, board_ip) 
		VALUES (#{title}, #{content}, (SELECT mno FROM member WHERE mid=#{mid}), #{ip})
		<selectKey resultType="Integer" keyProperty="board_no" order="AFTER">
			SELECT LAST_INSERT_ID()  <!-- 작성 후 내가 쓴 글로 돌아가기 -->
		</selectKey>
	</insert>

 

BoardService.java

댓글에 ip 추가하기 : commentWrite()

comment.setCip(util.getIP());

public int commentWrite(CommentDTO comment) { 
		comment.setMid((String) util.getSession().getAttribute("mid"));
		comment.setCip(util.getIP());
		return boardDAO.commentWrite(comment); // DAO에게 일 시키기
	}

 

board-mapper.xml

commentWrite에 cip와 #{cip} 추가

   <insert id="commentWrite" parameterType="commentDTO">
      INSERT INTO comment (board_no, ccomment, mno, cip) 
      VALUES (#{no}, #{comment}, (SELECT mno FROM member WHERE mid=#{mid}), #{cip})
   </insert>

BoardService.java

write에 엔터키 처리 코드 추가하기

	public int write(WriteDTO dto) {
		
		// 엔터키 처리
		dto.setContent(dto.getContent().replaceAll("(\r\n|\r|\n|\n\r)", "<br>"));

		dto.setMid((String) util.getSession().getAttribute("mid"));
		dto.setIp(util.getIP());
		return boardDAO.write(dto);
	}

 

board.jsp

글쓰기 버튼 : 로그인한 사용자들만 보도록  (modal에 있는 글쓰기 버튼이 로그인 해야 보임)

<c:if test="${sessionScope.mid ne null }">
	<button type="submit" class="btn btn-dark">글 등록하기</button>
</c:if>

 

글쓰기 모달 만드는 부분 전체코드

	<!-- 글쓰기 모달 만들기 -->
	<div class="modal" id="write">
		<div class="modal-dialog modal-xl">
			<div class="modal-content">
				<div class="modal-header">
					<h3 class="modal-title">글쓰기 창입니다.</h3>
					<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
				</div>
				<div class="modal-body">
					<div class="mt-2">
						<form action="./write" method="post" onsubmit="return writeCheck()" name="frm">
							<input type="text" id="title" name="title" class="form-control mb-2" required="required" placeholder="제목을 입력하세요">
							<textarea id="summernote" name="content" class="form-control mb-2 vh-500" required="required"></textarea>
							<!-- 글쓰기 버튼 (로그인 한 사람만 보이도록) -->
							<c:if test="${sessionScope.mid ne null }">
								<button type="submit" class="btn btn-dark">글 등록하기</button>
							</c:if>
						</form>
					</div>
				</div>
				<div class="modal-footer">로그인 한 사용자만 글을 쓸 수 있어요!</div>
			</div>
		</div>
	</div>

BoardController.java

로그인 여부 검사 코드 util.getSession().getAttribute("mid") != null

 

@PostMapping("/write")

 

// mid가 세션에 존재하면 boardService.write(dto)를 호출해서 게시글 작성하도록 함.
// 작성이 성공하면 해당 게시물의 상세페이지로 이동
// 작성이 실패하면 /error 페이지로 리다이렉트
// mid가 세션에 존재하지 않는다면 /login 페이지로 리다이렉트

 // 로그인하지 않은 사용자가 write페이지에 접근하려 할 때 생기는 오류
 		@GetMapping("/write")
		public String write() {
			return "redirect:/login?error=2077"; 
		}

		@PostMapping("/write") // 내용,제목 -> DB에 저장 -> 게시판으로 
		public String write(WriteDTO dto, HttpServletRequest request) { 
			
			if(util.getSession().getAttribute("mid") != null) {
				int result = boardService.write(dto); 
				if(result == 1) {
					return "redirect:/detail?no="+dto.getBoard_no();
				} else {
					return "redirect:/error";
				}
		} else {
			return "redirect:/login";
		}
	}
		
		// 로그인 확인
		@PostMapping("/commentWrite")
		public String commentWrite(CommentDTO comment) {
			if(util.getSession().getAttribute("mid") != null) {
				int result = boardService.commentWrite(comment); 
				return "redirect:/detail?no="+comment.getNo();
			} else {
				return "redirect:/login"; 
		}
	}

 

login.jsp

Swal 스크립트 추가

<!-- Swal alert -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

<!-- jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g=="
	crossorigin="anonymous" referrerpolicy="no-referrer"></script>
	<!-- 2024-02-21 파라미터로 오는 error가 있다면 에러 화면에 출력하기 -->
	<c:if test="${param.error ne null}">
		<script type="text/javascript">
			Swal.fire("ERROR HERE", "잘못된 접근입니다!", "warning");
		</script>
	</c:if>
	<c:if test="${param.login ne null }">
		<script type="text/javascript">
			Swal.fire("로그인 불가", "올바른 아이디와 비번을 입력해주세요.", "warning");
		</script>
	</c:if>
</body>
</html>

 

LoginController.java

// loginService.login(loginDTO) = 사용자 로그인 정보 확인

// 1이면 로그인 성공 -> 세션 생성 후 세션에 아이디, 이름 저장 -> index로 이동

// 로그인 실패 -> 로그인 페이지로 이동 후 오류 메세지 팝업

 

return 코드 추가

		LoginDTO login = loginService.login(loginDTO);
		if(login.getCount() == 1) {
			// 세션 만들기
			HttpSession session = request.getSession();
			session.setAttribute("mid", id);
			session.setAttribute("mname", login.getMname());
			return "redirect:/index";
		} else {
			return "redirect:login?login=2000"; 
		}
	}

 


 

LoginController.java

잘못된 로그인은 로그인 창으로 이동하기 = 5번 시도하면 잠그기

 

-> DB에서 member 테이블에  mcount 생성하기 (모두 1로 변경)

 

// loginService.login(loginDTO) = 사용자 로그인 정보 확인

// 로그인 성공하면 세션에 아이디, 이름 저장

// loginService.mcountUp(loginDTO);를 통해 시도 횟수 0으로 초기화

// 로그인 실패하면 loginService.mcountUp(loginDTO);을 통해 시도 횟수 증가시킴

	@PostMapping("/login")
	public String login(HttpServletRequest request) {
		String id = request.getParameter("id");
		String pw = request.getParameter("pw");
		
		LoginDTO loginDTO = new LoginDTO();
		loginDTO.setId(id);
		loginDTO.setPw(pw);
		
		LoginDTO login = loginService.login(loginDTO);
		if(login.getCount() == 1) {
			// 세션 만들기
			HttpSession session = request.getSession();
			session.setAttribute("mid", id);
			session.setAttribute("mname", login.getMname()); // mname은 DTO에 있으니까
			
			// 해당 id의 mcount를 0으로 만들기
			return "redirect:/index";
		} else {
			// 잘못된 로그인은 로그인 창으로 이동하기 = 5번 시도하면 잠그기.
			// 해당 id의 mcount를 +1
			loginService.mcountUp(loginDTO); // 로그인DTO에 다 있으니까
			return "redirect:/login?login=2000"; // 로그인 성공,실패 어떤 상황이어도 인덱스로 넘어감
		}
	}

 

-> mcountUp 메소드 생성해주기 (서비스, DAO)

LoginService.java 

return이 없음

	public void mcountUp(LoginDTO loginDTO) {
		loginDAO.mcountUp(loginDTO);
	}
}

 

LoginDAO.java

return이 없음

	public void mcountUp(LoginDTO loginDTO) {
		sqlSession.update("login.mcountUp", loginDTO);
	}

 

 

login-mapper.xml

login에 mcount 추가

 : UPDATE member SET mcount=mcount+1 WHERE mid=#{id}

	<select id="login" parameterType="loginDTO" resultType="loginDTO">
	<![CDATA[
		SELECT COUNT(*) as count, mname, mcount, mpw as pw 
		FROM member
		WHERE mid=#{id} AND mgrade > 4
	]]>
	</select>  


	<!-- 2024-02-21 로그인마다 +1씩 -->
	<update id="mcountUp" parameterType="loginDTO">
		UPDATE member SET mcount=mcount+1 WHERE mid=#{id}
	</update>

 


LoginController.java

	@PostMapping("/login")
	public String login(HttpServletRequest request) {
		String id = request.getParameter("id");
		String pw = request.getParameter("pw");
//		System.out.println("id: "+id+" / pw : "+pw);
		
		LoginDTO loginDTO = new LoginDTO();
		loginDTO.setId(id);
		loginDTO.setPw(pw);
		
		LoginDTO login = loginService.login(loginDTO);
		if(login.getCount() == 1 && login.getMcount() < 5) { // 로그인 성공 & 시도가 5번보다 작을 때

			if(login.getPw().equals(loginDTO.getPw())) { // 비번 비교하기
				// 세션 만들기
				HttpSession session = request.getSession();
				session.setAttribute("mid", id);
				session.setAttribute("mname", login.getMname()); // mname은 DTO에 있으니까
				
				// 해당 id의 mcount를 0으로 만들기
				loginService.mcountReset(loginDTO);
				return "redirect:/index";
			} else {
				// mcountUp
				loginService.mcountUp(loginDTO);
				return "redirect:/login?count="+login.getMcount();
			}
		} else {
			// 잘못된 로그인은 로그인 창으로 이동하기 = 5번 시도하면 잠그기.
			// 해당 id의 mcount를 +1
			loginService.mcountUp(loginDTO); // 로그인DTO에 다 있으니까
			return "redirect:/login?login=1004"; // 로그인 성공,실패 어떤 상황이어도 인덱스로 넘어감
	}
}

 

-> mcountReset 생성 (서비스, DAO)

 

LoginService.java

	public void mcountReset(LoginDTO loginDTO) {
		loginDAO.mcountReset(loginDTO);
	}

LoginDAO

	public void mcountReset(LoginDTO loginDTO) {
		sqlSession.update("login.mcountReset", loginDTO);
	}

login.jsp

<body> 섹션 아래에

	<c:if test="${param.count ne null }">
		<script type="text/javascript">
			let count = ${param.count};
			if(count < 5){
				Swal.fire("다시 생각해보자.", count+"번 시도했다!", "warning");	
			} else {
				Swal.fire("로그인 여러번 시도 감지", "ID 잠금처리 실시", "warning");
			}
		</script>
	</c:if>

 

login-mapper.xml

  	<update id="mcountReset" parameterType="loginDTO">
  		UPDATE member SET mcount=1 WHERE mid=#{id}
  	</update>

 

반응형

댓글