D+64::시큐리티적용(최종)_FinalBoard프로젝트/Shop프로젝트(계속)

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

202220927_tue

기존 프로젝트에 시큐리티 기능 구현

1) FinalBoard

권한에 따라 글수정,글삭제 버튼 및 글등록 시큐리티 적용 기능 구현

  • 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()
						.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();
	}

	//------css,js 요청 인증 무시 -------------------------------------------------//
	// css,js 파일 모두 열리면 인증을 받아야하기때문에 
	// /js,/css와 같은 파일 요청이 있으면 무시하도록 설정
	@Bean
	public WebSecurityCustomizer webSecurityCustomizer() {
		return (web) -> web.ignoring().antMatchers("/js/**",  "/css/**");
	}
}
  • 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 th:if="${board.memberId == #authentication.name}" >
					<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 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>	 -->
			<!-- 관리자 권한이 아닌사람이 첫번째 매개변수에 포함이 되는가? -->
			<!-- 결과값은 t/f 참과 거짓으로 나온다. -->
			<!-- <div th:text="${#authentication.authorities}"></div>'권한' 데이터 // [ROLE_N] -->
			<!-- <div th:text="${#strings.contains(#authentication.authorities,'ROLE_N')}"></div>true -->
			
			<!-- 위의 두 조건을 or로 충족시키는 if문을 요약하여 만들어 사용해보자. -->
			<!-- 작성자 아이디값 과 로그인 인증자의 아이디값이 같거나 관리자라면 수정,삭제 버튼이 뜨도록 만든다. -->
		<div align="center">
			<span th:if="${board.memberId == #authentication.name or #strings.contains(#authentication.authorities,'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>
			</span>
			<span>
				<button type="button" class="btn btn-dark" th:onclick="@{location.href='/board/list';}">뒤로가기</button>
			</span>
		</div>

</div>

</html>
  • BoardController
  • 글 등록(시큐리티)
    : 시큐리티 사용시, 세션 Session 객체 사용불가능!!!
    : 대신, Authentication 객체를 사용한다!!
  • [시큐리티를 사용해서 로그인한 정보를 가져오는 방법]
    • ✔✔ 시큐리티를 이용하면서 주의할점
    • 로그인한 사람의 정보를 MemberVO가 아니라
    • UserDetailsService에서 사용하는 user정보에 로그인 정보가 담겨져있다.
    • 항상 담겨있는 정보는 '아이디'와 '권한' 만 있다!!!(이를 이용해서 데이터 추출)
    • 형변환은 필수이다!
- BoardController
@PostMapping("/reg")
public String reg(@Valid BoardVO boardVO, BindingResult bindingResult, Model model
						,Authentication authentication) {
		User user = (User) authentication.getPrincipal();
		
		boardVO.setMemberId(user.getUsername());
		boardService.insertBoard(boardVO);
		
		
		// 주의! 순서중요하다. 유효성체크 먼저 한 후, 로그인정보값 boardVO에 넣어주기 !!
		if (bindingResult.hasErrors()) {
			System.out.println("에러발생!!!!");

			//주의!!! 컨트롤러(redirect)가 아닌 html페이지로 가야 데이터가 남아있는다
			return"content/board/reg_board";
		}

		// 작성자(memberId)는 PK이기때문에 글등록양식페이지에서 화면에 보이도록 띄워줘야함
		// <시큐리티 사용시> 세션session 사용불가능하기 때문에 생략
		// MemberVO loginInfo = (MemberVO)session.getAttribute("loginInfo");
		
		//boardVO.setMemberId(loginInfo.getMemberId());
		//boardService.insertBoard(boardVO);
		
		return "content/board/reg_result";a
	}
  • 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> 
	
	<!-- 시큐리티 적용 후_내가 구현한 방법 -->
	<!-- <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>

Shop

DB

  • DB 수정 및 생성
    • 파일명: SHOP_USER.sql
-- [SHOP_프로젝트 테이블]
DROP TABLE SHOP_MEMBER;
--회원정보 테이블
CREATE TABLE SHOP_MEMBER (
    MEMBER_ID VARCHAR2(100) CONSTRAINT SHOP_MEMBER_PK PRIMARY KEY
    -- CONSTRAINT : 오라클에서 내부적으로 저장된 제약조건 이름
    , MEMBER_PW VARCHAR2(100) NOT NULL
    , MEMBER_NAME VARCHAR2(100) NOT NULL
    , MEMBER_ADDR VARCHAR2(100)
    , ADDR_DETAIL VARCHAR2(100)
    , MEMBER_EMAIL VARCHAR2(100)
    , MEMBER_ROLE VARCHAR2(20)  -- 시큐리티적용_컬럼명 수정/기본값없이 권한등급 : MEMBER(일반회원), ADMIN(관리자)
    , MEMBER_STATUS VARCHAR2(100) -- ACTIVE / DELETED
);

--SHOP_MEMBER 테이블 코멘트 추가하는 방법
COMMENT ON TABLE SHOP_MEMBER IS '회원정보테이블';
COMMENT ON COLUMN SHOP_MEMBER.MEMBER_ID IS '회원아이디';
COMMENT ON COLUMN SHOP_MEMBER.MEMBER_PW IS '회원비밀번호';
COMMENT ON COLUMN SHOP_MEMBER.MEMBER_NAME IS '회원이름';
COMMENT ON COLUMN SHOP_MEMBER.MEMBER_ADDR IS '회원주소';
COMMENT ON COLUMN SHOP_MEMBER.ADDR_DETAIL IS '상세주소';
COMMENT ON COLUMN SHOP_MEMBER.MEMBER_EMAIL IS '회원이메일';
COMMENT ON COLUMN SHOP_MEMBER.MEMBER_ROLE IS '권한';
COMMENT ON COLUMN SHOP_MEMBER.MEMBER_STATUS IS '회원상태';

mapper

: 수정된 테이블 컬러명(IS_ADMIN -> MEMBER_ROLE) 변경하기.
: 회원가입,로그인 쿼리에 아이디,권한,비밀번호값 세가지 반드시 조회한다. 단, 조건절은 비밀번호 없이 아이디값만 넣는다!!

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--해당 파일에 모든 쿼리문을 작성 -->
<mapper namespace="memberMapper">
	<!-- 패키지명 클래스명 -->
 	<resultMap type="Kh.study.shop.member.vo.MemberVO" id="selectMember">
		<id column="MEMBER_ID" 	property="memberId"/>
		<result column="MEMBER_PW" property="memberPw"/>
		<result column="MEMBER_NAME" property="memberName"/>
		<result column="MEMBER_ADDR" property="memberAddr"/>
		<result column="ADDR_DETAIL" property="addrDetail"/>
		<result column="MEMBER_EMAIL" property="memberEmail"/>
		<result column="MEMBER_ROLE" property="memberRole"/>
		<result column="MEMBER_STATUS" property="memberStatus"/>
	</resultMap>  

<!-- 회원가입 -->
<!--  기본값 있는 관리자여부 제외 모두 입력해야함
     회원상태는 회원가입하면 기본적으로 활성화상태이기때문에 ACTIVE 입력-->
     <!-- 시큐리티 적용시, 디비 변경하면서 권한도 회원가입시 insert해야한다. -->
     <!-- Enum 파일을 사용하면서, 회원상태를 단순히 문자열이 아닌 변수로 받도록한다. -->
<insert id="join">
	INSERT INTO SHOP_MEMBER 
			(MEMBER_ID
			,MEMBER_PW 
			,MEMBER_NAME
			,MEMBER_ADDR
			,ADDR_DETAIL
			,MEMBER_EMAIL
			,MEMBER_STATUS
			,MEMBER_ROLE)
	VALUES (#{memberId}
			,#{memberPw}
			,#{memberName}
			,#{memberAddr}
			,#{addrDetail}
			,#{memberEmail}
			,#{memberStatus}
			,#{memberRole})
</insert>

<!-- 로그인 -->
<!-- 시큐리티 적용시, pw값은 조건절 포함시키지않는다 아이디값만 !!! -->
<!-- 그리고 항상 비밀번호값도 조회를 해야 비교를하여 로그인처리가 가능하다! -->
  <select id="login" resultMap="selectMember">
	SELECT MEMBER_ID , MEMBER_ROLE , MEMBER_PW 
	FROM SHOP_MEMBER
	WHERE MEMBER_ID = #{memberId}
</select>  
</mapper> 

패키지 및 파일 생성

  • src/main/java
    • Kh.study.shop
      • admin
      • config
      • item
      • member
  • Security 사용을 위해서 UserDetailsServiceImpl 을 생성하여 사용하는 것이 point이다!!

porm.xml

  • porm파일에서 validation 기능과 security 기능 태그 추가(총 3개)
  • 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>
  • porm.xml
<?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>Board</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>Board</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-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.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>    
		
		 <!-- jsp 라이브러리 , jsp에서 톰캣을 사용할 수 있는 라이브러리-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
        
        <!-- jstl 라이브러리 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </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>
  • 프로젝트 재실행 후, 기본경로 이동시 로그인창 띄워진다.

  • 이때 아이디값은 user , 비밀번호는 콘촐창에 있다.
    a8c410d6-1bb0-4643-b28b-3dfa3272296a

  • Config 패키지 생성

    • SecurityConfig
      package Kh.study.shop.config
  • Enum 파일 생성
    : 관리자 권한과 회원상태에 대한 enum파일을 만들어준다.

로그인여부에 따라 모달창 띄우기


실습내용
1. 로그인시, top의 내용 변경
ex) 000님 반갑습니다. logout ...
2. 로그아웃 기능 구현
3. 관리자 권한의 회원이 로그인하면, 관리자 페이지로 이동해야한다.
: 회원가입후, 데이터베이스 내에서 update 쿼리문으로 변경해줘야한다.
-> 처음 열리는 관리자 페이지는 상품등록페이지로 이동하면 된다.

  • 상품등록.html:content/admin/reg_item.html
    단, 중요한 거은 관리자 권한의 계정으로 로그인하면 화면의 레이아웃이 상단,사읻,컨텐츠영역의 총 3단으로 변경되어야한다. (기존 레이아웃에서 admin_layout.html에서 side.html을 구성하면된다.)
  • side : 상품등록,상품관리, 회원권한설정,메뉴관리

✔👀 point ✔👀

UserDetailsServiceImpl

:시큐리티 사용을 위해서는 해당 파일을 반드시 생성하여 사용해야한다. 컨트롤러에서 리턴값을 user로 만들려면 이 서비스임플을 필요로 하기 때문이다.

Enum 파일 생성

: Enum 파일 생성후 변수명 선언하기만 하면, 다른 파일에서 Enum객체 불러와 쉽게 사용가능하다.


  • AdminController
package Kh.study.shop.admin;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/admin")
public class AdminController {
	
	@GetMapping("/regItem")
	public String admin() {
		
		return "content/admin/reg_item";
	}
}
  • Security.config
package Kh.study.shop.config;

import javax.annotation.Resource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@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()
//						.anyRequest().authenticated()
//					.and()
						.formLogin()
						.loginPage("/member/login")
						.defaultSuccessUrl("/member/loginResult")
						.failureUrl("/member/loginResult")
						.loginProcessingUrl("/member/login")// 실제 로그인을 진행할  요청 정보
						// 아래 두개를 사용하면, 별도로 name값 username, password 값을 수정할 필요가 없다
						.usernameParameter("memberId")
						.passwordParameter("memberPw")
					.and()
						.logout()
						.invalidateHttpSession(true)
						.logoutSuccessUrl("/item/list")
					.and()
						.exceptionHandling()
						.accessDeniedPage("/member/accessDenied")
						;
		return security.build();
		
	}
	//------------------------- 스프링 암호화 -------------------------------//
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	//------css,js 요청 인증 무시 -------------------------------------------------//
	@Bean
	public WebSecurityCustomizer webSecurityCustomizer() {
		return (web) -> web.ignoring().antMatchers("/js/**",  "/css/**");
	}
}

Enum

  • MemberStatus
package Kh.study.shop.config;

public enum MemberStatus {
	ACTIVE,DELETED
}
  • MemberRole
package Kh.study.shop.config;

public enum MemberRole {
	MEMBER,ADMIN
}

로그인실패시, 모달창 띄우기

  • 모달창띄우기위해서 매개변수로 boolean값의 isLoginFail을 받아온다. 이 값을 (T/F) 사용하여 모달창을 띄울지 결정하도록 만든다.
  • ItemController
@Controller
@RequestMapping("/item")
class ItemController {

	//상품목록페이지
	//boolean isLoginFail : 로그인 실패시 true/ 그렇지않으면 false
	@GetMapping("/list")
	public String itemList(boolean isLoginFail, Model model) {
		//-----로그인 성공 및 실패 여부를 html에 데이터 전달하기-------//
		// 확인
		// System.out.println("@@@@@@@@@@@@@@@@@@@" + isLoginFail);
		model.addAttribute("isLoginFail",isLoginFail);
		
		return "content/item/item_list";
	}
	
}
  • ItemController
    • 시큐리티 적용하면서 기존 session 객체를 제거한다.
      • 시큐리티를 사용하게되면, 로그인,로그아웃은 별도로 컨트롤러에 메소드 사용없이 자동호출되어 사용가능하다.
      • 때문에 PostMapping 방식으로 불러올 필요없다. form태그를 사용하여 html 에서 불러와사용한다. 대신 config파일에도 작성해야한다.
package Kh.study.shop.member.controller;
@Controller
@RequestMapping("/member")
public class MemberController {
	@Resource(name = "memberService")
	private MemberService memberService;
	
	//---- config에서 만든 암호화 객체 불러와서 사용하기-----------------------------------------------------//
	@Autowired
	private PasswordEncoder encoder;
	
	//회원가입
	@PostMapping("/join")
	public String join(MemberVO memberVO) {
		// 쿼리문이기때문에 통상적으로는 컨트롤러에 작성하나
		// serviceImpl에 한번에 작성하기도 한다. (문제없음)
		// memberVO값에 status값 세팅해주기
		// null값들어가지않도록 Enum파일에 있는 'ACTIVE' 값넣어주기
		memberVO.setMemberStatus(MemberStatus.ACTIVE.toString());
		memberVO.setMemberRole(MemberRole.MEMBER.toString());
		
		// 위에서 불러온 암호화 객체를 사용해서 암호화한 비밀번호값 넣어 디비저장해준다.
		memberVO.setMemberPw(encoder.encode(memberVO.getMemberPw()));
		
		memberService.join(memberVO);
		
		return "redirect:/item/list";//redirect: 사용해야 컨트롤러 사용한다.(상품목록페이지 )
	}
	
// --------------------------------   시큐리티 적용  로그인--------------------------------------------------------//
	// !!!! 시큐리티 적용하면 post매핑 로그인 자동호출되어 필요 없다 !!!!! //

//	//로그인(ajax.ver)
//	@ResponseBody//ajax사용할때(단,리턴값은 필요한 데이터만! html페이지가 아님!)
//	@PostMapping("/ajaxLogin")
//	public boolean ajaxLogin(HttpSession session, MemberVO memberVO) {
//        MemberVO loginInfo = memberService.login(memberVO);
//        // 로그인 정보 세션 저장
//        if(loginInfo != null) {
//			session.setAttribute("loginInfo", loginInfo);
//        } 
//        // 바로 loginInfo를 주지않고 삼항연산자 사용한다
//		return loginInfo == null? false :true;//자료형 boolean
//	}
	
	
	// 로그인성공시,로그인실패시 -> 로그인페이지로 이동
	// 스프링 시큐리티 config에서 설정한 경로대로 보내준다.
	@GetMapping("/loginResult")
	public String loginResult() {
		System.out.println("로그인 결과");
		return "content/member/login_result";
	}
//------------------------------------------------------------------------------------------------------------//	
	
	//관리자_상품등록
	@GetMapping("/regItem")
	public String regItem() {
		System.out.println("상품등록 페이지이동");
		return "content/admin/reg_item";
	}
	
	
	
// --------------------------------   시큐리티 적용 전 --------------------------------------------------------//
//	//로그인(ajax.ver)
//	@ResponseBody//ajax사용할때(단,리턴값은 필요한 데이터만! html페이지가 아님!)
//	@PostMapping("/ajaxLogin")
//	public boolean ajaxLogin(HttpSession session, MemberVO memberVO) {
//		MemberVO loginInfo = memberService.login(memberVO);
//		// 로그인 정보 세션 저장
//		if(loginInfo != null) {
//			session.setAttribute("loginInfo", loginInfo);
//		} 
//		// 바로 loginInfo를 주지않고 삼항연산자 사용한다
//		return loginInfo == null? false :true;//자료형 boolean
//	}
//	//로그인(alert.ver)
//	@PostMapping("/login")
//	public String login(HttpSession session, MemberVO memberVO) {
//		//System.out.println(memberVO);
//		MemberVO loginInfo = memberService.login(memberVO);
//		// 로그인 정보 세션 저장
//		if(loginInfo != null) {
//			session.setAttribute("loginInfo", loginInfo);
//			
//		} else {
//			System.out.println("null!!!!!");
//			session.setAttribute("loginInfo", null);
//		}
//		return "content/member/login_result";
//	}
//---------------------------------------------------------------------------------------------------------------//	
	
	
	//로그아웃
	//get방식과 post방식의 차이점
	//GET 방식 : 어떠한 정보를 가져와서 조회하기 위해 사용되는 방식
	//POST 방식: 데이터를 서버로 제출하여 추가 또는 수정하기 위해서 데이터를 전송하는 방식
//	@GetMapping("/logout")//get메소드사용해야 에러가 안난다
//	public String logout(HttpSession session ) {
//		session.removeAttribute("loginInfo");
//		
//		return "redirect:/item/list";
//	}
}

그외 파일 생성

  • src/main/resources
    - mappers
    - static
    - templates

로그인 실패여부로 모달창을 띄워주는 기능

: 아래 코드를 사용해 하나의 메소드를 만들고 이 메소드를 실행하도록 변수선언해준다.

function isLoginFail(){
	const isLoginFail = document.querySelector('#isLoginFail').value;

	if(isLoginFail == 'true'){
		//아이디를 통해서 모달객체를 받아서 show로 강제로 열리도록(닫히도록) 할수있다.
		const myModalAlternative = new bootstrap.Modal('#login_modal');
		myModalAlternative.show(); //myModalAlternative.hide();
	}
// 확인) 로그인 실패여부를 js에 데이터 불러오기
//const a = `[[${isLoginFail}]]`;
//alert(a);
}
  • top.js
//----------------- 페이지 열림과 동시에 실행되는 코드-----------------------//
isLoginFail();// 아래 함수정의에서  사전에 구현된 기능을 가져오기



//------------------------------ 변수 ---------------------------------------//
//회원가입 모달
const join_modal = document.querySelector('#join_modal');
//로그인 모달
const login_modal = document.querySelector('#login_modal');


//------------------------------ 함수 정의 ----------------------------------//

//회원가입에서 search 버튼 클릭시, 진행 메소드
function searchAddr() {
	new daum.Postcode({
		oncomplete: function(data) {
			// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분입니다.
			// 예제를 참고하여 다양한 활용법을 확인해 보세요.

			// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.

			// 도로명 주소의 노출 규칙에 따라 주소를 조합한다.
			// 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
			const roadAddr = data.roadAddress; // 도로명 주소 변수
			document.querySelector('#memberAddr').value = roadAddr;

		}
	}).open();
}

//로그인 기능 함수(ajax로 로그인실행시 진행) 
function goLogin(loginInfo) {
	//[[${sessionScope.loginInfo}]] js에서 데이터가져오는 방법
	const member_id = login_modal.querySelector('#memberId').value;
	const member_pw = login_modal.querySelector('#memberPw').value;
	
		//ajax start
		//$을 사용하려면, 제이쿼리 문법이기때문에 자스보다 먼저 로딩해야한다
		$.ajax({
			url: '/member/ajaxLogin', //요청경로
			type: 'post',
			data: { 'memberId':member_id ,'memberPw' :member_pw ,'loginInfo' : loginInfo }, //필요한 데이터
			success: function(result) {
				if(result) {/*loginInfo 값이 null아니면(false)*/
					alert('로그인 성공!!!');
					location.href='/item/list';
				}
				else{
					alert('로그인 실패!!!');
				}
			},
			error: function() {
				alert('로그인 실패');
			}
		});
		//ajax end
	
}


//// 로그인 실패여부로 모달창을 띄워주는 기능
function isLoginFail(){
	const isLoginFail = document.querySelector('#isLoginFail').value;

	if(isLoginFail == 'true'){
		//아이디를 통해서 모달객체를 받아서 show로 강제로 열리도록(닫히도록) 할수있다.
		const myModalAlternative = new bootstrap.Modal('#login_modal');
		myModalAlternative.show(); //myModalAlternative.hide();
	}
// 확인) 로그인 실패여부를 js에 데이터 불러오기
//const a = `[[${isLoginFail}]]`;
//alert(a);
}

//----------------------------- 이벤트 정의 ---------------------------------//
//////////////////회원가입 모달이 닫히면 실행되는 이벤트(함수 매개변수 event)
join_modal.addEventListener('hidden.bs.modal', function(event) {//모달이 완전히 닫혔을 때 실행
	//--------- <모듈창을 다시 열었을 때, 이전 값들 남아있지 않도록 빈값 만들기> ---------//
	//[방법(1)_기본]
	//회원가입 모달창에 있는 input 태그를 모두들고온다
	//('')안에는 css와 동일하다
	//input태그 중 버튼만 제외하고 모두 선택한다
	//	const modal_inputs = join_modal.querySelectorAll('input:not[type="button"]');

	//반복문돌려서 모든 input태그 ''빈값을 주면 모듈창을 닫았을 때 빈값이다.
	//	for(const inputTag of modal_inputs){
	//		inputTag.value ='';
	//	}
	//[방법(2)_간략]
	// 모달창의 from태그를 선택하여 초기화
	join_modal.querySelector('form').reset();
});


//////////////////로그인 모달이 닫히면 실행되는 이벤트
login_modal.addEventListener('hidden.bs.modal', function(event) {
	login_modal.querySelector('form').reset();
});

관리자페이지 3단 레이아웃

  • base_layout.html
<!-- 모든 페이지에 적용시키는 파일 -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
		xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout"><!-- 타임리프,레이아웃 기능 사용하겠다 -->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- 레이아웃페이지에서 앞으로 모든 내용들이 나오기때문에 여기에 부트스트랩가져오면 모두 자동 적용된다 -->
<!-- 외부의 부트스트랩 사용위해 가져오기 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
<!-- css가져오기  단,static 경로설정은 /부터 넣고 시작한다!(template와의 차이)-->
<link href="/css/common.css" rel="stylesheet">

<!-- body아래 부분에 로딩을 하면 각각의 어떤 파일이 열리는지 순서에따라 상관이 있기때문에 문제가 생길수 있다. -->
<!-- 어떤파일이 열리든 순서 상관없이 로딩시켜서 적용시킬수있도록 가장먼저 로딩한다. -->
	<!-- 부트스트랩 로딩 -->
	<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script>
	<!-- ajax 제이쿼리 로딩 -->
	<script src="https://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>

<div class="container">
	<!-- 고정된 top.html 화면 -->
	<div class="row">
		<div class="col">
			<div th:replace="fragment/top::top"></div><!-- fragment폴더 안에 top html파일을 top이라는 이름으로, fragment를 불러와 대체하겠다 -->
		</div>
	</div>
	<!-- 계속바뀌는 베이스화면 -->
	<div class="row">
		<div class="col">
			<div layout:fragment="content"></div>
		</div>
	</div>
</div>

</body>
</html>
  • admin_layou.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
		xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout"><!-- 타임리프,레이아웃 기능 사용하겠다 -->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
<link href="/css/common.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>
<div class="container">
	<!-- 고정된 top.html 화면 -->
	<div style="margin-bottom: 50px;" class="row">
		<div class="col">
			<div th:replace="fragment/top::top"></div>
		</div>
	</div>
	<!-- 고정된 side.html 화면 -->
	<div  style="position: fixed; width: 200px;" class="row">
		<div class="col">
			<div th:replace="fragment/side::side"></div>
		</div>
	</div>
	<!-- 계속바뀌는 베이스화면 -->
	<div style="position: relative; left: 250px;" class="row">
		<div class="col">
			<div layout:fragment="content"></div>
		</div>
	</div>
</div>
</body>
</html>
  • top.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

<div th:fragment="top">
<!-- 아이템컨트롤러에서 던져진 로그인여부값을 히든으로 가져와서 받는다. -->
	<input type="hidden" id="isLoginFail" th:value="${isLoginFail}">

	<div class="row">
		<div class="col text-end" sec:authorize="isAnonymous()">
			<span data-bs-toggle="modal" data-bs-target="#join_modal" style="color: #3D8361;">JOIN</span>
			<span data-bs-toggle="modal" data-bs-target="#login_modal" style="color: #3D8361;">LOGIN</span>
		</div>
		<div class="col text-end" sec:authorize="isAuthenticated()" >
			<span sec:authentication="name"></span>	님 반갑습니다😊 
			<form th:action="@{/logout}" method="post">
				<button sec:authorize="isAuthenticated()" type="submit">LOGOUT</button>
			</form>
		</div>
	</div>
	<div class="row">
		<div class="col text-center" > 
			<span style=" color: #A1C298; font-weight: bold; font-size: 55px;">S H O P</span>
		</div>
	</div>
	<!--top MENU -->
	<div class="row">
		<div class="col">
			<nav class="navbar navbar-expand-lg bg-light">
			  <div class="container-fluid">
			    <a class="navbar-brand" href="#" style="color: #3D8361;">MENU</a>
			    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
			      <span class="navbar-toggler-icon"></span>
			    </button>
			    <div class="collapse navbar-collapse" id="navbarNav">
			      <ul class="navbar-nav">
			        <li class="nav-item">
			          <a class="nav-link active" aria-current="page" href="#">Home</a>
			        </li>
			        <li class="nav-item">
			          <a class="nav-link" href="#">Features</a>
			        </li>
			        <li class="nav-item">
			          <a class="nav-link" href="#">Pricing</a>
			        </li>
			        <li class="nav-item">
			          <a class="nav-link disabled">Disabled</a>
			        </li>
			      </ul>
			    </div>
			  </div>
			</nav>
		</div>
	</div>
	
	
<!-- login 클릭시 실행 Modal  -->
<div class="modal fade" id="login_modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
  
    <div class="modal-content">
    
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel" style="font-weight: bold;">L O G I N</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      
      <!-- 로그인창 modal  -->
      <div class="modal-body">
      <!-- 로그인 - 세션이용 데이터 form태그로 보내주기 -->
      <!-- 부트스트랩에서 가져왔기때문에 함부러 수정x 형식에 맞게 해야사용가능하다 -->
		<form class="row g-3" name="formLogin" method="post" action="/member/login" >
		  <div class="mb-3">
		  <!-- label은 input태그의 라벨(스티커)이다. 웬만하면, for값과 id값이 같아야한다 -->
		  <!-- html 파일 내에 memberId 라는 id값이 여러개 존재한다.(중복발생) -->
		    <label for="memberId" class="form-label" >ID</label>
		    <input id="memberId" type="text" class="form-control" name="memberId" aria-describedby="emailHelp" placeholder="Input your ID"  >
		  </div>
		  <div class="mb-3">
		    <label for="memberPw" class="form-label">Password</label>
		    <input type="password" class="form-control" name="memberPw"  id="memberPw" placeholder="Input your password">
		  </div>
	      <div class="modal-footer">
	        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
	        <button type="submit" class="btn btn-primary">Login</button>  <!--submit은 ajax에서 사용하는 것이 아니다!!! onclick이용해서 ajax? -->
	        <button type="button" class="btn btn-primary" onclick="goLogin();">AjaxLogin</button>
	      </div>
		</form>
      </div>

    </div>
  </div>
</div>

<!-- join 클릭시 실행 Modal -->
<div class="modal fade" id="join_modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
  
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel" style="font-weight: bold;">J O I N</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      
      <div class="modal-body">
      <!--  너비 조정-->
      <!-- col-12 / col-6 :  한 줄 너비 총 크기 12이기때문에 12분등 중 어느만큼 사용하는건지 -->
	  	<form class="row g-3" action="/member/join" method="post">
		  	<!-- class값은 고정 건드리면 안됨. 부트스트랩사용하기위한 의미 -->
		    <!-- for태그와 id태그 이름은 같게 하도록 -->
		  <div class="col-12">
		    <label for="memberId" class="form-label">ID</label>
		    <input type="text" class="form-control" id="memberId" placeholder="put your ID" name="memberId">
		  </div>
		  <div class="col-12">
		    <label for="memberPw" class="form-label">PASSWORD</label>
		    <input type="password" class="form-control" id="memberPw" name="memberPw" placeholder="put your Password">
		  </div>
		  <div class="col-12">
		    <label for="memberName" class="form-label">NAME</label>
		    <input type="text" class="form-control" id="memberName" name="memberName" placeholder="put your Name">
		  </div>

		 <!-- 주소(상세주소 + 검색버튼추가) -->
		 <!-- 줄을 맞추기위해 실제내용은 화면에 안보이도록 공백문자사용/ class에 form-control 붙여넣기 -->
		  <div class="col-9">
		    <label for="memberAddr" class="form-label"> ADDRESS </label>
		    <input type="text" class="form-control" id="memberAddr" name="memberAddr"  readonly onclick="searchAddr();"><!-- 값변경못하도록 읽기전용속성값부여하기 ( 데이터넘기기 가능) -->
		  </div>
		  <div class="col-3" >
		    <label for="" class="form-label">&nbsp;</label>
			<input type="button" class="btn btn-secondary form-control" onclick="searchAddr();" value="Search">
		  </div>
		  <div class="col-12" >
		    <input type="text" class="form-control" id="addrDetail" name="addrDetail">
		  </div>
		 
		  <div class="col-12">
		    <label for="memberEmail" class="form-label">EMAIL</label>
		    <input type="text" class="form-control" id="memberEmail" placeholder="put your Email" name="memberEmail">
		  </div>
		  
		  <div class="d-grid gap-2 col-12" >
		    <!-- 버튼클릭하나로 모든 데이터 가져가야하므로 무조건 submit!  -->
		    <button type="submit" class="btn btn-primary">JOIN</button>
		  </div>
		</form>
	  </div>      
    </div>
  </div>
</div>
<!-- 카카오API 사용위해 자바스크립트 사용 전, 미리 로드하기 -->
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<!-- js불러오는 방법_기본 (1) -->
<!-- 주의)html 파일 이외의 파일들은 static 폴더 기준이면서 맨앞은 '/'넣어줘야한다!!!(templates X) -->
<!-- src속성값을 넣어주면 해당 파일에 js찾아서 불러온다는 기능 -->
<!-- <script type="text/javascript" src="/js/layout/top.js"></script> -->

<!-- js불러오는 방법_타임리프 (2) -->
<!-- 기본방법과 비슷하지만 @{}사용해야한다! -->
<script type="text/javascript" th:src="@{/js/layout/top.js}"></script>

<!-- 위 방법의 차이점은? -->
<!-- 디자이너와 협업시 용이하다.
이클립스를 사용하지 않는 디자이너분들은 타임리프를 사용해서 타임리프로 된 html파일은 데이터 던져주면,
데이터값을 확인할 수 있다. 타임리프를 사용하지않으면 디자이너분들이 사용하기 불편하기때문에 사용하는 것이다. -->
</div>

</html>
  • side.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div th:fragment="side">


<!-- 사이드메뉴 -->
<div class="d-flex align-items-start">
  <div class="nav flex-column nav-pills me-3" id="v-pills-tab" role="tablist" aria-orientation="vertical">
    <button class="nav-link active" id="v-pills-home-tab" data-bs-toggle="pill" data-bs-target="#v-pills-home" type="button" role="tab" aria-controls="v-pills-home" aria-selected="true">상품등록</button>
    <button class="nav-link" id="v-pills-profile-tab" data-bs-toggle="pill" data-bs-target="#v-pills-profile" type="button" role="tab" aria-controls="v-pills-profile" aria-selected="false">상품관리</button>
    <button class="nav-link" id="v-pills-disabled-tab" data-bs-toggle="pill" data-bs-target="#v-pills-disabled" type="button" role="tab" aria-controls="v-pills-disabled" aria-selected="false" >회원권한설정</button>
    <button class="nav-link" id="v-pills-messages-tab" data-bs-toggle="pill" data-bs-target="#v-pills-messages" type="button" role="tab" aria-controls="v-pills-messages" aria-selected="false">메뉴관리</button>
  </div>
  <div class="tab-content" id="v-pills-tabContent">
    <div class="tab-pane fade show active" id="v-pills-home" role="tabpanel" aria-labelledby="v-pills-home-tab" tabindex="0"></div>
    <div class="tab-pane fade" id="v-pills-profile" role="tabpanel" aria-labelledby="v-pills-profile-tab" tabindex="0"></div>
    <div class="tab-pane fade" id="v-pills-disabled" role="tabpanel" aria-labelledby="v-pills-disabled-tab" tabindex="0"></div>
    <div class="tab-pane fade" id="v-pills-messages" role="tabpanel" aria-labelledby="v-pills-messages-tab" tabindex="0"></div>
  </div>
</div>
	
</div>
</html>
  • login_result.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	  xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

</head>
<body>
<!-- 시큐리티적용시 세션이 적용되면 실행이 되지 않는다!!! -->
<!--  시큐리티 적용 후-->
<!-- 로그인 인증 실패하면-->
	
 <div sec:authorize="isAnonymous()">
	<script type="text/javascript">
		alert('로그인 실패');
		location.href='/item/list?isLoginFail=true';
		//로그인 모달창 띄운다.
	</script>
</div>
<div sec:authorize="isAuthenticated()" >
	<div sec:authorize="hasRole('ROLE_MEMBER')" >
		<script type="text/javascript">
			alert('로그인 인증 성공');
			location.href='/item/list';
		</script>
	</div> 
	<div sec:authorize="hasRole('ROLE_ADMIN')" >
		<script type="text/javascript">
			alert('관리자 인증 성공');
			location.href='/admin/regItem';
		</script>
	</div>
</div>
</body>
</html>




<!-- 시큐리티 적용 전 ajax사용 -->
<!-- <th:block th:if="${session.loginInfo == null}"> 
	<script type="text/javascript">
		alert('로그인 실패!!');
		location.href='/item/list';
	</script>
</th:block>
<th:block th:unless="${session.loginInfo == null}"> 
	<script type="text/javascript">
		alert('로그인 성공!!!');
		location.href='/item/list';
	</script>
</th:block> -->
  • reg_item.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout" 
	layout:decorate="layout/admin_layout">

<div layout:fragment="content">
상품관리등록 관리자 페이지입니다.
</div>

</html>

결과

  • 원래 ajax를 이용하여 페이지이동시, ajax는 한페이지에서만 가능하기때문에 사라진다. 모달창이 꺼진다.

  • 하지만, 이를 로그인실패시에도 계속 로그인 모달창이 뜰 수 있도록 구현하려한다.

  • 잘못된 로그인 정보 값 입력하여 로그인 실패하기

  • 로그인 실패 alert창이 뜬다.

  • 확인 버튼 클릭시, 다시 로그인 창으로 이동한다.이때, 다시 모달창이 뜨도록 구현했다.(js모달창)

  • 관리자(사전 권한부여하여 디비 업데이트)로 로그인하여 관리자페이지 보이도록 만든다.

  • 관리자페이지 총 3단으로 구성하여 구현한다.
    : 왼쪽 사이드바, 위는 top으로 아이디값과 로그아웃버튼, 오른쪽은 계속바뀌는 content영역으로 가장처음은 상품관리등록페이지로 구현함.

profile
Dev.Vinch

0개의 댓글