본문 바로가기
코딩도전기/Spring Boot

Spring Boot - Properties / AOP(Interceptor)

by 코도꼬마 2023. 6. 2.

Properties 활용

  • Spring에서는 주요 정보를 properties 파일에 저장해놓거나 불러와서 사용할 수 있음
  • Xml이나 properties는 정보를 컴파일하지 않기 때문에 쉽게 읽고 수정이 가능
  • 보안성은 좋지 않음

 

Login Service(관리자 아이디 부여)

  • application.properties
    • 127.0.0.1 : 내 서버 ip
# super user
user.id=superAdmin
user.pw=pass@Goodee
user.ip=127.0.0.1

server.port=80

# encoding
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true

# logger
logging.level.root=info

# jsp
spring.mvc.view.prefix=/views/
spring.mvc.view.suffix=.jsp

# jdbc
spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
# add log4jdbc
spring.datasource.url=jdbc:log4jdbc:mariadb://localhost:1512/mydb
spring.datasource.username=web_user
spring.datasource.password=pass

# mapper
mybatis.mapper-locations=mapper/*_mapper.xml

 

  • MemberController
package kr.co.gudi.controller;

import javax.servlet.http.HttpServletRequest;

import kr.co.gudi.service.MemberService;

@RestController
public class MemberController {
	
	@Autowired MemberService service;
	
	Logger logger = LoggerFactory.getLogger(getClass());
	
	// RestController에서 jsp파일로 이동하고 싶을 때는 ModelAndView를 사용하면 이동가능
	@RequestMapping(value = "/")
	public ModelAndView main() {
		return new ModelAndView("login");
	}
	
	@PostMapping(value = "/login.do")
	public ModelAndView login(String id, String pw,HttpServletRequest req) {
		
		String ip = req.getRemoteAddr();
		logger.info("remote ip : "+ip);  // remote ip : 0:0:0:0:0:0:0:1 <- IPV6
		
		return service.login(id,pw,ip,req.getSession());
	}

}

 

  • MemberService(DB 로그인 생략)
package kr.co.gudi.service;

import javax.servlet.http.HttpSession;

import kr.co.gudi.dao.MemberDAO;

@Service
public class MemberService {
	
	@Autowired MemberDAO dao;
	
	// properties에 있는 값 가져오기
	@Value("${user.id}") private String adminId;
	@Value("${user.pw}") private String adminPw;
	@Value("${user.ip}") private String adminIp;
	
	Logger logger = LoggerFactory.getLogger(getClass());

	public ModelAndView login(String id, String pw, String ip, HttpSession session) {
		logger.info(id+" / "+pw);
		
		// 특정한 ID로 로그인 시도 시 일반 DB를 통한 로그인을 하지 않음
		logger.info(adminId+" / "+adminPw+" / "+adminIp);
		
		String page = "redirect:/";
		String msg = "로그인에 실패했습니다.";
		
		if(id.equals(adminId)) {
			// 관리자 로그인 진행
			if(pw.equals(adminPw) && ip.equals(adminIp)) {
				page = "result";
				msg = "로그인에 성공했습니다.";
				session.setAttribute("loginId", id);
				session.setAttribute("ip", ip);
				session.setAttribute("grade", "admin");
			}
		}else {
			// DB를 이용한 일반 로그인 진행
			if(dao.login(id,pw) == 1) {
				page = "result";
				msg = "로그인에 성공했습니다.";
				session.setAttribute("loginId", id);
				session.setAttribute("ip", ip);
				session.setAttribute("grade", "user");
			}
		}

		ModelAndView mav = new ModelAndView(page);
		mav.addObject("msg",msg);
		
		return mav;
	}

}

 

 

 

AOP(Aspect Oriented Programming)

  • 관점 지향 프로그래밍
  • 하나의 흐름에 특정한 시점에 수행되는 프로그램을 만드는 것

Written by zer0box

 

 

Interceptor

  • Interceptor를 활용하면 Controller 도착 전 특정 작업을 수행하거나 Controller를 지나 client에 도착하기 전 특정 작업을 수행 할 수 있음
  • Spring boot 2.6.x 부터는 Interceptor를 활용하기 위해서 아래의 Interface를 구현받아야 함(controller 생성시)
    • org.springframework.web.servlet.HandlerInterceptor
      org.springframework.web.servlet.config.annotation.WebMvcConfigurer

 

  • InterceptorConfig(config package 생성)
package kr.co.gudi.config;

import java.util.ArrayList;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import kr.co.gudi.utils.LoginCheck;

@Configuration // 이게 있어야 설정 클래스라는 의미
public class InterceptorConfig implements WebMvcConfigurer {

	@Autowired LoginCheck check;
	
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// registry에 인터셉트 할 내용이 들어감
		
		ArrayList<String> excludeList = new ArrayList<String>();
		excludeList.add("/");
		excludeList.add("/join*"); // join 뒤에 뭐가 오는 경우 예외
		excludeList.add("/resources/**"); // resources/ 이후 모든 경로들 예외
		excludeList.add("/*.ajax"); // .ajax로 끝나는 요청은 예외
		
		registry.addInterceptor(check) // 실행할 클래스
		.addPathPatterns("/**") // 인터셉트를 적용할 URL pattern
		.excludePathPatterns(excludeList); // 예외 패턴
	}	

}

 

  • LoginCheck(utils 생성)
package kr.co.gudi.utils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Component
public class LoginCheck implements HandlerInterceptor {
	
	Logger logger = LoggerFactory.getLogger(getClass());

	// preHandler : Controller 전에 이곳을 지나침
	// true : 컨트롤러 접근 허용 / false : 컨트롤러 접근 불가
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		logger.info("preHandler : "+request.getRequestURI());
		
		boolean pass = true;
		
		HttpSession session = request.getSession();
		if(session.getAttribute("loginId") == null) {
			response.sendRedirect("/");
			pass = false;
		}
		
		return pass;
	}

	// postHandler : 컨트롤러를 지나간 이후 들림
	// request / response 객체에 추가로 담아서 보낼때
	// xxxModelAndView에 무언가 추가할 때
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView mav) throws Exception {
		logger.info("postHandler");
		String loginId = (String) request.getSession().getAttribute("loginId");
		String content = "<div>안녕하세요 "+loginId+"님 <a href='logout.do'>로그아웃</a></div>";
		mav.addObject("loginBox",content);
	}	

}

 

 

Aspect-J

  • Aspect-J  :  point cut 표현식을 사용해서 특정 메서드 사용 전후에 처리를 도와주는 라이브러리
  • Spring에서는 이 기능을 활용하기 위해 최소 3가지 라이브러리가 필요하지만 Boot에서는 하나의 라이브러리만 설정하면 됨
  • 프로젝트 만들기 까다로워? 생략