20220927 [Spring Boot, H2DB, MyBatis]

Yeoonnii·2022년 9월 27일
0

TIL

목록 보기
35/52
post-thumbnail

RESTController 이미지 첨부, 수정, 삭제

이미지 첨부하기

ItemImageRestController.java

DTO로 데이터를 받을 때 보내는 타입을 정하는게 중요하다!
반드시 출력하여 확인해서 데이터가 잘 오는지 보고 진행해야함

@ModelAttribute
➡️ 컨텐츠 타입이 multipart/formdata = 이미지를 받을 수 있음
@RequestBody
➡️ 컨텐츠 타입이 application/json ⇒ 이미지를 받을 수 없음

System.out.println(itemImage.toString());
System.out.println(file.getOriginalFilename());
⇒ ItemImage번호와 파일명 확인용으로 두가지 정보를 확인 할 수 있다

이때 이미지에 대한 4개의 정보는 비어있다
➡️ 이미지 정보를 DTO에 담아준다

itemImage.setImagedata(file.getBytes());
이미지 데이터는 byte 배열형태 = byte[]로 넣어준다

📢 컨트롤러에서 itemImage.setImagename로 이미지 이름 지정시
file.getName()로 넣어주면 postman에서 지정한 키값이 들어가버린다!
➡️ file.getOriginalFilename(); 이용하면 첨부이미지 이름을 사용하여 이미지이름을 지정할 수 있다


이미지 가져오기

이미지 번호를 전달하면 해당하는 이미지의 URL을 전송

이미지 가져오기 = 이미지 url을 가져오는 개념
➡️ 실제 이미지를 꺼내어 사용도 가능하지만 byte배열 형태라 읽어오는데 시간이 오래걸리기 때문에 url을 가져오는게 더 효율적이다

ItemImageRestController.java

// 127.0.0.1:8080/BOOT1/api/itemimage/image?no=8
    @GetMapping(value = "/image")
    public ResponseEntity<byte[]> imageGET(
            @RequestParam(name = "no") Long no) throws IOException {

        System.out.println(no);
        // 아이템 이미지 번호가 존재하는 경우
        if (no > 0L) {
            ItemImageDTO item = imageMapper.selectImageOne(no);
            // System.out.println(item.toString());
            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 { // 이미지 파일이 존재하지 않는경우 = default이미지 설정
                InputStream is = resourceLoader.getResource("classpath:/static/image/noimage.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;
            }
        } else { // 아이템 이미지 번호가 존재하지 않는경우 = default이미지 설정
            InputStream is = resourceLoader.getResource("classpath:/static/image/noimage.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;
        }
    }

이미지 수정하기

ItemImageRestController.java

// 아이템 이미지 수정
    // 127.0.0.1:8080/BOOT1/api/itemimage/update.json
    @PostMapping(value = "/update.json")
    public Map<String, Object> updatePUT(
            @ModelAttribute ItemImageDTO itemImage,
            @RequestParam(name = "file") MultipartFile file,
            @RequestParam(name = "itemno") Long itemno,
            @RequestParam(name = "no") Long no) {
        // DTO에 정보 모아서 보내기 => @ModelAttribute에 ItemImageDTO를 담아 보낸다

        Map<String, Object> retMap = new HashMap<>();
        try {
            // 첨부한 이미지의 정보가 잘 담기는지 확인용 출력
            System.out.println(itemImage.toString());
            System.out.println(file.getOriginalFilename());

            itemImage.setNo(no);
            itemImage.setItemno(itemno);
            itemImage.setImagedata(file.getBytes());
            itemImage.setImagename(file.getOriginalFilename());
            itemImage.setImagesize(file.getSize());
            itemImage.setImagetype(file.getContentType());

            System.out.println("=======" + itemImage.getImagename().toString());
            int ret = imageMapper.updateImageOne(itemImage);
            // int ret = imageMapper.insertImage(itemImage);
            retMap.put("status", 200);
            retMap.put("result", ret);

        } catch (Exception e) {
            e.printStackTrace();
            retMap.put("status", -1);
        }
        return retMap;
    }

이미지 삭제하기

아이템 이미지 삭제
= 1개 이미지 삭제하는 경우 ITEMIMAGETBL의 기본키 NO를 조건으로 둔다

ItemImageRestController.java

    // 127.0.0.1:8080/BOOT1/api/itemimage/deleteOneImage.json
    @PostMapping(value = "/deleteOneImage.json")
    public Map<String, Object> imageDeleteOnePOST(
            @RequestParam(name = "no") Long no) {

        Map<String, Object> retMap = new HashMap<>();
        try {
            int ret = imageMapper.deleteImageOne(no);
            retMap.put("status", 200);
            retMap.put("result", ret);
        } catch (Exception e) {
            e.printStackTrace();
            retMap.put("status", -1);
        }
        return retMap;
    }

물품 하위 이미지 전체삭제

ItemImageMapper.java

    public int deleteAllItemImage(Long itemno);

itemimageMapper.xml

    <delete id="deleteAllItemImage" parameterType="Long">
        DELETE FROM ITEMIMAGETBL WHERE ITEMNO=#{itemno}
    </delete>

ItemImageRestController.java

Controller에서 PostMapping > RequestParam으로 itemno 보내줬으니
postman에서 > body > form-data로 itemno 보내야 한다

    // 127.0.0.1:8080/BOOT1/api/itemimage/deleteAllItemImage.json
    @PostMapping(value = "/deleteAllItemImage.json")
    public Map<String, Object> imageDeleteAllPOST(
            @RequestParam(name = "itemno") Long itemno
            ) {
        Map<String, Object> retMap = new HashMap<>();
        try {
            int ret = imageMapper.deleteAllItemImage(itemno);
            retMap.put("status", 200);
            retMap.put("result", ret);
        } catch (Exception e) {
            e.printStackTrace();
            retMap.put("status", -1);
        }
        return retMap;
    }

삭제 전/삭제후 리턴결과/삭제 후


JWT를 이용한 토큰발행

JSON Web Token = JWT
토큰 발행시 어떤 라이브러리를 사용해도 상관없다
➡️ JSON Web Token 이용해보기

JWT 라이브러리 설치

토큰발행을 위한 JSON Web Token(JWT) 라이브러리 설치

pom.xml

<!-- jjwt -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
</dependency>	
org.glassfish.jaxb jaxb-runtime 2.3.2
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

토큰 추출, 변경하기

jwt / JwtUtil.java

package com.example.jwt;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Component
public class JwtUtil {
    
    // 토큰 생성용 보안키
    private final String SECRETKEY = "fekjkfe43jfe";

    // 정보 추출용 메소드
    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = Jwts.parser().setSigningKey(SECRETKEY).parseClaimsJws(token).getBody();
        return claimsResolver.apply(claims);
    }

    // 토큰 생성(아이디 정보를 이용한 토큰 생성)
    public String generateToken(String username) {
        System.out.println(username);
        
        // 만료시간 1초=1000 ex) 30분 => 1000 * 60 * 30
        long tokenValidTime = 1000 * 60 * 60 * 4; // 4시간

        Map<String, Object> claims = new HashMap<>();
        String token = Jwts.builder().setClaims(claims).setSubject(username)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + tokenValidTime))
                .signWith(SignatureAlgorithm.HS256, SECRETKEY).compact();

        return token;
    }

    // 토큰 검증
    public Boolean validateToken(String token, String userid) {
        // 토큰에서 아이디 정보 추출
        final String username = this.extractUsername(token);
        if (username.equals(userid) && !isTokenExpired(token)) {
            return true;
        }
        return false;
    }

    // 토큰에서 아이디 정보 추출하기
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    // 토큰에서 만료 시간 추출하기
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    // 토큰의 만료시간이 유효한지 확인
    public Boolean isTokenExpired(String token) {
        // 만료시간 가져와서 현재시간보다 이전인지 확인
        return this.extractExpiration(token).before(new Date());
    }

}

Application.java

생성한 jwt파일 Application에 등록하여 사용하기

"com.example.jwt"

회원가입

MemberRestController.java

BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder();
➡️@Autowired 하여 사용한다

@Autowired
    PasswordEncoder bcpe;
// @RequestBody => postman 에서 Body > raw로 보내기
    // 127.0.0.1:8080/BOOT1/api/member/join.json
    // 회원가입
    @PostMapping(value = "/join.json")
    public Map<String, Object> joinPost(@RequestBody MemberDTO member){
        Map<String, Object> retMap = new HashMap<>();
        try {
            //BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder(); => @Autowired하여 사용
            member.setUserpw( bcpe.encode(member.getUserpw()) );

            int ret = mmapper.joinMember(member);
            retMap.put("status", 200);
            retMap.put("result", ret);
        } catch (Exception e) {
            e.printStackTrace();
            retMap.put("status", 1);
        }
        return retMap;
    }

로그인시 토큰생성

View에서 던져준 로그인 아이디 암호를 받아서 토큰 생성
postman 에서 보낼때 폼데이터인지 row를 보내는지 잘 확인해야한다

SecurityConfig.java

SecurityConfig에서 생성된 로그인 방식은 CDN방식이다 (Thymeleaf를 이용한 로그인 방식)
➡️ AuthenticationManager 를 이용한 REST용 인증방식 추가

// REST용 인증방식
        // @Bean => 객체 생성 (Autowired를 통해서 사용가능)
        // @Autowired AuthenticationManager
        @Bean
        public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception{
            return authenticationConfiguration.getAuthenticationManager();
        }

토큰 발행시 UsernamePasswordAuthenticationToken(아이디, 비밀번호, 권한) 메서드를 사용한다

MemberRestController.java

JwtUtil 컴포넌트 객체 생성 ➡️ @Autowired 하여 사용

@Autowired
    JwtUtil jwtUtil;
// 로그인하기
    //  127.0.0.1:8080/BOOT1/api/member/login.json
    @PostMapping(value = "/login.json")
    public Map<String, Object> loginPost(
        @RequestBody MemberDTO member){
        System.out.println(member.toString());
        Map<String, Object> retMap = new HashMap<>();
        try {
            // String권한으로 collection으로 변경
            String[] strRole = { member.getRole() }; 
            Collection<GrantedAuthority> role
                = AuthorityUtils.createAuthorityList(strRole);

            // CustomDetailsService와 같은 역할
            UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(member.getUserid(), member.getUserpw(), role);

            authenticationManager.authenticate(upat);
            retMap.put("status", 200);
            retMap.put("token", jwtUtil.generateToken(member.getUserid()));
        } catch (Exception e) {
            e.printStackTrace();
            retMap.put("status", -1);
        }
        return retMap;
    }

로그인 성공시 토큰발행/로그인 실패하는 경우 -1


토큰검증

회원가입, 로그인, 회원정보수정
➡️ 회원가입과 로그인은 토큰 발행여부와 관계 없지만,
로그인 이후에 진행되어야 하는 부분들 (= 회원정보 수정, 암호변경, 회원탈퇴)은
토큰 검증이 완료된 이후에 진행되어야 한다

JwtFilter.java

JwtFilter 사용하여 토큰검증진행하는 JwtFilter.java파일 생성

  • 필터를 적용하고자 하는 url 설정한다 ➡️ 로그인과 회원가입은 필터적용 제외시킨다
  • 토큰 검증 과정에서 토큰이 없는경우는 예외처리를 해준다
  • 오류시에는 filterChain.doFilter(request, response);가 작동하지 않으며,
    restcontroller로 넘어갈 수 없다
// filterChain 라인이 실행되어야 restcontroller로 넘어감
        filterChain.doFilter(request, response);
package com.example.jwt;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.filter.OncePerRequestFilter;

// 필터를 적용하고자 하는 url 설정
@WebFilter(urlPatterns = {
    "/api/member/update.json", // 회원정보 수정
    "/api/member/updatepw.json", // 암호변경
    "/api/member/delete.json" // 회원탈퇴
})
public class JwtFilter extends OncePerRequestFilter {

@Autowired
    JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {
        try {

            System.out.println("=======filter============");
            System.out.println(request.getRequestURI());
            System.out.println("=======filter============");

            // Header에 TOKEN심기
            String token = request.getHeader("TOKEN");
            if (token == null || token.length() == 0) { // TOKEN이 없는 경우
                // 강제로 오류발생 시킴
                throw new Exception();
            }

            String userid = jwtUtil.extractUsername(token);
            if (jwtUtil.validateToken(token, userid) == false) {
                throw new Exception();
            }

            // 아래 filterChain 라인이 실행되어야 restcontroller로 넘어감
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            e.printStackTrace();
            // 에러인 경우 에러메세지 전송
            response.sendError(-1, "token error");
        }
    }
}

회원정보수정

생성된 TOKEN을 이용하여 사용자 정보를 조회하여 userid를 가져온다
토큰에서 가져오는 userid는 세션과 다르고 세션이 아니다!

로직
1. 생성된 token을 이용하여 userid를 가져온다
2. useridMemberDTO member에 넣어준다
3. mmapperMemberDTO member를 담아 회원의 정보를 update한다 updateinfoMember

MemberRestController.java

    // 회원정보 수정
    @PutMapping(value = "/update.json")
    public Map<String, Object> updatePUT(
        @RequestHeader(name = "TOKEN") String token,
        @RequestBody MemberDTO member){
        Map<String, Object> retMap = new HashMap<>();
        try {
        // 생성된 TOKEN을 이용하여 userid를 가져옴
        String userid = jwtUtil.extractUsername(token);
        System.out.println("tokenUsername => " + userid);
        System.out.println("====member===="+member.toString());

        member.setUserid(userid);
        
        int ret = mmapper.updateinfoMember(member);
        System.out.println("=========ret======== : " + ret);
            retMap.put("status", 200);
            retMap.put("result", ret);
        } catch (Exception e) {
            e.printStackTrace();
            retMap.put("status", -1);
        }
        return retMap;
    }

암호변경

암호변경시 필요한 정보
1. 암호변경창에서 입력한 기존 비밀번호(rowpw)
2. DB에 암호화 되어있는 기존 비밀번호(userpw)
3. 사용자가 입력한 변경할 비밀번호(newpw)
➡️ MemberDTO에 임시변수 newpw를 생성해 준다
➡️ 새 변수 생성대신 Map에 변경할 비밀번호를 넣어 보내도 된다

로직
1. token을 이용하여 userid를 가져온다
2. userid로 기존 회원정보를 조회한다 mmapper.selectMemberOne(userid)
3. 조회한 회원정보에서 암호화된 비밀번호 정보를 가져온다
4. bcpe.matches(현재암호, DB암호) 를 이용해 현재암호와 DB암호가 일치하는지 확인한다
5. 현재암호와 DB암호가 일치하는 경우 map
해당 사용자의 아이디 userid + 암호화된 변경할 암호 bcpe.encode(member.getNewpw())를 넣어 비밀번호를 변경해준다 updateMemberPw(map)

MemberRestController.java

    // 암호변경 /updatepw.json
    @PutMapping(value = "/updatepw.json")
    public Map<String, Object> updatepwPUT(
        @RequestHeader(name = "TOKEN") String token, @RequestBody MemberDTO member
        ){
            Map<String, Object> retMap = new HashMap<>();
            try {
            // 생성된 TOKEN을 이용하여 userid를 가져옴
            String userid = jwtUtil.extractUsername(token);
            System.out.println("=========" + userid);
            
            // 기존 회원정보 조회
            MemberDTO member1 = mmapper.selectMemberOne(userid);
            BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder();
            
            // 조회한 회원정보에서 비밀번호 가져오기 = DB암호
            String memberpw = member1.getUserpw();
            // bcpe.matches("rawPassword = 현재암호", "encodedPassword = DB암호")
            if(bcpe.matches( member.getUserpw() , memberpw )){
                Map<String, Object> map = new HashMap<>();
                map.put("userid", userid);
                map.put("userpw", bcpe.encode(member.getNewpw()));
                mmapper.updateMemberPw(map);

                retMap.put("status", 200);
            }
            } catch (Exception e) {
                e.printStackTrace();
            retMap.put("status", -1);
            }
            return retMap;
        }

회원탈퇴

0개의 댓글