• ModelMapper 설정
    • 객체의 프로퍼티를 다른 객체의 프로퍼티로 맵핑해주는 유틸리티
    • 토그나이저 설정

 

  public void updateProfile(Account account, Profile profile) {
        accountRepository.save(account); // acount가 detached 상태이기 때문. completeSignUp의 account 와 상태 다름
        account.setUrl(profile.getUrl()); modelMapper 로 간단히 구현
        account.setOccupation(profile.getOccupation());
        account.setLocation(profile.getLocation());
        account.setBio(profile.getBio());

    }


public void updateNotifications(Account account, Notifications notifications) {
        account.setStudyCreatedByWeb(notifications.isStudyCreatedByWeb());
        account.setStudyCreatedByEmail(notifications.isStudyCreatedByEmail());
        account.setStudyUpdatedByWeb(notifications.isStudyUpdatedByWeb());
        account.setStudyUpdatedByEmail(notifications.isStudyUpdatedByEmail());
        account.setStudyEnrollmentResultByEmail(notifications.isStudyEnrollmentResultByEmail());
        account.setStudyEnrollmentResultByWeb(notifications.isStudyEnrollmentResultByWeb());
        accountRepository.save(account);
 }

이와 같은 set과 get으로 매핑이 필요한 코드를 ModelMapper를 이용하여 간결하게 줄일 수 있다.

 

 

ModelMapper 사용하기
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.6</version>
</dependency>
    @Bean // 여러번 매번 만들필요가 없기 때문에 빈으로 등록해서 사용
    public ModelMapper modelMapper() {
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration()
                .setDestinationNameTokenizer(NameTokenizers.UNDERSCORE) // 언더스코어로 구분
                .setSourceNameTokenizer(NameTokenizers.UNDERSCORE);
        return modelMapper;
    }

 

매번 만들 필요가 없기 때문에 빈으로 등록해서 사용하도록 하였다.

modelMapper의 여러 설정 속성들이 있으며 프로젝트에 맞게 잘 설정해 주어야 에러가 나지 않는다.

 

토크나이저 설정

필드명이 다를 경우, camelCase -> unser_score 형태인 경우 토크나이저 설정을 통해서 자동으로 매핑 시킬 수 있다

 

토크나이저를 설정하여 UNDERSCORE(_) 사용했을 때에만 nested 객체를 참조하는 것으로 간주하고

그렇지 않은 경우에는 해당 객체의 직속 프로퍼티에 바인딩 하도록 하였다.

 

 

ModelMapper는 매핑 설정을 통해서 필드명이나 구조가 일치하지 않더라도 TypeMap을

이용하여 매핑시킬 수 있으며 특정 필드 생략 등 다양한 설정할 수 할 수 있다.

 

http://modelmapper.org/user-manual/configuration/#matching-strategies

  • 프로필 수정 테스트
    • 인증된 사용자만 접근할 수 있는 URL이므로 인증된 Authentication 정보가 필요.
    • @WithSecurityContext, @WithAccount 를 이용하여 인증된 사용자를 제공하는 커스텀 어노테이션 생성

 

인증된 사용자만 접근할 수 있는 URL이라면 테스트 코드를 작성할 때 인증된 사용자 정보를 같이 제공해줘야 한다.

프로필 수정하는 테스트 코드를 작성할 때도 인증된 사용자 정보가 필요했다.

 

먼저 스프링 시큐리티에서 사용자 인증을 위해 다음과 같은 어노테이션이 제공된다.

 

@WithMockUser 사용자 이름, 패스워드, 권한으로 UserDetails를 생성

@WithUserDetails 지정한 사용자 이름으로 UserDetails 객체를 조회

@WithAnonymousUser  익명 사용자 생성

 

먼저 @WithUserDetails 를 이용하여 인증된 사용자 정보를 제공해보려고 하였다.

@WithUserDetails(value ="nyju") 는 beforeEach 전에 실행되기 때문에 테스트에 실패 하였다.
@WithUserDetails(value ="nyju", setupBefore = TestExecutionEvent.TEST_EXECUTION)

-> 시큐리티컨텍스트를 설정하는 위치를 지정하는 기능이며 비포다음 Test코드 바로 직전 실행하도록 하였다.
-> But.. 현재 버그가 있어서 그런지 여전히 before실행하기 전에 실행되기 때문에 테스트가 실패하였다.

 

그래서 @WithSecurityContext 를이용하여 인증된 사용자를 제공할 커스텀 어노테이션을 생성하였다.

 

 

@WithSecurityContext 를 이용한 커스텀 어노테이션 생성
// test.WithAccount.java

@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithAccountSecurityContextFacotry.class) // 시큐리티컨텍스트를 만들어줄 팩토리를 만듦
public @interface WithAccount {

    String value();

}
// test.WithAccountSecurityContextFacotry.java

@RequiredArgsConstructor
public class WithAccountSecurityContextFacotry implements WithSecurityContextFactory<WithAccount> {

    private final AccountService accountService;

    @Override
    public SecurityContext createSecurityContext(WithAccount withAccount) {
        String nickname = withAccount.value();

        SignUpForm signUpForm = new SignUpForm();
        signUpForm.setNickname(nickname);
        signUpForm.setEmail(nickname + "@email.com");
        signUpForm.setPassword("12345678");
        accountService.processNewAccount(signUpForm);

        UserDetails principal = accountService.loadUserByUsername(nickname);
        Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities());
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(authentication); // 시큐티리 컨텍스트에 넣어줌.
        return context;
    }
}

 

 

위와 같이 커스텀 어노테이션을 생성한 후 @WithAccount를 이용하여 인증된 사용자 정보를 제공할 수 있었다.

    @WithAccount("nyju")
    @DisplayName("프로필 수정 폼")
    @Test
    void updateProfileForm() throws Exception {
        mockMvc.perform(get(SettingsController.SETTINGS_PROFILE_URL))
                .andExpect(status().isOk())
                .andExpect(model().attributeExists("account"))
                .andExpect(model().attributeExists("profile"));
    }

 

 @AfterEach
    void afterEach() {
        accountRepository.deleteAll();
    }

주의 할 점은 WithAccount를 사용할 때마다 계정을 만들기 때문에 afterEach 에서 삭제해주어야 한다.

  • 로그인 기억하기 (RememberMe)
  • 로그인 방식
    • 로그인 방식에는 세션을 이용하는 방법과 쿠키를 이용하는 방법이 있다.
    • 세션방식은 데이터를 서버에 보관하여 안전하다는 장점이 있지만, 만료되었을때 사용할 수 없다는 단점이 있다
    • 쿠키에 인증 정보를 남겨두어 세션이 만료 되더라도
      쿠키에 남아있는 정보로 인증하여 로그인을 유지할 수 있다.
  • 쿠키사용하기
    • 해시 기반의 쿠키
      • username, password를 암호화(해싱)하여 쿠키게 담아둔다.
      • 쿠키를 다른 사람이 가져가면 그 계정은 탈취당한 것과 같다.
    • 조금 더 안전한 방법은?
      • 쿠키안에 랜덤한 문자열(토큰)을 만들어 같이 저장하여 매번 인증할 때마다 바꾼다.
      • Username, 토큰
      • 문제는 이 방법도 취약하다. 쿠키를 탈취 당하면 희생자는 쿠키로 인증하지 못한다.
      • 해커가 탈취한 쿠키만 유효하게 된다.
    • 조금 더 개선한 방법은?
      • Username, 토큰(랜덤, 매번 바뀜), 시리즈(랜덤, 고정)
      • 쿠키를 탈취 당한 경우, 희생자는 유효하지 않은 토큰과 유효한 시리즈와 Username으로 접속하게 된다.
        이 경우, 모든 토큰을 삭제하여 해커가 더이상 탈취한 쿠키를 사용하지 못하도록 방지할 수 있다.
  • RememberMe 사용하기
    • 스프링시큐리티  rememberMe() 설정
    • JdbcTokenRepositoryImpl 설정
    • persistent_logins 테이블

 

@Configuration
@EnableWebSecurity // 스프링 시큐리티 사용을 위한 선언
@RequiredArgsConstructor
public class SecutiryConfig extends WebSecurityConfigurerAdapter {

    private final AccountService accountService;
    private final DataSource dataSource;

        http.rememberMe() //Username, 토큰(랜덤, 매번 바뀜), 시리즈(랜덤, 고정) 세 가지 정보 이용
                .userDetailsService(accountService)
                .tokenRepository(tokenRepository());

    }

    @Bean // 토큰 레파지토리 구현체
    public PersistentTokenRepository tokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return jdbcTokenRepository;
    }

}

스프링시큐리티에서 제공하는 rememberMe()를 이용하여 설정 한다.

JdbcTokenRepositoryImpl 로 토큰 레파지토리 구현체를 정의해준다.

 

@Table(name = "persistent_logins") // 토큰 레파지토리가 사용할 테이블 필요, H2 를 사용하기 위해 생성
@Entity
@Getter @Setter
public class PersistentLogins {

    @Id
    @Column(length = 64)
    private String series;

    @Column(nullable = false, length = 64)
    private String username;

    @Column(nullable = false, length = 64)
    private String token;

    @Column(name = "last_used", nullable = false, length = 64)
    private LocalDateTime lastUsed;

}

쿠키 값을 사용하기 위한 엔티티를 구현한다.

 

<div class="form-group form-check">
	<input type="checkbox" class="form-check-input" id="rememberMe" name="remember-me" checked>
	<label class="form-check-label" for="rememberMe" aria-describedby="rememberMeHelp">로그인 유지</label>
</div>

로그인화면에 체크박스를 추가해준다.

스프링 시큐리티에서 remeberme 파타리터 이름의 default 값으로 remember-me를 가지고 있기에 

name을 remember-me로 설정해야 한다.

이름이 다르다면 http.rememberMe().rememberMeParameter("파라미터이름") 로 커스텀 해주면 된다.  

 

 

 

  • 스프링 시큐리티 로그인, 로그아웃
    • 시큐리티설정, UserDetailsService 설정
    • 테스트코드 Unit 5의 @BeforeEach와 @AfterEach

 

1. UserDetailsService 구현

 

DB에 있는 값으로 사용자 인증을 하기 위해서 UserDetailsService 를 구현해야 한다.

// AccountService.java

@Service
@RequiredArgsConstructor
public class AccountService implements UserDetailsService {

 @Override // 스프링 시큐리티에서 /login, /logout 에 대한 처리를 위해 구현해야함
    public UserDetails loadUserByUsername(String emailOrNickname) throws UsernameNotFoundException {
        Account account = accountRepository.findByEmail(emailOrNickname);
        if (account == null) {
            account = accountRepository.findByNickname(emailOrNickname);
        }

        if (account == null) {
            throw new UsernameNotFoundException(emailOrNickname);
        }

        return new UserAccount(account);
    }

}

loadUserByUsername을 오버라이딩하여 DB에 사용자 정보를 찾아 리턴해주도록 한다.

UserDetailsSeervice는 UserDetail을 구현한 객체를 리턴해야 하기 때문에

principal 객체인 UserAccount로 리턴한다.

 

UserDetailsSeervice를 구현하면 스프링시큐리티에서 로그인과 로그아웃에 대한 처리는 알아서 해준다고 한다..

  // SecurityConfig.java
    
    
    http.formLogin() // 커스텀한 로그인페이지 URL. 설정하지 않으면 시큐리티기본 로그인페이지가 보임
                .loginPage("/login").permitAll(); // GET, POST 모두 동일한 경로를 사용

    http.logout() // 로그아웃 했을때 이동할 URL
                .logoutSuccessUrl("/");

 

WebSecurityConfigurerAdapter 를 구현한 시큐리티 설정클래스에

로그인 및 로그아웃에 대한 커스텀설정을 할 수 있다.

 

 

2. 테스트코드

@SpringBootTest
@AutoConfigureMockMvc // Junit5에서는 RunWith 안써도됨
class MainControllerTest {

    // @RequireArgsConstructor 로는 주입안됨. Junit 이 먼저 개입하기 때문. Autowired를 써야함

    @Autowired
    MockMvc mockMvc;
    @Autowired
    AccountService accountService;
    @Autowired
    AccountRepository accountRepository;

    @BeforeEach // 매 테스트마다 실행
    void beforeEach() {
        SignUpForm signUpForm = new SignUpForm();
        signUpForm.setNickname("nyju");
        signUpForm.setEmail("nyju@email.com");
        signUpForm.setPassword("12345678");
        accountService.processNewAccount(signUpForm);
    }

    @AfterEach // 매 테스트마다 똑같은 닉네임이 등록되기 때문에 AfterEach로 삭제해줌
    void afterEach() {
        accountRepository.deleteAll();
    }

    @DisplayName("이메일로 로그인 성공")
    @Test
    void login_with_email() throws Exception {
        mockMvc.perform(post("/login")
                .param("username", "nyju@email.com")
                .param("password", "12345678") // username, password 파라미터 이름은 변경하려면 시큐티리 설정을 바꿔야함
                .with(csrf())) // 시큐티리티에서는 꼭 csrf와 함께 전송됨
                .andExpect(status().is3xxRedirection())
                .andExpect(redirectedUrl("/"))
                .andExpect(authenticated().withUsername("nyju"));
                // UserAccount 에서 닉네임을 리턴했기 때문에 이메일이 아닌 닉네임으로 로그인
    }

    @DisplayName("로그인 실패")
    @Test
    void login_fail() throws Exception {
        mockMvc.perform(post("/login")
                .param("username", "111111")
                .param("password", "000000000")
                .with(csrf()))
                .andExpect(status().is3xxRedirection())
                .andExpect(redirectedUrl("/login?error"))
                .andExpect(unauthenticated());
    }

    @WithMockUser
    @DisplayName("로그아웃")
    @Test
    void logout() throws Exception {
        mockMvc.perform(post("/logout")
                .with(csrf()))
                .andExpect(status().is3xxRedirection())
                .andExpect(redirectedUrl("/"))
                .andExpect(unauthenticated());
    }

}

테스트를 할때 매 테스트마다 필요한 코드들을 @BeforeEach와 @AfterEach 를 이용하여 간단하게 설정할 수 있다.

 

 

 

 

github

  •  현재 인증된 사용자 정보 참조

    • 스프링 시큐리티 : @AuthenticationPrincipal 사용

 

스프링시큐리티가 제공하는 @AuthenticationPrincipal를 이용하여 현재 인증된 사용자 정보를 가져올 수 있다.

@AuthenticationPrincipal으로 UserDetailsService에서 리턴한 객체를 받아오게 되는데

프로젝트에서 생성한 Account 객체로 리턴 타입을 변경하기 위해 Adapter 클래스를 생성한다.

 

 

1. Adapter 클래스 생성

// UserAccount.java

@Getter // 시큐리티에서 다루는 유저정보와 도메인에서 다루는 유저정보 사이의 갭을 위한 클래스
public class UserAccount extends User { // UserDetails를 구현하는 User 클래스를 상속

    private Account account;

    public UserAccount(Account account) {
        super(account.getNickname(), account.getPassword(), List.of(new SimpleGrantedAuthority("ROLE_USER")));
        this.account = account;
    }
}

UserDetails를 구현한 User 클래스를 상속받아 Adapter클래스를 만들어준다.

UserDetailsService 에서 리턴하는 객체는 UserDetails 타입이여야 하기 때문이다.

 

 

2. Account 객체 사용

 @GetMapping("/")
    public String home(@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account") Account account, Model model) 
        if (account != null) {
            model.addAttribute(account);
        }
        
        return "index";
    }

UserAccount 객체 안에 Account 객체가 있기 때문에 사용하기 위해서는

@AuthenticationPrincipal  UserAccount 로 받을 경우 Account 객체를  getAccount()로 가져와야 한다.

 

Account객체를 직접 가져오기 위해서는 위와 같이

@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account") Account account

처럼 사용하여야 하는데 너무 길다. 이를 위해 커스텀 어노테이션을 만들어 준다.

 

 

3. @CurrentUser 생성

@Retention(RetentionPolicy.RUNTIME) // 런타입까지 유지되어야 함
@Target(ElementType.PARAMETER) // 파라미터를 붙일 수 있도록
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account") // 현재 객체가 anonymousUser라면 null 아니면 account 값을 꺼내서 설정
public @interface CurrentUser {

}

 

 @GetMapping("/")
    public String home(@CurrentUser Account account, Model model) {
        if (account != null) {
            model.addAttribute(account);
        }

        return "index";
    }

@CurrentUser를 생성하여 현재 인증된 사용자 정보를 간편하게 가져올 수 있다.

 

 

 

github

  • 회원가입 : 가입완료 후 자동로그인 및 인증정보 확인
    • 스프링 시큐리티 : UsernamePasswordAuthenticationToken, SecurityContextHolder
    • 뷰에서 인증정보 확인 : 타임리프에서 제공하는 thymeleaf-extras-springsecurity5
    // AccountService.java
    
    public void login(Account account) {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
                account.getNickname(),
                account.getPassword(),
                List.of(new SimpleGrantedAuthority("ROLE_USER")));

        SecurityContextHolder.getContext().setAuthentication(token);

//        아래가 정석적인 방법. 위와 같은 기능을 함
//        정석적으로 하려면 인코딩된 비밀번호가 아닌 플레인텍스트로 받은 비밀번호를 사용하여야 함.
//        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
//        Authentication authentication = authenticationManager.authenticate(token);
//        SecurityContext context =SecurityContextHolder.getContext();
//        context.setAuthentication(authentication);
    }

회원가입 완료 후 서비스단에서 login() 을 실행하여 로그인 되도록 한다.

이 때 스프링 시큐리티에서 제공하는 UsernamePasswordAuthenticationToken 를 사용한다.

여기서 principal 키 값으로 닉네임을 넘겨줬다.

 

SecurityContextHolder 에는 접근 정보 및 인증주체를 저장한다.

 

 

 

뷰에서 사용자 인증정보가 있는 경우와 없는 경우 다르게 보이는데

타임리프에서 thymeleaf-extras-springsecurity5 로 스프링 시큐리티를 지원해준다.

<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
     
     
  <li class="nav-item" sec:authorize="!isAuthenticated()">
      <a class="nav-link" th:href="@{/sign-up}">가입</a>
  </li>
  <li class="nav-item" sec:authorize="isAuthenticated()">
      <a class="nav-link" th:href="@{/notifications}">알림</a>
  </li>

위와 같이 인증되었을 때와 인증되지 않았을 때 보여지는 링크를 다르게 할 수 있다.

 

 

 

 

 

github

  • 회원가입 이메일 인증

    • 인증메일 발송 JavaMailSender

    • 인증 토큰값 확인

@Profile("local") // 로컬일때 이 bean이 등록되도록
@Component
@Slf4j
public class ConsoleMailSender implements JavaMailSender {

    // 콘솔로 출력하는 형태로 임시로 구현. 추후 변경
    
    @Override
    public MimeMessage createMimeMessage() {
        return null;
    }

    @Override
    public MimeMessage createMimeMessage(InputStream inputStream) throws MailException {
        return null;
    }

    @Override
    public void send(MimeMessage mimeMessage) throws MailException {

    }

    @Override
    public void send(MimeMessage... mimeMessages) throws MailException {

    }

    @Override
    public void send(MimeMessagePreparator mimeMessagePreparator) throws MailException {

    }

    @Override
    public void send(MimeMessagePreparator... mimeMessagePreparators) throws MailException {

    }

    @Override
    public void send(SimpleMailMessage simpleMailMessage) throws MailException {
        log.info(simpleMailMessage.getText());
    }

    @Override
    public void send(SimpleMailMessage... simpleMailMessages) throws MailException {

    }
}

이메일 전송 기능을 위해 JavaMailSender 를 구현한다.

임의로 콘솔로 출력하는 형태로 작성하였으며, local에서만 실행되도록 한다.

@Slf4j 를 이용하여 발송시 로그를 확인 할 수 있도록 한다.

 

 

 

    // AccountController.java
    
    @PostMapping("/sign-up") //  JSR-303 Valid 검사
    public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors) {
        if (errors.hasErrors()) {
            return "account/sign-up"; //에러가 있으면 다시 폼으로
        }
        accountService.processNewAccount(signUpForm);

        return "redirect:/";
    }
@Service
@RequiredArgsConstructor
public class AccountService {

    private final AccountRepository accountRepository;
    private final JavaMailSender javaMailSender;
    private final PasswordEncoder passwordEncoder;

    @Transactional
    public void processNewAccount(SignUpForm signUpForm) {
        Account newAccount = saveNewAccount(signUpForm); // 새로운 Account 생성
        newAccount.generateEmailCheckToken(); // 이메일 체크 토큰 생성
        // saveNewAccount 후에 detached 상태가 되기 때문에 토큰값이 저장이 안됨
        // persist 상태 유지를 위해 @Transactional 을 붙여야 함

        sendSignUpConfirmEmail(newAccount); // 이메일 전송
    }

    private Account saveNewAccount(@Valid SignUpForm signUpForm) {
        Account account = Account.builder()
                .email(signUpForm.getEmail())
                .nickname(signUpForm.getNickname())
                .password(passwordEncoder.encode(signUpForm.getPassword()))
                .studyCreatedByWeb(true)
                .studyEnrollmentResultByWeb(true)
                .studyUpdatedByWeb(true)
                .build();
        return accountRepository.save(account);
    }

    private void sendSignUpConfirmEmail(Account newAccount) {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setTo(newAccount.getEmail());
        mailMessage.setSubject("StudyNy, 회원 가입 인증");
        mailMessage.setText("/check-email-token?token=" + newAccount.getEmailCheckToken() +
                "&email=" + newAccount.getEmail());
        javaMailSender.send(mailMessage);
    }
}
    // Account.java
    
    public void generateEmailCheckToken() {
        this.emailCheckToken = UUID.randomUUID().toString(); // 랜덤 UUID 사용
    }

 

sign-up요청이 들어오면 새로운 Account 생성 후 이메일 체크 토큰값을 생성한다. 

UUID를 이용하여 랜덤한 토큰 값을 생성하였으며,  이 토큰값과 함께 이메일을 전송하게 된다.

 

saveNewAccount 후에 detached 상태가 되기 때문에 persist 상태 유지를 위해 @Transactional 를 붙여줘야 한다.

 

 

// AccountController.java
    
    // 인증메일 확인
    @GetMapping("/check-email-token")
    public String checkEmailToken(String token, String email, Model model) {
        Account account = accountRepository.findByEmail(email);
        String view = "account/checked-email";
        if (account == null) {
            model.addAttribute("error", "wrong.email");
            return view;
        }

        if (!account.getEmailCheckToken().equals(token)) {
            model.addAttribute("error", "wrong.token");
            return view;
        }

        account.completeSignUp();
/*        account.setEmailVerified(true); 리팩토링 - Account로 이동
        account.setJoinedAt(LocalDateTime.now()); */
        model.addAttribute("numberOfUser", accountRepository.count());
        model.addAttribute("nickname", account.getNickname());
        return view;
    }

인증메일 확인을 하는 요청도 구현한다. 토큰 값을 받아 디비에 저장되어 있는 토큰값과 같은지 확인한다.

 

 

 

 

github

  • 회원가입 - 패스워드 인코더

    • 스프링 시큐리티 - PasswordEncoder

    • PasswordEncoderFactories.createDelegatingPasswordEncoder()
    • 해싱 알고리즘 - bcrypt, salt

@Configuration
public class AppConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder(); // BCryptEncoder 사용. Salt 사용
    }
}
 // AccountService.java
    
    private final PasswordEncoder passwordEncoder;

    private Account saveNewAccount(@Valid SignUpForm signUpForm) {
        Account account = Account.builder()
                .email(signUpForm.getEmail())
                .nickname(signUpForm.getNickname())
                .password(passwordEncoder.encode(signUpForm.getPassword()))
                .studyCreatedByWeb(true)
                .studyEnrollmentResultByWeb(true)
                .studyUpdatedByWeb(true)
                .build();
        return accountRepository.save(account);
    }

패스워드는 반드시 암호화 하여 저장해야 한다.

PasswordEncoder 를 이용하여 비밀번호를 암호화 할 수 있다. 

PasswordEncoderFactories.createDelegatingPasswordEncoder()를 통해  BCrypt와 Salt를 이용해 암호화 된다.

Salt 를 넣는 작업까지 하여, 입력값이 같아도 매번 다른 인코딩된 값을 리턴한다.

 

 

 

 

github

 

+ Recent posts