spring boot

59일차 개인사정으로 수업불참(웹보드로 보충 예정)

비루블 2022. 9. 20. 21:31
package com.example.handler;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
        HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        
        // 필요한 기능. 권한에 따라서 이동하는페이지를 다르게 구현
        // 시큐리티는 권한을 여러개 보관하기 위해서 Collection<>
        String role = authentication.getAuthorities().toArray()[0].toString();

        if(role.equals("CUSTOMER")) {
            response.sendRedirect( 
                request.getContextPath() + "/customer/home.do" );
        }
        else if(role.equals("SELLER")) {
            response.sendRedirect(   
                request.getContextPath() + "/seller/home.do" );
        }
        else if(role.equals("ADMIN")) {
            response.sendRedirect(  
                request.getContextPath() + "/admin/home.do" );
        }
    }
}


-------------------------------------

package com.example.handler;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

public class CustomLogoutSuccessHandler implements  LogoutSuccessHandler{

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {
        
        // 로그아웃 수행시 필요한 기능 구현                
        response.sendRedirect( request.getContextPath() + "/home.do" );                
    }
}


--------------------------------------


package com.example.config;

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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

import com.example.handler.CustomLoginSuccessHandler;
import com.example.handler.CustomLogoutSuccessHandler;
import com.example.service.CustomDetailsService;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    CustomDetailsService customDetailsService;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http)
            throws Exception {

        // 페이지별 권한 설정                
        http.authorizeRequests().antMatchers("/admin", "/admin/**")
                .hasAuthority("ADMIN")
            .antMatchers("/seller", "/seller/**")
                .hasAnyAuthority("ADMIN", "SELLER")
            .antMatchers("/customer", "/customer/**")
                .hasAuthority("CUSTOMER")                
            .anyRequest().permitAll();    


        // 로그인 페이지 설정
        // GET은 직접 생성 => POST는 시큐리티 사용
        // 127.0.0.1:8080/BOOT1/member/login.do
        // <form th:action="@{/member/loginaction.do}" method="post">
        // <input type="text"  name="userid" />
        http.formLogin()
            .loginPage("/member/login.do")
            .loginProcessingUrl("/member/loginaction.do")
            .usernameParameter("userid")
            .passwordParameter("userpw")
            .successHandler(new CustomLoginSuccessHandler())
            //.defaultSuccessUrl("/home.do")
            .permitAll();

        // 로그아웃 => GET 불가, POST로 사용
        http.logout()
            .logoutUrl("/member/logoutaction.do")
            //.logoutSuccessUrl("/home.do")
            .logoutSuccessHandler(new CustomLogoutSuccessHandler())
            .invalidateHttpSession(true)
            .clearAuthentication(true)
            .permitAll();

        http.userDetailsService(customDetailsService);

        // 접근 불가 페이지 설정
        http.exceptionHandling().accessDeniedPage("/page403.do");

        // 보안취약 (비권장) H2-console 사용하기 위해
        // http.csrf().disable();

        // 일부의 주소만 csrf를 해제
        // http.csrf().ignoringAntMatchers("/h2-console/**");
        // http.headers().frameOptions().sameOrigin();

        return http.build();
    }

    @Bean  
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}


=======================================

CREATE SEQUENCE SEQ_ITEMIMAGE_NO START WITH 1 INCREMENT BY 1 NOMAXVALUE NOCACHE;


CREATE TABLE ITEMIMAGETBL(
    NO NUMBER CONSTRAINT PK_ITEMIMAGE_NO PRIMARY KEY,
    ITEMNO NUMBER CONSTRAINT FK_ITEM_NO REFERENCES ITEMTBL(NO),
    IMAGENAME VARCHAR2(200),
    IMAGESIZE NUMBER,
    IMAGETYPE VARCHAR2(30),
    IMAGEDATA BLOB,
    REGDATE TIMESTAMP DEFAULT CURRENT_DATE
);


-------------------------------

<?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="com.example.mapper.ItemImageMapper">
     <update id="updateImageOne" parameterType="com.example.dto.ItemImageDTO">
          UPDATE ITEMIMAGETBL SET 
               IMAGETYPE=#{imagetype}, 
               IMAGENAME=#{imagename},
               IMAGESIZE=#{imagesize}, 
               IMAGEDATA=#{imagedata, jdbcType=BLOB}
          WHERE NO=#{no}
     </update>


     <delete id="deleteImageOne" parameterType="Long">
          DELETE FROM ITEMIMAGETBL WHERE NO=#{no}
     </delete>

     <select id="selectImageList" parameterType="long" 
          resultType="com.example.dto.ItemImageDTO">
          SELECT I.NO FROM ITEMIMAGETBL I 
               WHERE I.ITEMNO=#{itemno}
     </select>     

     <resultMap id="retMap1" type="com.example.dto.ItemImageDTO">
          <result property="imagedata" column="IMAGEDATA" 
               jdbcType="BLOB" javaType="[B" />
     </resultMap>
     <select id="selectImageOne" parameterType="long" resultMap="retMap1">
          SELECT I.* FROM ITEMIMAGETBL I WHERE I.NO=#{no}
     </select>


     <insert id="insertImage" parameterType="com.example.dto.ItemImageDTO">
          INSERT INTO ITEMIMAGETBL( NO, ITEMNO, IMAGENAME, 
               IMAGESIZE, IMAGETYPE, IMAGEDATA, REGDATE )
          VALUES ( SEQ_ITEMIMAGE_NO.NEXTVAL, #{itemno}, 
               #{imagename}, #{imagesize}, #{imagetype}, 
               #{imagedata, jdbcType=BLOB}, CURRENT_DATE )
     </insert>
     
</mapper>


-------------------------------

package com.example.mapper;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Mapper;

import com.example.dto.ItemImageDTO;

@Mapper
public interface ItemImageMapper {

    public int deleteImageOne(Long map);
    public int updateImageOne(ItemImageDTO obj);

    public int insertImage(ItemImageDTO obj);

    //view에서 <img src=""
    public ItemImageDTO selectImageOne( Long no );

    public List<ItemImageDTO> selectImageList(  Long  itemno );

}


-------------------------------

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>홈화면</title>
</head>
<body>
    <a th:href="@{/seller/home.do}">판매자홈으로</a>
    <hr />
    물품이미지추가
    <hr />
    <form th:action="@{/seller/insert_itemimage_batch.do}" 
        method="post" enctype="multipart/form-data">
        <input type="text" name="itemno" th:value="${itemno}" />
        <br />
        <th:block th:each="i : ${#numbers.sequence(1,2)}">
            <input type="file" accept="image/*" name="file" />
            <br />
        </th:block>
        <input type="submit" value="이미지일괄추가" />
    </form>   
    <hr />
    <th:block th:each="obj, idx : ${list}">
        <img th:src="@{/seller/image(no=${obj.no})}" 
            style="width:50px; height:50px" />
        
        <form th:action="@{/seller/update_item_image.do}" 
            enctype="multipart/form-data"
            method="post" style="display:inline-block; border:1px solid #cccccc;">
            <input type="file" name="file" />
            <input type="hidden" th:value="${itemno}" name="itemno" />
            <input type="hidden" th:value="${obj.no}" name="no" />            
            <button>수정</button>
        </form>
        
        <form th:action="@{/seller/delete_item_image.do}" 
            method="post" style="display:inline-block">
            <input type="hidden" th:value="${obj.no}" name="no" />
            <input type="hidden" th:value="${itemno}" name="itemno" />
            <button>삭제</button>
        </form>

        <br />
    </th:block>
</body>
</html>

----------------------------------
package com.example.controller;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
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.multipart.MultipartFile;

import com.example.dto.ItemDTO;
import com.example.dto.ItemImageDTO;
import com.example.mapper.ItemImageMapper;
import com.example.mapper.ItemMapper;


@Controller
@RequestMapping(value="/seller")
public class SellerController {
    
    @Autowired
    ItemMapper iMapper;

    @Autowired
    ItemImageMapper iiMapper;

    // resources에 있는 파일 정보 읽기
    @Autowired
    ResourceLoader resourceLoader;

    @PostMapping(value="/update_item_image.do")
    public String updatePOST(
        @RequestParam(name="no") Long no,
        @RequestParam(name="itemno") Long itemno,
        @RequestParam(name="file") MultipartFile file ) throws IOException{

        ItemImageDTO item = new ItemImageDTO();
        item.setNo(no);
        item.setImagetype(file.getContentType());
        item.setImagesize(file.getSize());
        item.setImagedata(file.getBytes());
        item.setImagename(file.getOriginalFilename());

        int ret = iiMapper.updateImageOne(item);
        if(ret > 0){
            return "redirect:/seller/itemimage.do?itemno=" + itemno;
        }
        return "redirect:/seller/home.do";
        
    }

    @PostMapping(value="/delete_item_image.do")
    public String deletePOST(
        @RequestParam(name="no") Long no,
        @RequestParam(name="itemno") Long itemno){
        int ret = iiMapper.deleteImageOne(no);
        if(ret > 0){
            return "redirect:/seller/itemimage.do?itemno="+itemno;
        }
        return "redirect:/seller/home.do";
    }

    
    //<img src="/seller/image?no=1" >
    @GetMapping(value="/image")
    public ResponseEntity<byte[]> imageGET(
        @RequestParam(name="no") Long no) throws IOException{
        
        ItemImageDTO item = iiMapper.selectImageOne(no);
        if(item.getImagesize() > 0L) {
            // 타입설정 png인지 jpg인지 gif인지
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(
                MediaType.parseMediaType( item.getImagetype() ) );
            // 실제이미지데이터, 타입이포함된 header, status 200    
            ResponseEntity<byte[]> response 
                = new ResponseEntity<>(
                    item.getImagedata(), headers, HttpStatus.OK);
            return response;        
        }
        else {
            InputStream is = resourceLoader.getResource("classpath:/static/image/default.jpg")
                .getInputStream();
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.IMAGE_JPEG);
            // 실제이미지데이터, 타입이포함된 header, status 200    
            ResponseEntity<byte[]> response 
                = new ResponseEntity<>(
                    is.readAllBytes(), headers, HttpStatus.OK);
            return response;
        }
    }


    @PostMapping(value="/insert_itemimage_batch.do")
    public String insertItemImagePOST(
        @RequestParam(name="itemno") Long itemno,
        @RequestParam(name="file") MultipartFile[] file) 
                throws IOException {
        List<ItemImageDTO> list = new ArrayList<>();            
        for(int i=0;i<file.length;i++){
            ItemImageDTO item = new ItemImageDTO();
            item.setItemno(itemno);
            item.setImagetype(file[i].getContentType());
            item.setImagename(file[i].getOriginalFilename());
            item.setImagesize(file[i].getSize());
            item.setImagedata(file[i].getBytes());

            list.add(item);
            // 반복문 종료후에 list를 전달해서 일괄추가해야함.
            // 반복문 내부에서 1개씩 추가함(비권장)
            iiMapper.insertImage(item);
        }
        return "redirect:/seller/home.do";
    }


    @GetMapping(value="/itemimage.do")
    public String itemImageGET(
            Model model,
            @RequestParam(name="itemno") Long itemno){

        List<ItemImageDTO> list = iiMapper.selectImageList(itemno);
        System.out.println(list.toString());

        //<img src 사용할 번호들
        model.addAttribute("list", list);
        model.addAttribute("itemno", itemno);
        return "seller/insert_item_image";
    }




    // 127.0.0.1:8080/BOOT1/seller/home.do?page=1
    @GetMapping(value="/home.do")
    public String homeGET(Model model,
        @RequestParam(name="page", defaultValue = "1") int page,
        @AuthenticationPrincipal User user){
        if(user == null){
            return "redirect:/member/login.do";
        }
        // ex) 1=>  1, 10
        // ex) 2=> 11, 20
        Map<String, Object> map = new HashMap<>();
        map.put("userid", user.getUsername());
        map.put("start", page*10-9);  
        map.put("end", page*10);

        List<ItemDTO> list = iMapper.selectList(map);
        model.addAttribute("list", list);
        return "seller/home";
    }



    @GetMapping(value="/insert_item_batch.do")
    public String insertBatch() {
        return "seller/insert_item_batch";
    }
    
    @PostMapping(value="/insert_item_batch.do")
    public String insertBatchPOST(
        @AuthenticationPrincipal User user,
        @RequestParam(name = "name") String[] name,
        @RequestParam(name = "content") String[] content,
        @RequestParam(name = "price") Long[] price,
        @RequestParam(name = "quantity") Long[] quantity) {
        
        List<ItemDTO> list = new ArrayList<>();
        for(int i=0; i<name.length; i++) {
            ItemDTO item = new ItemDTO();
            item.setName(name[i]);
            item.setContent(content[i]);
            item.setPrice(price[i]);
            item.setQuantity(quantity[i]);
            item.setSeller(user.getUsername());

            list.add(item);

            // 원래는 반복문 종료 후 list를 전달해서 일괄 추가해야 하나
            // 현재는 반복문 내부에서 1개씩 추가하는 방식(비권장)
            // iMapper.insertBatch(item);
        }
        iMapper.insertBatchList(list);
       

        return "redirect:/seller/home.do";
    }
    
}


-------------------------------

package com.example.dto;
import java.util.Date;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter // mybatis Mapper에서 사용함
@Setter // view에서 사용
@ToString() //현재 객체의 내용 확인용
@NoArgsConstructor //생성자
@AllArgsConstructor

public class BoardDTO {
    private Long no; //NUMBER => Long 
    private String title = null;
    private String writer = null;
    private Long hit = 1L;
    private Date regdate = null; 
    private String content = null;
}


-------------------------------

//interface만 있음  => interface + xml

package com.example.mapper;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.mapping.StatementType;

import com.example.dto.BoardDTO;

@Mapper
public interface BoardMapper {

    // ;사용불가, commit은 auto로 설정되어있음.

    // 1. 전체 글목록 
    @Select({
        "<script>",
            " SELECT B.* FROM BOARDTBL B",
        "</script>"
    })
    public List<BoardDTO> selectBoardList();    

    // 2. 게시글 1개 조회
    @Select({
        "<script>",
            " SELECT B.* FROM BOARDTBL B WHERE B.NO=#{no}",
        "</script>"
    })
    public BoardDTO selectBoardOne(@Param("no") Long no);

    // 3. 검색어 포함해서 글목록
    @Select({
        "<script>",
            " SELECT B.* FROM BOARDTBL B WHERE B.TITLE LIKE '%' || #{text} || '%' ",
        "</script>"
    })
    public BoardDTO selectBoardListLike(@Param("text") String text);

    // 4. 검색어 페이지네이션 포함 글목록(제목)
    @Select({
        "<script>",
            " SELECT B.* FROM ( ",
                " SELECT B.*, ROW_NUMBER() OVER (ORDER BY NO DESC) ROWN ",
                " FROM BOARDTBL B WHERE B.TITLE LIKE '%' || #{text} || '%' ",
            ") B WHERE ROWN BETWEEN #{start} AND #{start}+9 ",
        "</script>"
    })
    public BoardDTO selectBoardListLikePagenation(
        @Param("text") String text, @Param("start") int start);


    // 5. 검색어 페이지네이션 포함 글개수(제목)
    @Select({
        "<script>",
            " SELECT COUNT(*) CNT FROM BOARDTBL B ",
            " WHERE B.TITLE LIKE '%' || #{text} || '%' ",
        "</script>"
    })
    public Long countBoardListLikePagenation(
        @Param("text") String text);

    // 6. 조회수 증가
    @Update({
        " UPDATE BOARDTBL SET HIT=HIT+1 WHERE NO=#{no} "
    })
    public int updateBoardHit(@Param("no") Long no);

    // 7. 이전글
    @Select({
        " SELECT NVL(MAX(NO),0) FROM BOARDTBL WHERE NO < #{no} "
    })
    public Long selectBoardPrev(@Param("no") Long no);

    // 8. 다음글 
    @Select({
        " SELECT NVL(MIN(NO),0) FROM BOARDTBL WHERE NO > #{no} "
    })
    public Long selectBoardNext(@Param("no") Long no);
    

    // 게시판글쓰기, INSERT ,UPDATE ,DELETE
    // INSERT INTO 테이블명(컬럼명들) VALUES(추가할값들)
    @Insert({
        " INSERT INTO BOARDTBL(NO, TITLE, WRITER, HIT, REGDATE, CONTENT) ", 
        " VALUES(FUNC_BOARDSEQ, #{board.title}, #{board.writer}, #{board.hit}, CURRENT_DATE, #{board.content} ) "
    })
    public int insertBoardOne(@Param("board") BoardDTO board);



    
    @Insert({
        "{ call PROC_BOARD_UPSERT(", 
            "#{map.no, mode=IN, jdbcType=NUMERIC, javaType=Long }, ",
            "#{map.title, mode=IN, jdbcType=VARCHAR, javaType=String }, ",
            "#{map.content, mode=IN, jdbcType=VARCHAR, javaType=String }, ",
            "#{map.writer, mode=IN, jdbcType=VARCHAR, javaType=String }, ",
            "#{map.ret, mode=OUT, jdbcType=NUMERIC, javaType=Integer }",
        ")}"
    })
    @Options(statementType = StatementType.CALLABLE)
    public void callProcedure(@Param("map") Map<String, Object> map);

}


-------------------------------
package com.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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 com.example.dto.BoardDTO;
import com.example.mapper.BoardMapper;

@Controller
@RequestMapping(value = "/board")
public class BoardController {
   
    @Autowired
    BoardMapper bMapper;

    // 글쓰기
    // 127.0.0.1:8080/BOOT1/board/insert.do
    @GetMapping(value="/insert.do")
    public String insertGET(){
        // resources/templates/board/insert.html
        return "board/insert";
    }

    @PostMapping(value="/insert.do")
    public String insertPOST(@ModelAttribute BoardDTO board){
        System.out.println(board.toString());
        int ret = bMapper.insertBoardOne(board);
        return "redirect:/board/select.do";//목록으로 
    }

}