Coding Note

SpringBoot) 블로그 프로젝트_8.로그인,암호화, 글등록 본문

SpringBoot/블로그만들기PJ

SpringBoot) 블로그 프로젝트_8.로그인,암호화, 글등록

jinnkim 2022. 3. 18. 03:37

 

1. 해쉬 암호화

https://bamdule.tistory.com/53

 

[Spring Boot] Spring Security 적용하기

Spring Security 란? Spring Security는 스프링 기반의 어플리케이션 보안을 담당하는 프레임워크입니다. Spring Security를 사용하면 사용자 인증, 권한, 보안처리를 간단하지만 강력하게 구현 할 수 있습니

bamdule.tistory.com

 

시큐리티는 비번 암호화되어있어야만 로그인이 된다!

 

 

 

Q1. 해쉬

- 고정 길이의 문자 값으로 변경

 

Q2. 해쉬 장점

- 내용 변경에 따라 해쉬값이 변경되는데 값을 비교해서 변경되었는지 확인할 수 있다.

 

 

- SecurityConfig

//빈 등록: 스프링컨테이너에서 객체를 관리할 수 있게 하는 것
//아래 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());//pwd 비교
	}
	
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.csrf().disable() //csrf 토큰 비활성화(테스트시 걸어두는게 좋음!)
			.authorizeRequests()
				.antMatchers("/", "/auth/**", "/js/**", "/css/**", "/image/**")
				.permitAll()
				.anyRequest()
				.authenticated()
			.and()	
				.formLogin()
				.loginPage("/auth/loginForm")
				.loginProcessingUrl("/auth/loginProc")
				.defaultSuccessUrl("/");//스프링 시큐리티가 해당 주소로 요청오는 로그인을 가로채서 로그인 해준다.
		
	}
}

 

 

- PrincipalDetail

//스프링 시큐리티가 로그인 요청을 가로챠서 로그인을 진행하고 완료가 되면  UserDetails 타입의 오브젝트를
//스프링 시큐리티의 고유한 세션 저장소에 저장을 해준다.
@Getter
public class PrincipalDetail implements UserDetails{
	private User user; //콤포지션 : 객체를 품고 있는 것

	public PrincipalDetail(User user) {
		this.user =user;
	}
 	
	@Override
	public String getPassword() {
		return user.getPassword();
	}

	@Override
	public String getUsername() {
		return user.getUsername();
	}

	//계정이 만료되지 않았는지 리턴함.(true:만료안됨)
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	//계정이 잠겨있지 않았는지 리턴함(true: 잠기지 않음)
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	//비밀전호가 만료되지 않았는지 리턴함(true: 만료 안됨)
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	//계정이 활성화(사용가능)인지 리턴함(true: 활성화)
	@Override
	public boolean isEnabled() {
		return true;
	}

	//계정이 갖고있는 권한 목록을 리턴함(권한이 여러개 있을 수 있어서 루프를 돌아야 하는데 우리는 한개만 리턴)
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
	    
		Collection<GrantedAuthority> collectors = new ArrayList<>();
		collectors.add(()-> { return "ROLE_"+user.getRole();});
		
		return collectors;
	}
}

 

 

- PrincipalDetailService

@Service //Bean 등록
public class PrincipalDetailService implements UserDetailsService{

	@Autowired
	private UserRepository userRepository;
	
	//스프링이 로그인 요청을 가로챌 때, username, password 변수 2개를 가로채는데 
	//password 부분 처리는 알아서 함.
	//username이 DB에 있는지 확인해주면 됨.
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User principal = userRepository.findByUsername(username)
				.orElseThrow(()->{
					return new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다. :"+username);
				}) ;
		return new PrincipalDetail(principal); //시큐리티 세션의 유저 정보가 저장됨.
	}
}

 

 

- UserService

//스프링이 컴포넌트 스캔을 통해서 Bean에 등록 해줌. => Ioc해줌
@Service
public class UserService {
	
	@Autowired
	private UserRepository userRepository;
	
	@Autowired
	private BCryptPasswordEncoder encoder;
	
	@Transactional
	public void 회원가입(User user) {
		String rawPassword = user.getPassword();//1234원문
		String encPasswrod = encoder.encode(rawPassword);//해쉬
		user.setPassword(encPasswrod);
		user.setRole(RoleType.USER);
		userRepository.save(user); 
	}
}

 


2. csrf/xss

XSS

- 자바스크립트 공격

 

Lucy XSS Filter

- XSS(Cross Site Scripting) 공격을 방어하는 Java 라이브러리

 

http://naver.github.io/lucy-xss-filter/kr/

 

Lucy XSS Filter

XSS 공격이 가능한 HTML 요소를 신뢰할 수 있는 코드로 변환하거나 삭제하는 기능을 제공한다. 공격이 가능하지 않은 HTML 요소는 허용을 한다. 4.1. 설정 파일 Lucy-XSS Filter의 필터링 규칙은 화이트리

naver.github.io

 

 

 

CSRF(Cross Site Request Forgery)

- 사이트 간 위조 요청

CSRF(Cross Stie Request Forgery) : 사이트간 요청 위조
웹 애플리케이션 취약점 중 하나로 사용자가 자신의 의지와 무관하게 공격자가 의도한 행동을 하여 특정 웹페이지를 보안에 취약하게 한다거나 수정, 삭제 등의 작업을 하게 만드는 공격방법을 의미한다 - 나무위키

https://sj602.github.io/2018/07/14/what-is-CSRF/

 

[보안] CSRF(Cross Site Request Forgery)란 무엇인가?

CSRF란 무엇인가?12CSRF(Cross Stie Request Forgery) : 사이트간 요청 위조웹 애플리케이션 취약점 중 하나로 사용자가 자신의 의지와 무관하게 공격자가 의도한 행동을 하여 특정 웹페이지를 보안에 취약

sj602.github.io

 


 

3. 로그인 구현

로그인 구현완

 

- loginForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="../layout/header.jsp"%>

<div class="container">

	<form action="/auth/loginProc" method="post">
		<div class="form-group">
			<label for="username">Username</label> 
				<input type="text" name="username" class="form-control" placeholder="Enter username" id="username">
		</div>
		
		<div class="form-group">
			<label for="password">Password</label>
				<input type="password" name="password" class="form-control" placeholder="Enter password" id="password">
		</div>
		<button id="btn-login" class="btn btn-primary">로그인</button>
	</form>
</div>

<%@include file="../layout/footer.jsp"%>

 

- UserRepository

public interface UserRepository  extends JpaRepository<User, Integer>{
	//select * from user where username =1?;
	Optional<User> findByUsername(String username);
}

 

- UserApiController

package com.cos.blog.controller.api;

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

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.cos.blog.dto.ResponseDto;
import com.cos.blog.model.User;
import com.cos.blog.service.UserService;

@RestController
public class UserApiController {
	
	@Autowired
	private UserService userService;

	
	@PostMapping("/auth/joinProc")
	public ResponseDto<Integer> save(@RequestBody User user) { //json 형식을 받을때 @RequestBody 선언함
		System.out.println("UserApiController: save 호출 완료");
		userService.회원가입(user);
		return new ResponseDto<Integer>(HttpStatus.OK.value(), 1);	// 200 : http 전송 성공
	
	} 
}

 

 

 

 


 

4. 글쓰기 구현

- 썸머 노트 사용

 

아래 코드 복붙!

 

 

물론 파일, 사진 업로드도 수월하게 가능!

 

 

- Board

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder//빌더 패턴
@Entity
public class Board {
	

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)//auto_increment
	private int id;
	
	@Column(nullable = false, length = 100)
	private String title;
	
	@Lob//대용량 데이터 사용시
	private String content;//섬머노트 라이브러리 사용 - <html>태그가 섞여서 디자인이됨
	
	
	private int count;//조회수
	
	@ManyToOne(fetch = FetchType.EAGER)//many =board, user= one 즉, 한명이 여러개의 게시물 작성 가능
	@JoinColumn(name="userId")
	private User user;//DB는 오브젝트를 저장할 수 없다. FK, 자바는 오브젝트를 저장할 수 있다. - ORM을 사용시 오브젝트 사용 가능
	
	@OneToMany(mappedBy="board", fetch = FetchType.EAGER)//mappedBy 연관관계의 주인이 아니다(난 FK가 아니예요!) DB에 컬럼을 만들지 마세요!
	private List<Reply>  reply;

	
	@CreationTimestamp
	private Timestamp createDate;

}

 

- BoardRepository

public interface BoardRepository  extends JpaRepository<Board, Integer>{
}

 

- BoardController

@Controller
public class BoardController {
	
	@Autowired
	private BoardService boardService;

	@GetMapping({"", "/"}) 
	public String index(Model model) {
		 model.addAttribute("boards", boardService.글목록());
		return "index";	//viewResolver 작동!
	}
	//USER 권한 필요
	@GetMapping("/board/saveForm")
	public String saveForm() {
		return "board/saveForm";
	}
}

 

- BoardService

//스프링이 컴포넌트 스캔을 통해서 Bean에 등록 해줌. => Ioc해줌
@Service
public class BoardService {
	
	@Autowired
	private BoardRepository boardRepository;
	
	
	@Transactional
	public void 글쓰기(Board board, User user) {//title, content
		board.setCount(0);
		board.setUser(user);
		boardRepository.save(board); 
	}
	
	public List<Board> 글목록(){
		return boardRepository.findAll();
	}
}

 

 

- BoardApiController

@RestController
public class BoardApiController {
	
	@Autowired
	private BoardService boardService;

	
	@PostMapping("/api/board")
	public ResponseDto<Integer> save(@RequestBody Board board, @AuthenticationPrincipal PrincipalDetail principal) { 
		boardService.글쓰기(board, principal.getUser());
		return new ResponseDto<Integer>(HttpStatus.OK.value(), 1);	// 200 : http 전송 성공
	
	} 
}

 


 

3일 차 스터디 완

 

 

 

Comments