728x90

패스워드 인코더

클라이언트로부터 받아온 패스워드 같은 민감한 정보는

받아온 그대로 데이터베이스에 저장하는 것이 아니라

저장하기 전에 암호화해줘야 한다.

 

스프링 시큐리티에서는 이러한 암호화 기능을 제공해 주기 때문에

PasswordEncoder 클래스를 사용기만 하면 된다.

@Bean
public PasswordEncoder passwordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

우선 PasswordEncoder의 구현 객체를 만들기 위해

PasswordEncoderFactories의 createDelegatingPasswordEncoder 메서드를 사용하여

DelegatingPasswordEncoder 구현 객체를 만든다.

@Configuration
public class JavaConfiguration {
    @Bean
    public MemberService inMemoryMemberService(UserDetailsManager userDetailsManager, 
                                               PasswordEncoder passwordEncoder) {
        return new InMemoryMemberService(userDetailsManager, passwordEncoder);
    }
}

Configuration 클래스를 하나 추가한 후에

멤버 서비스 인터페이스의 구현 객체를 빈으로 등록해 준다.

 

이때 멤버 서비스 객체에서는 회원가입, 즉 회원을 생성하는 기능을 수행하기 때문에

암호화된 패스워드를 등록하기 위해 PasswordEncoder 객체를 주입받게 한다.

 

UserDetailsManager 객체는 학습을 위해 인메모리 DB를 사용하기 위해 추가했다.

유저 등록 (인메모리 DB 사용)

public Member createMember(Member member) {
    List<GrantedAuthority> authorities = createAuthorities(Member.MemberRole.ROLE_USER.name());

    String encryptedPassword = passwordEncoder.encode(member.getPassword());

    UserDetails userDetails = new User(member.getEmail(), encryptedPassword, authorities);

    userDetailsManager.createUser(userDetails);

    return member;
}

위와 같이 멤버 서비스 클래스에 회원가입을 처리하는 메서드를 추가한다.

List<GrantedAuthority> authorities = createAuthorities(Member.MemberRole.ROLE_USER.name());

한 줄씩 살펴보면 우선 GrantedAuthority 객체를 담는 리스트를 선언하고

createAuthorities 메서드를 통해 받아온다.

 

GrantedAuthority는 회원의 권한으로 회원을 등록하기 위해서는 권한을 지정해야 한다.

 

관리자가 아닌 일반회원의 회원가입을 진행하기 위해 일반회원의 권한을 가져온다.

private List<GrantedAuthority> createAuthorities(String... roles) {
    return Arrays.stream(roles)
            .map(role -> new SimpleGrantedAuthority(role))
            .collect(Collectors.toList());
}

createAuthorities 메서드의 로직은 위와 같다.

 

SimpleGrantedAuthority 생성자의 파라미터로 회원의 역할을 전달하여

객체를 생성한 후에 리스트 형태로 리턴한다.

 

이때 SimpleGrantedAuthority를 사용해 역할 기반 형태의 권한을 지정하려면

ROLE_권한명 형태로 지정해야 적절한 권한 매핑이 이루어진다.

String encryptedPassword = passwordEncoder.encode(member.getPassword());

PasswordEncoder의 encode 메서드를 사용하여

받아온 회원가입 정보에서 비밀번호를 암호화한다.

UserDetails userDetails = new User(member.getEmail(), encryptedPassword, authorities);

User 생성자의 파라미터로 이메일과 암호화된 패스워드와 권한을 전달하고

User 정보를 관리하기 위해 UserDetails를 생성한다.

 

이때 이전 단계에서 패스워드를 암호화하지 않으면 등록은 할 수 있지만

 

userDetailsManager.createUser(userDetails);

UserDetailsManager의 createUser 메서드를 사용하여 User를 등록한다.

유저 등록 (DB 사용)

인메모리 DB를 통해 회원등록이 어떻게 진행되는지 학습했으니

실제 데이터베이스를 사용하여 구현해 보겠다.

@Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
        		// 추가 부분
                .headers().frameOptions().sameOrigin()
                // 
                .and()
                .csrf().disable()
                .formLogin()
                .loginPage("/auths/login-form")
                .loginProcessingUrl("/process_login")
                .failureUrl("/auths/login-form?error")
                .and()
                // 로그아웃 시작
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/")
                // 로그아웃 종료
                .and()
                .exceptionHandling().accessDeniedPage("/auths/access-denied")
                .and()
                .authorizeHttpRequests(authorize -> authorize
                        .antMatchers("/orders/**").hasRole("ADMIN")
                        .antMatchers("/members/my-page").hasRole("USER")
                        .antMatchers("⁄**").permitAll()
                );

        return httpSecurity.build();
    }

우선 기존의 필터체인의 코드를 위와 같이 수정한다.

 

frameOptions 메서드는 HTML의 frame, iframe, object 태그에서

페이지의 랜더링을 허용할지 여부를 결정할 때 사용한다.

 

스프링 시큐리티에서는 기본값으로 클릭재킹 공격을 막기 위해

랜더링을 허용하지 않으려 DENY를 적용한다.

 

sameOrigin() 메서드를 사용하면 동일 출처로부터 들어오는 요청만 랜더링을 허용하는데

현재 코드에서는 H2 데이터베이스를 편리하게 사용하기 위해 부분적으로 허용한다.

@Bean
public MemberService dbMemberService(MemberRepository memberRepository,
                                     PasswordEncoder passwordEncoder) {
    return new DBMemberService(memberRepository, passwordEncoder);
}

이전에는 인메모리 방식의 회원등록을 위해서 UserDetailsManager를 사용하였지만

실제 데이터베이스를 사용할 것이기 때문에 MemberRepository를 추가로 주입받는다.

public void createMember(Member member) {
    String encryptedPassword = passwordEncoder.encode(member.getPassword());
    member.setPassword(encryptedPassword);

    memberRepository.save(member);
}

인메모리DB를 사용하던 createMember 메서드와 마찬가지로

PasswordEncoder를 사용해 암호화된 비밀번호로 수정한 후에

해당 회원을 데이터베이스에 저장하면 된다.

'Back-End > Security' 카테고리의 다른 글

[스프링 시큐리티] 커스텀 로그인 인증 구현  (0) 2023.07.11
[스프링 시큐리티] 커스텀 인증 처리 구현  (0) 2023.07.11
[스프링 시큐리티] 기본  (0) 2023.07.10
Session  (0) 2023.07.07
Cookie  (0) 2023.07.07

+ Recent posts