2019. 10. 10. 23:41ㆍProject B (SPMS)/Project B 파트7
스프링 시큐리티 작업
- 로그인과 회원 가입 페이지 작성
- 기존 화면과 컨트롤러에 시큐리티 관련 내용 추가
- Ajax 부분 변경
스프링 시큐리티 관련 설정 파일 확인
controller
CommonController.java
domain
AuthVO.java
MemberVO.java
mapper
MemberMapper.java
security
CustomAccessDeniedHandler.java
CustomLoginSuccessHandler.java
CustomNoOpPasswordEncoder.java
CustomUserDetailsService.java
security.domain
CustomUser.java
mapper 리소스
MemberMapper.xml
로그인 페이지 처리
로그인 페이지 기본 HTML 코드는 webapp/resources 내의 pages/login.html 페이지를 이용해서 만든다.
views
customLogin.jsp
<%@ 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">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</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>
<c:if test="${param.logout != null}">
<script>
$(document).ready(function(){
alert("로그아웃하였습니다.");
});
</script>
</c:if>
</body>
</html>
customLogin.jsp 작성 시 주의점
- <form> 태그 내의 <input> 태그의 name 속성을 스프링 시큐리티에 맞게 수정
- JSTL 이나 스프링 시큐리티 태그를 사용할 수 있게 선언
- CSS 파일이나 JS 파일의 링크는 절대 경로를 쓰도록 수정
- CSRF 토큰 항목 추가
- 자바스크립트를 통한 로그인 전송
로그인 화면표시 테스트
http://localhost/customLogin
세큐리티 환경 파일
src/main/java
com.spms.config
SecurityConfig.java
package com.spms.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import com.spms.security.CustomLoginSuccessHandler;
import com.spms.security.CustomUserDetailsService;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@Configuration
@EnableWebSecurity
@Log4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Setter(onMethod_ = { @Autowired })
private DataSource dataSource;
@Bean
public UserDetailsService customUserService() {
return new CustomUserDetailsService();
}
// in custom userdetails
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService()).passwordEncoder(passwordEncoder());
}
@Bean
public AuthenticationSuccessHandler loginSuccessHandler() {
return new CustomLoginSuccessHandler();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/sample/all").permitAll()
.antMatchers("/sample/admin").access("hasRole('ROLE_ADMIN')")
.antMatchers("/sample/member").access("hasRole('ROLE_MEMBER')");
http.formLogin()
.loginPage("/customLogin")
.loginProcessingUrl("/login");
http.logout()
.logoutUrl("/customLogout")
.invalidateHttpSession(true)
.deleteCookies("remember-me","JSESSION_ID");
http.rememberMe()
.key("spms")
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(604800);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl repo = new JdbcTokenRepositoryImpl();
repo.setDataSource(dataSource);
return repo;
}
}
게시물 작성 시 스프링 시큐리티 처리
1. 게시글 작성 버튼 클릭
2. 로그인 하지 않은 상태라면, 로그인 화면 표시
3. 로그인 되어있는 상태라면, 게시글 작성 폼
src/main/java
com.spms.controller
BoardController.java
package com.spms.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.spms.domain.BoardVO;
import com.spms.domain.Criteria;
import com.spms.domain.PageDTO;
import com.spms.service.BoardService;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j;
@Controller
@Log4j
@RequestMapping("/board/*")
@AllArgsConstructor
public class BoardController {
private BoardService boardService;
@GetMapping("/list")
public void list(Criteria cri, Model model) {
log.info("list" + cri);
boardService.getList(cri).forEach(board -> log.info(board));
model.addAttribute("list", boardService.getList(cri));
int total = boardService.getTotal(cri);
log.info("total : " + total);
model.addAttribute("pageMaker", new PageDTO(cri, total));
}
@GetMapping("/register")
@PreAuthorize("isAuthenticated()")
public void register() {
}
@PostMapping("/register")
@PreAuthorize("isAuthenticated()")
public String register(BoardVO board, RedirectAttributes rttr) {
log.info("register : " + board);
boardService.register(board);
rttr.addFlashAttribute("result", board.getBno());
return "redirect:/board/list";
}
@GetMapping({ "/get", "/modify" })
public void get(@RequestParam("bno") Long bno, @ModelAttribute("cri") Criteria cri, Model model) {
log.info("/get or modify");
model.addAttribute("board", boardService.get(bno));
}
@PostMapping("/modify")
public String modify(BoardVO board, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
log.info("modify:" + board);
if (boardService.modify(board)) {
rttr.addFlashAttribute("result", "success");
}
return "redirect:/board/list" + cri.getListLink();
}
@PostMapping("/remove")
public String remove(@RequestParam("bno") Long bno, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
log.info("remove..." + bno);
if (boardService.remove(bno)) {
rttr.addFlashAttribute("result", "success");
}
return "redirect:/board/list" + cri.getListLink();
}
}
@PreAuthorize 이용시 표현식은 isAuthenticated() 로 어떠한 사용자든 로그인이 성공한 사용자만 해당 기능을 사용할 수 있게 처리한다.
웹 환경설정 파일에 스프링 시큐리티 환경설정 추가
com.spms.config
WebConfig.java
package com.spms.config;
import javax.servlet.Filter;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfig.class, SecurityConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return new Class[] { ServletConfig.class };
}
@Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[] { "/" };
}
@Override
protected Filter[] getServletFilters() {
// TODO Auto-generated method stub
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return new Filter[] { characterEncodingFilter };
}
}
시큐리티 초기화 작업자
com.spms.config
SecurityIntializer.java
package com.spms.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityIntializer extends AbstractSecurityWebApplicationInitializer {
}
게시글 작성 시 로그인한 사용자의 아이디 출력
views/board
register.jsp
<%@ 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"%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec"%>
<%@ include file="../includes/header.jsp"%>
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">게시판 등록</h1>
</div>
<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">게시글 등록 페이지</div>
<!-- /.panel-heading -->
<div class="panel-body">
<form action="/board/register" role="form" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<div class="form-group">
<label>작성자</label>
<input class="form-control" name="writer" value='<sec:authentication property="principal.username"/>' readonly="readonly" >
</div>
<div class="form-group">
<label>제목</label>
<input class="form-control" name="title">
</div>
<div class="form-group">
<label>내용</label>
<textarea class="form-control" rows="10" name="content"></textarea>
</div>
<button type="submit" class="btn btn-primary">등록</button>
<a href="/board/list">
<button type="button" class="btn btn-success">목록</button>
</a>
</form>
</div>
<!-- /.panel-body -->
</div>
<!-- /.panel -->
</div>
<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
</div>
<!-- /#page-wrapper -->
<%@ include file="../includes/footer.jsp"%>
게시글 작성은 로그인한 사용자들만 허가되므로, 작성자 항목에는 현재 사용자 아이디가 출력될 수 있게 수정한다.
등록 페이지와 같이 스프링 시큐리티의 영향을 받는 JSP 파일에는 반드시 시큐리티 관련 태그 라이브러리 설정을 하도록 주의한다.
작성자에 현재 사용자는 현재 로그인한 사용자 아이디를 출력한다.
(스프링 시큐리티에서는 username이 사용자의 아이디이다.)
유저(admin90)로 로그인한 상태
(*) /customLogin 로 가서 로그인하면, 에러가 떨어지는데, 당황하지말고 바로 /board/list 화면으로 가서 등록버튼을 누르면 로그인 되어있음을 확인할 수 있다.
유저 계정 : admin90 / pw90
작성자명이 자동으로 로그인 아이디로 들어가있음을 알 수 있다.
'Project B (SPMS) > Project B 파트7' 카테고리의 다른 글
[B -2-59] 스프링 시큐리티 6 (0) | 2019.10.12 |
---|---|
[B -2-58] 스프링 시큐리티 5 (0) | 2019.10.12 |
[B -2-57] 스프링 시큐리티 4 (0) | 2019.10.11 |
[B -2-56] 스프링 시큐리티 3 (0) | 2019.10.11 |
[B -2-55] 스프링 시큐리티 2 (0) | 2019.10.10 |