Bcrypt 란?
Bcypt는 브루스 슈나이어가 설계한 키(key) 방식의 대칭형 블록 암호에 기반을 둔 암호화 해시 함수다. Niels Provos 와 David Mazières가 설계했다. Bcrypt는 레인보우 테이블 공격을 방지하기 위해 단방향 암호화에 솔팅과 키 스트레칭을 적용한 대표적인 예이며 복호화가 불가하다. 이를 활용하여 비밀번호 암호화를 해볼 예정이다 !
1. Spring Security 의존성 주입
우선 비밀번호 암호화에 사용되는 PasswordEncoder를 사용하기 위해서는 Spring Security 의존성을 주입해줘야 한다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
2. Config 설정
Spring Security를 사용하여 웹 보안을 구성하는 설정 클래스인 SecurityConfig이다. Config 객체는 WebSecurityConfigurerAdapter를 상속받아서 configure()를 구현한다.
@EnableWebSecurity 어노테이션은 Spring Security를 활성화하는 역할을 한다.
암호화 방식으로 위에서 말한 BCrypt를 이용하기 위해 BCrypt라는 해시 함수를 이용하여 패스워드를 암호화하는 구현체인BCryptPasswordEncoder를 적용했다.
package com.gld.model.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().disable()
.csrf().disable()
.formLogin().disable()
.headers().frameOptions().disable();
}
}
3. 회원가입/로그인 구현
회원가입 :
passwordEncoder.encode() 메서드는 주어진 문자열을 암호화하여 반환하는 메서드이다. 이 메서드를 사용하여 사용자의 비밀번호를 암호화할 수 있다.
로그인 :
passwordEncoder.matches() 메서드는 주어진 원본 문자열과 암호화된 문자열을 비교하여 일치 여부를 확인하는 메서드이다. 이 메서드를 사용하여 사용자가 제공한 비밀번호와 저장된 암호화된 비밀번호를 비교할 수 있다.
@Controller
package com.gld.model.controller;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.gld.model.biz.LoginBiz;
import com.gld.model.dto.UserDto;
@Controller
@RequestMapping("/login")
public class LoginController {
@Autowired
private LoginBiz loginBiz;
@GetMapping("/login")
public String moveToLogin() {
System.out.println("go to loginform");
return "loginform";
}
@GetMapping("/join")
public String moveToJoin() {
System.out.println("go to joinform");
return "joinform";
}
@GetMapping("/find")
public String moveToFind() {
System.out.println("go to findform");
return "findform";
}
@PostMapping("/logincheck")
public String login(String userId, String userPw, HttpSession session,Model model) {
System.out.println("logincheck");
System.out.println(userId);
UserDto user = loginBiz.validationLogin(userId,userPw);
if (user!=null) {
session.setAttribute("user", user);
return "redirect:/challenge/main";
} else {
model.addAttribute("error", "아이디 또는 비밀번호가 일치하지 않습니다.");
return "loginform";
}
}
@PostMapping("/joincheck")
public String join(UserDto dto, RedirectAttributes redirectAttributes) {
System.out.println("joincheck");
loginBiz.insert(dto);
System.out.println("join");
redirectAttributes.addFlashAttribute("message","회원가입이 완료되었습니다. 로그인 해주세요.");
return "redirect:/login/login";
}
@GetMapping("/logout")
public String logout(HttpSession session) {
session.removeAttribute("user");
return "redirect:/";
}
}
@Service
package com.gld.model.biz;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.gld.model.dto.UserDto;
import com.gld.model.mapper.UserMapper;
import com.gld.model.repository.UserRepository;
@Service
@Transactional
public class LoginBiz{
private UserRepository userRepository;
private PasswordEncoder passwordEncoder;
private UserMapper usermapper;
public LoginBiz(UserRepository userRepository, PasswordEncoder passwordEncoder, UserMapper usermapper) {
super();
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.usermapper = usermapper;
}
public UserDto findByUserId(String userId) {
UserDto user = userRepository.findByUserId(userId);
return user;
}
public int changePw(UserDto user, String userPw) {
return usermapper.updatePw(user.getId(), passwordEncoder.encode(userPw));
}
public void insert(UserDto user) {
String encodedPassword = passwordEncoder.encode(user.getUserPw());
user.setUserPw(encodedPassword);
userRepository.save(user);
}
public UserDto validationLogin(String userId,String userPw) {
UserDto user = userRepository.findByUserId(userId);
if(user==null) {
return null;
}
else if(!passwordEncoder.matches(userPw, user.getUserPw())) {
return null;
}else {
return user;
}
}
}
'Spring' 카테고리의 다른 글
[Spring + JPA] No Property Found for Type Exception 에러 해결 (2) | 2023.06.03 |
---|---|
[Spring + Jpa] 테이블 Join & 문제해결 (0) | 2023.05.15 |
[Spring + Jpa] 로그인 구현하기 & 문제해결 (0) | 2023.04.29 |