Java

[Spring Boot] Spring Security 패스워드 암호화 방법

cob 2022. 10. 26. 13:11

 

Spring Cecurity

 

Spring Security 란?
간단히 말하면 Spring의 보안(인증과 권한, 인가 등)을 담당하는 서블릿 필터의 집합이다.
서블릿 필터는 서블릿 실행 전에 실행되는 클래스들로 토큰 인증을 위해 컨트롤러 메서드의 첫 부분마다 인증 코드를 작성하는 고민을 해결하기 위해 서블릿 필터를 사용한다.

 

 


1. Spring Security 라이브러리 설치

implementation 'org.springframework.boot:spring-boot-starter-security'

 

 

 

 


2. 패스워드 암호화 사용 방법

* Source Code *
https://github.com/kangilbin/TodoList/blob/master/src/main/java/com/example/demo/controller/UserController.java

 

ackage com.example.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.dto.ResponseDTO;
import com.example.demo.dto.UserDTO;
import com.example.demo.model.UserEntity;
import com.example.demo.security.TokenProvider;
import com.example.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequestMapping("auth")
public class UserController {
	
	@Autowired
	private UserService userService;
	
	@Autowired
	private TokenProvider tokenProvider;
	
	// Bean으로 작성해도 됨
	private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
	
	@PostMapping("/signup")
	public ResponseEntity<?> registerUser(@RequestBody UserDTO userDTO) {
		try { 
			// 요청을 이용해 저장할 사용자 만들기
			UserEntity user = UserEntity.builder()
					.email(userDTO.getEmail())
					.username(userDTO.getUsername())
                    			// 패스워드를 인코딩하고 인코딩 값을 저장
					.password(passwordEncoder.encode(userDTO.getPassword()))
					.build();
			
			// 서비스를 이용해 리포지터리에 사용자 저장
			UserEntity registeredUser = userService.create(user);
			UserDTO responseUserDTO = UserDTO.builder()
					.email(registeredUser.getEmail())
					.id(registeredUser.getId())
					.username(registeredUser.getUsername())
					.build();
					
			return ResponseEntity.ok().body(responseUserDTO);
		} catch (Exception e) {
			// 사용자 정보는 항상 하나이므로 리스트로 만들어야 하는 ResponseDTO를 사용하지 않고 그냥 UserDTO 리턴
			
			ResponseDTO responseDTO = ResponseDTO.builder().error(e.getMessage()).build();
			return ResponseEntity.badRequest().body(responseDTO);
		}
	}
	
	@PostMapping("/signin")
	public ResponseEntity<?> authenticate(@RequestBody UserDTO userDTO) {
    	// 로그인 시 패스워드 검증
		UserEntity user = userService.getByCredentials(userDTO.getEmail(), userDTO.getPassword(), passwordEncoder);
		
		if(user != null) {
			// 토큰 생성
			final String token = tokenProvider.create(user);
			final UserDTO responseUserDTO = UserDTO.builder()
					.email(user.getEmail())
					.id(user.getId())
					.token(token)
					.build();
			return ResponseEntity.ok().body(responseUserDTO);
		} else {
			ResponseDTO responseDTO = ResponseDTO.builder()
					.error("Login faild.")
					.build();
			return ResponseEntity.badRequest().body(responseDTO);
		}
	}
}
  • BCryptPasswordEncoder :  같은 값을 인코딩하더라도 할 때마다 값이 다르고, 패스워드에 랜덤 하게 의미 없는 값을 붙여 생성한다. (이런 의미 없는 값을 보안 용어로 Salt라고 하고 Salt를 붙여 인코딩하는 것을 Salting이라고 한다.)
  • encode() : BCryptPasswordEncoder의 encode() 메서드를 통해 인코딩한다.
  • getByCredentials : 로그인 시 패스워드 검증을 위한 메서드 생성

 

 

 


3. 인코딩된 패스워드  비교 방법

로그인 시 패스워드 검증을 위한 메서드 생성
* Source Code *
https://github.com/kangilbin/TodoList/blob/master/src/main/java/com/example/demo/service/UserService.java

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.example.demo.model.UserEntity;
import com.example.demo.persistence.UserRepository;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class UserService {
	/* 기존 코드 */
	
	public UserEntity getByCredentials(final String email, final String password, final PasswordEncoder encoder) {
		final UserEntity originalUser = userRepository.findByEmail(email);
		
		// matches 메서드를 이용해 패스워드가 같은지 확인
		if(originalUser != null && encoder.matches(password, originalUser.getPassword())) {
			return originalUser;
		}
		return null;
	}
}
  • matches() : BCryptPasswordEncoder는 어떤 두 값의 일치 여부를 알려주는 matches() 메서드를 제공한다.
    이 메서드는 Salt(의미 없는 값)를 고려해 두 값을 비교해 일치 여부를 알려준다.

 

반응형