D+63:: FinalBoard 프로젝트_시큐리티(Security)_최종실습

Am.Vinch·2022년 9월 27일
0
post-thumbnail

20220926_mon

실습내용

  • 게시판 대표적 기능
    • 로그인,로그아웃,게시글목록,게시글 상세조회,글쓰기,글수정,글삭제

    1. 인증을 받지않더라도 접근가능한 페이지 기능 구현
      : 로그인,게시글 목록, 회원가입
    1. 인증을 받아야 접근가능한 페이지를 (기준)
      : 로그아웃,게시글 상세조회,글쓰기
    1. 인증을 받는다고 하더라도 인가가 필요한 페이지(권한여부)
      : 로그인은 기본이고 관리자만 가능한지, 실제 로그인한 사람만 가능한지
      : 글수정,글삭제 -> 글을 쓴 "작성자"만이 글을 수정 및 삭제가 가능하기 때문에

FinalBoard

시큐리티 적용

1. 시큐리티 관련 디펜던시를 porm.xml에 추가

    1. 시큐리티 관련 디펜던시를 porm.xml에 추가
      : html에서 태그를 사용하기위 위해서는 별도로 dependency 태그를 추가해야한다.
    • (1) ctrl+스페이스바 클릭 후 > Add starter 클릭 > spring security 기능 추가 > 두개의 초록색 선 두개 클릭 후 finish 완료
    • (2) < html에서 시큐리티 정보를 가져올수 있는 태그 사용 (2개) > 도 추가해줘야한다.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.3</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>Kh.study</groupId>
	<artifactId>Shop</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>Shop</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- log4jdbc : 쿼리실행은 콘솔에 출력하기 위해 추가 -->
		<dependency>
			<groupId>org.bgee.log4jdbc-log4j2</groupId>
			<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
			<version>1.16</version>
		</dependency>
		<!-- 타임리프의 fragment 기능 사용을 위한 추가 -->
		<dependency>
			<groupId>nz.net.ultraq.thymeleaf</groupId>
			<artifactId>thymeleaf-layout-dialect</artifactId>
			<version>3.1.0</version>
		</dependency>
		<!--  validation처리 추가 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
        <!-- html에서 시큐리티 정보를 가져올수 있는 태그 사용 (2개) -->
      <dependency>
          <groupId>org.springframework.security</groupId>
          <artifactId>spring-security-taglibs</artifactId>
          <version>5.0.7.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.thymeleaf.extras</groupId>
          <artifactId>thymeleaf-extras-springsecurity5</artifactId>
          <version>3.0.4.RELEASE</version>
      </dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>
  • 시큐리티 태그 추가한 결과
    : list 기본경로페이지 이동할 경우, 로그인 페이지가 자동으로 뜬다.
  • 로그인 방법
    : 프로젝트 run후, 콘솔창에 비밀번호값이 보인다.
    Using generated security password: 0a017fd7-15a2-4998-a5be-46b0cece7b15
    : ID값은 항상 user 이다.

2. 시큐리티 설정 파일 생성하기(클래스파일)

    1. 시큐리티 설정 파일 생성하기(클래스파일)
      kh.study.board.config.SecurityConfig
      업로드중..
  • SecurityConfig
package kh.study.board.config;
//  어노테이션 정리: 객체를 만들어주는 어노테이션                       
//  Controller,Service,Configuration,Bean                              
// @Bean : "메소드의 리턴 타입"에 대한 객체를 생성하는데,               
// 클래스 위에서 생성하는 다른 어노테이션과 달리 메소드 위에서 정의한다 

@Configuration //컨피규레이션 객체 생성 어노테이션
@EnableWebSecurity //내부적으로 이 객체를 가져가 파일을 실행해주는 어노테이션
public class SecurityConfig {
	
	@Bean 
	public SecurityFilterChain filterChain(HttpSecurity security) throws Exception{
		security.csrf().disable()
						.authorizeRequests()
						.antMatchers("/member/login", "/board/list","/member/join").permitAll()
//						.antMatchers("/board/logout", "/board/detail","/board/reg").hasAnyRole("MEMBER","WRITER")
//						.antMatchers("/board/update","/board/delete").hasRole("WRITER")
						.anyRequest().authenticated()
					.and()
						.formLogin()
						.loginPage("/member/login")
						.defaultSuccessUrl("/board/list")
						.failureUrl("/member/loginFail")
					.and()
						.exceptionHandling()
						.accessDeniedPage("/member/accessDenied")
						;
		return security.build();
		
	}
}

3. 로그인,로그아웃 기능 구현

    1. 로그인,로그아웃 기능 구현
      UserDetailsService 인터페이스의 메소드를 구현하기
      : 이 메소드가 로그인을 시켜주는 기능을 담당한다.
      : kh.study.board.member.service.userDetailsServiceImpl
  • UserDetailsService 파일 생성
package kh.study.board.member.service;
//직접호출하지 않아서 만들지않아도 상관없지만 편의상 이해를 돕기위해 생성
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService{//자동완성해야함!
//	@Autowired
//	private SqlSessionTemplate sqlSession;
	
	@Resource(name = "memberService")
	private MemberService memberService;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		MemberVO loginInfo =  memberService.login(username);
		
		if (loginInfo == null) {
			throw new UsernameNotFoundException(" 오류발생 :: 조회되는 아이디가 없습니다.");
		}
		
		UserDetails userDetails = User
									.withUsername(loginInfo.getMemberId())
									.password("{noop}"+loginInfo.getMemberPw())
									.roles(loginInfo.getIsAdmin())// -- Y/N
									.build();
		
		return userDetails;
	}
}
  • member-mapper 로그인 쿼리 수정하기
    • 로그인
      • 주의사항!! : 조회시 관리자여부같이조회해야하면서, 회원상태가 활성화되어있어야하는 조건이 필요하다!!!
      • 시큐리티 추가 수정사항
        - 로그인쿼리 실행시 비번은 필요없이 아이디값만 필요하다!
        - 왜, 비밀번호를 조회하는건가?
        - 이유, 조회하여 가져온 데이터베이스 저장된 비밀번호를 가져와서 내가 입력한 비밀번호와 비교하여 일치해야 로그인 진행된다.
        - 단, 아이디,비번,권한(IS_ADMIN ) 만 조회해야한다!
        - memberId라는 매개변수가 아닌 userName값으로 사용하는 이유는?
        - 넘어오는 데이터가 하나일 때는! 데이터 변수명은 달라도 상관이 없다!!!
        - 어차피 넘어오는 데이터 하나! 빈 공간도 하나! 이면 알아서 채워주기 때문에
        <select id="login" resultMap="selectMember">
          SELECT MEMBER_ID , MEMBER_PW ,IS_ADMIN
          FROM BOARD_MEMBER
          WHERE MEMBER_ID = #{memberId} 
          AND MEMBER_STATUS = 'ACTIVE'
        </select>
  • MemberController

    시큐리티 로그인 쿼리 실행시
    - 시큐리티로 userDetailsServiceImpl 사용시,
    - 로그인 메소드 자동 호출된다.
    - 언제?어떻게?
    - post방식으로 /login 이라는 요청이 발생하면 로그인 메소드를 자동으로 실행한다.
    - 그래서 컨트롤러에 로그인 메소드 생략한다.

시큐리티 적용 되는 로그인 방법 설명

  • 시큐리티 적용 전 로그인 form태그
    <form th:action="@{/member/login}" class="row g-3" method="post" th:object = "${memberVO}" >
  • 시큐리티 적용 후 로그인 form태그
    <form th:action="@{/login}" class="row g-3" method="post" th:object="${memberVO}">
    • UserDetailsServiceImpl 사용을 하면, 자동으로 로그인 메소드 호출한다.
    • 단, 컨트롤러에 포스트매핑 로그인을 생략하게된다.
    • 대신 원래 memebrId 값(name)으로로 넘어가는 데이터를 받을 컨트롤러가 없으니
      • 아이디의 경우 name값은 항상 username 으로 넘겨줘야한다! -> name="username"
        :input태그 입력된 비번값과 데이터베이스에 저장된 비밀번호값 비교해야하니까
      • 비밀번호의 경우, name값을 password 로 반드시 속성값 입력해줘야한다 -> name="password"
  • login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout" 
	layout:decorate="~{layout/base_layout}">
<div layout:fragment="content">
	<div class="row justify-content-center">
		<div class="col-6">  
<form th:action="@{/login}" class="row g-3" method="post" th:object="${memberVO}">
				<div class="col-12">
					<label for="memberId" class="form-label" >ID</label>
				</div>
				<div class="col-12">
					<label for="memberPw" class="form-label" >PW</label>
					<input type="password" class="form-control" name="password" >
				</div>
				<div class="col-12 d-grid gap-2">
					<button type="submit" class="btn btn-outline-success">로그인</button>
					<button type="button" class="btn btn-outline-warning" onclick="location.href='/member/join';" >회원가입</button>
				</div>
			</form>
		</div>
	</div>	
</div>
</html>

실습내용

    1. 로그아웃
      : 로그아웃은 post방식으로 /logout 이라는 요청이 발생하면 로그인 메소드를 자동으로 실행한다.
    1. 로그인,로그아웃에 따른 상단 내용의 변화를 sercurity를 적용해보기
      : 로그인 성공후, ~~ 님 반갑습니다 로 바꾸기
    1. 게시글 목록 페이지에서 "글쓰기 버튼"은 로그인한 사람만 보일수있도록 security사용하기
    1. 수정,삭제 버튼은 로그인한 사람이 관리자,로그인한 사람이 해당 글의 작성자 일때만 보이도록 기능 구현하기.

css, js 파일 설정

css,js 파일이 있을 시, 모든 파일은 인증을 받아야하기때문에 /js , /css 와 같은 파일 요청이 있으면 무시하도록 설정해야 문제없이 페이지가 이동한다.

@Bean
	public WebSecurityCustomizer webSecurityCustomizer() {
		return (web) -> web.ignoring().antMatchers("/js/**",  "/css/**");
}
  • SecurityConfig
package kh.study.board.config;
@Configuration //컨피규레이션 객체 생성 어노테이션
@EnableWebSecurity //내부적으로 이 객체를 가져가 파일을 실행해주는 어노테이션
public class SecurityConfig {
	@Bean 
	public SecurityFilterChain filterChain(HttpSecurity security) throws Exception{
		security.csrf().disable()
						.authorizeRequests()
						.antMatchers("/member/login", "/board/list","/member/join").permitAll()
//						.antMatchers("/board/logout", "/board/detail","/board/reg").hasAnyRole("MEMBER","WRITER")
//						.antMatchers("/board/update","/board/delete").hasRole("WRITER")
						.anyRequest().authenticated()
					.and()
						.formLogin()
						.loginPage("/member/login")
						.defaultSuccessUrl("/board/list")
						.failureUrl("/member/loginFail")
						.loginProcessingUrl("/member/login")// 실제 로그인을 진행할  요청 정보
					.and()
						.logout()
						.invalidateHttpSession(true)
						.logoutSuccessUrl("/board/list")
					.and()
						.exceptionHandling()
						.accessDeniedPage("/member/accessDenied")
						;
		return security.build();
		
	}
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Bean
	public WebSecurityCustomizer webSecurityCustomizer() {
		return (web) -> web.ignoring().antMatchers("/js/**",  "/css/**");
	}
}
  • UserDetailsService
  //직접호출하지 않아서 만들지않아도 상관없지만 편의상 이해를 돕기위해 생성
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService{//자동완성해야함!
//	@Autowired
//	private SqlSessionTemplate sqlSession;
	
	@Resource(name = "memberService")
	private MemberService memberService;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		MemberVO loginInfo =  memberService.login(username);
		
		if (loginInfo == null) {
			throw new UsernameNotFoundException(" 오류발생 :: 조회되는 아이디가 없습니다.");
		}
		
		UserDetails userDetails = User
									.withUsername(loginInfo.getMemberId())
									.password(loginInfo.getMemberPw())//-- 비밀번호 암호화처리할땐 noop제외하기
									.roles(loginInfo.getIsAdmin())// -- Y/N
									.build();
		
		return userDetails;
	}
}
  • MemberServiceImpl
  @Service("memberService")//컨트롤러에 보내 사용할 객체를 어노테이션을 이용해 보내준다.
public class MemberServiceImpl implements MemberService{
	@Autowired//쿼리실행 어노테이션으로 객체생성
	private SqlSessionTemplate sqlSession;
	
	@Autowired//암호화 어노테이션으로 객체생성
	private PasswordEncoder passwordEncoder;
	
	//회원가입
	@Override
	public void join(MemberVO memberVO) {

		// -------------------- 비밀번호 암호화 하기 --------------------------//
		// 회원가입 후, 디비를 조회해보면 암호화되어 비밀번호 조회 가능함
		String pw =	passwordEncoder.encode(memberVO.getMemberPw()); 
		memberVO.setMemberPw(pw);
		
		sqlSession.insert("memberMapper.join", memberVO);
	}
	//로그인
	@Override
	public MemberVO login(String memberId) {
		return sqlSession.selectOne("memberMapper.login", memberId);
	}
	
}

html

  • top.html
  <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
		xmlns:sec="http://www.thymeleaf.org/extras/spring-security"><!-- sec 문법 사용 -->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<div th:fragment="topFragment">
<!-- <div  align="right">
	<div>
		<span sec:authorize="isAuthenticated()" th:text="${#authentication.name}">
		님 반갑습니다✨
		</span>
			<form action="/logout" method="post">
				<button sec:authorize="isAuthenticated()" type="submit"  >로그아웃</button>
			</form>
	</div>
	<div >
		<span > 
			<button sec:authorize="isAnonymous()" type="button"
				onclick="location.href='/member/login'">로그인</button>
			<button sec:authorize="isAnonymous()" type="button"
				onclick="location.href='/member/join'">회원가입</button>
		</span>
	</div>
</div> -->

	<!-- 시큐리티 적용 로그인/로그아웃 표기  -->
	<!-- 아래문법사용위해서 sec 태그 위에 추가해야한다. -->
	<!--  sec:authorize="isAnonymous()" : if문과 같다. 인증이 안되어있다면~-->
	<!--  sec:authorize="isAuthenticated()"  : 인증이 되었다면~~-->
	<!--  sec:authentication="principal.authorities" : 인증된사용자의 정보(아이디,권한 중 '권한') 불러오기-->
	<!--  sec:authentication="name" : 인증된사용자의 정보(아이디,권한 중 '아이디') 불러오기-->
	<!--  타임리프방법 으로 인증된 사용자의 데이터 불러오기-->
	<!--  <span th:text="${#authentication.name}"></span>-->
	<!--  로그아웃은 post방식으로 /logout 이라는 요청이 발생하면 로그인 메소드를 자동으로 실행한다.-->
	<!-- 때문에 post방식이 아닌 get방식(a태그)사용시 오류발생한다. -->
	<div class="row" >
		<div class="col text-end" style="color:#224B0C;">
		<!-- 로그인 인증이 되지 않을 때 -->
			<div sec:authorize="isAnonymous()">
				<a th:href="@{/member/login}">login</a>
				<a th:href="@{/member/join}">join</a>
			</div>
		<!-- 로그인 인증 되었을 때 -->
			<div sec:authorize="isAuthenticated()" >
				<span sec:authentication="name"></span>님 반갑습니다😊 
				<form th:action="@{/logout}" method="post">
					<button sec:authorize="isAuthenticated()" type="submit"  >로그아웃</button>
				</form>
			</div >
		</div>
	</div> 


	
<!-- 로그인 클릭시 로그인html파일 이동방법 -->
	<!-- 1번 <a href="/board/login">login</a> -->
	<!-- 2번 <a th:href="@{/board/login}"> login</a><!-- 타임리프 경로이동시!!! 골뱅이+중괄호 -->
	<!-- <div class="row">
		<div class="col text-end" style="color:#224B0C;">
			주의할 점 : sessionScope 는 jsp // session 은 타임리프 th
			<th:block th:if="${session.loginInfo == null}">
				<a th:href="@{/member/login}">login</a>a태그 -> get방식!!!
				<a th:href="@{/member/join}">join</a>a태그 -> get방식!!!
				<span th:onclick="location.href='/board/join';"></span>
			</th:block>
			<th:block th:unless="${session.loginInfo == null}">
					<p th:text="${#authentication.name}"></p>님 반갑습니다😊 
				<a th:href="@{/member/logout}">LOGOUT</a>a태그 -> get방식!!!
			</th:block>
		</div>
	</div> -->
	
	
	
	<br>
	<br>
	<div class="row">
		<div class="col text-center" style="background-color: #EEF2E6;">
			<span style="font-weight: bold; font-size: 50px; color: #3D8361;"><a href="/board/list">B O A R D</a></span>
		</div>
	</div>
	
</div>

</body>
</html>
  • MemberController

    컨트롤러에서 시큐리티를 사용하는 대신 기존에 있던 session 세션 사용을 제거 하고, 자동호출되는 @postMapping의 로그인과 로그아웃 컨트롤러는 생략한다.

  package kh.study.board.member.controller;

@Controller
@RequestMapping("/member")
public class MemberController {
	@Resource(name = "memberService")//서비스임플에서 어노테이션으로 만든 객체가져온다.
	private MemberService memberService;


	// ---------------------- 커맨드객체 정의 -------------------------------------------//
	// 커맨드객체는 html로 데이터를 전달하는 코드를 작성하지 않아도 자동으로 넘어간다.
	// 이때 데이터가 넘어가는 이름은 클래스명에서 앞글자만 소문자로 변경된 이름으로 넘어간다.
	//-----------------------------------------------------------------------------------//
	
	// 회원가입_a태그로 top파일에서 넘어올 때 @GetMapping매핑
	@GetMapping("/join") // a태그는 무조건 get 방식!!!(암기)
	public String join(MemberVO memberVO) {
		// 자동으로 커맨드 객체는 넘어간다.(memberVO) //커맨드객체에 아이디값 임의로 넣어주면, html로 넘어갈때 데이터 넘어감
		System.out.println(memberVO);

		return "content/member/join";
	}
	
	
	//-------------validation처리 :유효성 검사-----------------------------------------//
	// @Valid : post로 전달된 데이터가 검증 규칙을 따르는지 판단하는 어노테이션
	// 해당 어노테이션 다음에는 반드시 검증할 객체가 valid 다음 바로 와야한다
	// BindingResult :검증 대상 객체와 검증 결과의 대한 정보를 담고 있는 객체.
	// 검증객체 바로 다음에 선언되어야 한다.
	// 실제로 input태그에 입력한 데이터가 memberVO에 들어오는 걸 바인딩이라고하는데, 그 결과를 말함.
	// model도 매개변수로 넣어준이유?
	// : model 같이 매개변수 사용하면 데이터를 굳이 담지않아도(눈에보이지않아도) 바인딩결과의 오류여부정보와 검증한 객체memberVO 값을
	// 자동으로 리턴값 join.html에 보내준다.
	//----------------------------------------------------------------------------------//
	
	// 회원가입_form태그로 top파일에서 넘어올 때 @PostMapping매핑
	@PostMapping("/join")
	public String joinProcess(@Valid MemberVO memberVO, BindingResult bindingResult, Model model) {

		//System.out.println("!!!!!!" + memberVO.getMemberTell()); // "010,1111,2222"
		//System.out.println("!!!!!!" + memberVO.getMemberPw()); // "010,1111,2222"
		//String[] tells = memberVO.getMemberTells(); for(String e : tells) {//리스트는
		//반복문으로 데이터꺼내기 System.out.println(e); }

		// 1) validation 체크 (데이터 유효성 검증)
		if (bindingResult.hasErrors()) {// 바인딩하는데 오류가 생겼니?
			System.out.println("error발생!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
			return "content/member/join";// 에러발생하면 join페이지로 다시 이동
		}

		// 2)회원가입 쿼리 실행
		memberService.join(memberVO);

		// 3)페이지이동
		return "redirect:/board/list";
	}

	
	// 로그인_a태그로 top파일에서 넘어올 때
	//주의사항!!! login.html에서 커맨드객체를 object로 사용하려면, 반드시 매개변수로 memberVO가 있어야한다!
	@GetMapping("/login") // a태그는 무조건 get 방식!!!(암기)
	public String login(MemberVO memberVO) {
		System.out.println("로그인 a태그로 클릭해서 넘어왔다!!!!!!!!!!!!!!!!!!");
		
		// return "redirect:/member/login"; //redirection 너무 많아서 오류
		// 컨트롤러로 페이지이동해야 로그인 성공 후, 목록페이지로 넘어간다
		 return "content/member/login"; //(x)
	}
	
	// -------------------------- 시큐리티 로그인 쿼리 실행시--------------------------------//
	// 시큐리티로 userDetailsServiceImpl 사용시, 
	// 로그인 메소드 자동 호출된다.
	// 언제?어떻게?
	// post방식으로 /login 이라는 요청이 발생하면 로그인 메소드를 자동으로 실행한다.
	// 그래서 컨트롤러에 로그인 메소드 생략한다.

	
	// 로그인_form태그로 top파일에서 넘어올 때
	//!!로그인은 유효성검사 필요없다
//	@PostMapping("/login")//form태그와 ajax일때 -> 무조건 post!! 나머지 get!
//	public String loginProcess(HttpSession session, MemberVO memberVO) {
//		
//		MemberVO loginInfo = memberService.login(memberVO);  
//		
//		
//		if (loginInfo != null) {
//			//로그인 세션사용하려면 위의 쿼리문으로 만든 로그인인포만든것을 세션에 값을 넣어주는대신,
//			//위에 매개변수로 만들어주면 된다.!!
//			session.setAttribute("loginInfo", loginInfo);
//			//로그인성공하면 바로 게시판목록페이지이동
//		}
//		else {
//			System.out.println("로그인실패!!!!!!!!!!!!!!!!!!!!!!!!!!");
//			//로그인 실패시에도 다시 로그인페이지로 이동
//			// 회원가입 실패시, 입력 데이터를 유지???
//			// 화면에 입력한 데이터를 그대로 가져가려면! 무조건 페이지로 이동해야한다.
//			// 다시말해서, 아래 redirect로는 데이터를 가져갈 수 있는 방법을 아직 안배워서 안된다.
//			//  return "redirect:/member/login"; 데이터가져갈수없다!(아직..방법안배움)
//			return "content/member/login";//데이터가져가기때문에 input태그에 입력한 값들이 남아있는다.
//			//대신 html에 데이터넘기려면 model객체 매개변수필요할 수 있지만!
//			//커맨드 객체인 memeberVO가 이미 있기때문에 데이터를 보낼 때 굳이 model 객체가 매개변수로 필요하지않다.
//
//		}
//		return "redirect:/board/list";
//	}
//  ------------------------------------------------------------------------------------------------------------------//	
	
	
	// 로그인 실패시
	@GetMapping("/loginFail")
	public String loginFail( ) {
		
		return "content/member/login";
	}
	
	// 접근거부시
	@GetMapping("/accessDenied")
	public String accessDenied( ) {
		
		return "content/member/accessDenied";
	}
//---------- 시큐리티 사용시 자동호출되기때문에 로그인과 마찬가지로 생략한다 ----------//
//	// 로그아웃
//	@GetMapping("/logout")
//	public String logout(MemberVO memberVO ) {
//		memberVO.setMemberId(null);
//		
//		return "redirect:/board/list";
//	}
}
  • board_list.html
  <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout" 
	layout:decorate="~{layout/base_layout}"
	xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

</head>
<body>

<div layout:fragment="content">
<br>
<br>

	<div>
		<table class="table">
		  <thead class="table-light">
			<tr>
				<td>글번호</td>
				<td>글제목</td>
				<td>작성일</td>
				<td>작성자</td>
			</tr>
		  </thead>
		  
		  <tbody>
		  <th:block th:if="${#lists.size('boardList') == 0}">
			<tr>
				<td colspan="4"> 게시글이 없습니다.</td>
			</tr>
			</th:block>
			<!--  boardListr가져올때 홀따옴표 감싸서 가져오기 주의!!!! -->
		  <th:block th:unless="${#lists.size('boardList') == 0}" >
			<tr th:each="board : ${boardList}">
				<td th:text="${board.boardNum}"></td>
				<td>
					<a th:href="@{/board/detail(boardNum=${board.boardNum})}" th:text="${board.title}"></a>
				</td>
				<td th:text="${board.createDate}"></td>
				<td th:text="${board.memberId}"></td>
			</tr>
		  </th:block>
		  </tbody>
		</table>	
	</div>
	
	
	<!-- 시큐리티 적용 후_Teacher -->
	 <div align="center">
		<button  sec:authorize="isAuthenticated()" type="button" class="btn btn-outline-success" th:onclick="@{location.href='/board/reg';}">글쓰기</button>
	</div> 
	<!-- 시큐리티 적용 후_Myself -->
	<!-- <div align="center">
		<div sec:authorize="isAuthenticated()" >
			<button>
				글쓰기
			</button> 
		</div>
	</div> -->
	
	<!-- 시큐리티 적용 전 -->
	<!-- <div align="center">
		<th:block th:if="${session.loginInfo != null}">
			<button  sec:authorize="isAuthenticated()" type="button" class=" btn btn-outline-success" th:onclick="@{location.href='/board/reg';}">글쓰기</button>
		</th:block>
	</div> -->
	
	
</div>
</body>
</html>
  • reg_board.html
  <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout"
	layout:decorate="~{layout/base_layout}"
	xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<div layout:fragment="content">
	<div class="row justify-content-center">
		<form th:action="@{/board/reg}" method="post" th:object="${boardVO}">
			<div class="mb-3">
				<!--  시큐리티 사용하기위해서는 session 을 삭제해야 오류가 발생하지 않는다!-->
				<label for="exampleFormControlInput1" class="form-label">작성자 :</label> 
				<span sec:authorize="isAuthenticated()" sec:authentication="name"></span>
			</div>
			<div class="mb-3">
				<label for="exampleFormControlInput1" class="form-label" >글 제목 :</label> 
				<input name="title" type="text" class="form-control" th:field="*{title}" placeholder="제목을 입력하세요">
				<div class="hasError" th:if="${#fields.hasErrors('title')}" th:errors="*{title}" ></div>
			</div>
			<div class="mb-3">
				<label for="exampleFormControlTextarea1" class="form-label">글 내용 :</label>
				<textarea name="content" class="form-control" th:field="*{content}" rows="3" placeholder="내용을 입력하세요"></textarea>
				<div class="hasError" th:if="${#fields.hasErrors('content')}" th:errors="*{content}" ></div>
			
			</div>
			<div align="center">
				<button class="btn btn-light" type="submit" th:onclick="@{location.href='/board/reg';}">글등록</button>
			</div>
		</form>
	</div>
</div>

</html>
  • board_detail.html
  <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout" 
	layout:decorate="~{layout/base_layout}"
	xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
	
<div layout:fragment="content">
		<div class="mb-3" >
		  <label for="exampleFormControlspan1" class="form-label">글 번호 :</label>
		<th:block th:text="${board.boardNum}"></th:block> 
		</div>
		
		<div class="mb-3">
		  <label for="exampleFormControlspan1" class="form-label"> 작성자 :</label>
		  	<th:block th:text="${board.memberId}"></th:block> 
		</div>
		
		<div class="mb-3">
		  <label for="exampleFormControlspan1" class="form-label"> 작성일 :</label>
		  <th:block th:text="${board.createDate}"></th:block> 
		</div>
		
		<div class="mb-3">
		  <label for="exampleFormControlspan1" class="form-label">글제목 :</label>
	  		  <th:block th:text="${board.title}"></th:block> 
		</div>
		
		<div class="mb-3">
		  <label for="exampleFormControlTextarea1" class="form-label">글내용 :</label>
		  	<div th:text="${board.content}"></div>
		</div>
		
		
		<!-- 세션 정보는 삭제해야 시큐리티 적용했을 때 오류발생하지 않는다!!! -->
			<!-- 게시글 작성자 데이터 불러오기 -->
			<!-- <div th:text="${board.memberId}"></div> -->
			<!-- 로그인 인증자 아이디 정보 불러오기 -->
			<!-- <div sec:authentication="name"></div> -->
			<!-- 위 두개가 일치한다면, 글수정및삭제 버튼 보이기 -->

		<div align="center">
			<div th:if="${board.memberId == #authentication.name}" >
				<div sec:authorize="hasRole('ROLE_Y')">
					<button type="button" class="btn btn-light" th:onclick="|location.href='@{/board/update(boardNum=${board.boardNum})}'|">수정</button>
					<button type="button" class="btn btn-danger" th:onclick="|location.href='@{/board/delete(boardNum=${board.boardNum})}'|">삭제</button>
				</div>
			</div>
			<button type="button" class="btn btn-dark" th:onclick="@{location.href='/board/list';}">뒤로가기</button>
		</div>

		
</div>

</html>
profile
Dev.Vinch

0개의 댓글