먼저, 로그인 화면에 로그인유지 체크박스를 만들었다.
<div class="form-group">
<div class="custom-control custom-checkbox small">
<label>
<input type="checkbox" name="useCookie"> 로그인유지
</label>
</div>
</div>
로그인 인터셉터의 postHandle 부분
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("interceptor postHandel");
HttpSession httpSession = request.getSession();
ModelMap modelMap = modelAndView.getModelMap();
Object memberVO = modelMap.get("member");
if (memberVO != null) {
logger.info("new login success");
httpSession.setAttribute(LOGIN, memberVO);
// response.sendRedirect("/board/list");
if(request.getParameter("useCookie")!=null) {
logger.info("remember me...");
Cookie loginCookie = new Cookie("loginCookie",httpSession.getId());
loginCookie.setPath("/");
loginCookie.setMaxAge(60*60*24*7);
response.addCookie(loginCookie);
}
Object destination = httpSession.getAttribute("destination");
Object URL = httpSession.getAttribute("URL");
response.sendRedirect(destination != null ? (String) destination : (String) URL);
}
}
이전 로그인 관련 글들에서 여러번 작성했던 로그인인터셉터의 postHandle이다.
자동로그인을 위해선 쿠키를 사용하는데, 쿠키와 세션의 차이는 이전 글에 올렸으니 설명은 생략하겠다.
로그인시, 쿠키를 생성해줘야하기 때문에 로그인 인터셉터에서 처리한다.
쿠키 체크박스에 체크를 하면 useCookie!=null이 되고,
Cookie 객체를 선언해서 메서드를 활용한다.
먼저 setPath("/")는 모든 웹어플리케이션의 주소에서 쿠키를 전송한다.
setMaxAge는 쿠키의 유효시간을 지정한다.
60초 * 60분 * 24시간 * 7일 이라고 생각하면 된다.(초단위)
만약 10분을 설정하고 싶다면 60 * 10 이런식으로 작성하면 되겠다.
그 후, response에 loginCookie라는 쿠키를 추가한다.
Mapper.xml
<resultMap id="MemberVOResultMap" type="kr.co.vo.MemberVO">
<result property="memberId" column="MEMBER_ID"/>
<result property="memberPw" column="MEMBER_PW"/>
<result property="memberName" column="MEMBER_NAME"/>
<result property="memberEmail" column="MEMBER_EMAIL"/>
<result property="memberJoinDate" column="MEMBER_JOIN_DATE"/>
<result property="memberImg" column="MEMBER_IMG"/>
<result property="memberPoint" column="MEMBER_POINT"/>
</resultMap>
<update id="keepLogin">
UPDATE mp_member
SET session_key = #{sessionId},
session_limit = #{sessionLimit}
WHERE member_id=#{memberId}
</update>
<select id="check" resultMap="MemberVOResultMap">
SELECT
*
FROM MP_MEMBER
WHERE session_key = #{value}
</select>
데이터베이스 컬럼명과 VO의 명이 달라서 resultMap을 생성해서 사용했다.
상당히 불편하니 이름을 맞춰서 resultType으로 편하게 사용하도록 하자..
update문은 로그인유지를 선택한경우, 세션아이디와 로그인유지시간을 갱신한다.
select문은 session_key 값이 일치하는 회원의 모든 정보를 가져온다
DAO
// 로그인 유지 처리
void keepLogin(String MemberId, String sessionId, Date sessionLimit) throws Exception;
// 세션키 검증
UserVO checkUserWithSessionKey(String value) throws Exception;
DAOImpl
@Override
public void keepLogin(String memberId,String sessionId, Date sessionLimit)throws Exception{
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("memberId", memberId);
paramMap.put("sessionId", sessionId);
paramMap.put("sessionLimit", sessionLimit);
sqlsession.update("memberMapper.keepLogin", paramMap);
}
@Override
public MemberVO checkUserWithSessionKey(String value) throws Exception{
return sqlsession.selectOne("memberMapper.check", value);
}
Service
public void keepLogin(String memberId, String sessionId, Date sessionLimit) throws Exception;
public MemberVO checkLoginBefore(String value) throws Exception;
}
ServiceImpl
@Override
public void keepLogin(String userId, String sessionId, Date sessionLimit) throws Exception{
memberDAO.keepLogin(userId, sessionId, sessionLimit);
}
public MemberVO checkLoginBefore(String value) throws Exception{
return memberDAO.checkUserWithSessionKey(value);
}
Controller
@RequestMapping(value="/login" , method= RequestMethod.POST)
public void loginPost(LoginVO loginVO, HttpSession httpSession,Model model)throws Exception{
logger.info("loginVO"+loginVO.getMemberId());
MemberVO memberVO = memberService.login(loginVO);
logger.info("Pw"+memberVO);
if( memberVO == null || !BCrypt.checkpw(loginVO.getMemberPw(), memberVO.getMemberPw())) {
return;
}
model.addAttribute("member", memberVO);
if(loginVO.isUseCookie()) {
int amount = 60*60*24*7;
Date sessionLimit = new Date(System.currentTimeMillis() + (1000*amount));
memberService.keepLogin(memberVO.getMemberId(), httpSession.getId(), sessionLimit);
}
}
로그인유지 인풋박스에 체크를해서, UseCookie값이 있다면,
System.currentTimeMillis는 서버시간을 밀리세컨트단위로 가져온다. (1000은 초단위로 가져오게 해줌)
RememberInterceptor 생성
public class RememberInterceptor extends HandlerInterceptorAdapter {
private static final Logger logger = LoggerFactory.getLogger(RememberInterceptor.class);
@Inject
private MemberService memberService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession httpSession = request.getSession();
Cookie loginCookie = WebUtils.getCookie(request, "loginCookie");
if (loginCookie != null) {
MemberVO memberVO = memberService.checkLoginBefore(loginCookie.getValue());
if (memberVO != null)
httpSession.setAttribute("login", memberVO);
}
return true;
}
}
디스패쳐.xml(servlet-context.xml)
<beans:bean id="rememberInterceptor" class="kr.co.commons.interceptor.RememberInterceptor" />
<interceptors>
<interceptor>
<mapping path="/**/" />
<beans:ref bean="rememberInterceptor" />
</interceptor>
</interceptors>
서비스 클래스를 의존주입해주고, WebUtils.getCookie로 request요청값의 쿠키를 가져온다.
쿠키가 있다면 mapper의 check 로직으로 쿠키의 저장된 값에 맞는 id의 값을 memberVO에 담고,
login이라는 key로 set한다.
조금 복잡해진 정리가 된 것 같아서 흐름대로, 로직을 설명해보려고 한다.
- 로그인페이지에서 로그인유지 체크박스(useCookie) 클릭 후 로그인
- 로그인인터셉터에서 로그인유지체크박스 여부에 따라 쿠키생성
- 로그인컨트롤러에서 쿠키의 ID와 유지기간을 DB에 저장
- 따로 로그아웃버튼을 눌러서 로그아웃하지않고, 브라우저를 끄거나 하는등의 세션값만 사라지게 함
- 다시 접속 시 rememberInterceptor가 개입(모든 주소)
- 인터셉터에서 브라우저에 쿠키가 있는지 확인
- 있다면 Service의 checkLoginBefore메서드로 쿠키의 Value를 보냄
- Value를 매개변수로 회원의 모든 정보를 select (Mapper의 select id="check") 해서 memberVO로 받음
- login키에 memberVO를 값으로 넣음
간단하게,
자동로그인을 체크한 회원의 DB에 sessionID와 유지기한을 넣는다
재접속시
쿠키의 value값(sessionID) 과 회원의 sessionID로 select, 정보를 memberVO로 받고
그 정보를 login이라는 key로 memberVO를 담아서 세션을 부여한다.
서버에서 세션을 부여한다는 것은, 클라이언트에서 로그인을 한다는 것과 같다.
이렇게 자동로그인이 구현된다.
이제 로그아웃을 수정하려고 한다. 이제 로그아웃버튼을 누르면 세션뿐만아니라 쿠키까지 삭제해줘야 하겠다.
Controller의 logout
@RequestMapping(value="/logout" , method=RequestMethod.GET)
public String logout(HttpServletRequest request, HttpSession session, HttpServletResponse response,ModelMap model)throws Exception{
logger.info("logout");
Object URL = session.getAttribute("URL");
Object object = session.getAttribute("login");
if(object != null) {
MemberVO memberVO = (MemberVO) object;
session.removeAttribute("login");
session.invalidate();
Cookie loginCookie = WebUtils.getCookie(request, "loginCookie");
if(loginCookie != null) {
loginCookie.setPath("/");
loginCookie.setMaxAge(0);
response.addCookie(loginCookie);
memberService.keepLogin(memberVO.getMemberId(),"none",new Date());
}
}
logger.info("URL"+ URL);
String requestURL = "/";
if(StringUtils.isNotBlank((String) URL))
requestURL = (String)URL;
return "redirect:"+requestURL;
}
자동로그인에서 사용한 로직과 아주 비슷하다.
일단 쿠키를 불러온다음 null이 아니라면 시간을 0으로 해준다. (바로 삭제)
그 후 add해주고, session_id 컬럼에 none과 현재시간을 넣어준다( default값)
결과
1장. 로그인유지체크 후 로그인
2장. 세션과 쿠키가 생성됨
3장.로그아웃시 세션과 쿠키가 삭제됨
서버를 끄거나 브라우저를 닫고, 다시 접속해도 로그인이 풀리지않고 계속 되어있다.
이제 회원정보수정과 추천기능을 구현하고,
인기게시글과 인기댓글, 추천수순위 공지사항등이 있는 메인페이지를 구현한 후,
촌스러운 커뮤니티 디자인을 전체적으로 개선해보려고 한다.
'SPRING > IceWater Community' 카테고리의 다른 글
[스프링] 아이디찾기 구현 (1) | 2021.09.24 |
---|---|
[스프링]회원가입 이메일인증 구현 (1) | 2021.09.19 |
[스프링]로그인 권한설정과(인터셉터) 로그인,로그아웃 전 페이지 기억 기능 - 로그인 3 (1) | 2021.09.16 |
[스프링]인터셉터를 활용한 로그인구현 (세션부여) - 로그인2 (0) | 2021.09.15 |
[스프링]회원가입(ajax 유효성 검사,비밀번호 암호화) - 로그인1 (0) | 2021.09.10 |