package com.choigoyo.config.auth;
import com.choigoyo.entity.UserEntityJWT;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
@NoArgsConstructor // 기본 생성자
public class PrincipalDetails implements UserDetails {
private UserEntityJWT user;
// 생성자
public PrincipalDetails (UserEntityJWT user){
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
/*
* user.getRoleList()를 통해 해당 유저가 가지고 있는 모든 역할(Role)을 가져온 뒤, 각각의 역할에 대한 GrantedAuthority 객체를 생성하여 이를 authorities 리스트에 추가
* GrantedAuthority는 권한 정보를 나타내는 인터페이스
* */
user.getRoleList().forEach(r ->{
authorities.add(() -> r);
});
return null;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUserName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
getAuthorities() 메소드는 해당 유저 객체가 가지고 있는 권한 정보를 Spring Security가 이해할 수 있는 형태로 반환하는 역할을 합니다.
위 코드에서는 user.getRoleList()를 통해 해당 유저가 가지고 있는 모든 역할(Role)을 가져온 뒤, 각각의 역할에 대한 GrantedAuthority 객체를 생성하여 이를 authorities 리스트에 추가하고 있습니다.
Spring Security에서 GrantedAuthority는 권한 정보를 나타내는 인터페이스이며, 각각의 권한은 문자열 형태로 저장됩니다.
위 코드에서는 람다식을 이용하여 역할(Role) 이름을 GrantedAuthority 객체로 변환하여 authorities 리스트에 추가하고 있습니다.
따라서 getAuthorities() 메소드는 해당 유저 객체가 가지고 있는 모든 역할에 대한 GrantedAuthority 리스트를 반환하는 역할을 합니다.
이외 @Override 메서드는 spring security 시리즈에 정리하였으므로 다음으로 넘어가겠습니다.
package com.choigoyo.config.auth;
import com.choigoyo.entity.UserEntityJWT;
import com.choigoyo.repository.UserRepository;
import lombok.RequiredArgsConstructor;
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;
@Service
@RequiredArgsConstructor
public class PrincipalDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
System.out.println("PrincipalDetails 의 loadUserByUsername()");
UserEntityJWT userEntity = userRepository.findByUserName(userName);
return new PrincipalDetails(userEntity);
}
}
package com.choigoyo.repository;
import com.choigoyo.entity.UserEntityJWT;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<UserEntityJWT,Long> {
UserEntityJWT findByUserName(String userName);
}
PrincipalDetailsService 클래스는
http://localhost:8081/login요청이 들어왔을 때 동작을 합니다.
하지만 설정에서 Spring Security 기본 로그인 요청 주소를 SecurityConfig 설정에 form 로그인을 사용하지 않겠다 정했기 때문에 404에러가 발생하게됩니다.
로그인 시도를 테스트하기위해 필터를 추가해 주었습니다.
package com.choigoyo.config.JWT;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
// http://localhost:8081/login 요청이 들어오면 authenticationManager 통해서 로그인시도
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
System.out.println("JwtAuthenticationFilter : 로그인 시도중...");
return super.attemptAuthentication(request, response);
}
}
Spring Security 의 UsernamePasswordAuthenticationFilter 를 확장하여 사용하게되면
사용자가 http://localhost:8081/login 주소로 userName,password 를 post 형식으로 전송하였을 때
UsernamePasswordAuthenticationFilter 가 동작합니다.
SecurityConFig 클래스에 form 로그인을 비활성화 시켜서 동작하지 않는 상태이나,
JwtAuthenticationFilter 를 다시 SecurityConFig 클래스에 등록해주면 동작하게됩니다.
만들어 둔 필터를 추가해주고 postman으로 로그인 요청을 보내면
필터가 잘 작동하는지 확인해보겠습니다.
404 에러는 사라졌고, 500에러가 생겼지만 아직 코드를 추가하지 않아서 생긴 오류니 무시하고 intelliJ 콘솔창을 확인해보겠습니다.
메서드가 정상적으로 실행되어 print문이 출력됩니다.