Spring 강의 day 6

주세환·2023년 5월 7일
0

Spring

목록 보기
6/18

pom.xml

<dependencies>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>

	<dependency>
		<groupId>jakarta.xml.bind</groupId>
		<artifactId>jakarta.xml.bind-api</artifactId>
	</dependency>

	<dependency>
		<groupId>org.glassfish.jaxb</groupId>
		<artifactId>jaxb-runtime</artifactId>
	</dependency>
	
	<dependency>
		<groupId>io.jsonwebtoken</groupId>
		<artifactId>jjwt</artifactId>
		<version>0.9.1</version>
	</dependency>
	
	<dependency>
		<groupId>org.json</groupId>
		<artifactId>json</artifactId>
		<version>20230227</version>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
	</dependency>

	<!--  DB로 세션관리-->
	<dependency>
		<groupId>org.springframework.session</groupId>
		<artifactId>spring-session-jdbc</artifactId>
	</dependency>

	<!-- h2, oracle, mysql -->
	<dependency>
		<groupId>com.h2database</groupId>
		<artifactId>h2</artifactId>
	</dependency>

	<!-- mybatis -->
	<dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>3.0.0</version>
	</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.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
		<scope>runtime</scope>
		<optional>true</optional>
	</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>
</dependencies>

dto

@Getter
@Setter
@ToString(exclude = {"password"})
public class CustomUser extends User{
    private String id;     // username
    private String password;    //password
    private Collection<GrantedAuthority> authorities; //role
    private String name;
    private int age;
    
    
    public CustomUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }


    public CustomUser(String username, String password, Collection<GrantedAuthority> authorities, String name, int age) {
        super(username, password, authorities);
        this.id = username;
        this.password = password;
        this.authorities = authorities;
        this.name = name;
        this.age = age;
    }
}

CustomUser.java


controller

CustomerController

@Controller
@RequestMapping(value ="/customer")
@Slf4j
@RequiredArgsConstructor
public class CustomerController {
    
    final String format = "CustomerController => {}";
    final MemberMapper mMapper;

    @GetMapping(value="/home.do")
    public String homeGET(
                Model model,
                @AuthenticationPrincipal User user,            
                @RequestParam(name = "menu", required = false, defaultValue = "0")int menu) {
        if(menu == 1){
            // 세션에서 아이디정보를 꺼내서 mapper에서 조회
            Member member = mMapper.selectMemberOne1(user.getUsername());       
            log.info(format, member.toString());    
            model.addAttribute("member", member);

            // 체크박스에 표시할 항목들
            String[] checkLabel = {"가가가","가나다","나나나","다다다","가나다"};
            model.addAttribute("checklabel", checkLabel);
        }
        return "/customer/home";
    }

    //@Authen usser user => HttpSession httpSession => httpSession.getAttribute("user")
    @PostMapping(value="/home.do")
    public String homePOST(@RequestParam(name="menu", required = false) int menu, 
                            HttpServletRequest request,
                            HttpServletResponse response,
                            @ModelAttribute Member member,
                            @AuthenticationPrincipal CustomUser user) {
        log.info("CustomerController menu => {}", user.toString());
        log.info("CustomerController menu => {}", menu);
        if(menu == 0){
            return "redirect:home.do?menu=1";
        }
        if(menu == 1){
            member.setId(user.getUsername());
            int ret = mMapper.updateMemberOne(member);
            log.info("updateMemberOne menu => {}", ret);
            // 아이디 정보 가져오기 => user.getUsername();
            return "redirect:/customer/home.do?menu=1";
        }
        else if(menu == 2){
            BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder();

            // 비밀번호확인 => matches(바꾸기전 비번, 해시된 비번)
            if(bcpe.matches("hash전비번", "hash후 비번")){

            }

        }
        else if(menu == 3){
            // 아이디 정보를 이용해서 db에서 1명 조회
            // 조회된 정보와 현재암호가 일치하는지 matches로 비교
            // 비교가 true이면 db에서 삭제, 로그아웃
            // 컨트롤러에서 logout처리하기
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            log.info("CustomerController => {}", auth.toString());
        }

        return "/customer/home";
    }

    @GetMapping(value="/join.do")
    public String joinGET() {
        return "/customer/join";
    }

    @PostMapping(value="/join.do")
    public String joinPOST(@ModelAttribute Member member) {
        log.info(format, member.toString()); // 화면이 정확하게 표시되고 사용자가 입력한 항목을 member객체에 저장했음을 확인함.

        BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder(); // salt값을 자동으로 부여함.
        member.setPassword( bcpe.encode(member.getPassword()) ); // 기존암호를 암호화시켜서 다시 저장함.
        int ret = mMapper.insertMemberOne(member);
        if(ret == 1){
            return "redirect:joinok.do"; //주소창에 127.0.0.1:9090/ROOT/customer/joinok.do입력후 엔터키를 자동화
        }
        // return "redirect:home.do";  //주소창에 127.0.0.1:9090/ROOT/customer/home.do입력후 엔터키를 자동화
        return "redirect:join.do";   // 실패시 회원가입화면으로
    }
    
    @GetMapping(value="/joinok.do")
    public String joinokGET() {
        return "/customer/joinok";
    }   
}

CustomerController.java


HomeController

// 백엔드 Restful api 연동가능자 + mysql, oracle, mongodb ... + jpa + mybatis
// 프론트엔드 spa 싱글페이지..?

@Controller // 이게 서블렛이다..
public class HomeController {

    // 127.0.0.1:9090/ROOT/home.do
    @GetMapping(value = { "/home.do", "/" }) // 배열
    public String homeGET(Model model, @AuthenticationPrincipal User user) {
        if(user != null){ //로그인 되었음
            System.out.println(user.toString());
        }
        model.addAttribute("user", user);
        return "/home";
        
        // request.setAttribute("key","value")
        // model.addAttribute("title", "전송된타이틀");
        // model.addAttribute("abc", "마음대로");
        // model.addAttribute("xyz", "잠온다");
        
        // templates/ home.html
        // return "home"; // request.getRequestDispatcher("/WEB-INF/home.jsp").forward(request, response);
    }

    // 127.0.0.1:9090/ROOT/main.do
    @GetMapping(value = "/main.do")
    public String MainGET(Model model) {
        model.addAttribute("title", "메인화면");
        return "main"; // request.getRequestDispatcher("/WEB-INF/main.jsp").forward(request, response);
    }

    @GetMapping(value="/403page.do")
    public String PageGET() {
        return "/error/403page";
    }
    
    @GetMapping(value="/login.do")
    public String loginGET() {
        return "login";
    }

    @GetMapping(value="/logout.do")
    public String logoutGET() {
        return "logout";
    }
}

HomeController.java


WebErrorController

@Controller
public class WebErrorController implements ErrorController {
    @GetMapping("/error")
    public String handleError(HttpServletRequest request) {
        Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);

        if(status != null){
            int statusCode = Integer.valueOf(status.toString());

            if(statusCode == HttpStatus.NOT_FOUND.value()) {
                return "/error/404page";
            } else {
                return "/error/errorpage";
            }
        }
        return "/error/errorpage";
    }
}

WebErrorController.java


restcontroller

JwtUtil.java

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

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

    // 토큰 생성(아이디 정보를 이용한 토큰 생성)
    // 아이디만포함. json -> string -> 토큰 -> string -> json
    // {"uid":"aaa", "role":"CUSTOMER"}
    public String generateToken(String username, String role) {
        // 1. jsonobject로 변환
        JSONObject jobj = new JSONObject();
        jobj.put("username", username);
        jobj.put("role", role);

        // ex) 30분 => 1000 * 60 * 30
        long tokenValidTime = 1000 * 60 * 60 * 4; // 4시간

        // 2. 문자형태로 추가한 후 토큰 생성
        Map<String, Object> claims = new HashMap<>();
        String token = Jwts.builder().setClaims(claims)
                .setSubject(jobj.toString())
                .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) {
        String sObj = extractClaim(token, Claims::getSubject);
        JSONObject jobj = new JSONObject(sObj);
        return jobj.getString("username");
    }

    // 토큰에서 권한 정보 정보 추출하기
    public String extractRole(String token) {
        String sObj = extractClaim(token, Claims::getSubject);
        JSONObject jobj = new JSONObject(sObj);
        return jobj.getString("role");
    }

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

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

JwtUtil.java


RestBoardController

// => html을 표시할수 없음
// Map, Member, Board, List를 반환하면 자동으로 json으로 바꿔줌
@RestController
@RequestMapping(value="/api/board")
@RequiredArgsConstructor
// GET => 조회
// POST => 추가, 로그인
// DELETE => 삭제
// PUT => 전체수정, PATCH => 일부수정
@Slf4j
public class RestBoardController {

    final BoardMapper bMapper; // 매퍼객체생성
    final String format = "RESTBoard => {} ";




    // 게시글 삭제
    @DeleteMapping(value="/delete.json")
    public Map<String, Integer> deleteDelete(@RequestBody Board board){
        
        int ret = bMapper.deleteBoardOne(board.getNo());

        Map<String, Integer> retMap = new HashMap<>();
        retMap.put("result", ret);
        return retMap;
    }

    // 게시글 수정
    @PutMapping(value="/update.json")
    public Map<String, Integer> updatePut(@RequestBody Board board){
        
        int ret = bMapper.updateBoardOne(board);
        
        Map<String, Integer> retMap = new HashMap<>();
        retMap.put("result", ret);
        return retMap;
    }

    // 게시글 조회수 증가
    // 게시글 번호가 전달되면 update를 이용해서 게시글조회수를 증가시키고 결과를 result:1, result:0 
    //127.0.0.1:9090/ROOT/api/board/updatehit.json
    @RequestMapping(value = "/updatehit.json", method = {RequestMethod.PUT})
    public Map<String, Integer> updatehitPUT(@RequestBody Board board){
        log.info(format, board.toString());

        int ret = bMapper.updateBoardHit(board.getNo());
        
        Map<String, Integer> retMap = new HashMap<>();
        retMap.put("result", ret);
        return retMap;
    }



    //127.0.0.1:9090/ROOT/api/board/insert.json
    // 게시판 글쓰기 => 제목, 내용, 작성자 =>{"title" : "a", "content" : "b", "writer" : "c"}
    @RequestMapping(value = "/insert.json", method = {RequestMethod.POST})
    public Map<String, Integer> insertPOST(@RequestBody Board board ){
        
        //전송되는 값 확인
        log.info(format, board.toString());
        
        // DB에 추가하고 결과를 1또는 0으로 반환
        int ret = bMapper.insertBoardOne(board);

        Map<String, Integer> retMap = new HashMap<>();
        retMap.put("result", ret);
        return retMap;
    }

    //127.0.0.1:9090/ROOT/api/board/selectlist.json
    @RequestMapping(value="/selectlist.json", method={RequestMethod.GET})
    public List<Board> requestMethodName() {
        
        // [{},{},{},{},{},...,{}]
        return bMapper.selectBoardList();
    }
    
    //127.0.0.1:9090/ROOT/api/board/select.json
    @GetMapping(value="/select.json")
    public Map<String, String> selectGET() {
        Map<String, String> retMap = new HashMap<>();
        retMap.put("result", "ok");
        return retMap;
    }
    
}

RestBoardController.java


RestMemberController

@RestController
@RequestMapping(value = "/api/member")
@RequiredArgsConstructor
@Slf4j
public class RestMemberController {
    
    final JwtUtil jwtUtil; // Component 객체 생성
    final MemberMapper mMapper;
    BCryptPasswordEncoder pcpe = new BCryptPasswordEncoder(); // 객체생성

    // 로그인 => 로그인 성공시 세션에 정보를 저장하고 다른페이지에서 확인
    // 로그인 => 토큰을 발행함.(토큰에는 로그인 사용자 정보 만료시간이 포함되어 있음.)

    //127.0.0.1:9090/ROOT/api/member/login.json => {"id":"cccc", "password":"a"}
    @PostMapping(value = "/login.json") 
    public Map<String, Object> loginPOST(@RequestBody Member member){
        // 아이디를 이용해서 회원정보 가져오기
        Member retMember = mMapper.selectMemberOne1(member.getId());
        log.info("RestMember => {}",retMember.toString());

        Map<String, Object> retMap = new HashMap<>();
        retMap.put("result", 0);

        if(retMember != null){
            if(pcpe.matches(member.getPassword(), retMember.getPassword() )){
                // 암호가 일치하면 토큰 발행
                retMap.put("result", 1);
                retMap.put("token", jwtUtil.generateToken(member.getId(), "CUSTOMER"));
            }
            
        }
        // 가져온 암호와 전달된 암호가 일치하는지 확인
        return retMap;
    }

    // 회원가입
    @PostMapping(value = "/join.json")
    public Map<String, Integer> joinPOST(){
        int ret =1;
        Map<String, Integer> retMap = new HashMap<>();
        retMap.put("result", ret);
        return retMap;
    }
}

RestMemberController.java


service

@Service
@Slf4j
@RequiredArgsConstructor
public class SecurityServiceImpl implements UserDetailsService{

    final MemberMapper mMapper;
    final String format = "SecurityServiceImpl => {}";

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info(format,username);   
        // 아이디를 전달해서 정보를 받아옴 암호까지 받아옴
        // DB연동....

        Member member = mMapper.selectMemberOne1(username);
        if(member != null){ // 가져올 정보가 있음. 존재하는 아이디가 있음
            // Member DTO를 사용해서 처리하나 시큐리티는 User DTO를 사용함
            // 세션에는 User타입으로 저장됨.
            
            // User를 이용할 경우 (세션내용 -> 아이디, 암호, 권한)
            // return User.builder().username(member.getId()).password(member.getPassword()).roles(member.getRole()).build();

            String[] strRole = { "ROLE_" + member.getRole() };
            Collection<GrantedAuthority> role = AuthorityUtils.createAuthorityList(strRole);
            return new CustomUser(member.getId(), member.getPassword(), role, member.getName(),member.getAge());
        }
        // 존재하지 않는 아이디
            return User.builder()
                .username("_")
                .password("_")
                .roles("_")
                .build();   
    }   
}

SecurityServiceImpl.java


mapper

BoardMapper

@Mapper
public interface BoardMapper {

    // 글쓰기
    @Insert({
            " Insert into board( title, content, writer)",
            " values(#{obj.title},#{obj.content},#{obj.writer}) "
    })
    public int insertBoardOne(@Param("obj") Board obj);

    // 게시글 목록 조회
    @Select({
            " select b.* From Board b Order By no DESC "
    })
    public List<Board> selectBoardList();

    // 게시글 1개 조회
    @Select({
            " Select b.* from board b where no=#{no} "
    })
    public Board selectBoardOne(@Param("no") long no);

    // 게시글 수정
    // @Select({
    // " UPDATE board SET title=#{title} WHERE NO =#{no} "
    // })

    // sql문이 없음 => resources/mappers/파일명Mapper.xml
    public int updateBoardOne(Board obj);

    // sql문이 없음 => resources/mappers/파일명Mapper.xml
    public int deleteBoardOne(long no);

    // // 게시글 조회수 증가
    public int updateBoardHit(long no);
}

BoardMapper.java


boardMapper.xml

<?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.BoardMapper">
    <update id="updateBoardOne" parameterType="com.example.dto.Board">
        UPDATE board SET title=#{title}, content=#{content}, writer=#{writer} WHERE NO =#{no}
    </update>

    <delete id="deleteBoardOne" parameterType="long">
        DELETE FROM BOARD WHERE no=#{no}
    </delete>

    <update id="updateBoardHit" parameterType="Board">
        UPDATE board SET hit=hit+1 WHERE NO =#{no}
    </update>
</mapper> 

boardMapper.xml


MemberMapper

@Mapper
public interface MemberMapper {

        public int insertMemberOne(Member member);

        public Member selectMemberOne(Member member);
        
        public Member selectMemberOne1(String userid);

        public int updateMemberOne(Member member);

}

MemberMapper.java


memberMapper.xml

<?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.MemberMapper">
    
    <insert id="insertMemberOne" parameterType="com.example.dto.Member">
        INSERT INTO MEMBER(id, password, name, age, role)
        VALUES(#{id}, #{password}, #{name}, #{age}, #{role})
    </insert>

    <select id="selectMemberOne" parameterType="Member" resultType="Member">
        SELECT m.id, m.name, m.age, m.role FROM member m WHERE m.id=#{id} AND m.password=#{password}
    </select>

    <select id="selectMemberOne1" parameterType="string" resultType="Member">
        SELECT m.* FROM member m WHERE m.id=#{id}
    </select>
    
    <update id="updateMemberOne" parameterType="Member">
        update member set name=#{name}, age=#{age} WHERE id=#{id}
    </update>
</mapper>

memberMapper.xml

0개의 댓글