📂 Filter (클라이언트 -> Filter -> Dispatcher Servlet)
" 클라이언트의 요청/ 응답을 걸러내거나, 첨가하는 클래스 "
📕 로그인 필터
: 로그인 필더가 없다면 로그인하지 않는 회원이 마이페이지의 주소를 주소창에 입력해서
회원이 아닌데도 마이페이지에 접근할 수 있다.
이를 방지하기 위해 로그인한 회원만 마이페이지에 접근할 수 있도록 로그인 필터를 만들어준다!
✳ LoginFilter
package edu.kh.project.common.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import edu.kh.project.member.model.dto.Member;
// Filter : 클라이언트의 요청/ 응답을 걸러내거나, 첨가하는 클래스
// 클라이언트 -> Filter -> Dispatcher Servlet
// @WebFilte : 해당 클래스를 필터로 등록하고 지정된 주소로 요청이 올 때 마다 동작
@WebFilter(filterName="loginFilter",
urlPatterns = {"/myPage/*"})
public class LoginFilter implements Filter {
public void init(FilterConfig fConfig) throws ServletException {
// 서버가 켜질 때, 필터코드가 변경 되었을 때 필터가 생성됨
// -> 초기화 용도로 사용하는 메소드
System.out.println("--- 로그인 필터 생성 ---");
}
public void destroy() {
// 필터 코드가 변경 되었을 때
// 변경 이전 필터를 파괴하는 메소드
System.out.println("--- 이전 로그인 필터 파괴 ---");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 필터링 코드를 작성하는 메소드
// 1) ServletRequest, ServletResponse 다운 캐스팅
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 2) HttpServletRequest를 이용해서 HttpSession 얻어오기 (Session에 있는 로그인 멤버 확인해야하기 때문에)
HttpSession session = req.getSession();
/*
Member loginMember = (Member) session.getAttribute("loginMember");
if(loginMember.getAuthority() != 2) { // 관리자가 아니라면 메인 페이지로 이동 !
resp.sendRedirect("/");
}
*/
// 3) session에서 "loginMember" key를 가진 속성을 얻어와
// null인 경우는 메인 페이지로 redirect 시키기
if(session.getAttribute("loginMember") == null) {
resp.sendRedirect("/");
// 4) 로그인 상태인 경우 다음 필터 또는 DispatcherServlet으로 전달
}else {
chain.doFilter(request, response);
}
}
}
📕 비밀번호 변경
✳ MyPageController
// 비밀번호 변경
@PostMapping("/changePw")
public String changPw(String currentPw ,String newPw
,@SessionAttribute("loginMember") Member loginMember
,RedirectAttributes ra) {
// 로그인한 회원 번호 (DB에서 어떤 회원을 조회, 수정하는지 알아야 하니까)
int memberNo = loginMember.getMemberNo();
// 비밀번호 변경 서비스 호출
int result = service.changePw(currentPw,newPw,memberNo);
String path = "redirect:";
String message = null;
if(result>0) { // 성공
message = "비밀번호가 변경되었습니다.";
path += "info";
}else {
message = "현재 비밀번호가 일치하지 않습니다.";
path += "changePw";
}
ra.addFlashAttribute("message",message);
return path;
}
✳ MyPageService
/**비밀번호 변경 서비스
* @param currentPw
* @param newPw
* @param memberNo
* @return result
*/
int changePw(String currentPw, String newPw, int memberNo);
✳ MyPageServiceImpl
// 비밀번호 수정
// @Transactional // Unchecked Exception 발생 시 rollback
@Transactional(rollbackFor = { Exception.class }) // 모든 Exception 발생 시 rollback
@Override
public int changePw(String currentPw, String newPw, int memberNo) {
// 1. 현재 비밀번호, DB에 저장된 비밀번호 비교
// 1) 회원번호가 일치하는 MEMBER 테이블의 행의 MEMBER_PW 조회
String encPw = dao.selectEncPw(memberNo);
// 2) bcrypt.mathes(평문, 암호문) - 같으면 true -> 비밀번호 수정
if (bcrypt.matches(currentPw, encPw)) {
// 2. 비밀번호 변경 (UPDATE DAO 호출) -> 결과 반환
return dao.changePw(bcrypt.encode(newPw), memberNo);
}
// 3. 비밀번호 일치하지 않으면 - 0 반환
return 0;
}
✳ MyPageDAO
/**회원 비밀번호 조회
* @param memberNo
* @return encPw
*/
public String selectEncPw(int memberNo) {
return sqlSession.selectOne("myPageMapper.selectEncPw", memberNo);
}
/**비밀번호 변경
* @param newPw
* @param memberNo
* @return result
*/
public int changePw(String newPw, int memberNo) {
// Mybatis에서 SQL 수행 시 전달할 수 있는 파라미터는 딱 ! 1개
// 지금 처럼 여러 파라미터를 전달해야하는 경우 Map 또는 DTO로 묶어서 전달
Member member= new Member();
member.setMemberNo(memberNo);
member.setMemberPw(newPw);
return sqlSession.update("myPageMapper.changePw",member);
}
✳ MyPage-mapper.xml
<!-- 비밀번호 조회 -->
<!--
마이바티스 기본 별칭
java mybatis
int _int
Integer int
String string
-->
<!-- 조회 결과가 1개인 경우(int, string 등)
또는 조회 결과를 모두 Map에 담는 경우
resultType을 사용
-->
<select id="selectEncPw" parameterType="_int" resultType="string">
SELECT MEMBER_PW FROM MEMBER WHERE MEMBER_NO = #{memberNo}
</select>
<!-- 비밀번호 변경 -->
<!-- Member : 사용자 지정 별칭 (mybatis-config.xml) -->
<update id="changePw" parameterType="Member">
UPDATE MEMBER SET
MEMBER_PW = #{memberPw}
WHERE MEMBER_NO = #{memberNo}
</update>
📕 회원 탈퇴
✳ MyPageController
@PostMapping("/secession")
public String secession(@SessionAttribute("loginMember") Member loginMember // 로그인한 회원의 회원 번호 얻어오기
, String memberPw // 입력한 비밀번호
,SessionStatus stauts // 세션 관리 객체
,HttpServletResponse resp // 서버 -> 클라이언트 응답하는 방법 제공 객체
,RedirectAttributes ra // 리다이렉트시 request로 값 전달하는 객체
) {
//1. 로그인한 회원의 회원 번호 얻어오기
int memberNo = loginMember.getMemberNo();
//2. 회원 탈퇴 서비스 호출
// - 비밀번호가 일치하면 MEMBER_DEL_FL -> 'Y' 바꾸고 1반환
// - 비밀번호가 일치하지 않으면 -> 0반환
int result = service.secession(memberNo,memberPw);
String path = "redirect:";
String message = null;
//3. 탈퇴 성공 시
if(result>0) {
// message : 탈퇴 되었습니다.
message = "탈퇴 되었습니다.";
// - 메인페이지로 리다이렉트
path += "/";
// - 로그아웃
stauts.setComplete();
// + 쿠키 삭제
Cookie cookie = new Cookie("saveId","");
// 같은 쿠키가 이미 존재하면 덮어쓰기 된다.
cookie.setMaxAge(0); // 0초 생존 -> 삭제
cookie.setPath("/"); // 요청 시 쿠키가 첨부되는 경로
resp.addCookie(cookie); // 요청 객체를 통해서 클라이언트에게 전달
// -> 클라이언트 컴퓨터에 파일로 생성된다.
//4. 탈퇴 실패시
}else {
// - message : 현재 비밀번호가 일치하지 않습니다.
message = "현재 비밀번호가 일치하지 않습니다. ";
// - 회원 탈퇴 페이지로 리 다이렉트
path += "secession";
}
ra.addFlashAttribute("message",message);
return path;
}
✳ MyPageService
/** 회원 탈퇴 서비스
* @param memberNo
* @param memberPw
* @return result
*/
int secession(int memberNo, String memberPw);
✳ MyPageServiceImpl
// 회원 탈퇴 서비스
@Transactional (rollbackFor = Exception.class)
@Override
public int secession(int memberNo, String memberPw) {
//1. 회원 번호가 일치하는 회원의 비밀번호 조회
String encPw = dao.selectEncPw(memberNo);
//2. 비밀번호가 일치하면
if(bcrypt.matches(memberPw, encPw)) {
return dao.secession(memberNo);
}
return 0;
}
✳ MyPageDAO
/** 회원 탈퇴 DAO
* @param memberNo
* @return result
*/
public int secession(int memberNo) {
// SqlSessionTemplate : 마이바티스 + DBCP + close자동 + 트랜잭션처리
return sqlSession.update("myPageMapper.secession",memberNo);
}
✳ MyPage-mapper.xml
<!-- 회원 탈퇴 -->
<update id="secession" parameterType="_int">
UPDATE MEMBER SET
MEMBER_DEL_FL = 'Y'
WHERE MEMBER_NO = #{memberNo}
</update>
📕 회원 가입 - 유효성 검사
📖 정규 표현식 (Regular Expression)
: 특정한 규칙을 가진 문자열 집합을 표현하는데 사용하는 형식 언어
-> 문자열에 대한 검색, 일치 여부, 치환 등을 수행할 수 있음
[JavaScript] 정규 표현식 | 객체생성 및 확인
📌정규표현식(Regular Expression) -> 특정한 규칙을 가진 문자열 집합을 표현하는데 사용하는 형식 언어 정규 표현식을 이용하면 입력된 문자열에 대한 특정 조건 검색, 일치여부 판단, 치환에 대한
jnaa.tistory.com
📖 JS 객체
{"K":V, "K":V} (Map 형식)
📖 JS 특징
1) 원하는 value를 얻어오는 방법
객체명.key
객체명["key"]
2) 객체에 특정 key가 존재하지 않으면 추가할 수 있다.
const obj = {"a":1, "b":2}
obj.c = 3 // -> {"a":1, "b":2, "c":3}
3) 객체에 특정 key를 삭제할 수 있다. (delete 연산자)
const obj = {"a":1, "b":2}
delete obj.b; // {"a":1}
✳ signUp.js
📝 이메일 유효성 검사 📝
/* 유효성 검사 진행 여부 확인용 객체 */
// -> 모든 value가 true인 경우에만 회원 가입 진행
const checkObj = {
"memberEmail" : false,
"memberPw" : false,
"memberPwConfirm" : false,
"memberNickname" : false,
"memberTel" : false
};
// 이메일 유효성 검사
const memberEmail = document.getElementById("memberEmail");
const emailMessage = document.getElementById("emailMessage");
// 이메일이 입력될 때마다
memberEmail.addEventListener("input", () => {
// 입력된 이메일이 없을 경우
if(memberEmail.value.trim().length == 0){
memberEmail.value = "";
emailMessage.innerText = "메일을 받을 수 있는 이메일을 입력해주세요.";
// confirm, error 클래스 삭제해서 검정 글씨로 바꾸기
emailMessage.classList.remove("confirm", "error");
checkObj.memberEmail = false; // 빈칸 == 유효 X
return;
}
// 정규 표현식을 이용해서 유효한 형식인지 판별
// 1) 정규 표현식 객체 생성
const regEx = /^[A-Za-z\d\-\_]{4,}@[가-힣\w\-\_]+(\.\w+){1,3}$/;
// 2) 입력 받은 이메일과 정규식 일치 여부 판별
if(regEx.test(memberEmail.value)){ // 유효한 경우
emailMessage.innerText = "유효한 형식입니다.";
emailMessage.classList.add("confirm");
emailMessage.classList.remove("error");
checkObj.memberEmail = true; // 유효 O
} else{ // 유효하지 않은 경우
emailMessage.innerText = "이메일 형식이 유효하지 않습니다.";
emailMessage.classList.add("error");
emailMessage.classList.remove("confirm");
checkObj.memberEmail = false; // 유효 X
}
})
📝 비밀번호 유효성 검사 📝
// 비밀번호/비밀번호 확인 유효성 검사
const memberPw = document.getElementById("memberPw");
const memberPwConfirm = document.getElementById("memberPwConfirm");
const pwMessage = document.getElementById("pwMessage");
// 비밀번호 입력 시 유효성 검사
memberPw.addEventListener("input", ()=>{
// 비밀번호가 입력되지 않은 경우
if(memberPw.value.trim().length == 0){
memberPw.value = "";
pwMessage.innerText = "영어,숫자,특수문자(!,@,#,-,_) 6~20글자 사이로 입력해주세요.";
pwMessage.classList.remove("confirm", "error"); // 검정 글씨
checkObj.memberPw = false; // 빈칸 == 유효 X
return;
}
// 정규 표현식을 이용한 비밀번호 유효성 검사
const regEx = /^[a-zA-Z0-9\!\@\#\-\_]{6,20}$/;
// 입력한 비밀번호가 유효한 경우
if(regEx.test(memberPw.value)){
checkObj.memberPw = true;
// 비밀번호가 유효하게 작성된 상태에서
// 비밀번호 확인이 입력되지 않았을 때
if(memberPwConfirm.value.trim().length == 0){
pwMessage.innerText = "유효한 비밀번호 형식입니다.";
pwMessage.classList.add("confirm");
pwMessage.classList.remove("error");
} else{
// 비밀번호가 유효하게 작성된 상태에서
// 비밀번호 확인이 입력되어 있을 때
// 비밀번호 == 비밀번호 확인 (같을 경우)
if(memberPw.value == memberPwConfirm.value){
pwMessage.innerText = "비밀번호가 일치합니다.";
pwMessage.classList.add("confirm");
pwMessage.classList.remove("error");
checkObj.memberPwConfirm = true;
} else{ // 다른 경우
pwMessage.innerText = "비밀번호가 일치하지 않습니다.";
pwMessage.classList.add("error");
pwMessage.classList.remove("confirm");
checkObj.memberPwConfirm = false;
}
}
} else { // 유효하지 않은 경우
pwMessage.innerText = "비밀번호 형식이 유효하지 않습니다.";
pwMessage.classList.add("error");
pwMessage.classList.remove("confirm");
checkObj.memberPw = false;
}
})
📝 비밀번호 확인 유효성검사 📝
// 비밀번호 확인 유효성 검사
memberPwConfirm.addEventListener("input", ()=>{
if(checkObj.memberPw){ // 비밀번호가 유효하게 작성된 경우에
// 비밀번호 == 비밀번호 확인 (같을 경우)
if(memberPw.value == memberPwConfirm.value){
pwMessage.innerText = "비밀번호가 일치합니다.";
pwMessage.classList.add("confirm");
pwMessage.classList.remove("error");
checkObj.memberPwConfirm = true;
} else{ // 다른 경우
pwMessage.innerText = "비밀번호가 일치하지 않습니다.";
pwMessage.classList.add("error");
pwMessage.classList.remove("confirm");
checkObj.memberPwConfirm = false;
}
} else{ // 비밀번호가 유효하지 않은 경우
checkObj.memberPwConfirm = false;
}
})
📝 닉네임 유효성 검사 📝
// 닉네임 유효성 검사
const memberNickname = document.getElementById("memberNickname");
const nickMessage = document.getElementById("nickMessage");
// 닉네임이 입력이 되었을 때
memberNickname.addEventListener("input", ()=>{
// 닉네임 입력이 되지 않은 경우
if(memberNickname.value.trim().length == 0){
nickMessage.innerText = "한글,영어,숫자로만 2~10글자";
nickMessage.classList.remove("confirm", "error"); // 검정 글씨
checkObj.memberNickname = false; // 빈칸 == 유효 X
memberNickname.value = "";
return;
}
// 정규 표현식으로 유효성 검사
const regEx = /^[가-힣\w\d]{2,10}$/;
if(regEx.test(memberNickname.value)){ // 유효
nickMessage.innerText = "유효한 닉네임 형식입니다.";
nickMessage.classList.add("confirm");
nickMessage.classList.remove("error");
checkObj.memberNickname = true;
} else{ // 무효
nickMessage.innerText = "닉네임 형식이 유효하지 않습니다.";
nickMessage.classList.add("error");
nickMessage.classList.remove("confirm");
checkObj.memberNickname = false;
}
})
📝 휴대폰 번호 유효성 검사 📝
// 전화번호 유효성 검사
const memberTel = document.getElementById("memberTel");
const telMessage = document.getElementById("telMessage");
// 전화번호가 입력되었을 때
memberTel.addEventListener("input", ()=>{
// 전화번호가 입력이 되지 않은 경우
if(memberTel.value.trim().length == 0){
telMessage.innerText = "전화번호를 입력해주세요.(- 제외)";
telMessage.classList.remove("confirm", "error"); // 검정 글씨
checkObj.memberTel = false; // 빈칸 == 유효 X
memberTel.value = "";
return;
}
// 정규 표현식으로 유효성 검사
const regEx = /^0(1[01679]|2|[3-6][1-5]|70)\d{3,4}\d{4}$/;
if(regEx.test(memberTel.value)){
telMessage.innerText = "유효한 전화번호 형식입니다.";
telMessage.classList.add("confirm");
telMessage.classList.remove("error");
checkObj.memberTel = true;
} else{
telMessage.innerText = "전화번호 형식이 유효하지 않습니다.";
telMessage.classList.add("error");
telMessage.classList.remove("confirm");
checkObj.memberTel = false;
}
})
회원 가입 form 태그가 제출될때 checkObjc를 이용해서 모든 value값이 true인지 검사하면 유효성 검사가 끝난다.
`
// 회원 가입 form 태그가 제출되었을 때
document.getElementById("signUpFrm").addEventListener("submit", e=>{
// checkObj에 모든 value가 true인지 검사
// (배열용 for문)
// for ... of : 향상된 for문
// -> iterator(반복자) 속성을 지닌 배열, 유사 배열 사용 가능
// (객체용 for문)
// *** for ... in ***
// -> JS 객체가 가지고 있는 key를 순서대로 하나씩 꺼내는 반복문
for(let key in checkObj){
if(!checkObj[key]){ // 각 key에 대한 value(true/false)를 얻어와서
// false인 경우 == 유효하지 않다!
switch(key){
case "memberEmail" : alert("이메일이 유효하지 않습니다."); break;
case "memberPw" : alert("비밀번호가 유효하지 않습니다."); break;
case "memberPwConfirm" : alert("비밀번호가 확인되지 않았습니다."); break;
case "memberNickname" : alert("닉네임이 유효하지 않습니다."); break;
case "memberTel" : alert("전화번호가 유효하지 않습니다."); break;
}
// 유효하지 않은 input 태그로 focus 이동
// - key를 input의 id와 똑같이 설정했음
document.getElementById(key).focus();
// form 태그 기본 이벤트 제거
e.preventDefault();
return; // 함수 종료
}
}
})
'ON > spring' 카테고리의 다른 글
[Spring] ajax 이용하기 | 자바스크립트의 fetch() 함수 ② POST 방식 (0) | 2023.08.18 |
---|---|
[Spring] ajax 이용하기 | 자바스크립트의 fetch() 함수 ① GET 방식 (0) | 2023.08.17 |
[Spring] 드롭다운 메뉴 | 회원정보 수정 ③ (0) | 2023.08.14 |
[Spring] 회원가입 | 주소 API 활용 ② (0) | 2023.08.11 |
[Spring] MyBatis 로그인 | 로그아웃 | 아이디 저장 | 암호화하기 | 예외페이지 만들기 세팅하기 ① (0) | 2023.08.11 |