SPRING(자바) 로 login 구현

1. 로그인 테이블 만들기(DB)

  • 회원테이블을 만든다. 물론 그외에 칼럼이 있지만 생략 하였다.
    CREATE TABLE user (
      userNo int(11) NOT NULL AUTO_INCREMENT,
      userEmail varchar(50) NOT NULL,
      userPwd varchar(50) DEFAULT NULL
    )
    


2. 로그인창 jsp 만들기

  • 로그인폼을 기본적인 형식으로 만들었다. 아이디저장, 자동로그인는 추후 할 예정이라서 만들었다.
<form action="/login/loginPost" method="post" id="loginForm">
	<div class="form-group label-floating">
		<label class="control-label">이메일 주소</label> <input name="userEmail" type="text" id="exampleInputEmail1" class="form-control" value="${cookie.rememberID.value}">
	</div>
	<div class="form-group label-floating">
		<label class="control-label">비밀번호</label> <input name="userPwd" type="password" id="exampleInputPassword1" class="form-control">
	</div>
	<div class="checkbox">
		<label> <input type="checkbox" name="rememberEmail"> 아이디저장 </label>
		<label> <input type="checkbox" name="useCookie"> 자동로그인 </label>
	</div>
	<button type="submit" class="btn btn-default">로그인</button>
</form>


3. 필요한 pom.xml에 메이븐파일 작성

  • Mybatis 를 사용하기 위해 아래 4개 메이븐파일을 pom.xml에 추가
<!-- jdbc 연결 -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jdbc</artifactId>
	<version>${org.springframework-version}</version>
</dependency>

<!-- mysql -->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.41</version>
</dependency>

<!-- mybatis -->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.4.1</version>
</dependency>

<!-- mybatis-spring 연결 -->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>1.3.0</version>
</dependency>


4. 로그인 Controller 작성

  • loginGet 메서드에서는 이전 페이지 기억을 위해 request.getHeader(“referer”) 를 썼다.
  • 허나 예외적으로 바로 로그인 URI 접근이라든지 로그인창 막클릭 했을때는 저장을 안한다.
  • dest 세션은 기존 페이지URI 를 저장하고있다.
@Controller
@RequestMapping("/login")
public class LoginController {

  @Inject
  private BCryptPasswordEncoder pwdEncoder;

  @Inject
  private UserService userService;

  // 로그인창
  @RequestMapping(value = "/loginGet", method = RequestMethod.GET)
  public String loginGet(HttpServletRequest request) throws Exception {
    HttpSession session = request.getSession();
    // requestUrl 가 null 이 아니거나 referer 가 null이 아닐경우
    if (request.getRequestURI() != null && request.getHeader("referer") != null) {
      // 이전페이지가 loginGet이거나 직접 로그인URL에 접속하지 않았을때만 referrer 저장
      if (!(request.getRequestURI().equals("/login/loginGet") && request.getHeader("referer").equals("http://localhost/login/loginGet"))) {
        // dest 세션에 이전 페이지 정보 저장
        session.setAttribute("dest", request.getHeader("referer"));
      }

    }

    return "/user/login";
  }

  // 로그인 submit
  @RequestMapping(value = "/loginPost", method = RequestMethod.POST)
  public void loginPost(LoginDTO dto, Model model, HttpSession session) throws Exception {
    UserVO vo = null;
    // DB 비밀번호와 로그인 비밀번호가 틀릴경우 loginFail 모델은 내려준다.
    if (!pwdEncoder.matches(dto.getUserPwd(), userService.getPwd(dto))) {
      model.addAttribute("loginFail", true);
      return;
    }
    // vo에 userNo, userEmail, UserNick, userAuth 저장
    UserVO vo = userService.login(dto);
    model.addAttribute("userVO", vo); // model에 {userVO : vo} 저장

  }

  // 로그아웃
  @RequestMapping(value = "/logout", method = RequestMethod.GET)
  public String logout(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws Exception {
    Object obj = session.getAttribute("login");

    // 세션 제거
    if (obj != null) {
      UserVO vo = (UserVO) obj;
      session.removeAttribute("login");
      session.invalidate();
    }
    return "/login/logout";
  }
}



5. loginInterCepter.class 작성

  • servlet-context.xml 에 아래내용을 추가한다.
  • /login/loginPost URI 들어갈때 인터셉터를 거치도록 한다.
<beans:bean id="loginInterceptor" class="패키지명.LoginInterceptor" />

<interceptors>
	<interceptor>
		<mapping path="/login/loginPost" />
		<beans:ref bean="loginInterceptor" />
	</interceptor>
</interceptors>



  • 그리고나서 HandlerInterceptorAdapter 를 상속받으면 인터셉터를 사용 할수있다.
  • ‘preHandle’은 컨트롤러를 거치기 전이고, ‘postHandle’은 컨트롤러를 거친 후 뷰를 거치기 전에 실행된다.
  • 인터셉터때문에 자바단에서 alert창을 띄어야 하기 때문에 PrintWriter 를 썼다. (추천하지 않습니다.)
    param을 던지고 javascript가 param을 받고 처리하는게 나을듯…
public class LoginInterceptor extends HandlerInterceptorAdapter {
  private static final String LOGIN = "login";

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HttpSession session = request.getSession();

    // 기존 로그인정보 삭제
    if (session.getAttribute(LOGIN) != null) {
      session.removeAttribute(LOGIN);
    }

    return true;
  }

  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    HttpSession session = request.getSession();
    ModelMap modelMap = modelAndView.getModelMap();
    Object userVO = modelMap.get("userVO"); // userVO 모델속성 가져옴
    UserVO vo = (UserVO) userVO;

    response.setContentType("text/html; charset=UTF-8");
    PrintWriter out = response.getWriter();

    // 아이디,비밀번호가 일치하지 않을 경우
    if (modelMap.get("loginFail") != null) {
      // 실패알림 후 로그인창 가기
      out.println("<script>alert('아이디와 비밀번호가 일치하지 않습니다.'); location.href = '/login/loginGet'</script>");
      out.close();
      return;
    }

    // 로그인 성공시(객체가 있을 경우)
    if (userVO != null) {
      session.setAttribute(LOGIN, userVO); // userVO 세션저장

      String dest = (String) session.getAttribute("dest"); // URI 세션 저장

      // 아이디 비밀번호가 실패할 경우 다시 로그인할때 referer를 loginPost 저장하기떄문에 체크
      if (dest.equals("http://localhost/login/loginPost")) {
        dest = "/";
      }
      response.sendRedirect(dest != null ? (String) dest : "/");
    } else {
      response.sendRedirect("/login/loginGet");
    }

  }

}



6. userMapper.xml 작성

  • 로그인할때에는 userNo, UserEamil, userNick, userAuth (유저권한) 을 가져온다.
<select id="org.sbang.mapper.UserMapper.login" resultType="UserVO"> <!-- 로그인 -->
	select userNo, userEmail, userNick, userAuth from user where userEmail = #{userEmail}
</select>

7. userDAO, userService 작성

  • DAO는 DB등 하나의 데이터 접근 및 갱신만 처리하며, Service 는 DAO들을 호출하여 읽은 데이터에 대한 비지니스 로직을 수행하며 트랜잭션으로 처리 하기 위함이다.
 // userDAO
 @Repository
 public class UserDAOImpl implements UserDAO {

 	@Inject
 	private SqlSession session;

 	@Override
 	public UserVO login(LoginDTO dto) throws Exception {
 		return session.selectOne("org.sbang.mapper.UserMapper.login", dto);
 	}
}
// userService
@Service
public class UserServiceImpl implements UserService {

	@Inject
	private UserDAO userDAO;

	@Override
	public UserVO login(LoginDTO dto) throws Exception {
		return userDAO.login(dto);
	}
}
  • 기본적인 로그인 방식을 알아보았다. (+기존페이지 기억 ) 하지만 이외에도 아이디 저장 및 자동로그인 또는 권한에 따른 로그인 접근 등등 그외에도 예외적인것을 다 처리 해야한다. 스프링 시큐리티를 좀더 공부하여 시큐리티로 간단히 로직을 짤수 있다고 한다. 시도는 해봤는데 잘 안되어서 포기를 여러번했다. 언젠가는 시큐리티를 넘어야 할 산이기 때문에 공부가 꾸준히 필요하다.
  • (추가) 스프링 시큐리티는 [오픈스터디 개편] 프로젝트 에 소스가 있으니 확인해보세요. (실무에서 자주 사용합니다.)
chanhee.kim's profile image

chanhee.kim

2017-11-29 09:37

Read more posts by this author