Spring Security (5)

ysh·2023년 8월 7일
0

Spring Boot

목록 보기
51/53
post-custom-banner

password encoder

config

package com.example.my.config.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class PasswordConfig {
    
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

}

인코딩 해서 저장

String encodedPassword = passwordEncoder.encode(dto.getUser().getPassword());

        UserEntity userEntityForSaving = UserEntity.builder()
                .id(dto.getUser().getId())
                .password(encodedPassword)
                .createDate(LocalDateTime.now())
                .build();

확인 시

passwordEncoder.matches(dto.getUser().getPassword(), userEntity.getPassword());

getAuthorities

grandtedAuthority안에 getAuthority는
그냥 정보에 "ROLE_"을 붙이는 것 뿐

익명 클래스 - 인터페이스를 사용할 때 구현하기 귀찮으니 그냥 만듦

@Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();

        for (String role : user.roleList) {
            GrantedAuthority grantedAuthority = new GrantedAuthority() {
                @Override
                public String getAuthority() {
                    return "ROLE_" + role;
                }
            };
            grantedAuthorityList.add(grantedAuthority);
        }

        return grantedAuthorityList;
    }

UserDetailsService에서 아이디로만 찾으면 패스워드 등은 알아서 처리해줌.

전체 코드

PasswordConfig.java

package com.example.my.config.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class PasswordConfig {
    
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

CustomUserDetails.java

package com.example.my.config.security.auth;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@Getter
public class CustomUserDetails implements UserDetails {

    // private String username;
    // private String password;
    // private List<GrantedAuthority> authorities;

    private User user;

    @NoArgsConstructor
    @AllArgsConstructor
    @Getter
    @Builder
    public static class User{
        private Long idx;
        private String id;
        private String password;
        private List<String> roleList;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {

        // return user.roleList
        // .stream()
        // .map(role -> (GrantedAuthority) () -> "ROLE_" + role)
        // .toList();
        
        
        List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();

        for (String role : user.roleList) {
            GrantedAuthority grantedAuthority = new GrantedAuthority() {
                @Override
                public String getAuthority() {
                    return "ROLE_" + role;
                }
            };
            grantedAuthorityList.add(grantedAuthority);
        }

        return grantedAuthorityList;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getId();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
    
}

CustomUserDetailsService.java

package com.example.my.config.security.auth;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.my.common.exception.BadRequestException;
import com.example.my.config.security.auth.CustomUserDetails.User;
import com.example.my.model.user.entity.UserEntity;
import com.example.my.model.user.entity.UserRoleEntity;
import com.example.my.model.user.repository.UserRepository;

import lombok.RequiredArgsConstructor;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService{

    private final UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        
        Optional<UserEntity> userEntityOptional = userRepository.findByIdAndDeleteDateIsNull(username);
        
        if(userEntityOptional.isEmpty()){
            throw new BadRequestException("아이디 또는 비밀번호 정확히 입력해주세요.");
        }

        UserEntity userEntity = userEntityOptional.get();

        List<String> roleList = new ArrayList<>();
        
        // List<Stirng> roleList userEntity.getUserRoleEntityList()
        
        // .stream()
        // // for each 앞이 앞에
        // .map(userRoleEntity -> userRoleEntity.getRole())
        // .toList();

        for (UserRoleEntity userRoleEntity : userEntity.getUserRoleEntityList()) {
            roleList.add(userRoleEntity.getRole());
        }

        User user = CustomUserDetails.User.builder()
        .idx(userEntity.getIdx())
        .id(userEntity.getId())
        .password(userEntity.getPassword())
        .roleList(roleList)
        .build();

        return new CustomUserDetails(user);
    }  
}

끄적 :

UserDetails에서는 유저의 정보를 SpringSecurity에서 쓰일 수 있는 형식으로 변환(UserDetails)
UserDetailsService는 DB에서 유저의 정보를 찾아서 UserDetails형식으로 변환 후 반환.

회원가입은 직접 만들어서?
-> 직접 만들어서 쓰고, 로그인 할 시에 SpringSecurity의 형식에 맞게 불러오면 됨(그게 UserDetails, UserDetailsService에서 하는 작업)

그 후 DB에 저장된 정보들로 로그인하고, Autentication 에 정보를 저장해서 편하게 쓰기 위한 것?
-> 맞다. 로그인하고 권한 등을 저장 / 로그인해서 Autentication에 넣는 과정.

그러면 DB에 만료, 정지 정보등을 넣어놓으면 user.getId() 형식으로 간단하게 설정 가능?
-> 맞다. UserDetails 아래에서 각 함수의 이름에 맞게 내용을 작성해주면 됨.

@Override
    public boolean isAccountNonExpired() {
    	// User에 저장된 exprieDate(임시 이름)이
        // 오늘보다 앞이면 false, 뒤면 true를 발생시켜
        return result;
        // result값이 false가 되면, Exception 발생(나중에 Exception 이름 GPT한테 물어보기)
    }

Autentication의 정보로 config?의 authorizeHttpRequests?
실행할 때 적용? 계속 확인하는 게 아니라?
-> 요청이 들어올 때마다 컨트롤러에서 우리가

@AuthenticationPrincipal CustomUserDetails customUserDetails

형식으로 정보를 보내고 요청을 보낼 때마다 securityFilterChain, 즉 SecurityConfig의 내용을 확인함

허용하는 방식이 다른 방식도 있나? "/auth/** "
-> 어차피 vue도 api 요청할 때는 형식이 같아서 상관 없다.
근데 페이지 자체 접근에 대해서는 vue에서 처리해야 할 듯.

profile
유승한
post-custom-banner

0개의 댓글