기존 프로젝트에 그동안 공부했던 스프링 시큐리티를 접목하는 작업을 진행하도록 하겠다.
그전에 해당 기능은
-로그인과 회원 가입 페이지의 작성
- 기존 화면과 컨트롤러에 시큐리티 관련 내용 추가
- Ajax부분의 변경
로그인 페이지 처리
기존의 로그인화면은 정말 끔찍하다.. 어쩌면 스프링에서 기본으로 주는화면급이었다.
똑같았다 그냥 그럼 조금 이쁘게 부트스트랩을 적용하고, 아까의 로그인의 화면의 태그라던가 id는 동일하게 설정한뒤 생성해보았다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>SB Admin 2 - Bootstrap Admin Theme</title>
<!-- Bootstrap Core CSS -->
<link href="/resources/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- MetisMenu CSS -->
<link href="/resources/vendor/metisMenu/metisMenu.min.css" rel="stylesheet">
<!-- Custom CSS -->
<link href="/resources/dist/css/sb-admin-2.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href="/resources/vendor/font-awesome/css/font-awesome.min.css"
rel="stylesheet" type="text/css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="login-panel panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Please Sign In</h3>
</div>
<div class="panel-body">
<form role="form" method='post' action="/login">
<fieldset>
<div class="form-group">
<input class="form-control" placeholder="userid"
name="username" type="text" autofocus>
</div>
<div class="form-group">
<input class="form-control" placeholder="Password"
name="password" type="password" value="">
</div>
<div class="checkbox">
<label> <input name="remember-me" type="checkbox">Remember
Me
</label>
</div>
<!-- Change this to a button or input when using this as a form -->
<a href="index.html" class="btn btn-lg btn-success btn-block">Login</a>
</fieldset>
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
</form>
</div>
</div>
</div>
</div>
</div>
<!-- jQuery -->
<script src="/resources/vendor/jquery/jquery.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="/resources/vendor/bootstrap/js/bootstrap.min.js"></script>
<!-- Metis Menu Plugin JavaScript -->
<script src="/resources/vendor/metisMenu/metisMenu.min.js"></script>
<!-- Custom Theme JavaScript -->
<script src="/resources/dist/js/sb-admin-2.js"></script>
<script>
$(".btn-success").on("click", function(e){
e.preventDefault();
$("form").submit();
});
</script>
</body>
</html>
폼태그가 붙고, 거기에 컨테이너 div가 붙고 해서 뭔가 복잡해 보이는데
화면으로보면 크게 달라진것은 없다. 귀찮다면 그전의 화면을 계속사용해도 문제는 없다. 하지만
- JSTL이나 스프링 시큐리티의 태그를 사용할 수 있도록 선언해주어야한다!!!
- <form>태그내의 input태그의 name속성을 시큐리티에 맞게 수정해야주어야한다. 반드시!!!!
- CSRF토큰 항목 추가
- JavaScript를 통한 로그인전송 기능 구현
로그인이 정상적으로 이루어지는 지 확인하자
우린 지금 로그인 성공 후에 CustomLoginSuccessHandler를 이용해서 사용자의 권한에 따라서 이동하도록 했다.
스프링 시큐리티는 기본적인 로그인 후 처리를 SavedRequestAwareAuthnticationSuccessHandeler라는 클래스를
이용한다. 해당 클래스는 사용자가 원래 보려고 했던 페이지의 정보를 유지해서 로그인후에 다시 원했던 페이지로
이동하는 방식이다.
SavedRequestAwareAuthnticationSuccessHandeler를 이용하는 설정은 기존에 XML에서
authentication-success-handler-ref 속성을 삭제하고 관련 스프링 빈의 설정도 사용하지 않도록 한다.
게시물의 작성(/board/register)시 로그인 페이지로 이동하고, 로그인 후에 다시 게시물의 작성 페이지로 이동하는 방식을 적용해보겠다.
<!-- <bean id="customLoginSuccess" -->
<!-- class="org.study.security.CustomLoginSuccessHandler"></bean> -->
<!-- security http 태그부분에서
<security:form-login login-page="/customLogin"/>
<!--로수정해줄것-->
해당 클래스를 제거한 모습
게시물 작성 시 스프링 시큐리티 처리
일반적인 경우, 게시물 리스트는 모든사람이 제약없이 사용가능하고, 읽는것도, 구론데 게시물작성시나 댓글시에는
로그인한 사용자에 한해서 처리된다.
이것을 고려해 security-context.xml에 스프링 시큐리티 관련 설정을 추가하고, BoardController에 어노테이션을 통해 제어하도록 해야한다.
겟방식으로 들어오면
// @GetMapping("/register")
// public void register() {
// }
@GetMapping("/register")
@PreAuthorize("isAuthenticated()")
public void register() {
}
기존의 등록을 주석처리하고 @PreAutherize를 추가해주었다.
포스트방식으로 들어오면
// @PostMapping("/register")
// public String register(BoardVO board, RedirectAttributes rttr) {
// if (board.getAttachList() != null) {
// board.getAttachList().forEach(attach -> log.info(attach));
// }
// service.register(board);
// rttr.addFlashAttribute("result", board.getBno());
// return "redirect:/board/list";
// }
@PostMapping("/register")
@PreAuthorize("isAuthenticated()")
public String register(BoardVO board, RedirectAttributes rttr) {
log.info("==========================");
log.info("register: " + board);
if (board.getAttachList() != null) {
board.getAttachList().forEach(attach -> log.info(attach));
}
log.info("==========================");
service.register(board);
rttr.addFlashAttribute("result", board.getBno());
return "redirect:/board/list";
}
@preAuthorize를 이용할 떄의 표현식은 isAuthenticated()로 어떠한 사용자든 로그인이 성공한 사용자만이 해당 기능을 사용할 수 있도록 처리한다.
게시물 작성 시 로그인한 사용자의 아이디 출력
게시물의 작성은 이제 작성자를 직접우리가 써서 작성되는것이아닌 작성자항목에는 현재 사용자의 아이디가 출력될수 있게한다.
register.jsp파일에서 작성자를 입력받는 태그에 <sec:authentication property="principal.username"/> 를 넣어주고
읽기전용인 readonly를 붙여 수정해주자
<div class="form-group">
<label>Writer</label> <input class="form-control" name='writer'
value='<sec:authentication property="principal.username"/>' readonly="readonly">
</div>
그리고 로그인이 된 상태에서 글 작성을 해보자
정상적으로 로그인 사용자의 아이디가 출력되었다.
CSRF 토큰을 설정
스프링 시큐리티를 사용할 때 post방식의 전송은 반드시 CSRF토큰을 사용하도록 추가해야한다.
<form>태그 내에 CSRF토큰의 값을 히든타입으로 처리하여 추가하여 주자
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
게시물 조회와 로그인 처리
일반적인 경우라면 게시물의 조회는 그 자체의 로그인 여부에 관계없이 처리되지만, 게시물의 조회화면에서
현재 로그인한 사용자만이 수정/삭제 작업을 할 수 있는 기능이 활성화 된다.
게시물 조회를 담당하는 get.jsp파일에
현재 게시물의 작성자와 현재 로그인한 사용자 정보를 비교해서 이를 처리할 수 있도록한다.
본인이 작성하면 수정버튼이 생성, 아니라면 생성해 있지않음
상단에는 시큐리티 태그를 넣어주자.
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>
<!-- <button data-oper='modify' class="btn btn-default">Modify</button> -->
<sec:authentication property="principal" var="pinfo"/>
<sec:authorize access="isAuthenticated()">
<c:if test="${pinfo.username eq board.writer}">
<button data-oper='modify' class="btn btn-default">Modify</button>
</c:if>
</sec:authorize>
기존 수정버튼을 위와같이 수정했다. <c:if>태그를 이용해 만약 유저정보와 해당 작성자가 같다면 수정버튼이 있도록 한것이다.
그리고 조회화면에서 댓글 추가버튼도 동일하게 넣어주자.
<!-- <div class="panel-heading"> -->
<!-- <i class="fa fa-comments fa-fw"></i> Reply -->
<!-- <button id='addReplyBtn' class='btn btn-primary btn-xs pull-right'>New -->
<!-- Reply</button> -->
<!-- </div> -->
div class="panel-heading">
<i class="fa fa-comments fa-fw"></i> Reply
<sec:authorize access="isAuthenticated()">
<button id='addReplyBtn' class='btn btn-primary btn-xs pull-right'>New Reply</button>
</sec:authorize>
</div>
기존의 버튼도 위와같이 등록창은 로그인이 되어있다면! 나타나도록 했다.
게시물의 수정/삭제
개사물의 수정과 삭제는 브라우저에서는 로그인한 사용자만이 접글 할 수 있지만, 사용자가 URL을 조작해서 접근이 가능하다.
그래서 화면과 POST방식으로 처리되는 부분에 CSRF토큰과 스프링 시큐리티를 적용해주자!
수정기능을 하는 modify.jsp 파일을 수정해야한다.
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>
먼저 태그를 넣어주고,
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
토큰을 히든타입에 넣어준다.
조회와 마찬가지로 현재 로그인한 사용자가 게시물의 작성자인 경우에만 수정과 삭제가 가능하도록 제어한다.
<!-- <button type="submit" data-oper='modify' class="btn btn-default">Modify</button> -->
<!-- <button type="submit" data-oper='remove' class="btn btn-danger">Remove</button> -->
<sec:authentication property="principal" var="pinfo"/>
<sec:authorize access="isAuthenticated()">
<c:if test="${pinfo.username eq board.writer}">
<button type="submit" data-oper='modify' class="btn btn-default">Modify</button>
<button type="submit" data-oper='remove' class="btn btn-danger">Remove</button>
</c:if>
</sec:authorize>
기존의 버튼태그들은 주석처리하고 로그인상태와 해당 작성자가 같은 조건일경우 버튼이 나타나도록 했다.
이제 컨트롤단에서 해당 기능이 수정,삭제 될 수 있도록 수정해준다.
// @PostMapping("/remove")
// public String remove(@RequestParam("bno") Long bno, Criteria cri, RedirectAttributes rttr) {
// List<BoardAttachVO> attachList = service.getAttachList(bno);
// if (service.remove(bno)) {
// deleteFiles(attachList);
// rttr.addFlashAttribute("result", "success");
// }
// return "redirect:/board/list" + cri.getListLink();
// }
@PreAuthorize("principal.username == #writer")
@PostMapping("/remove")
public String remove(@RequestParam("bno") Long bno, Criteria cri, RedirectAttributes rttr, String writer) {
log.info("remove..." + bno);
List<BoardAttachVO> attachList = service.getAttachList(bno);
if (service.remove(bno)) {
deleteFiles(attachList);
rttr.addFlashAttribute("result", "success");
}
return "redirect:/board/list" + cri.getListLink();
}
내용은 크게 다를게없다. 대신 표현식에서 차이가 있는데 이때 #wirter를 통해서 해당 작성자와 현재 username이 맞는지를 판단한다. 그리고 그 witer를 매개변수로 사용할 수 있다. 중요하다 이건
수정도 파라미터로 Board타입의 객체를 받로록 설계해서 변경해준다.
// @PostMapping("/modify")
// public String modify(BoardVO board, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
// if (service.modify(board)) {
// rttr.addFlashAttribute("result", "success");
// }
// rttr.addAttribute("pageNum", cri.getPageNum());
// rttr.addAttribute("amount", cri.getAmount());
// return "redirect:/board/list";
// }
@PreAuthorize("principal.username == #board.writer")
@PostMapping("/modify")
public String modify(BoardVO board, Criteria cri, RedirectAttributes rttr) {
log.info("modify:" + board);
if (service.modify(board)) {
rttr.addFlashAttribute("result", "success");
}
return "redirect:/board/list" + cri.getListLink();
}
로그인이 되어있지않을때 수정기능이없다.
댓글기능또한이다.
하지만 본계정으로 들어가니 수정과 댓글기능이 활성화 되어있는걸 볼 수 있다.
그래서 댓글을 작성하려고보니 경고창에 이상한 것들이 나타난다.
아마도 Ajax와 스프링시큐리티 단을 처리 하지않아서 일것같다.
다음 포스팅은 ajax와 스프링 시큐리티 처리에 대해 공부해 보곘다.
'Spring공부 > 4-스프링시큐리티' 카테고리의 다른 글
프로젝트(2) (0) | 2021.10.21 |
---|---|
자동 로그인 (0) | 2021.10.21 |
스프링 시큐리티와 JSP (0) | 2021.10.19 |
커스텀 UserDetailsService (0) | 2021.10.19 |
JDBC를 이용하는 간편 인증/ 권한처리 (0) | 2021.10.18 |
댓글