You are looking for information, articles, knowledge about the topic nail salons open on sunday near me 스프링 부트 암호화 on Google, you do not find the information you need! Here are the best content compiled and compiled by the https://chewathai27.com/to team, along with other related topics such as: 스프링 부트 암호화 Spring boot 비밀번호 암호화, Spring-security 비밀번호 암호화, Spring boot 암호화 복호화, 스프링부트 비밀번호 체크, Spring 암호화 복호화, Spring 로그인 암호화, Spring boot Security 비밀번호 체크, BCryptPasswordEncoder
[Spring Boot] Spring Security 적용하기 – 암호화
- Article author: hou27.tistory.com
- Reviews from users: 10945 Ratings
- Top rated: 3.9
- Lowest rated: 1
- Summary of article content: Articles about [Spring Boot] Spring Security 적용하기 – 암호화 [Spring Boot] Spring Security 적용하기 – 암호화. hou27 2022. 5. 20. 01:12. 프로젝트를 진행하면서 사용자 시스템을 구축한다면 필연적으로 인증 로직도 구현해야 … …
- Most searched keywords: Whether you are looking for [Spring Boot] Spring Security 적용하기 – 암호화 [Spring Boot] Spring Security 적용하기 – 암호화. hou27 2022. 5. 20. 01:12. 프로젝트를 진행하면서 사용자 시스템을 구축한다면 필연적으로 인증 로직도 구현해야 … 프로젝트를 진행하면서 사용자 시스템을 구축한다면 필연적으로 인증 로직도 구현해야한다. 이 과정에서 만약 사용자의 비밀번호를 평문(Plain Text)으로 저장한다면, 심각한 보안상 문제를 초래하게 된다. 그렇기..개인 공부 내용을 기록한 블로그로, 정보가 부정확할 수 있습니다.
- Table of Contents:
Spring Security
1 의존성 주입
2 설정
PasswordEncoder
Config file
User Entity
User Service
User Controller
Test Code
Postman
스프링 시큐리티 이용하여 패스워드 암호화 적용하기 | 기록하는개발자 | DevAndy
- Article author: youngjinmo.github.io
- Reviews from users: 49243 Ratings
- Top rated: 3.9
- Lowest rated: 1
- Summary of article content: Articles about 스프링 시큐리티 이용하여 패스워드 암호화 적용하기 | 기록하는개발자 | DevAndy 우선 비밀번호 암호화에 사용되는 PasswordEncoder 를 사용하기 위해서는 Spring Security 의존성을 주입해줘야 한다. Spring-Boot-Starter-Security … …
- Most searched keywords: Whether you are looking for 스프링 시큐리티 이용하여 패스워드 암호화 적용하기 | 기록하는개발자 | DevAndy 우선 비밀번호 암호화에 사용되는 PasswordEncoder 를 사용하기 위해서는 Spring Security 의존성을 주입해줘야 한다. Spring-Boot-Starter-Security … tutorial,springsecurity이 포스팅은 2년전 처음에 작성했던 글을 수정하는 글이다. 스프링 시큐리티를 공부하고 적용한게 아니라 프로젝트중에 빠르게 개발에 적용해본 경험을 기반으로 간단한 튜토리얼을 위한 글임을 서두에 밝힌다. ToC 왜 암호화가 필요한가 Spring Security 의존성 주입 Config 설정 테스트 코드 작성 회원가입/로그인 구현 왜 암호화가 필요한가패스
- Table of Contents:
왜 암호화가 필요한가
Spring Security 의존성 주입
Config 설정
테스트 코드 작성
회원가입로그인 구현
Spring Boot) BCryptPasswordEncoder 사용해서 사용자 비밀번호 암호화하기
- Article author: seungyooon.tistory.com
- Reviews from users: 44584 Ratings
- Top rated: 4.7
- Lowest rated: 1
- Summary of article content: Articles about Spring Boot) BCryptPasswordEncoder 사용해서 사용자 비밀번호 암호화하기 티스토리 · 1. 의존성 추가 · 2. SpringSecurity.java · 3. 사용자 엔티티 저장하는 서비스단에서 encoder 사용하기 · 4. 암호화된 패스워드 일치 여부 확인 … …
- Most searched keywords: Whether you are looking for Spring Boot) BCryptPasswordEncoder 사용해서 사용자 비밀번호 암호화하기 티스토리 · 1. 의존성 추가 · 2. SpringSecurity.java · 3. 사용자 엔티티 저장하는 서비스단에서 encoder 사용하기 · 4. 암호화된 패스워드 일치 여부 확인 … 1. 의존성 추가 build.gradle 파일에 의존성을 추가해준다. spring-boot-starter-security dependencies { … implementation ‘org.springframework.boot:spring-boot-starter-security’ … } 2. SpringSecurity..
- Table of Contents:
1 의존성 추가
2 SpringSecurityjava
3 사용자 엔티티 저장하는 서비스단에서 encoder 사용하기
4 암호화된 패스워드 일치 여부 확인하기
태그
‘웹 개발Spring Boot’ Related Articles
티스토리툴바
[Spring Boot] 스프링 부트에서 비밀번호 암호화하기 :: Gyun’s 개발일지
- Article author: devlog-wjdrbs96.tistory.com
- Reviews from users: 15001 Ratings
- Top rated: 4.8
- Lowest rated: 1
- Summary of article content: Articles about [Spring Boot] 스프링 부트에서 비밀번호 암호화하기 :: Gyun’s 개발일지 Spring Security에서는 비밀번호를 안전하게 저장할 수 있도록 비밀번호의 단방향 암호화를 지원하는 PasswordEncoder 인터페이스와 구현체들을 제공한다. …
- Most searched keywords: Whether you are looking for [Spring Boot] 스프링 부트에서 비밀번호 암호화하기 :: Gyun’s 개발일지 Spring Security에서는 비밀번호를 안전하게 저장할 수 있도록 비밀번호의 단방향 암호화를 지원하는 PasswordEncoder 인터페이스와 구현체들을 제공한다. 이번 글에서는 비밀번호 암호화 개념에 대해 정리하고 스프링부트를 이용해서 비밀번호 암호화를 하여 DB에 저장하는 방법에 대해 정리해보려 한다. 단방향 해시 함수 보통 비밀번호를 저장하는 방법은 2가지 정도..개발자를 꿈꾸는 대학생
- Table of Contents:
Reference
TAG
관련글 관련글 더보기
인기포스트
티스토리툴바
Springboot에서 BCrypt 암호화(해시화) 구현하기 feat. 시큐리티 인증 없이 BCrypt 사용하기
- Article author: bbubbush.tistory.com
- Reviews from users: 4841 Ratings
- Top rated: 4.2
- Lowest rated: 1
- Summary of article content: Articles about Springboot에서 BCrypt 암호화(해시화) 구현하기 feat. 시큐리티 인증 없이 BCrypt 사용하기 우선 Spring boot + Spring security가 필요하다. maven이나 gradle을 통해 준비하면 된다. 필자는 gradle을 통해 준비했다. build.gradle … 생략 … …
- Most searched keywords: Whether you are looking for Springboot에서 BCrypt 암호화(해시화) 구현하기 feat. 시큐리티 인증 없이 BCrypt 사용하기 우선 Spring boot + Spring security가 필요하다. maven이나 gradle을 통해 준비하면 된다. 필자는 gradle을 통해 준비했다. build.gradle … 생략 … 들어가며 스프링 시큐리티를 공부하다가 만나게 된 BCrypt 알고리즘은 어떤 특징이 있나 살펴보기 위해 포스팅을 작성했다. 또한 예전에 기술면접에서 BCrypt에 대해 잘못된 대답을 했던 부끄러운 기억이 생각나서..모두에 컴에서도 되길 바랍니다 : )
- Table of Contents:
Springboot에서 BCrypt 암호화(해시화) 구현하기 feat 시큐리티 인증 없이 BCrypt 사용하기
들어가며
BCrypt 암호화란
코드로 만들어보기
마치며
21. [SpringBoot] 간단하게 비밀번호 암호화해보기
- Article author: dkyou.tistory.com
- Reviews from users: 42936 Ratings
- Top rated: 4.6
- Lowest rated: 1
- Summary of article content: Articles about 21. [SpringBoot] 간단하게 비밀번호 암호화해보기 spring-security 의존성이 필요하다.
org.springframework.boot … … - Most searched keywords: Whether you are looking for 21. [SpringBoot] 간단하게 비밀번호 암호화해보기 spring-security 의존성이 필요하다.
org.springframework.boot … 1. 서론 – 회원가입 로직을 구현하던 중 평문 비밀번호를 그대로 저장하는 것을 알게 되었고, Spring security 를 이용하여 암호화하는 방법을 학습해야겠다고 생각했다. 2. 본론 의외로 간단하다. spring-securi.. - Table of Contents:
태그
관련글
댓글2
공지사항
최근글
인기글
최근댓글
태그
전체 방문자
티스토리툴바
Springboot 웹프로젝트 (17) – 암복호화
- Article author: velog.io
- Reviews from users: 45298 Ratings
- Top rated: 4.2
- Lowest rated: 1
- Summary of article content: Articles about Springboot 웹프로젝트 (17) – 암복호화 복호화가 가능한 암호화는 AES를 사용한다. 복호화가 불가능한 알고리즘은 MD5, … Spring. 목록 보기. 17/28. 암복호화에 대한 방법을 알아본다. …
- Most searched keywords: Whether you are looking for Springboot 웹프로젝트 (17) – 암복호화 복호화가 가능한 암호화는 AES를 사용한다. 복호화가 불가능한 알고리즘은 MD5, … Spring. 목록 보기. 17/28. 암복호화에 대한 방법을 알아본다. 암복호화에 대한 방법을 알아본다. 복호화가 가능한 암호화는 AES를 사용한다. 복호화가 불가능한 알고리즘은 MD5, SHA-256, SHA3-256을 사용한다. 이러한 알고리즘을 사용한 암호화를 위해서 MessageDigest를 사용한다. 로그인과 같이 민감한 정보를
- Table of Contents:
Spring
CryptUtils 소스
키생성
암호화복호화
RSAUtils를 사용한 암호화복호화
의존성 추가
키 쌍 생성
키 저장
클라이언트에서 RSA 암호화
JSEncrypt 라이브러리 로드
암호화
복호화
Spring – Spring-security이용한 비밀번호 암호화, 복호화
- Article author: kdg-is.tistory.com
- Reviews from users: 5683 Ratings
- Top rated: 3.2
- Lowest rated: 1
- Summary of article content: Articles about Spring – Spring-security이용한 비밀번호 암호화, 복호화 암호화를 하기 위해서는 Spring-security에서 제공하는 BCryptPasswordEncoder 클래스를 이용합니다. BCryptPasswordEncoder 클래스 객체를 생성하고 객체 … …
- Most searched keywords: Whether you are looking for Spring – Spring-security이용한 비밀번호 암호화, 복호화 암호화를 하기 위해서는 Spring-security에서 제공하는 BCryptPasswordEncoder 클래스를 이용합니다. BCryptPasswordEncoder 클래스 객체를 생성하고 객체 … Step 01 – Spring-security 모듈 추가 암호화 하기 위해서는 Spring-security에서 제공하는 클래스가 필요합니다. Spring-security 라이브러리를 pom.xml에 추가하고 빌딩,업데이트를 완료합니다. 1 2 3 4 5..
- Table of Contents:
티스토리 뷰
티스토리툴바
[Spring security password Encoding] Spring boot 패스워드 간단 암호화
- Article author: keepbang.tistory.com
- Reviews from users: 41276 Ratings
- Top rated: 4.1
- Lowest rated: 1
- Summary of article content: Articles about [Spring security password Encoding] Spring boot 패스워드 간단 암호화 spring boot에서 패스워드 암호화를 하기 위해 spring security 를 사용해봤다. 먼저 가장 기본인 pom.xml 에 dependency를 추가해준다 … …
- Most searched keywords: Whether you are looking for [Spring security password Encoding] Spring boot 패스워드 간단 암호화 spring boot에서 패스워드 암호화를 하기 위해 spring security 를 사용해봤다. 먼저 가장 기본인 pom.xml 에 dependency를 추가해준다 … spring boot에서 패스워드 암호화를 하기 위해 spring security 를 사용해봤다. 먼저 가장 기본인 pom.xml 에 dependency를 추가해준다 org.springframework.security spring-security-core 5.2.1.RELEASE 그냥 패..
- Table of Contents:
개발 블로그
[Spring security password Encoding] Spring boot 패스워드 간단 암호화 본문See more articles in the same category here: 607+ tips for you.
[Spring Boot] Spring Security 적용하기
프로젝트를 진행하면서 사용자 시스템을 구축한다면 필연적으로 인증 로직도 구현해야한다.
이 과정에서 만약 사용자의 비밀번호를 평문(Plain Text)으로 저장한다면, 심각한 보안상 문제를 초래하게 된다.
그렇기 때문에 반드시 암호화하여 관리해야만 한다.
암호화? 해싱?
해싱과 암호화 두 단어가 이런 류의 포스트에서 마구 등장해 헷갈릴 수 있다.
해싱(Hashing)과 암호화(Encryption) 모두 데이터의 보안을 위해 사용하는 것이지만,
가장 큰 차이점은
Hash는 단방향 암호화 기법이고
Encryption는 양방향 암호화 기법이라는 것이다.
때문에 비밀번호를 저장할 때는 행여나 탈취될 가능성을 염두에 두어
평문을 암호화하는 것은 가능하지만 다시 평문으로 복호화하는 것은
불가능한 단방향 암호화 방식을 사용한다.
같은 데이터를 같은 해시 알고리즘을 통해 암호화할 경우 항상 같은 결과가 나오기 때문에
복호화가 불가능해도 사용자 인증은 가능하다.
Spring Security
spring-security
공식 문서를 살펴보면
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
위와 같이 Spring 기반의 Application의 보안을 위한 Spring의 프레임워크라고 설명되어 있다.
보안(인증과 권한) 관련 처리를 Filter 기반으로 처리하며,
기본적으로 Session – Cookie 방식으로 인증 정보를 처리한다.
Spring에서 password를 암호화하기 위해,
Spring Boot 프로젝트에 Spring Security를 적용하기 위한 포스트이므로 설명은 간단히 마치고
Gradle 프로젝트로 진행해보도록 하겠다.
1. 의존성 주입
build.gradle에
위와 같이 dependency를 추가해준다.
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security/2.6.7
2. 설정
Spring Security를 사용하기 위해선
PasswordEncoder
public interface PasswordEncoder { /** * Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or * greater hash combined with an 8-byte or greater randomly generated salt. */ String encode(CharSequence rawPassword); /** * Verify the encoded password obtained from storage matches the submitted raw * password after it too is encoded. Returns true if the passwords match, false if * they do not. The stored password itself is never decoded. * @param rawPassword the raw password to encode and match * @param encodedPassword the encoded password from storage to compare with * @return true if the raw password, after encoding, matches the encoded password from * storage */ boolean matches(CharSequence rawPassword, String encodedPassword); /** * Returns true if the encoded password should be encoded again for better security, * else false. The default implementation always returns false. * @param encodedPassword the encoded password to check * @return true if the encoded password should be encoded again for better security, * else false. */ default boolean upgradeEncoding(String encodedPassword) { return false; } }
위 PasswordEncoder라는 인터페이스를 이용해야한다.
PasswordEncoder란
모든 Spring Security의 비밀번호 Encoder는 위 PasswordEncoder 인터페이스를 구현한다.
이 인터페이스에는 평문인 비밀번호를 암호화하는 encode(),
평문 비밀번호를 인코딩(암호화)된 비밀번호와 비교하는 matches()가 존재한다.
필자가 사용할 구현체는
BCryptPasswordEncoder
클래스로, 설명은 아래와 같다.
Implementation of PasswordEncoder that uses the BCrypt strong hashing function. Clients can optionally supply a “version” ($2a, $2b, $2y) and a “strength” (a.k.a. log rounds in BCrypt) and a SecureRandom instance. The larger the strength parameter the more work will have to be done (exponentially) to hash the passwords. The default value is 10.
Author: Dave Syer
BCrypt의 강력한 해시 기능을 사용하기 위해 이제 설정을 해주어야 한다.
Config file
/** * Spring Security 사용을 위한 Configuration Class를 작성하기 위해서 * WebSecurityConfigurerAdapter를 상속하여 클래스를 생성하고 * @Configuration 애노테이션 대신 @EnableWebSecurity 애노테이션을 추가한다. */ @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * PasswordEncoder를 Bean으로 등록 */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * 인증 or 인가에 대한 설정 */ @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() // post 방식으로 값을 전송할 때 token을 사용해야하는 보안 설정을 해제 .authorizeRequests() .antMatchers(“/”, “/user/signUp”).permitAll() .anyRequest().authenticated(); } }
PasswordEncoder interface의 구현체 중 BCrypt 알고리즘을 사용한 구현체인
BCryptPasswordEncoder
를 Bean으로 등록해주고,
추가적인 설정을 해주었다.
Spring Security의 각종 설정은 HttpSecurity로 한다.
antMatchers
.antMatchers(“/”, “/home”).permitAll()
특정 URL을 설정하며,
여기서 뒤에 붙은 permitAll()은 antMatchers에서 설정한 URL의 접근을 인증없이 허용한다는 뜻이다.
hasAnyRole
antMatchers(“/admin/**”).hasAnyRole(“ADMIN”)
antMatchers로 설정한 URL들은 ADMIN 권한의 유저만 허용한다는 뜻이다.
anyRequest
anyRequest().authenticated()
이 옵션은 모든 리소스가 인증을 해야만 접근이 허용된다는 뜻이다.
보통 이 옵션 이전에 다른 설정을 완료하고 나머지에 위 옵션을 적용한다.
이 외의 많은 옵션들은 아래 포스트에서 참고했다.
spring-security-options
User Entity
@Entity @Getter @Setter @NoArgsConstructor @ToString public class User extends CoreEntity { … /** * 비밀번호를 암호화 * @param passwordEncoder 암호화 할 인코더 클래스 * @return 변경된 유저 Entity */ public User hashPassword(PasswordEncoder passwordEncoder) { this.password = passwordEncoder.encode(this.password); return this; } /** * 비밀번호 확인 * @param plainPassword 암호화 이전의 비밀번호 * @param passwordEncoder 암호화에 사용된 클래스 * @return true | false */ public boolean checkPassword(String plainPassword, PasswordEncoder passwordEncoder) { return passwordEncoder.matches(plainPassword, this.password); } }
다음으로 User Entity에 PasswordEncoder를 사용하여 인코딩하고,
비밀번호가 맞는지 확인하는 메소드를 구현해주었다.
User Service
@Service @RequiredArgsConstructor @Transactional public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final PasswordEncoder bCryptPasswordEncoder; @Override public User signUp(UserSignUpRequest signUpReq) throws Exception { if(this.isEmailExist(signUpReq.getEmail())) { throw new Exception(“Your Mail already Exist.”); } User newUser = signUpReq.toUserEntity(); newUser.hashPassword(bCryptPasswordEncoder); return userRepository.save(newUser); } … /** * 이메일 중복 여부를 확인 * * @param email * @return true | false */ private boolean isEmailExist(String email) { Optional
byEmail = userRepository.findByEmail(email); return !byEmail.isEmpty(); } } 그 후 User Service에서 회원가입 진행 시(signUp 메서드),
생성자를 통해 주입받은 PasswordEncoder를 이용하여 비밀번호를 해싱한 후,
저장하도록 하였다.
User Controller
@RestController @RequestMapping(“/user”) @RequiredArgsConstructor public class UserController { private final UserService userService; @PostMapping(“/signUp”) public User signUp(@ModelAttribute @Validated UserSignUpRequest signUpReq) throws Exception { return userService.signUp(signUpReq); } … }
컨트롤러에선 /user/signUp에 POST 요청이 들어오면 회원가입을 진행할 수 있도록 해주었다.
보통의 경우라면 User Entity를 반환하지 않지만 지금은 테스트를 위해 위와 같이 작성하였다.
Test Code
@SpringBootTest @Transactional @DisplayName(“User Service Test”) class UserServiceTest { private static final String EMAIL = “[email protected]”; private static final String PASSWORD = “12345”; private static final String NAME = “김정호”; private UserService userService; @Autowired private UserRepository userRepository; @Autowired private PasswordEncoder bCryptPasswordEncoder; @BeforeEach public void beforeEach() { AppConfig appConfig = new AppConfig(userRepository, bCryptPasswordEncoder); userService = appConfig.userService(); } … @Test @DisplayName(“비밀번호는 암호화되어야 한다.”) void hashPassword() throws Exception { // given UserSignUpRequest user = createSignUpRequest(); // when User newUser = userService.signUp(user); // then System.out.println(“newUser pw = ” + newUser.getPassword()); assertThat(newUser.getPassword()).isNotEqualTo(PASSWORD); } … private UserSignUpRequest createSignUpRequest() { return UserSignUpRequest.builder() .email(EMAIL) .password(PASSWORD) .name(NAME) .build(); } }
Test 코드를 작성하여 확인을 진행하겠다.
테스트는 성공하였으며,
위 사진에 표시한 출력문을 살펴보면 비밀번호가 잘 해싱되었음을 확인할 수 있다.
이번엔 Postman을 통해 확인해보겠다.
Postman
Spring Security 적용 전에는,
위와 같이 비밀번호가 평문 그대로 DB에 저장되었었다.
Spring Security를 적용한 후 테스트해보니,
위와 같이 반환받은 User Entity에 password가 암호화되어있었으며
DB에도 역시나 암호화된 값이 들어있었다.
성공!!
참고자료
양방향, 단방향 암호화
spring-security
spring-security-password-handling
spring-security-options
Spring Boot) BCryptPasswordEncoder 사용해서 사용자 비밀번호 암호화하기
728×90
1. 의존성 추가
build.gradle 파일에 의존성을 추가해준다.
spring-boot-starter-security
dependencies { … implementation ‘org.springframework.boot:spring-boot-starter-security’ … }
2. SpringSecurity.java
이걸 추가하면 웹 처음 화면에 기본적으로 제공해주는 로그인 창이 뜨는데 이건 사용하지 않을 것이므로 컨피그를 아래와 같이 추가해준다.
package com.kpmg.lighthouse.tpplatform.tpplatform; 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 SpringSecurity extends WebSecurityConfigurerAdapter{ @Override protected void configure(HttpSecurity http) throws Exception{ http .cors().disable() //cors방지 .csrf().disable() //csrf방지 .formLogin().disable() //기본 로그인 페이지 없애기 .headers().frameOptions().disable(); } @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } }
3. 사용자 엔티티 저장하는 서비스단에서 encoder 사용하기
아래 예시와 같이 사용하면 된다.
이때 User 테이블과 JPA레포지토리를 상속한 userRepository가 있다는 것을 가정한다.
@Service(“userInfoService”) public class UserInfoService { @Autowired UserRepository userRepository; @Autowired PasswordEncoder passwordEncoder; @Transactional public void encryptPassword(String userPw){ User user = new User(); String enPw = passwordEncoder.encode(userPw); user.setPw(enPw); userRepository.save(user); } }
이렇게 하고 h2 console로 데이터를 확인해보면 raw password는 볼 수없고 암호화된 패스워드만 보인다.
4. 암호화된 패스워드 일치 여부 확인하기
내가 구글링해본 결과 BCrypPasswordEncoder는 따로 decode 함수를 제공하는 것 같지 않다. 그냥 raw 데이터와 암호화된 데이터가 일치하는지에 대한 여부를 반환하는 matches 함수를 사용해야 한다.
로그인 처리를 위해 사용자가 입력한 패스워드와 사용자 디비에 저장된 암호화된 패스워드가 일치하는지 확인하기 위한 함수를 아래와 같이 사용한다.
@Autowired PasswordEncoder passwordEncoder; public Boolean login(String id, String rawPw){ // 이 함수는 user repository에 따로 구현이 되어 있어야 한다. User user = userRepository.findById(id); if(passwordEncoder.matches(rawPw, user.getUserPw())){ return true; } else{ return false; } }
728×90
[Spring Boot] 스프링 부트에서 비밀번호 암호화하기
728×90
반응형
이번 글에서는 비밀번호 암호화 개념에 대해 정리하고 스프링부트를 이용해서 비밀번호 암호화를 하여 DB에 저장하는 방법에 대해 정리해보려 한다.
단방향 해시 함수
보통 비밀번호를 저장하는 방법은 2가지 정도가 있다.
단순 텍스트(plain text)
단방향 해시 함수(one-way hash function)의 다이제스트(digest)
단순텍스트로 패스워드를 저장하는 것은 범죄를 저지르는 것이나 다름없다. DB가 해킹당하는 순간, 비밀번호를 그대로 해커들에게 넘겨주게 되기 때문이다. 그러면 DB를 해킹당하지 않으면 되지 않나? 라고 할 수 있다. 물론 애초에 DB를 해킹당하지 않도록 보안에 신경써야 겠지만 해야겠지만, 다양한 변수의 상황들이 존재하기 때문에 언제든 해킹 당할 가능성이 존재한다. 따라서 해시함수를 사용하여야 한다.
단방향 해시 함수는 수학적인 연산을 통해 원본 메세지를 변환하여 암호화된 메세지인 다이제스트를 생성한다. 원본 메세지를 알면 암호화된 메세지를 구하기는 쉽지만 암호화된 원본 메세지를 구할 수 없어야 하며 이를 ‘단방향성’이라고 한다. 단방향 암호화는 복호화할 수 없는 암호화 방법이다. 복호화란 문자열을 다시 원래 문자열로 돌려놓는 것을 의미한다.
단방향 암호화를 사용하는 이유에 대해 생각해보면, 홈페이지 비밀번호같은 경우는 복호화할 이유가 없고, 운영자들이 사용자들의 비밀번호를 알아야할 이유가 없다. 그래서 대부분의 사이트에서 비밀번호를 잃어버렸을 때 찾으려면 비밀번호를 알려주는 것이 아니라 다시 설정해야 할 것이다. 따라서 DB에 암호화된 비밀번호를 저장해놓고, 나중에 사용자가 로그인할 때 다시 입력받은 비밀번호를 같은 알고리즘으로 암호화해서 DB에 저장된 문자열과 비교하여 인증절차를 거치게 된다.
이제 해시함수에 대해 예시를 보면서 좀 더 자세히 알아보자. 만약 사용자의 비밀번호가 “hunter” 라면 문자열을 암호화할 때 흔히 사용하는 해시 알고리즘인 SHA-256으로 인코딩하면 아래와 같은 값을 얻을 수 있다.
f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7
이렇게 저장하면 사용자의 패스워드를 직접 저장하는 위험을 피할 수 있다. 이번에는 “hunter3″라는 값을 SHA-256 알고리즘을 이용하여 인코딩해보자.
fb8c2e2b85ca81eb4350199faddd983cb26af3064614e737ea9f479621cfa57a
그러면 위와 같이 완전히 달라진 값이 나온 것을 알 수 있다. 이 특징을 avalanche 효과라고 하며, 사용자의 원본 패스워드를 추론하기 어렵게 만드는 중요한 요소이다. 그러나 이것만으로는 패스워드 보안이 충분히 안전하다고 말할 수 없다. 왜냐하면 원본 메세지에 같은 알고리즘을 사용하면 매번 같은 결과가 나오기 때문이다.
동일한 메시지가 언제나 동일한 다이제스트를 갖는다면, 공격자가 전처리(pre-computing)된 다이제스트를 가능한 한 많이 확보한 다음 이를 탈취한 다이제스트와 비교해 원본 메시지를 찾아내거나 동일한 효과의 메시지를 찾을 수 있다. 이와 같은 다이제스트 목록을 레인보우 테이블(rainbow table)이라 하고, 이와 같은 공격 방식을 레인보우 공격(rainbow attack)이라 한다. 게다가 다른 사용자의 패스워드가 같으면 다이제스트도 같으므로 한꺼번에 모두 정보가 탈취될 수 있다.
단방향 해시 함수 보완하기
솔트(salt)는 단방향 해시 함수에서 다이제스트를 생성할 때 추가되는 바이트 단위의 랜덤의 문자열이다. 그리고 이 원본 메시지에 문자열을 추가하여 다이제스를 생성하는 것을 솔팅(salting)이라 한다. 예를들어 다음과 같이 “redfl0wer”에 솔트인 “8zff4fgflgfd93fgdl4fgdgf4mlf45p1″를 추가해 다이제스트를 생성할 수 있다.
출처 : https://d2.naver.com/helloworld/318732
이 방법을 사용하면, 공격자가 “redfl0wer”의 다이제스트를 알아내더라도 솔팅된 다이제스트를 대상으로 패스워드 일치 여부를 확인하기 어렵다. 또한 사용자별로 다른 솔트를 사용한다면 동일한 패스워드를 사용하는 사용자의 다이제스트가 다르게 생성되어 인식 가능성 문제가 크게 개선된다.
입력한 패스워드의 다이제스트를 생성하고, 생성된 다이제스트를 입력 값으로 하여 또 다른 다이제스트를 생성할 수 있다.
출처 : https://d2.naver.com/helloworld/318732
이렇게 N번에 걸쳐서 하는 이유는 억지 기법 공격(brute-force-attack)으로 패스워드를 추측하는데 많은 시간이 소요되도록 하기 위함이다.
이제 스프링부트에서 비밀번호 암호화 하는 예제를 진행해보자.
pom.xml
org.springframework.boot spring-boot-starter-security Spring에서 인증/인가 등의 처리가 필요할 때 사용하는 security 의존성을 pom.xml에 추가하자.
스프링에서 비밀번호를 암호화하기 할 때 사용하는 가장 대표적인 PasswordEncoder 인터페이스를 이용하여 비밀번호 암호화를 진행해보자.
PasswordEncoder란?
Spring Security에서는 비밀번호를 안전하게 저장할 수 있도록 비밀번호의 단방향 암호화를 지원하는 PasswordEncoder 인터페이스와 구현체들을 제공한다. 그리고 PasswordEncoder 인터페이스 내부는 아래와 같다.
public interface PasswordEncoder { // 비밀번호 단방향 암호화 String encode(CharSequence rawPassword); // 암호화되지 않은 비밀번호(raw)와 암호화된 비밀번호(encode)가 일치하는지 비교 boolean matches(CharSequence rawPassword, String encodedPassword); // 기본적으로 false를 return, Custom하게 구현할 경우 이를 기반으로 더 강력한 암호화 구현 default boolean upgradeEncoding(String encodedPassword) { return false; } }
Spring Security 5.3.3에서 공식 지원하는 PasswordEncoder 구현 클래스들은 아래와 같다.
BcryptPasswordEncoder : Bcrypt 해시 함수를 사용하여 비밀번호 암호화
Argon2PasswordEncoder : Argon2 해시 함수를 사용하여 비밀번호 암호화
Pbkdf2PasswordEncoder : Pbkdf2 해시 함수를 사용하여 비밀번호 암호화
SCryptPasswordEncoder : SCrypt 해시 함수를 사용하여 비밀번호 암호화
이번에는 BcryptPasswordEncoder 클래스를 사용하여 암호화 예제를 진행할 것이고, 먼저 내부코드를 살펴보면 아래와 같다.
package org.springframework.security.crypto.bcrypt; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.security.crypto.password.PasswordEncoder; import java.security.SecureRandom; import java.util.regex.Matcher; import java.util.regex.Pattern; public class BCryptPasswordEncoder implements PasswordEncoder { public String encode(CharSequence rawPassword) { if (rawPassword == null) { throw new IllegalArgumentException(“rawPassword cannot be null”); } String salt; if (random != null) { salt = BCrypt.gensalt(version.getVersion(), strength, random); } else { salt = BCrypt.gensalt(version.getVersion(), strength); } return BCrypt.hashpw(rawPassword.toString(), salt); } public boolean matches(CharSequence rawPassword, String encodedPassword) { if (rawPassword == null) { throw new IllegalArgumentException(“rawPassword cannot be null”); } if (encodedPassword == null || encodedPassword.length() == 0) { logger.warn(“Empty encoded password”); return false; } if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) { logger.warn(“Encoded password does not look like BCrypt”); return false; } return BCrypt.checkpw(rawPassword.toString(), encodedPassword); } @Override public boolean upgradeEncoding(String encodedPassword) { if (encodedPassword == null || encodedPassword.length() == 0) { logger.warn(“Empty encoded password”); return false; } Matcher matcher = BCRYPT_PATTERN.matcher(encodedPassword); if (!matcher.matches()) { throw new IllegalArgumentException(“Encoded password does not look like BCrypt: ” + encodedPassword); } else { int strength = Integer.parseInt(matcher.group(2)); return strength < this.strength; } } } encode 메소드 내부코드를 보면 BCrypt.gensalt() 메소드를 통해서 salt를 Random으로 만드는 것을 알 수 있다. 그리고 BCrypt.hashpw() 메소드는 유저가 입력한 비밀번호와 랜덤 값 salt를 이용하여 비밀번호 암호화를 진행하게 된다. public class BCrypt { public static String gensalt(String prefix, int log_rounds, SecureRandom random) throws IllegalArgumentException { StringBuilder rs = new StringBuilder(); byte rnd[] = new byte[BCRYPT_SALT_LEN]; if (!prefix.startsWith("$2") || (prefix.charAt(2) != 'a' && prefix.charAt(2) != 'y' && prefix.charAt(2) != 'b')) { throw new IllegalArgumentException ("Invalid prefix"); } if (log_rounds < 4 || log_rounds > 31) { throw new IllegalArgumentException (“Invalid log_rounds”); } random.nextBytes(rnd); rs.append(“$2”); rs.append(prefix.charAt(2)); rs.append(“$”); if (log_rounds < 10) rs.append("0"); rs.append(log_rounds); rs.append("$"); encode_base64(rnd, rnd.length, rs); return rs.toString(); } } gensalt() 메소드 내부는 위와 같이 되어 있고 랜덤으로 salt 값을 만들어주는 메소드이고 내부코드는 가볍게 보면 될 것 같다. 이제 포스트맨을 이용하여 사용자 로그인 정보가 서버로 전달되었을 때 비밀번호를 암호화 하여 DB에 저장하는 것을 해보려 한다. 스프링부트에서 MySQL, MyBatis 사용법은 아래의 블로그에서 설정하고 예제를 따라하면 될 것 같다. 1. 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 JavaConfig extends WebSecurityConfigurerAdapter { @Bean public PasswordEncoder getPasswordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http .cors().disable() // cors 비활성화 .csrf().disable() // csrf 비활성화 .formLogin().disable() //기본 로그인 페이지 없애기 .headers().frameOptions().disable(); } } PasswordEncoder 인터페이스는 Bean으로 등록되어 있지 않기 때문에 따로 Bean으로 등록해야 한다. 방법은 위와 같다. @Configuration : 설정파일이라는 것을 알려주는 어노테이션 @Bean : 빈으로 등록하는 어노테이션 (return 타입이 주입됨) Config 클래스에서 WebSecurityConfigurerAdapter 클래스를 상속받아 configure를 오버라이딩 하고, 파라미터인 HttpSecurity 클래스를 이용하여 설정한다. 먼저 클라이언트에게 REST API 응답을 보내는 방법이 궁금하다면 여기의 글을 먼저 읽고 다시 읽는 것을 추천한다. Member 클래스 import lombok.Data; @Data public class Member { private int memberIdx; private String id; private String password; } MySQL에서 Member 테이블을 위와 같이 만든 후에, Java 코드로 Member 클래스를 만들었다. Controller import com.example.demo.dto.Member; import com.example.demo.model.DefaultRes; import com.example.demo.service.MemberService; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; 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; @RestController @Slf4j @RequestMapping("member") public class MemberController { MemberService memberService; // Service 생성자 의존성 주입 public MemberController(MemberService memberService) { this.memberService = memberService; } // 회원가입 @PostMapping("signUp") public ResponseEntity signUp(@RequestBody Member member) { try { return new ResponseEntity(memberService.signUp(member), HttpStatus.OK); } catch (Exception e) { log.info(e.getMessage()); return new ResponseEntity(DefaultRes.FAIL_DEFAULT_RES, HttpStatus.INTERNAL_SERVER_ERROR); } } // 로그인 @PostMapping("signIn") public ResponseEntity siginIn(@RequestBody Member member) { try { return new ResponseEntity(memberService.signIn(member), HttpStatus.OK); } catch (Exception e) { log.error(e.getMessage()); return new ResponseEntity(DefaultRes.FAIL_DEFAULT_RES, HttpStatus.INTERNAL_SERVER_ERROR); } } } Controller 계층과 Service 계층을 분리하였고 Controller에서는 위와 같이 로그인, 회원가입 URL 맵핑을 하였다. Service import com.example.demo.dto.Member; import com.example.demo.mapper.MemberMapper; import com.example.demo.model.DefaultRes; import com.example.demo.utils.ResponseMessage; import com.example.demo.utils.StatusCode; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; @Service @Slf4j public class MemberService { private final MemberMapper memberMapper; private final PasswordEncoder passwordEncoder; /** * UserMapper 생성자 의존성 주입 * * @param userMapper, passwordEncoder */ public MemberService(MemberMapper memberMapper, PasswordEncoder passwordEncoder) { this.memberMapper = memberMapper; this.passwordEncoder = passwordEncoder; } /** * 로그인 * * @return DefaultRes */ public DefaultRes signIn(final Member member) { try { // 회원아이디 체크 Member m = memberMapper.checkById(member.getId()); // 아이디가 틀렸을 때 if (m == null) { return new DefaultRes(StatusCode.BAD_REQUEST, ResponseMessage.LOGIN_FAIL); } // parameter1 : rawPassword, parameter2 : encodePassword boolean check = passwordEncoder.matches(member.getPassword(), m.getPassword()); // 로그인 성공 if (check) { return new DefaultRes(StatusCode.OK, ResponseMessage.LOGIN_SUCCESS); } return new DefaultRes(StatusCode.BAD_REQUEST, ResponseMessage.LOGIN_FAIL); } catch (Exception e) { log.error(e.getMessage()); return new DefaultRes(StatusCode.DB_ERROR, ResponseMessage.DB_ERROR); } } /** * 회원가입 * * @return DefaultRes */ @Transactional public DefaultRes signUp(final Member member) { try { // 아이디 중복 체크 final Member m = memberMapper.checkById(member.getId()); // 이미 유저가 존재할 때 if (m != null) { return DefaultRes.res(StatusCode.BAD_REQUEST, ResponseMessage.ALREADY_USER); } // 비밀번호 암호화 String encodePassword = passwordEncoder.encode(member.getPassword()); member.setPassword(encodePassword); memberMapper.insertMember(member); return DefaultRes.res(StatusCode.OK, ResponseMessage.CREATED_USER); } catch (Exception e) { //Rollback TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); log.error(e.getMessage()); return DefaultRes.res(StatusCode.DB_ERROR, ResponseMessage.DB_ERROR); } } } Service 계층에서는 비밀번호를 암호화하기 위해 Bean으로 등록했던 PasswordEncoder 인터페이스와 데이터베이스를 연결하기 위한 Mapper 인터페이스를 주입을 받는다. 위의 코드에서 DB에 접근하여 회원가입, 로그인 로직이 진행된다. 먼저 회원가입을 보면서 비밀번호 암호화 예제를 이해해보자. 회원가입 /** * 회원가입 * * @return DefaultRes */ @Transactional public DefaultRes signUp(final Member member) { try { // 아이디 중복 체크 final Member m = memberMapper.checkById(member.getId()); // 이미 유저가 존재할 때 if (m != null) { return DefaultRes.res(StatusCode.BAD_REQUEST, ResponseMessage.ALREADY_USER); } // 비밀번호 암호화 String encodePassword = passwordEncoder.encode(member.getPassword()); member.setPassword(encodePassword); memberMapper.insertMember(member); return DefaultRes.res(StatusCode.OK, ResponseMessage.CREATED_USER); } catch (Exception e) { //Rollback TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); log.error(e.getMessage()); return DefaultRes.res(StatusCode.DB_ERROR, ResponseMessage.DB_ERROR); } } 먼저 파라미터로 사용자가 입력한 ID, password를 Member 클래스를 통해서 받아온다. 그리고 주입받은 PasswordEncoder 인터페이스의 encode 메소드를 사용하여 암호화를 진행하면 된다. public String encode(CharSequence rawPassword) { if (rawPassword == null) { throw new IllegalArgumentException("rawPassword cannot be null"); } String salt; if (random != null) { salt = BCrypt.gensalt(version.getVersion(), strength, random); } else { salt = BCrypt.gensalt(version.getVersion(), strength); } return BCrypt.hashpw(rawPassword.toString(), salt); } 위의 코드를 보면 알 수 있듯이, encode 메소드는 내부적으로 BCrypt.gensalt 메소드를 통해서 salt를 랜덤으로 만들어 암호화를 해준다 이제 PostMan을 이용하여 암호화가 제대로 되어 DB에 저장되는지 확인해보자. [POST] http://localhost:8080/member/signUp { "id" : "wjdrbs", "password" : "1234" } POST방식으로 Request Body에 위의 JSON 형식을 담아 요청을 보내보자. memberIdx id password 1 wjdrbs $2a$10$TuKGiVuLJl3xhaVPDNj3EOcjDyKrMcFcc7m.d.PsFX7UjbTgrl1Ju 그러면 위와 같이 암호화되어 테이블에 저장이 된 것을 확인할 수 있다. 이번에는 암호화된 비밀번호를 이용하여 어떻게 로그인 요청을 처리할 지에 대해서 알아보자. 로그인 /** * 로그인 * * @return DefaultRes */ public DefaultRes signIn(final Member member) { try { // 회원아이디 체크 Member m = memberMapper.checkById(member.getId()); // 아이디가 틀렸을 때 if (m == null) { return new DefaultRes(StatusCode.BAD_REQUEST, ResponseMessage.LOGIN_FAIL); } // parameter1 : rawPassword, parameter2 : encodePassword boolean check = passwordEncoder.matches(member.getPassword(), m.getPassword()); // 로그인 성공 if (check) { return new DefaultRes(StatusCode.OK, ResponseMessage.LOGIN_SUCCESS); } return new DefaultRes(StatusCode.BAD_REQUEST, ResponseMessage.LOGIN_FAIL); } catch (Exception e) { log.error(e.getMessage()); return new DefaultRes(StatusCode.DB_ERROR, ResponseMessage.DB_ERROR); } } 이번에는 PasswordEncoder 인터페이스의 메소드인 matches 메소드를 사용하면 된다. 첫 파라미터에는 사용자의 원본 password를 넣어주고 두 번째 파라미터에는 DB에 저장되어 있던 암호화된 비밀번호를 넣어준다. (그런데 내부로직이 정확히 어떻게 되어 있어서 원본패스워드와 암호화된 패스워드를 비교해 true, false를 반환하는지 모르겠다. 원본패스워드 + salt = 암호화된 패스워드인데 salt를 저장하지 않았는데 말이다) Reference https://www.zerocho.com/category/NodeJS/post/593a487c2ed1da0018cff95d https://velog.io/@corgi/Spring-Security-PasswordEncoder%EB%9E%80-4kkyw8gi https://d2.naver.com/helloworld/318732 반응형
So you have finished reading the 스프링 부트 암호화 topic article, if you find this article useful, please share it. Thank you very much. See more: Spring boot 비밀번호 암호화, Spring-security 비밀번호 암호화, Spring boot 암호화 복호화, 스프링부트 비밀번호 체크, Spring 암호화 복호화, Spring 로그인 암호화, Spring boot Security 비밀번호 체크, BCryptPasswordEncoder