- 지난 포스팅까지 해서 필터를 등록하고 동작하는 것까지 확인해 보았다. 이제 jwt로그인을 위한 준비를 해보자. 먼저 로그인을 위한 준비 작업을 해주자
PrincipalDetails.java 생성
- UserDetails역할을 할 PrincipalDetails를 생성해주자
package com.pem.jwt.config.auth;
import com.pem.jwt.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
public class PrincipalDetails implements UserDetails {
private User user;
public PrincipalDetails(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
user.getRoleList().forEach(r ->{
authorities.add(() -> r);
});
return authorities;
}
@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;
}
}
PrincipalDetailsService.java
- 이제 username을 받아 PrincipalDetails를 생성해줄 PrincipalDetailsService를 생성해주자
@Service
@RequiredArgsConstructor
public class PrincipalDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("PrincipalDetailsService의 loadUserByUsername");
User user = userRepository.findByUsername(username);
return new PrincipalDetails(user);
}
}
UserRepository 인터페이스 생성
public interface UserRepository extends JpaRepository<User, Long> {
public User findByUsername(String username);
}
- 사실 이렇게 설정을 하고 '/login'로 요청을 보내면 PrincipalDetails와 PrincipalDetailsService가 동작을 해야하는데 SecurityConfig에서 formLogin()메서드를 disable()로 설정하였기 때문에 위의 각 메서드가 동작하지 않는다.
- PrincipalDetails와 rincipalDetailsService가 동작할 수 있도록 필터를 새로 생성해주자!!
JwtAuthenticationFilter.java 생성
- config/jwt 패키지를 생성하여 그 클래스를 생성해주자
// 스프링 시큐리티에서 UsernamePasswordAuthenticationFilter가 있음
// '/login'요청을 해서 username, password를 POST로 전송하면
// UsernamePasswordAuthenticationFilter가 동작함
// 그런데 현재는 '/login'이 작동하지 않기 때문에 필터를 새로 등록해줘야함
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
// '/login'요청을 하면 로그인 시도를 위해서 실행되는 함수
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
System.out.println("JwtAuthenticationFilter : 로그인 시도중");
return super.attemptAuthentication(request, response);
}
}
- 이제 이 필터를 등록해주자. 이게 작동을 한다면 "JwtAuthenticationFilter : 로그인 시도중"이라는 출력문이 콘솔에 찍힐 것이다.
SecurityConfig.java 수정
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
@Override
public void configure(HttpSecurity http) throws Exception {
// AuthenticationManager 생성
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http
.addFilter(corsConfig.corsFilter())
// 추가
// 얘는 AuthenticationManager라는 객체를 인자로 받음
.addFilter(new JwtAuthenticationFilter(authenticationManager));
}
}
- 이제 '/login'으로 요청을 보내보면 아래 필터가 동작하는 것을 확인할 수 있다.
- username, password를 받아서
- 정상인지 로그인을 시도해본다. authenticationManager로 로그인을 시도하면 PrincipalDetailsService의 loadUserByUsername()가 실행되고
- PrincipalDetails를 세션에 담고 (권한 관리를 위해서)
- JWT토큰을 만들어서 응답해주면 된다.