본문 바로가기
목차
요약

메타코딩 스프링부트 강좌 요약

by 지각생 2022. 1. 24.
728x90

 

스프링부트 강좌 3강(블로그 프로젝트) - 의존성 설정


스프링부트 강좌 4강(블로그 프로젝트) - 프로젝트 실행해보기


스프링부트 강좌 5강(블로그 프로젝트) - MySQL57 환경세팅(한글설정,유저생성)

 

1. 사용자 생성 및 권한 주기 및 DB 생성

 

-- 유저이름@아이피주소
create user 'cos'@'%' identified by 'cos1234';
-- ON DB이름.테이블명
-- TO 유저이름@아이피주소
GRANT ALL PRIVILEGES ON *.* TO 'cos'@'%';
CREATE DATABASE blog CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
use blog;


스프링부트 강좌 6강(블로그 프로젝트) - MySQL 스프링연결

(application.yml)

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/blog?serverTimezone=Asia/Seoul
    username: cos
    password: cos1234


스프링부트 강좌 9강(블로그 프로젝트) - Git의 3가지 영역


스프링부트 강좌 10강(블로그 프로젝트) - http1.1 요청방식

 

1. Postman 설치

https://www.postman.com/downloads

 

2. 통신방법 4가지


스프링부트 강좌 11강(블로그 프로젝트) - stateless란

 

stateless : 요청시마다 스트림을 연결해서 Data를 주고 받는 방식


스프링부트 강좌 12강(블로그 프로젝트) - MIME타입이란

 

head : data 정리를 위한 정보

body : data


스프링부트 강좌 13강(블로그 프로젝트) - http요청 실습1

 

인터넷 브라우저 요청은 무조건 get요청밖에 할 수 없다.

그래서 PostMan을 깐 것임.

 


스프링부트 강좌 14강(블로그 프로젝트) - http요청 실습2

 

 

1. Get방식으로 data를 요청할때 방법은 쿼리 스트링 방법뿐이다.

	@GetMapping("/http/get")
		public String getTest(@RequestParam int id, @RequestParam String username) {
		
			return "get 요청"+id;
	}

 

    @GetMapping("/http/get")
		public String getTest(Member m) {
		
			return "get 요청" + m.getid();
	}

@RequetParam int id처럼 하면 하나하나 다 적어줘야하는데

Member m 으로하면 해당 클래스 메서드들 사용가능

 

2. Post방식은 data를 Body에 담아 보낸다.

 

1. form 방식

@PostMapping("/http/post")
public String postTest(Member m){
	return "post 요청 : "+m.getId();
}

2-1. JSON data요청(text)

@PostMapping("/http/post")
public String postTest(@RequestBody String text){
	return "post 요청 : "+text;
}

 

2-2. JSON data요청(JSON)

@PostMapping("/http/post")
public String postTest(@RequestBody Member m){
	return "post 요청 : "+m.getId();
}

 

 

3. Put 방식

@PutMapping("/http/put")
public String putTest(@RequestBody Member m){
	return "put 요청 : "+m.getId();
}

여기서 null이 나오는 상황을 잘 확인하고 넘어가자.

@RequestBody Member m

을 파라미터로 정하고

Json으로 id, password만 넘겨주면

값이 오지 않은 username과 email의 값은 null이 된다.

모르면 Nullpoint~ 에러 유발하기 좋다.

 

4. Delete 방식

@DeleteMapping("/http/delete")
public String deleteTest(@RequestBody Member m){
	return "delete 요청 : "+m.getId();
}

스프링부트 강좌 15강(블로그 프로젝트) - maven이란


스프링부트 강좌 16강(블로그 프로젝트) - lombok세팅 및 사용해보기


스프링부트 강좌 17강(블로그 프로젝트) - yml설정하기

server:
  port: 8000
  servlet:
    context-path: /blog
    encoding:
      charset: UTF-8
      enabled: true
      force: true
    
spring:
  mvc:
    view:
      prefix: /WEB-INF/views/
      suffix: .jsp
      
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/blog?serverTimezone=Asia/Seoul
    username: cos
    password: cos1234
    
  jpa:
    open-in-view: true
    hibernate:
      ddl-auto: create
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
      use-new-id-generator-mappings: false
    show-sql: true
    properties:
      hibernate.format_sql: true

  jackson:
    serialization:
      fail-on-empty-beans: false

스프링부트 강좌 18강(블로그 프로젝트) - User테이블 생성

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
package com.cosblog.model;

import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.DynamicInsert;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;



@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
//@DynamicInsert //insert시에 null인 필드를 제외시켜준다.
public class User {
	@Id //Primary key
	@GeneratedValue(strategy = GenerationType.IDENTITY)//프로젝트에서 연결된 DB의 넘버링 전략을 따라간다.
	private int id;//시퀀스, auto_increment
	
	@Column(nullable = false, length = 30)
	private String username;
	
	@Column(nullable = false, length = 100) //123456=> 해쉬 (비밀번호 암호화)
	private String password;
	
	@Column(nullable = false, length = 50)
	private String email;
	
	//@ColumnDefault("'user'")
	@Enumerated(EnumType.STRING)
	private RoleType role; //Enum을 쓰는게 좋다. // admin, user, manager
	
	@CreationTimestamp //시간이 자동 입력
	private Timestamp createDate;
}

시퀀스 : 자동증가 사용안함

TABLE : 테이블에 번호 만들고 쓰겟음

AUTO : 자동으로 맞춰진다는거.

IDENTITY : 프로젝트에서 연결된 DB의 넘버링 전략을 따라간다.

               만약 오라클 연결시면 시퀀스

               MySQL이면 오토인크리먼트를 사용한다는거

저 값이 ture면

JPA가 사용하는 기본 넘버링 방식을 따라간다.

false면 당근 반대란 뜻이고 우린 false세팅해서 진행함.

create : 테이블 새로 만들겠음(프로젝트 재실행할때마다)

그래서 최초에만 이렇게하고 다음부턴 update로 해야한다.

update

none

저기가 true라서 콘솔창에 보여줌

그리고 한 줄로 보기 안좋게 나오는걸

저 문구가 줄 바꿈 해준다.

naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

이건 Entity를 만들때(테이블)을 만들때

변수명 그대로 테이블 필드로 만들어준다.

nmaing:

값을 저걸로 바꾸면 

myExmail->my_email

방식으로 바뀌어서 저장된다.

(우린 안쓸거임)


스프링부트 강좌 19강(블로그 프로젝트) - 데이터베이스 한글인코딩 문제해결


스프링부트 강좌 20강(블로그 프로젝트) - Board테이블 생성

 

package com.cosblog.model;

import java.sql.Timestamp;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;



@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
public class Board {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
	
	@Column(nullable = false, length = 100)
	private String title;
	
	@Lob //대용량 데이터
	private String content; //섬머노트 라이브러리<html>태그가 섞여서 디자인 됨.
	
	@ColumnDefault("0")
	private int count;
						//@ManyToOne(fetch = FetchType.LAZY)는 디폴트값, 펼치기 방식에서 쓰일꺼(무조건 가져오는게 아님)
	@ManyToOne(fetch = FetchType.EAGER) //Many = Board, User = One, 무조건 정보 가져오라는 거
	@JoinColumn(name="userId")//DB테이블 필드값은 userId로 만들어지고 연관관계는 ManyToOne으로 만들어진다.
	private User user;//DB는 오브젝트를 저장할 수 없다. FK, 자바는 오브젝트를 저장할 수 있다.
	
	@OneToMany(mappedBy = "board", fetch = FetchType.EAGER) //Many = Board, User = One
	private List<Reply> reply;//DB는 오브젝트를 저장할 수 없다. FK, 자바는 오브젝트를 저장할 수 있다.
	
	
	@CreationTimestamp
	private Timestamp createDate;
	
}

 


스프링부트 강좌 21강(블로그 프로젝트) - Reply 테이블 생성

package com.cosblog.model;

import java.sql.Timestamp;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import org.hibernate.annotations.CreationTimestamp;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
public class Reply {

	@Id //Primary key
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id; //시퀀스, auto_increment
	
	@Column(nullable = false,length =200)
	private String content;
	
	@ManyToOne
	@JoinColumn(name="boardId")
	private Board board;
	
	@ManyToOne
	@JoinColumn(name="userId")
	private User user;
	
	@OneToMany(mappedBy="board")//mappedBy는 연관관계의 주인이 아니다(난 FK가 아니에요) DB에 칼럼을 만들지 마세요.
	private List<Reply> reply;
	
	@CreationTimestamp
	private Timestamp createDate;
}

*외래키란?

외래(Foreign Key) 또는 포린란 하나(또는 여러개)의 다른 릴레이션의 기본(PK) 필드를 참조하는 데이터의 참조 무결성(Referential integrity)을 확인하기 위해 사용되는 (Key)를 의미한다



스프링부트 강좌 22강(블로그 프로젝트) - 연관관계의 주인

 

연관관계의 주인 = FK를 가진 오브젝트

 

아직 감이 잘 안 온다.


스프링부트 강좌 23강(블로그 프로젝트) - JSON 사용법


스프링부트 강좌 24강(블로그 프로젝트) - 회원가입 위한 insert 테스트

 

package com.cosblog.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.cosblog.model.User;

//DAO
//자동으로 bean등록이 된다.
//@Repository 생략가능하다
public interface UserRepository extends JpaRepository<User, Integer>{

}

form방식으로 전송

package com.cosblog.test;

import java.util.Optional;
import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cosblog.model.RoleType;
import com.cosblog.model.User;
import com.cosblog.repository.UserRepository;

@RestController
public class DummyControllerTest {

	@Autowired//의존성 주입
	private UserRepository userRepository;
	
	@PostMapping("/dummy/join")
	public String join(User user) {
		System.out.println("id:"+user.getId());
		System.out.println("username:"+user.getUsername());
		System.out.println("password:"+user.getPassword());
		System.out.println("email:"+user.getEmail());
		System.out.println("role:"+user.getRole());
		System.out.println("createDate:"+user.getCreateDate());
		
		user.setRole(RoleType.USER);
		userRepository.save(user);
		return "회원가입이 완료되었습니다.";	
	}	
}

스프링부트 강좌 25강(블로그 프로젝트) - 회원가입을 위한 enum사용법

 

 

package com.cosblog.model;

public enum RoleType {
	USER, ADMIN
}

 

45행, 46행이 @DynamicInsert를 대신하기 위함

 

25행이 @DynamicInsert를 대신하기 위함

 

이렇게 하면 role에 값을 insert하지 않아도 행 생성시 자동으로 User값이 기본값으로 들어가진다.


스프링부트 강좌 26강(블로그 프로젝트) - id로 select 테스트

 

방법1

package com.cosblog.test;

import java.util.Optional;
import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cosblog.model.RoleType;
import com.cosblog.model.User;
import com.cosblog.repository.UserRepository;

@RestController
public class DummyControllerTest {

	@Autowired//의존성 주입
	private UserRepository userRepository;
	
	//{id} 주소로 파라미터를 전달 받을 수 있음
	//http:// localhost:8000/blog/dummy/user/3
	@GetMapping("/dummy/user/{id}")
	public User detail(@PathVariable int id) {
		//user/4을 찾으면 내가 데이터 베이스에서 못 찾아오게 되면 user가 null이 될 것 아냐?
		//그럼 return null이 리턴이 되자나.. 그럼 프로그램에 문제가 있지 않겠니?
		//Optional로  너의 User객체를 감싸서 가져올테니 null인지 아닌지 판단해서 return해
		
		User user = userRepository.findById(id).orElseThrow(new Supplier<IllegalArgumentException>() {
			public IllegalArgumentException get() {
				return new IllegalArgumentException("해당 사용자가 없습니다.");
			}
		});
		
		return user;
	}
}

없는 값 넣으면 없다고 알려줌

해당유저는 없습니다

 


방법2

package com.cosblog.test;

import java.util.Optional;
import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cosblog.model.RoleType;
import com.cosblog.model.User;
import com.cosblog.repository.UserRepository;

@RestController
public class aa {

	@Autowired // 의존성 주입
	private UserRepository userRepository;

	// {id} 주소로 파라미터를 전달 받을 수 있음
	// http:// localhost:8000/blog/dummy/user/3
	@GetMapping("/dummy/user/{id}")
	public User detail(@PathVariable int id) {

		User user = userRepository.findById(id).orElseGet(new Supplier<User>() {
			@Override
			public User get() {
				return new User();
			}
		});
		return user;
	}
}

없는 값 넣으면 null 리턴

 

방법3 람다식

package com.cosblog.test;

import java.util.Optional;
import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cosblog.model.RoleType;
import com.cosblog.model.User;
import com.cosblog.repository.UserRepository;

@RestController
public class aa {

	@Autowired // 의존성 주입
	private UserRepository userRepository;

	@GetMapping("/dummy/user/{id}")
	public User detail(@PathVariable int id) {

		User user = userRepository.findById(id).orElseThrow(() -> {
			return new IllegalArgumentException("해당 사용자는 없습니다.");
		});
		return user;
	}
}


스프링부트 강좌 28강(블로그 프로젝트) - update 테스트

package com.cosblog.test;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.cosblog.model.User;
import com.cosblog.repository.UserRepository;

@RestController
public class DummyControllerTest {

	@Autowired // 의존성 주입
	private UserRepository userRepository;
	
	@Transactional
	@PutMapping("/dummy/user/{id}")
	public User updateUser(@PathVariable int id, @RequestBody User requestUser) {
		System.out.println("id :"+id);
		System.out.println("password :"+requestUser.getPassword());
		System.out.println("email :"+requestUser.getEmail());
		
		User user = userRepository.findById(id).orElseThrow(()->{
			return new IllegalArgumentException("수정에 실패하였습니다.");
		});
		user.setPassword(requestUser.getPassword());
		user.setEmail(requestUser.getEmail());
		
        //userRepository.save(user);
		return null;
	}
}

만약 save할때 이미 있는 id를 save하면 update를 해준다.

그리고

save를 하지 않아도 @Transactional을 걸면 업데이트 해준다.

 


"어떤 @Transactional을 사용해야 할까?"의 결론

  • Spring에서 @Transactional을 사용할 때, 더 많은 옵션과 더 많은 버전을 지원하는 @org.springframework.transaction.annotation.Transactional를 사용하는 것이 개발에 유리해 보입니다.
  • 옵션 사용을 하지 않고, Spring Version도 높다면, 어느 @Transactional을 사용하던 상관없다는 게 여기까지 결론입니다.

출처: https://interconnection.tistory.com/123 [Ryan Server]



스프링부트 강좌 29강(블로그 프로젝트) - 영속성 컨텍스트와 더티체킹

 1차 캐시에 user가 생긴걸 영속화라 한다.

 

47행 이때 영속화 된다.

50, 51행에서 user오브젝트 변경.

그럼 영속화된거랑 변경된걸 비교해서 자동으로 변경됐다고 인식을 함.

그 뒤 종료되면 commit으로 DB에 업데이트해 준다.

이걸 더티체킹이라한다.


스프링부트 강좌 30강(블로그 프로젝트) - 삭제하기 테스트

 

package com.cosblog.test;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.cosblog.model.User;
import com.cosblog.repository.UserRepository;

@RestController
public class DummyControllerTest {

	@Autowired // 의존성 주입
	private UserRepository userRepository;
	
	@DeleteMapping("/dummy/user/{id}")
	public String delete(@PathVariable int id) {
		try {
			userRepository.deleteById(id);
		} catch(EmptyResultDataAccessException e) {
			return "삭제에 실패하였습니다. 해당 id는 DB에 없습니다.";
		}
		return "삭제되었습니다. id:"+id;
	}
}

스프링부트 강좌 31강(블로그 프로젝트) - Exception처리하기

package com.cosblog.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;

@ControllerAdvice
@RestController
public class GlobalExeptionalHandler {

@ExceptionHandler(value=IllegalArgumentException.class)
public String handleArgumentException(IllegalArgumentException e) {

return "<h1>"+e.getMessage()+"</h1>";
}
}

스프링부트 강좌 32강(블로그 프로젝트) - 스프링 기본파싱전략과 json통신

form 태그의 한계 get요청과 post 요청 밖에 못한다.

form:form 태그는 post, put, delete, get 요청 다 할 수 있다.

<div class="container">

<form>
<div class="form-group">
<label for="username">유저네임</label> 
<input type="text" id="username">
</div>
<div class="form-group">
<label for="password">패스워드</label> 
<input type="password" id="password">
</div>

<div class="form-group">
<label for="email">이메일</label> 
<input type="email" id="email">
</div>
</form>

<button id="join--submit" class="btn btn-primary">회원가입</button>

</div>

<script src="/js/join.js"></script>
<script>
$('#join--submit').on('click', function() {
var data = {
username : $('#username').val(),
password : $('#password').val(),
email : $('#email').val()
};

$.ajax({
type : 'POST',
url : '/user/join',
data : JSON.stringify(data),
contentType : 'application/json; charset=utf-8',
dataType : 'json'
}).done(function(r) {
if (r.statusCode == 200) {
console.log(r);
alert('회원가입 성공');
location.href = '/user/login';
} else {
if (r.msg == '아이디중복') {
console.log(r);
alert('아이디가 중복되었습니다.');
} else {
console.log(r);
alert('회원가입 실패');
}
}
}).fail(function(r) {
var message = JSON.parse(r.responseText);
console.log((message));
alert('서버 오류');
});
});
</script>

스프링부트 강좌 33강(블로그 프로젝트) - 메인화면 만들기

[window]-[Preferences]

에서 Line width : 72->200으로 해주면

이클립스에서 

ctrl+shif+F 정렬시 글이 엉망진창되는걸 막아준다.

 

대충 navbar 만듦

CSS관련한 부분은 완성 후 완성본만 첨부하겠음.


스프링부트 강좌 34강(블로그 프로젝트) - 로그인, 회원가입 화면 만들기


스프링부트 강좌 35강(블로그 프로젝트) - 회원가입을 위한 기초세팅


스프링부트 강좌 36강(블로그 프로젝트) - Ajax를 사용하는 이유 첫번째

data를 회신하는 서버로는 브라우저 뿐만 아니라 앱에도 사용가능하다는 장점이 있다.


스프링부트 강좌 37강(블로그 프로젝트) - Ajax를 사용하는 이유 두번째

 

두번째 이유는 비동기 통신을 하기 위해


스프링부트 강좌 38강(블로그 프로젝트) - 회원가입 하기 Ajax요청

 

<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>

<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>

로 바꿔주자


스프링부트 강좌 39강(블로그 프로젝트) - 회원가입 하기 두번째 완료

 


스프링부트 강좌 40강(블로그 프로젝트) - ResponseDto 수정


스프링부트 강좌 41강(블로그 프로젝트) - DB격리수준 READ COMMIT

트랜잭션 : 일을 처리하기 위한 가장 작은 단위


스프링부트 강좌 42강(블로그 프로젝트) - READ COMMIT의 정합성 문제

transaction과 commit 사이쯤 맞물린 select의 값으로 Insert를 하려고 하면

data변경으로 인해 정확성이 떨어진다.


스프링부트 강좌 43강(블로그 프로젝트) - REPEACTABLE READ

1. 결과가 다르면 부정합?하다고 하고

2. 결과가 보였다 안보였다 하면 Phantom read라 한다.

 

요약

스프링할 때 Select할 때도 @Transactional을 붙인다

왜? 정합성을 위해


스프링부트 강좌 44강(블로그 프로젝트) - 스프링의 전통적인 트랜잭션


스프링부트 강좌 45강(블로그 프로젝트) - 스프링 JPA의 OSIV 전략

이해가 안간다

마지막 정리 29분!!


스프링부트 강좌 46강블로그 프로젝트 전통적인 방식의 로그인 방법


스프링부트 강좌 47강(블로그 프로젝트) - 시큐리티 시작전 요청 주소 변경


스프링부트 강좌 48강(블로그 프로젝트) - 스프링 시큐리티 체험해보기

어느 페이지로 접근하던지 시큐리티가 강제로 login화면으로 이동하게 만든다.

 

id는

user

비밀번호는

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:authorize access="isAuthenticated()">
	<sec:authentication property="principal" var="principal"/>
</sec:authorize>
<c:choose>
	<c:when test="${empty principal}">
		<ul class="navbar-nav">
			<li class="nav-item"><a class="nav-link" href="/auth/loginForm">로그인</a></li>
			<li class="nav-item"><a class="nav-link" href="/auth/joinForm">회원가입</a></li>
		</ul>
	</c:when>
	<c:otherwise>
		<ul class="navbar-nav">
			<li class="nav-item"><a class="nav-link" href="/board/saveform">글쓰기</a></li>
			<li class="nav-item"><a class="nav-link" href="/user/updateform">회원정보</a></li>
			<li class="nav-item"><a class="nav-link" href="/logout">로그아웃</a></li>
		</ul>
	</c:otherwise>
</c:choose>

principal의 값이 궁금해서 출력해보니

저 글들이 pricipal 값이다.

나중에 custom할 거다.


스프링부트 강좌 49강(블로그 프로젝트) - 스프링 시큐리티 로그인 페이지 커스터마이징

하기 위해 URL경로 재정리

package com.cosblog.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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import com.cosblog.config.auth.PrincipalDetailService;

//빈 등록 : 스프링 컨테이너에서 객체를 관리할 수 있게 하는 것

//아래 3개는 세트라고 생각해라
@Configuration //빈등록 IOC관리
@EnableWebSecurity //시큐리티 필터가 등록이 된다.
@EnableGlobalMethodSecurity(prePostEnabled = true)//특정 주소로 접근을 하면 권한 및 인증을 미리 체크하겠다는 뜻.
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Autowired
	private PrincipalDetailService principalDetailService;
	
	@Bean //IOC가 됨
	public BCryptPasswordEncoder encodePWD() {
		return new BCryptPasswordEncoder();
	}
	
	//시큐리티가 대신 로그인해주는데 password를 가로채기를 하는데
	//해당 password가 뭘로 해쉬가 되어 회원가입이 되었는지 알아야
	//같은 해쉬로 암호화해서 DB에 있는 해쉬랑 비교할 수 있음.
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception{
		auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD());
	}
	
	
	@Override
	protected void configure(HttpSecurity http)throws Exception{
		http
		.csrf().disable() //csrf 토큰 비활성화 (테스트시 걸어두는 게 좋음)
			.authorizeRequests()
			.antMatchers("/", "/auth/**", "/js/**", "/css/**", "/image/**","/dummy/**")
			.permitAll()
			.anyRequest()
			.authenticated()
		.and()
			.formLogin()
			.loginPage("/auth/loginForm")
			.loginProcessingUrl("/auth/loginProc")//스프링 시큐리티가 해당 주소로 요청오는 로그인을 가로채서 대신 로그인 해준다.
			.defaultSuccessUrl("/");
			//.failureUrl("/fail");//실패시 보낼 Url위치 적는 방법
	}
}

스프링부트 강좌 50강(블로그 프로젝트) - 비밀번호 해쉬 후 회원가입하기

 

1. user오브젝트 쓸 수 없고

시큐리티의 UserDetails라는 type이 정해져 있다.

user오브젝트 쓰려면 extends UserDetails 하면 된다

 

2. 해쉬는 내용이 아무리 많아도 고정길이의 문자열로 변경됨. 


package com.cosblog.controller.api;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.cosblog.dto.ResponseDto;
import com.cosblog.model.RoleType;
import com.cosblog.model.User;
import com.cosblog.service.UserService;

@RestController
public class UserApiController {
	
	@Autowired
	private UserService userService;
	
	
	//@Autowired //메서드 파라메터 대신 이렇게 넣어줘도 가능하다.
	//private HttpSession session;
	
	@PostMapping("/auth/joinProc")
	public ResponseDto<Integer> save(@RequestBody User user) {
		System.out.println("UserApiController : save 호출됨");
		
		userService.회원가입(user);
		return new ResponseDto<Integer>(HttpStatus.OK.value(), 1);//자바오브젝트를 JSON으로 변환해서 리턴
	}
	
	//전통적인 로그인 방식, 스프링 시큐리티 이용해서 로그인하면 이 방식 사용하지 않는다.
	/*
	 * @PostMapping("/api/user/login") public ResponseDto<Integer>
	 * login(@RequestBody User user, HttpSession session){
	 * System.out.println("UserApiController:login호출됨"); User principal =
	 * userService.로그인(user); //접근주체
	 * 
	 * if(principal != null) { session.setAttribute("principal", principal); }
	 * 
	 * return new ResponseDto<Integer>(HttpStatus.OK.value(),1);
	 * 
	 * }
	 */
}

 

package com.cosblog.service;



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.cosblog.model.RoleType;
import com.cosblog.model.User;
import com.cosblog.repository.UserRepository;

//스프링이 컴포넌트 스캔을 통해서 bean에 등록을 해줌
@Service
public class UserService {

	@Autowired
	private UserRepository userRepository;
	
	@Autowired
	private BCryptPasswordEncoder encoder;
	
	@Transactional
	public void 회원가입(User user) {
		
		String rawPassword = user.getPassword(); //1234원문
		String encPassword = encoder.encode(rawPassword); //해쉬
		user.setPassword(encPassword);
		user.setRole(RoleType.USER);
		userRepository.save(user);
	}
	
	
	/* 이 로그인도 사용 안 할거라 삭제
	 * @Transactional(readOnly = true)//Select할 때 트랜잭션 시작, 서비스 종료시에 트랜잭션 종료(정합성)
	 * public User 로그인(User user) { return
	 * userRepository.findByUsernameAndPassword(user.getUsername(),
	 * user.getPassword()); }
	 */
}
package com.cosblog.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;


//인증이 안된 사용자들이 출입할 수 있는 경로를 /auth만 허용할 것임
//그냥 주소가 /이면 index.jsp 허용
//static이하에 있는 /js/**, /css/**, /image/**

@Controller
public class UserController {

	@GetMapping("/auth/joinForm")
	public String joinForm() {

		return "user/joinForm";
	}
	
	@GetMapping("/auth/loginForm")
	public String loginForm() {

		return "user/loginForm";
	}
}

스프링부트 강좌 51강(블로그 프로젝트) - XSS와 CSRF

 

XSS공격 은 뭐 대충 alert창을 5만번 띄우는 방식으로 싸이트 마비시키는 거?

 

하이퍼링크 주소를 포인트 적립시켜주는 곳으로 보내는 방식등으로 대충 악용하는 그런 공격도 있다.

(하이퍼링크는 무조건 Get방식임)

방지 방법

1. POST방식

2. Referre검증

3. CSRF Token사용


스프링부트 강좌 52강(블로그 프로젝트) - 스프링 시큐리티 로그인


스프링부트 강좌 53강(블로그 프로젝트) - 글쓰기 완료

 

728x90

'요약' 카테고리의 다른 글

뉴렉처 Spring 강의 요약  (0) 2022.01.10
뉴렉처 스프링 5~17강 요약  (0) 2022.01.01

댓글