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";//목록으로
}
}