스프링 MVC 3 세션, 인터셉터, 필터
비밀번호 변경 기능 구현
DTO
ChangePassword 메소드
package spring;
import java.time.LocalDateTime;
public class Member {
private Long id;
private String email;
private String password;
private String name;
private LocalDateTime registerDateTime;
public Member(String email, String password,
String name, LocalDateTime regDateTime) {
this.email = email;
this.password = password;
this.name = name;
this.registerDateTime = regDateTime;
}
void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public String getName() {
return name;
}
public LocalDateTime getRegisterDateTime() {
return registerDateTime;
}
public void changePassword(String oldPassword, String newPassword) {
if (!password.equals(oldPassword))
throw new WrongIdPasswordException();
this.password = newPassword;
}
public boolean matchPassword(String password) {
return this.password.equals(password);
}
}
form에서 현재 비밀번호와 새 비밀번호를 받기 위한 커맨드 객체
package controller;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ChangePwdCommand {
private String currentPassword;
private String newPassword;
}
DAO (변경 할 email 정보 데이터베이스에서 가져옴 )
public Member selectByEmail(String email) {
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where EMAIL = ?",
new RowMapper<Member>() {
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member(
rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
}, email);
return results.isEmpty() ? null : results.get(0);
}
Service
package spring;
import org.springframework.transaction.annotation.Transactional;
public class ChangePasswordService {
private MemberDao memberDao;
@Transactional
public void changePassword(String email, String oldPwd, String newPwd) {
Member member = memberDao.selectByEmail(email);
if (member == null)
throw new MemberNotFoundException();
member.changePassword(oldPwd, newPwd);
memberDao.update(member);
}
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
}
입력 받은 객체를 검증할 Validator 클래스 작성
package controller;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
public class ChangePwdCommandValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return ChangePwdCommand.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(
errors, "currentPassword", "required");
ValidationUtils.rejectIfEmpty(errors, "newPassword", "required");
}
}
비밀번호 변경 요청을 처리하는 컨트롤러 클래스
로그인한 사용자 정보는 HttpSession의 authInfo 속성을 사용
package controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import spring.AuthInfo;
import spring.ChangePasswordService;
import spring.WrongIdPasswordException;
@Controller
@RequestMapping("/edit/changePassword")
public class ChangePwdController {
private ChangePasswordService changePasswordService;
public void setChangePasswordService(
ChangePasswordService changePasswordService) {
this.changePasswordService = changePasswordService;
}
@GetMapping
public String form(
@ModelAttribute("command") ChangePwdCommand pwdCmd) {
return "edit/changePwdForm";
}
@PostMapping
public String submit(
@ModelAttribute("command") ChangePwdCommand pwdCmd,
Errors errors,
HttpSession session) {
new ChangePwdCommandValidator().validate(pwdCmd, errors);
if (errors.hasErrors()) {
return "edit/changePwdForm";
}
AuthInfo authInfo = (AuthInfo) session.getAttribute("authInfo");
try {
changePasswordService.changePassword(
authInfo.getEmail(),
pwdCmd.getCurrentPassword(),
pwdCmd.getNewPassword());
return "edit/changedPwd";
} catch (WrongIdPasswordException e) {
errors.rejectValue("currentPassword", "notMatching");
return "edit/changePwdForm";
}
}
}
changePwdForm.jsp
<%@ 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="change.pwd.title" /></title>
</head>
<body>
<form:form>
<p>
<label><spring:message code="currentPassword" />:<br>
<form:input path="currentPassword" />
<form:errors path="currentPassword"/>
</label>
</p>
<p>
<label><spring:message code="newPassword" />:<br>
<form:password path="newPassword" />
<form:errors path="newPassword"/>
</label>
</p>
<input type="submit" value="<spring:message code="change.btn" />">
</form:form>
</body>
</html>
changedPwd.jsp
<%@ 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="change.pwd.title" /></title>
</head>
<body>
<p>
<spring:message code="change.pwd.done" />
</p>
<p>
<a href="<c:url value='/main'/>">
[<spring:message code="go.main" />]
</a>
</p>
</body>
</html>
뷰 코드에서 사용 할 메시지 파일
change.pwd.title=비밀번호 변경
currentPassword=현재 비밀번호
newPassword=새 비밀번호
change.btn=변경하기
notMatching.currentPassword=비밀번호를 잘못 입력했습니다.
change.pwd.done=비밀번호를 변경했습니다.
@Bean 등록
ControllerConfig
package config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import controller.ChangePwdController;
import controller.LoginController;
import controller.LogoutController;
import controller.RegisterController;
import spring.AuthService;
import spring.ChangePasswordService;
import spring.MemberRegisterService;
@Configuration
public class ControllerConfig {
@Autowired
private ChangePasswordService changePasswordService;
@Bean
public ChangePwdController changePwdController() {
ChangePwdController changePwdController = new ChangePwdController();
changePwdController.setChangePasswordService(changePasswordService);
return changePwdController;
}
}
MemberConfig
package config;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import spring.AuthService;
import spring.ChangePasswordService;
import spring.MemberDao;
import spring.MemberRegisterService;
@Configuration
@EnableTransactionManagement
public class MemberConfig {
@Bean(destroyMethod = "close")
public DataSource dataSource() {
DataSource ds = new DataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
ds.setUsername("spring5");
ds.setPassword("spring5");
ds.setInitialSize(2);
ds.setMaxActive(10);
ds.setTestWhileIdle(true);
ds.setMinEvictableIdleTimeMillis(60000 * 3);
ds.setTimeBetweenEvictionRunsMillis(10 * 1000);
return ds;
}
@Bean
public PlatformTransactionManager transactionManager() {
DataSourceTransactionManager tm = new DataSourceTransactionManager();
tm.setDataSource(dataSource());
return tm;
}
@Bean
public MemberDao memberDao() {
return new MemberDao(dataSource());
}
@Bean
public ChangePasswordService changePwdSvc() {
ChangePasswordService pwdSvc = new ChangePasswordService();
pwdSvc.setMemberDao(memberDao());
return pwdSvc;
}
}
인터셉터 사용하기
로그인이 되지 않았는데 비밀번호 변경페이지로 들어가게 되면 로그인페이지로 이동되게 하기
컨트롤러에서 로그인 객체가 존재하는 검사하고 로그인이 안되있으면 login페이지로 리다이렉트 할 수도 있지만
많은 기능에 로그인 여부를 확인을 할 수도 있기 때문에 인터셉터를 사용해야 된다.
if(authInfo == null){
return "redirect:/login";
}
HandlerInterceptor 구현
preHandel() 메서드는 HttpSession에 "authInfo" 속성이 존재하면 true를 리턴
존재하지 않으면 리다이렉트 응답을 생성한 뒤 false를 리턴
package interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AuthCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
log.info("[{preHandle}]");
HttpSession session = request.getSession(false);
if(session != null) {
Object authInfo = session.getAttribute("authInfo");
if(authInfo != null) {
return true;
}
}
response.sendRedirect(request.getContextPath()+ "/login");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
log.info("[{postHandle}]");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
log.info("[{afterCompletion}]");
}
}
MvcConfig 설정 클래스에 addInterceptors()메서드 오버라이드
메서드 오버라이드는 소스에서 직접 오버라이드 해줘야 된다.
addPathPatterns("/edit/**")
해당 경로는 디렉터리가 아니라 컨트롤러 경로 / 매핑 주소
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authCheckInterceptor()).addPathPatterns("/edit/**")
.excludePathPatterns("/edit/help/**");
}
@Bean
public AuthCheckInterceptor authCheckInterceptor() {
return new AuthCheckInterceptor();
}
필터(Filter)로 위에 기능 구현 해보기
1. 필터 구현
package 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 lombok.extern.slf4j.Slf4j;
@Slf4j
public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("-------------LogFilter.init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
log.info("-------------LogFilter.dofilter");
chain.doFilter(request, response);// 해당 코드있어야 인터셉터로 넘어감
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
package filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.util.PatternMatchUtils;
import lombok.extern.slf4j.Slf4j;
@Slf4j
//@Order(Integer.MIN_VALUE)
public class LoginCheckFilter implements Filter {
private static final String[] whitelist = {"/", "/order", "/login", "/logout", "/css/*"};
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
HttpServletResponse httpResponse = (HttpServletResponse) response;
try {
log.info("인증 체크 필터 시작{}", requestURI);
if (isLoginCheckPath(requestURI)) {
log.info("인증 체크 로직 실행 {}", requestURI);
HttpSession session = httpRequest.getSession(false);
if (session == null || session.getAttribute("authInfo") == null) {
log.info("미인증 사용자 요청 {}", requestURI);
//로그인으로 리다이렉트
// httpResponse.sendRedirect(httpRequest.getContextPath() + "/login?redirectURL=" + requestURI);
// httpResponse.sendRedirect(httpRequest.getContextPath() + "/main");
RequestDispatcher dispatcher = request.getRequestDispatcher("/login");
dispatcher.forward(httpRequest, httpResponse);
return;
}
}
chain.doFilter(request, response);
} catch (Exception e) {
throw e;
} finally {
log.info("인증 체크 필터 종료 {}", requestURI);
}
}
/**
* whiteList의 경우 인증 체크를 안하도록 한다.
*/
private boolean isLoginCheckPath(String requestURI) {
return !PatternMatchUtils.simpleMatch(whitelist, requestURI);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
2. web.xml 구현 한 filter 추가
<filter>
<filter-name>LogFilter</filter-name>
<filter-class>filter.LogFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LogFilter</filter-name>
<url-pattern>/edit/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>LoginCheckFilter</filter-name>
<filter-class>filter.LoginCheckFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginCheckFilter</filter-name>
<url-pattern>/edit/*</url-pattern>
</filter-mapping>
'프로젝트 기반 자바(JAVA) 응용 SW개발자 취업과정' 카테고리의 다른 글
2023-08-31 73일차 (0) | 2023.08.31 |
---|---|
2023-08-30 72일차 (0) | 2023.08.30 |
2023-08-25 69일차 (0) | 2023.08.25 |
2023-08-24 68일차 (0) | 2023.08.24 |
2023-08-23 67일차 (0) | 2023.08.23 |