티스토리 뷰

study/Spring

chap 13 - HttpSession

xoxowo 2023. 2. 21. 23:45

초보 웹 개발자를 위한 스프링 5 프로그램 입문을 보고 복습 겸 개인 학습 정리입니다. (windows 기준)

나니..?

chap13에서는 세션, 인터셉터, 쿠키에 대해 공부한다. 이번 글에서는 세션(HttpSession) 적용하는 방법을 선행해 보았다.

 

로그인 기능을 이용하기 때문에 1번부터 9번까지는 로그인 기능 및 뷰를 만들고 10번부터 컨트롤러에서 로그인 상태를 유지하는 HttpSession를 사용한 코드를 변경하였다.

 

 

예제 프로젝트 만들기 (메이븐 기준)  예제 코드

sp5-chap13 프로젝트 폴더 생성

+ chap12의 src 파일 복붙

chap12의 pom.xml 복붙 후 <artifactId> 13로 변경

이클립스에서 sp5-chap12 폴더에 생성한 메이븐 프로젝트 import

 

 

1. 로그인 성공 후 인증 상태 정보를 세션에 보관할 때 사용할 AuthInfo 클래스를 작성한다.

 

sp5-chap13/src/main/java/spring/AuthInfo.java

package spring;

public class Authinfo {
	
	private Long id;
	private String email;
	private String name;
	
	public Authinfo(Long id, String email, String name) {
		this.id =id;
		this.email=email;
		this.name=name;
	}
	public Long getId() {
		return id;
	}
	public String getEmail() {
		return email;
	}
	public String getName() {
		return name;
	}
}

 

2. 암호 일치 여부를 확인하기 위해 Member 클래스에 matchPassword() 메서드를 추가한다.

 

→ sp5-chap13/src/main/java/spring/Member.java

package spring;

import java.time.LocalDateTime;

public class Member {	
... 생략 ...    
    public boolean matchPassword(String password) {
		return this.password.equals(password);
	}
 }

 

3. 이메일과 비밀번호가 일치하는지 확인해서 AuthInfo 객체(1번에서 만든 클래스)를 생성하는 AuthService 클래스를 작성한다.

 

→ sp5-chap13/src/main/java/spring/AuthService.java

package spring;

public class AuthService {

	private MemberDao memberDao;

	public void setMemberDao(MemberDao memberDao) {
		this.memberDao = memberDao;
	}
	public AuthInfo authenticate(String email, String password) {
		Member member = memberDao.selectByEmail(email);
		if (member == null) {
			throw new WrongIdPasswordException();
		}
		if (!member.matchPassword(password)) {
			throw new WrongIdPasswordException();
		}
		return new AuthInfo(member.getId(),
				member.getEmail(),
				member.getName());
	}
}

 

4. 위에서 작성한 AuthService를 이용해서 로그인 요청을 처리하는 LoginCommand 클래스를 작성한다.

 

→ sp5-chap13/src/main/java/controller/LoginCommand.java

package controller;

public class LoginCommand {

	private String email;
	private String password;
	private boolean rememberEmail;

	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public boolean isRememberEmail() {
		return rememberEmail;
	}
	public void setRememberEmail(boolean rememberEmail) {
		this.rememberEmail = rememberEmail;
	}

}

 

5. LoginController 클래스와 폼에 입력된 값이 올바른지 검사하는 LoginCommandValidator 클래스를 작성한다.

 

→ sp5-chap13/src/main/java/controller/LoginCommandValidator.java

package controller;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class LoginCommandValidator implements Validator {

	@Override
	public boolean supports(Class<?> clazz) {
		return LoginCommand.class.isAssignableFrom(clazz);
	}

	@Override
	public void validate(Object target, Errors errors) {
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "required");
		ValidationUtils.rejectIfEmpty(errors, "password", "required");
	}
}

 

6. 로그인 요청을 처리하는 LoginController 클래스를 작성한다.

 

→ sp5-chap13/src/main/java/controller/LoginController.java

package controller;

..import 생략...

@Controller
@RequestMapping("/login")
public class LoginController {
    private AuthService authService;    
    
    public void setAuthService(AuthService authService) {
        this.authService = authService;
    }

    @GetMapping
    public String form(LoginCommand loginCommand) {
    	return "login/loginForm";
    }

    @PostMapping
    public String submit(LoginCommand loginCommand, Errors errors) {
        new LoginCommandValidator().validate(loginCommand, errors);
        if (errors.hasErrors()) {
            return "login/loginForm";
        }
        try {
            AuthInfo authInfo = authService.authenticate(
                    loginCommand.getEmail(),
                    loginCommand.getPassword());
                    
        // TODO 세션에 authInfo 저장해야 함
            return "login/loginSuccess";
        } catch (WrongIdPasswordException e) {
            errors.reject("idPasswordNotMatching");
            return "login/loginForm";
        }
    }
}

 

7. login/loginForm, loginSuccess 뷰를 위한 JSP를 작성하자.  (접은 글로 대체)

 

→ sp5-chap13/src/main/WEB-INF/view/login/loginForm.java

더보기
<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
    <title><spring:message code="login.title" /></title>
</head>
<body>
    <form:form modelAttribute="loginCommand">
    <form:errors />
    <p>
        <label><spring:message code="email" />:<br>
        <form:input path="email" />
        <form:errors path="email"/>
        </label>
    </p>
    <p>
        <label><spring:message code="password" />:<br>
        <form:password path="password" />
        <form:errors path="password"/>
        </label>
    </p>
    <input type="submit" value="<spring:message code="login.btn" />">
    </form:form>
</body>
</html>

→ sp5-chap13/src/main/WEB-INF/view/login/loginSuccess.java

더보기
<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
    <title><spring:message code="login.title" /></title>
</head>
<body>
    <p>
        <spring:message code="login.done" />
    </p>
    <p>
        <a href="<c:url value='/main'/>">
            [<spring:message code="go.main" />]
        </a>
    </p>
</body>
</html>

 

8. 뷰에서 사용할 메시지 label.properties 파일에 추가  (접은 글로 대체)

 

→ sp5-chap13/src/main/resources/message/label.properties

더보기

login.title=로그인
login.btn=로그인하기
idPasswordNotMatching=아이디와 비밀번호가 일치하지 않습니다.
login.done=로그인에 성공했습니다.

 

8. 컨트롤러와 서비스를 스프링 빈에 등록하기 

 

→ sp5-chap13/src/main/java/config/MemberConfig.java

package config;

..생략..

import spring.AuthService;
import spring.ChangePasswordService;
import spring.MemberDao;
import spring.MemberRegisterService;

@Configuration
@EnableTransactionManagement
public class MemberConfig {
..생략..

// 인증 서비스 빈 추가
    @Bean
    public AuthService authService() {
    	AuthService authService = new AuthService();
    	authService.setMemberDao(memberDao());
    	return authService;
    }
}

→ sp5-chap13/src/main/java/config/ControllerConfig.java

package config;
..생략..

import controller.LoginController;
import controller.RegisterController;
import spring.MemberRegisterService;
import spring.AuthService;

@Configuration
public class ControllerConfig {

    @Autowired
    private MemberRegisterService memberRegSvc;
    //추가
    @Autowired 
    private AuthService authService;

    @Bean
    public RegisterController registerController(){
        RegisterController controller = new RegisterController();
        controller.setMemberRegisterService(memberRegSvc);
        return controller;
    }
    //추가
    @Bean
    public LoginController loginController() {
    	LoginController controller = new LoginController();
    	controller.setAuthService(authService);
    	return controller;
    }
   
}

 

9. 서버 실행 후 "localhost:8090/sp6-chap13/login" 접속하기

[Run As] → [Run on Server] → [톰캣😺 서버 실행] → [브라우저에 "localhost:8090/sp6-chap13/login" 접속]

 

 

 

📌컨트롤러에서 HttpSession 사용하기

컨트롤러에서 HttpSession 사용하려면 다음 두 가지 방법 중 하나를 사용하면 된다.

 

- 요청 매핑 애노테이션 적용 메서드에 HttpSession 파라미터 추가

스프링 MVC는 컨트롤러의 메서드를 호출할 때 HttpSession 객체를 파라미터로 전달한다. 이 HttpSession을 생성하기 전이면 새로운 HttpSession을 생성하고 그렇지 않으면 기존에 존재하는 HttpSession을 사용한다. (항상 HttpSession 생성)

    @PostMapping
    public String submit(LoginCommand loginCommand, Errors errors, HttpSession session) {
 	... 생략
}

 

- 요청 매핑 애노테이션 적용 메서드에 HttpSevletRequest 파라미터를 추가하고 HttpServletRequest를 이용해서 HttpSession을 구한다. (필요한 시점에만 HttpSession을 생성)

 

    @PostMapping
    public String submit
    	(LoginCommand loginCommand, Errors errors, HttpServletRequest req) {
 	HttpSession session = req.getSession();
    	... 생략
}

 

10. LoginController 코드에 인증 후 인증 정보를 세션에 담도록 submit() 메서드의 코드를 수정한다.

로그인에 성공하면 HttpSession의 authInfo 속성에 인증 정보 객체를 저장하도록 수정했다.

 

→ sp5-chap13/src/main/java/controller/LoginController.java

 package controller;

..생략

import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/login")
public class LoginController {

 ... 생략

 @PostMapping                                                   // HttpSession 파라미터 추가
    public String submit(LoginCommand loginCommand, Errors errors, HttpSession session) {
        new LoginCommandValidator().validate(loginCommand, errors);
        if (errors.hasErrors()) {
            return "login/loginForm";
        }
        try {
            AuthInfo authInfo = authService.authenticate(
                    loginCommand.getEmail(),
                    loginCommand.getPassword());
            // 세션 추가한 것
            session.setAttribute("authInfo", authInfo);
            
            return "login/loginSuccess";
        } catch (WrongIdPasswordException e) {
            errors.reject("idPasswordNotMatching");
            return "login/loginForm";
        }
}

 

11. LogoutController 클래스를 만들고 로그아웃을 위해 HttpSession을 삭제한다.

 

→ sp5-chap13/src/main/java/controller/LooutController.java

package controller;

import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class LogoutController {
	
	@RequestMapping("/logout")
	public String logout(HttpSession session) {
		session.invalidate();
		return "redirect:/main";
	}
}

 

12.  LogoutController 클래스 빈을 ControllerConfig 설정 클래스에 추가하자

 

→ sp5-chap13/src/main/java/config/MemberConfig.java

package config;
..생략..
import controller.LogoutController;
import controller.LoginController;
import controller.RegisterController;
import spring.MemberRegisterService;
import spring.AuthService;

@Configuration
public class ControllerConfig {

    @Autowired
    private MemberRegisterService memberRegSvc;
    //추가
    @Autowired 
    private AuthService authService;

    @Bean
    public RegisterController registerController(){
        RegisterController controller = new RegisterController();
        controller.setMemberRegisterService(memberRegSvc);
        return controller;
    }
    //추가
    @Bean
    public LoginController loginController() {
    	LoginController controller = new LoginController();
    	controller.setAuthService(authService);
    	return controller;
    }
    // 12 추가
    @Bean
    public LogoutController logoutController() {
    	return new LogoutController();
    }   
}

 

13. HttpSession을 사용하는지 확인할 수 있도록 main.jsp를 수정한다.

 

→ sp5-chap13/src/main/WEB-INF/view/main.java

수정 전

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
    <title>메인</title>
</head>
<body>
    <p>환영합니다.</p>
    <p><a href="<c:url value="/register/step1" />">[회원 가입하기]</a>
</body>
</html>

수정 후

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
    <title>메인</title>
</head>
<body>
    <c:if test="${empty authInfo}">
    <p>환영합니다.</p>
    <p>
        <a href="<c:url value="/register/step1" />">[회원 가입하기]</a>
        <a href="<c:url value="/login" />">[로그인]</a>
    </p>
    </c:if>
    
    <c:if test="${! empty authInfo}">
    <p>${authInfo.name}님, 환영합니다.</p>
    <p>
        <a href="<c:url value="/edit/changePassword" />">[비밀번호 변경]</a>
        <a href="<c:url value="/logout" />">[로그아웃]</a>
    </p>
    </c:if>
</body>
</html>

 

14. 서버 실행 후 "localhost:8090/sp6-chap13/main" 접속하기

[Run As] → [Run on Server] → [톰캣😺 서버 실행] → [브라우저에 "localhost:8090/sp6-chap13/main" 접속]

 

로그인에 성공할 경우 HttpSession"authInfo" 속성에 인증 정보를 저장한다.

 

 

 

로그인에 실패경우 아래와 같이 화면에 8에서 작성한 멘트("아이디와 비밀번호가 일치하지 않습니다.")가 나온다.

'study > Spring' 카테고리의 다른 글

chap 13 - Cookie  (0) 2023.02.23
chap 13 - HandlerInterceptor  (0) 2023.02.23
스프링 프로젝트 생성하기 (start.spring.io)  (0) 2023.02.16
chap 12 - 커맨드 객체 검증과 에러 메시지  (0) 2023.02.15
chap 12 - MVC 메세지 처리  (0) 2023.02.13
댓글