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());
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에서 아이디로만 찾으면 패스워드 등은 알아서 처리해줌.
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();
}
}
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;
}
}
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에서 처리해야 할 듯.