일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 스프링 게시판
- Gradle
- Ajax
- Maven
- reactjs
- State
- 다형성
- 스프링부트
- mybatis
- Render
- 자바
- spring
- 스프링
- spring framework
- springboot
- Stateless
- 둔산동
- 스프링게시판
- 컴포넌트
- 지족동
- Restful
- 스프링프레임워크
- spring게시판
- mysql
- Java
- SpringFramework
- react
- rest
- 재정의
- 리액트
- Today
- Total
Dev.GA
[spring/게시판] #5 Interceptor를 이용한 로그인 처리 본문
개발환경
Server OS : Windows10
Language : JAVA 1.6
Framework : Spring 3.1.1
WEB Server : Apache
WAS Server : Tomcat 7
build tool : maven 2.5.1
DB : MySQL 5.7.16
ORM : mybatis 3.2.7
5. Interceptor를 이용한 로그인 처리
너무나 오랜만에 포스팅을 하게된다. 두달만..
이전까지 게시판의 기본적인 CRUD와 와꾸(?)를 잡아 주었기 때문에 하나씩 기능을 추가해보겠다.
이번 포스팅은 Interceptor를 이용한 로그인 세션 처리를 해보겠다.
먼저 Interceptor란 ?
"가로챈다" 라는 의미를 가지고 있는 뜻으로, Spring에서는 Controller로 가는 요청을 가로채어 Controller를 제어하는 역할을 한다.
spring에서는 HandlerInterceptorAdapter라는 추상 클래스를 지원하는데 HandlerInterceptorAdapter는 3가지 메소드를 제공한다.
- preHandle : Controller가 호출되기 전 수행
- postHandle : Controller가 완료된 이후에 수행
- afterCompletion : Controller 수행 후 view단 작업까지 완료 된 후 호출
3가지 메소드를 활용하여 Interceptor는 다양한 방법으로 사용될 수 있는데, 그 중 대표적인 방법이 로그인 세션을 처리하는 것이다.
만약, Interceptor로 로그인을 처리하지 않는다면 사용자가 접속하여 여러가지 기능(읽고, 쓰고 등등)과 화면전환 시 마다 세션정보를 확인하여야 하는 문제가 발생한다. Spring에서는 이러한 기능을 Interceptor가 대신 할 수 있는 것이다.
우선 로그인 처리를 하기 위해서는 member테이블과 회원가입 절차가 필요하기에 간단하게 만들어본다.
1 2 3 4 5 6 7 8 | CREATE TABLE `member` ( `code` INT(10) NOT NULL AUTO_INCREMENT, `mbrId` VARCHAR(20) DEFAULT NULL, `mbrPw` VARCHAR(20) DEFAULT NULL, `mbrPw_check` VARCHAR(20) DEFAULT NULL, `mbr_name` VARCHAR(20) DEFAULT NULL, PRIMARY KEY (`code`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | package com.ga.member.service; public class MemberVO { private String mbr_code = ""; private String mbrId = ""; private String mbrPw = ""; private String mbrPw_check = ""; private String mbr_name = ""; public String getMbr_code() { return mbr_code; } public void setMbr_code(String mbr_code) { this.mbr_code = mbr_code; } public String getMbrId() { return mbrId; } public void setMbrId(String mbrId) { this.mbrId = mbrId; } public String getMbrPw() { return mbrPw; } public void setMbrPw(String mbrPw) { this.mbrPw = mbrPw; } public String getMbrPw_check() { return mbrPw_check; } public void setMbrPw_check(String mbrPw_check) { this.mbrPw_check = mbrPw_check; } public String getMbr_name() { return mbr_name; } public void setMbr_name(String mbr_name) { this.mbr_name = mbr_name; } } | cs |
나의 경우는 위와 같이 회원가입 창을 만들고 '회원가입' 버튼을 통해 Action을 MemberController로 전달하고
MemberServiceImpl에서 회원가입 절차를 만들었다.
MemberServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package com.ga.member.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ga.common.LoginUtil; import com.ga.member.service.MemberVO; @Service("memberServiceImpl") public class MemberServiceImpl implements MemberService { @Autowired private MemberDAO memberDAOService; @Override public void insertMembership(MemberVO memberVO) throws Exception { String encode_password = LoginUtil.encryptPassword(memberVO.getMbrId(), memberVO.getMbrPw()); memberVO.setMbrPw(encode_password); memberVO.setMbrPw_check(encode_password); memberDAOService.insertMembership(memberVO); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package com.ga.common; import java.security.MessageDigest; import org.apache.commons.codec.binary.Base64; public class LoginUtil { public static String encryptPassword(String id, String pw) throws Exception{ if(pw == null){ return ""; } byte[] hashValue = null; MessageDigest md = MessageDigest.getInstance("SHA-512"); md.reset(); md.update(id.getBytes()); hashValue = md.digest(pw.getBytes()); return new String(Base64.encodeBase64(hashValue)); } } | cs |
이 로그인 세션처리 과정을 Interceptor라는 것을 사용할 것이다.
먼저, 사용자의 정보를 가지고 DB에서 로그인 할 수 있도록 처리하겠다.
main_SQL.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ga.main.service.mapper.MainMapper"> <select id="loginAction" parameterType="com.ga.common.LoginVO" resultType="com.ga.common.LoginVO"> select mbrId as user_id , mbrPw as user_pw , mbr_name as user_name from member where 1=1 and mbrId = #{user_id} and mbrPw = #{user_pw} </select> </mapper> | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | package com.ga.main.web; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import com.ga.common.LoginVO; import com.ga.common.annotation.NoLoginCheck; import com.ga.main.service.impl.MainService; @Controller public class MainController { @Autowired private MainService mainServiceImpl; @RequestMapping(value="/main.do") public String mainPage() throws Exception{ return "main/loginForm"; } @RequestMapping(value="/main/login.do") public String login(@ModelAttribute("loginVO") LoginVO loginVO, HttpServletRequest request, Model model) throws Exception{ LoginVO resultVO = new LoginVO(); resultVO = mainServiceImpl.loginAction(loginVO); if(resultVO != null && !resultVO.getUser_id().equals("") && !resultVO.getUser_pw().equals("")){ request.getSession().setAttribute("loginVO", resultVO); return "forward:/board/boardList.do"; } else { model.addAttribute("msg", "사용자의 ID 혹은 패스워드가 일치하지 않습니다."); return "redirect:/main.do"; } } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure --> <!-- Enables the Spring MVC @Controller programming model --> <annotation-driven /> <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory --> <resources mapping="/resources/**" location="/resources/" /> <resources mapping="/css/**" location="/css/"/> <resources mapping="/js/**" location="/js/"/> <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --> <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <beans:property name="prefix" value="/WEB-INF/views/" /> <beans:property name="suffix" value=".jsp" /> </beans:bean> <context:component-scan base-package="com.ga" /> <beans:bean id="LoginInterceptor" class="com.ga.common.interceptor.LoginInterceptor"></beans:bean> <interceptors> <interceptor> <mapping path="/board/*.do"/> <beans:ref bean="LoginInterceptor"/> </interceptor> </interceptors> </beans:beans> | cs |
servlet-context.xml에서
30번째 라인) <interceptors> </interceptors> 태그 안에 <mapping path="" /> 로 설정을 해주면 interceptor가 컨트롤러를 제어 할 수 있게 된다. 컨트롤러 단위별로 하나씩 해도 되며, bean으로 선언된 interceptor에 해당하는 기능을 통해 제어 하고 싶은 컨트롤러를 지정해 주면된다.
추가로 spring 3.2버전 부터는 exclude-mapping이라 해서 해당 interceptor로부터 제외시키는 기능을 제공해준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package com.ga.common.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import com.ga.common.LoginVO; import com.ga.common.annotation.NoLoginCheck; public class LoginInterceptor extends HandlerInterceptorAdapter{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); LoginVO loginVO = (LoginVO) session.getAttribute("loginVO"); if(loginVO == null){ response.sendRedirect("/main.do"); return false; } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub super.afterCompletion(request, response, handler, ex); } } | cs |
25번째 라인) 과 같이 세션정보가 없을 경우 로그인 페이지로 이동시킨다.(여기서는 /main.do가 로그인 페이지가 된다)
26번째 라인) return 값을 false로 해주어, 다음 요청으로 넘어가지 않게 해준다.
여기서 봐야할 Interceptor의 특징은,
1. prehandle에서 return값이 false일 경우 다음으로 넘어가지 않고 끝나게 된다.
2. posthandle의 경우 Controller에서 Exception이 발생 할 경우 posthandle로 요청이 넘어오지 않는다.
3. afterCompletion의 경우 Exception이 발생하여도 뷰단은 실행된다.
따라서, 로그인 세션정보가 없을 경우 모두 로그인 페이지로 넘어가도록 redirect시켜주었다.
메인페이지 화면이다.
상단에 header를 두어 세션정보를 모든 화면에서 볼 수 있도록 하였다.
현재 세션 정보가 없기에 오른쪽 상단에 로그인 버튼이 출력되며, 글쓰기 혹은 게시글을 클릭할 경우 preHandle에 따라 로그인 페이지로 redirect된다.
로그인에 성공하여 세션정보를 가지고 있으면, 상단에 세션정보가 나타나게 되고 글쓰기 혹은 게시글을 클릭하여도 정상적인 요청으로 실행이 된다.
1 2 3 4 5 6 7 8 9 | @RequestMapping(value="/main/logout.do") public String logout(@ModelAttribute("loginVO") LoginVO loginVO, HttpServletRequest request, Model model) throws Exception{ request.getSession().removeAttribute("loginVO"); return "redirect:/board/boardList.do"; } | cs |
다음으로 로그아웃 기능을 만들어 MainController에 추가한다.
6번째 라인) 세션정보를 제거하여 로그아웃에 성공하도록 해주며, 메인페이지(게시판)로 redirect시켜준다.
이로써, Interceptor를 통한 로그인 처리를 완료하였다. Interceptor로는 로그인 처리 이외에 감사관리, 권한관리 등을 처리할 수 있으며
예외처리에도 유용하게 사용할 수 있다.
'Dev.Project > project1-spring게시판' 카테고리의 다른 글
[spring/게시판] #7 계층형 게시판(답글달기) (9) | 2018.03.09 |
---|---|
[spring/게시판] #6 게시판 페이징 처리하기 (19) | 2018.02.23 |
[spring/게시판] #4 게시판에 부트스트랩(bootstrap) 적용하기 (0) | 2017.11.27 |
[spring/게시판] #3 게시판 등록, 조회, 수정, 삭제하기 (15) | 2017.11.21 |
[spring/게시판] #2 mybatis 연동하기 (2) | 2017.11.14 |