저번글에서, 로그인처리까지 완료했으니 이번엔 로그인 후, 권한에 따라 기능을 제한하고 나눠볼 예정이다.
로그인구현 첫번째 글에서 설명했듯이,인터셉터를 이용한 로그인 방식을 구현했다.
그래서 인터셉터를 이용해서 로그인 시, 글작성, 글수정과같은 기능을 제한 , 로그인 페이지로 넘어가게 만들고
이동 전 페이지를 기억해서 원래의 페이지로 돌아가도록 기능을 구현 할 예정이다.
먼저 kr.co.commons.interceptor 패키지에 AuthInterceptor 클래스를 만든다.
public class AuthInterceptor extends HandlerInterceptorAdapter{
private static final Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);
private void saveDestination(HttpServletRequest request) {
String uri = request.getRequestURI();
String query = request.getQueryString();
if(query == null || query.equals("null")) {
query = "";
}else {
query = "?"+query;
}
if(request.getMethod().equals("GET")) {
logger.info("destination : " + (uri+query));
request.getSession().setAttribute("destination", uri + query);
}
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession httpSession = request.getSession();
if(httpSession.getAttribute("login") == null) {
logger.info("current user is not logged");
saveDestination(request);
response.sendRedirect("/member/errorLogin");
return false;
}
return true;
}
}
saveDestination 부분은 뒤에서 설명하고(페이지기억) , 먼저 preHandle 부분부터 설명하겠다.
컨트롤러 처리 전에 개입하는 preHandle이다.
로그인 여부에 따라 제한하고 싶은 컨트롤러가 호출되면,
먼저 HttpServletRequest의 요청값에서 Session값을 get한다.
로그인하면 login이라는 key로 세션값이 저장되는데, login이라는 key가 null이라면 (로그인이 안되있으면)
/member/errorLogin으로 리다이렉트 시키고 false를 리턴한다.
(인터셉터는 false를 리턴하면 뒤의 메서드까지 처리한후 리턴하고, true를 리턴하면 리턴없이 다음로직으로 넘어간다.)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
</head>
<body>
<script>
alert("로그인이 필요합니다.");
self.location = "/member/loginView";
</script>
</body>
</html>
errorLogin.jsp 이다. 로그인이 필요합니다 창을 띄우고 로그인페이지로 리턴한다.
디스패쳐서블릿xml이다 (servlet-context.xml)
<beans:bean id="authInterceptor" class="kr.co.commons.interceptor.AuthInterceptor"/>
<interceptors>
<interceptor>
<mapping path="/board/writeView" />
<mapping path="/board/updateView" />
<mapping path="/board/delete"/>
<beans:ref bean="authInterceptor" />
</interceptor>
</interceptors>
지금 들어가있는 매핑path는 글작성, 글수정, 글삭제이다.
비로그인상태로 해당 url로 접근하면(버튼을 누르면) errorlogin페이지로 넘어간다.
이제 errorlogin 페이지로 넘어간 후, 로그인했을때 다시 원래의 페이지로 돌아가도록 한다.
간혹 홈페이지들중 글수정이나 삭제를 눌렀을때 세션기한이 만료되서 로그인페이지로 넘어간 후,
로그인했을때 메인으로 넘어가는 !! 경우가 있는데 사용자를 상당히 열받게함으로 꼭 필요한 기능이겠다...(경험담)
맨 위의 authInterceptor 인터셉터에서 saveDestination을 보자.
private void saveDestination(HttpServletRequest request) {
String uri = request.getRequestURI();
String query = request.getQueryString();
if (query == null || query.equals("null")) {
query = "";
} else {
query = "?" + query;
}
if (request.getMethod().equals("GET")) {
logger.info("destination : " + (uri + query));
request.getSession().setAttribute("destination", uri + query);
}
}
간단하다. http 요청에서 URI와 QueryString을 가져와서 합친후, destination이란 key에 넣는다.
URI와 QueryString이 뭔지, 어떻게 작용하는지는 http프로토콜에 대한 공부가 필요하기에 따로 작성하지는 않겠다.
이제 로그인페이지로 넘어가는 인터셉터를 작성했으니, 로그인에 개입하는 인터셉터도 수정해줘야겠다.
@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);
Object destination = httpSession.getAttribute("destination");
Object URL = httpSession.getAttribute("URL");
response.sendRedirect(destination != null ? (String) destination : (String) URL);
}
}
로그인페이지로 넘겨주는 인터셉터에서, destination이라는 key를 httpSession에 담았다.
그걸 key값으로 꺼내서 리다이렉트url에 붙여준다.
response.sendRedirect(destination != null ? (String) destination : (String) URL);
destination이 null이 아닐때 destination을(if) , null이면 URL을 리다이렉트한다(else).
URL은 뒤에 로그인, 로그아웃시 페이지를 기억하는 부분인데 뒤에서 설명하겠다.
결과
1장 - 로그인을 하지 않은상태이다. 새글작성을 누르면
2장-errorLogin페이지로 이동 확인버튼을 누르면
3장-login페이지로 이동
4장-글작성페이지로 이동 ( 글작성버튼을 누르면 글작성페이지로 가기 때문에 해당 url로 이동함)
이제 화면 상단의 로그인과 로그아웃버튼을 눌러서 로그인시에도, 이전페이지로 돌아가는 기능을 구현해보겠다.
URIInterceptor 생성
public class URIInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session = request.getSession();
if (session.getAttribute("loginVO") == null) {
String uri = request.getRequestURI();
String query = request.getQueryString();
if (query == null || query.equals("null")) {
query = "";
} else {
query = "?" + query;
}
if (request.getMethod().equals("GET")) {
request.getSession().setAttribute("URL", uri + query);
}
}
return true;
}
saveDestination과 별다른 부분은 없다.
마찬가지로 인터셉터를 구현했으니 스프링에 등록해준다. (디스패쳐xml)
<beans:bean id="uRlInterceptor" class="kr.co.commons.interceptor.URIInterceptor" />
<interceptors>
<interceptor>
<mapping path="/**/**"/>
<exclude-mapping path="/resources/**"/>
<exclude-mapping path="/member/**"/>
<exclude-mapping path="/reply/**" />
<beans:ref bean="uRlInterceptor"/>
</interceptor>
</interceptors>
리소스와 관련된 패치와,member(로그인페이지,회원가입페이지 등), reply(댓글관련) 패치를 제외시키고
모든 url에 인터셉터가 개입하게 만들어준다.
saveDestination과 마찬가지로 URI라는 key로 세션에 저장되어있는데,
위를보면 로그인 인터셉터 postHandle 아랫부분에
Object URL = httpSession.getAttribute("URL");
response.sendRedirect(destination != null ? (String) destination : (String) URL);
가 있다. URL의 값을 가져오고 destination key가 없는 상황에선 URL이 수집한 주소로 리다이렉트 하게된다.
로그인창으로 가게되면, /member/**의 url을 호출하는데, url수집 인터셉터가 해당 url은 제외해서
그 전의 url인 원래 페이지의 url을 세션에서 저장하고 있다.
댓글기능을 구현한 후라서, 댓글url도 제외시켜줬다.
URL인터셉터를 이용해서 로그아웃도 간단하게 구현할 수 있다.
로그아웃컨트롤러
@RequestMapping(value="/logout" , method=RequestMethod.GET)
public String logout(HttpServletRequest request, HttpSession session )throws Exception{
logger.info("logout");
Object URL = session.getAttribute("URL");
session.invalidate();
return "redirect:"+(String)URL;
invalidate 메서드는 세션에 있는 값을 모두 초기화해준다.
그 후 URL인터셉터의 주소로 리다이렉트 한다.
결과
1장에서, 로그인버튼을 누르면
2장,로그인페이지로 넘어가서 로그인을 하면
3장,원래의 페이지로 돌아오고, 로그아웃버튼을 누르면
4장,원래의 페이지로 돌아오면서 로그아웃을 한다.
5장,컨트롤러에 찍힌 (URL) key값의 value값
사실, 처음 saveDestination은 구현할 필요없이 URLInterceptor를 이용해서 모두 해결할 수 있다.
모든 부분에서 url을 가져오는 url인터셉터를 authInterceptor의 preHandle에
HttpSession.getAttribute로 가져오고, (destination)을 없애고,
그냥 (URL)로 리다이렉트 하도록 적용시켜주면 동일하게 동작한다.
간결하게 기능을 짜는것이 베스트지만 여러가지 인터셉터를 활용해보고 싶어서 이렇게 구현해보았다.
인터셉터를 이용해서 게시글 작성, 수정, 삭제를 구분했으니
이제 프론트단에서 세션값을 이용해 댓글작성,수정,삭제도 구분해주려고 한다. (그 외 약간의 수정을 포함)
앞서, 로그인인터셉터에서 login이라는 key값으로 memberVO(세션값) 을 세션에 set했다.
먼저, 프론트단의 readView(게시글내용) 에서 댓글작성 부분에 c:if 로 login을 이용했다.
비로그인과 로그인시, 보이는 부분이 달라지고, 댓글작성을 막을 수 있도록 나누었다.
<label for="content">Reply</label>
<c:if test="${not empty login}">
<form name="commentInsertForm" action="/reply/writeReply" enctype="multipart/form-data" method="post" id="commentInsertForm">
<input type="hidden" name="bno" value="${read.bno}" />
<div class="input-group">
<div class="row">
<input class="form-control" id="writer" name="writer" value="${login.memberId}" style="width:40%" readonly>
<textarea
id="content" name="content"
class="myEditor" ></textarea>
<input type="file" name="file" />
<button class="btn btn-primary" type="button" name="commentInsertBtn" style="width: 40%; margin-top: 10px">댓글등록</button>
</div>
</div>
</form>
</c:if>
<c:if test="${empty login}">
<a href="/member/loginView" class="btn btn-default btn-block" role="button">
<i class="fa fa-edit"></i>댓글을 등록하면 로그인을 해주세요.(클릭시 로그인페이지)</a>
</c:if>
writer(작성자) 입력부분에 login.memberId 로 작성자의 Id를 넣는다.
후에 회원정보수정 기능을 구현하면 닉네임(login.memberName) 으로 설정 할 예정이다.
댓글 수정,삭제 기능이다.
<script>
var bno = '${read.bno}'; //게시글 번호
var writer = "${login.memberId}" ;
//댓글 목록
function commentList(){
$.ajax({
url : '/reply/readReply',
type : 'get',
data : {'bno':bno },
success : function(data){
var a ='';
if(data<1){
a +='<h2>등록된 댓글이 없습니다</h2> ';}
$.each(data, function(key, value){
a += '<div class="commentArea" >';
a += '<div class="commentInfo'+value.rno+'">'+'댓글번호 : '+value.rno+' / 작성자 : '+value.writer;
a += '<div class="commentDate'+value.rno+'"> <p> 작성일 : '+value.regdate+'</p>' ;
a += '<tr><td class="commentContent'+value.rno+'"> <p> 내용 : '+value.content +'</p> </td><tr> ';
if(writer == value.writer){
a += '<button type="button" class="btn btn-success" onclick="commentUpdate('+value.rno+',\''+value.content+'\');"> 수정 </button>';
a += '<button type="button" class="btn btn-dark" onclick="commentDelete('+value.rno+');"> 삭제 </button> ';
}
a += ' </div></div> </div> <hr/>';
});
$(".commentList").html(a);
}
});
}
ajax를 사용해서 비동기방식으로 구현한 댓글기능이다.
수정, 삭제 버튼부분에 if 조건문을 걸어서, login.memberId와 writer(작성자)가 맞으면 수정/삭제버튼을 출력한다.
나중에 이 부분을 login.memberName으로 바꿀 예정이다.
결과
1장은 비로그인, 2장은 로그인시
좀 더 수정할 부분이 남았지만, 이런식으로 login이라는 session에 저장된 key값을 이용하면 된다.
나머지 부분은 따로 글을 작성할 필요가 없을 것 같아서 마무리하겠다.
'SPRING > IceWater Community' 카테고리의 다른 글
[스프링]회원가입 이메일인증 구현 (1) | 2021.09.19 |
---|---|
[스프링] 자동로그인 기능 구현 - 로그인4 (0) | 2021.09.17 |
[스프링]인터셉터를 활용한 로그인구현 (세션부여) - 로그인2 (0) | 2021.09.15 |
[스프링]회원가입(ajax 유효성 검사,비밀번호 암호화) - 로그인1 (0) | 2021.09.10 |
[스프링]게시판 조회수,댓글수 순 정렬과 게시글 10개,20개씩 보기 구현 (3) | 2021.09.07 |