본문 바로가기

Spring실습

Spring Security + jwt를 이용하여 로그인 구현하기(2)

반응형

이번시간에는 저번 글에 이어서 SpringAuthenticationFilter.java와

SpringAuthenticationProvider.java 에 대해서 이야기 해보겠습니다.

 

* 이전 글을 읽어보고 오시는 것을 추천드립니다.

https://pinlib.tistory.com/entry/Spring-Security-jwt%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B01

 

Spring Security + jwt를 이용하여 로그인 구현하기(1)

드디어 spring security에 관하여 실습편을 작성하게 되었습니다. 저도 공부를 하다보니 이에 대하여 작성하는데 시간이 많이 소요되었습니다. 우선 이론편에서 말씀드린데로 해당 Architecture에 맞추

pinlib.tistory.com

 

우선 SpringAuthenticationFilter에 대해서 이야기해보겠습니다.

public class SpringAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

우선 spring security에서 기본적으로 제공하는 UsernamePasswordAuthenticationFilter를 이용합니다.

 

public SpringAuthenticationFilter(final AuthenticationManager authenticationManager) {
    super.setAuthenticationManager(authenticationManager);
}

@Override
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException {
    final UsernamePasswordAuthenticationToken authRequest;
    try{
        final User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
        authRequest = new UsernamePasswordAuthenticationToken(user.getUserId(), user.getUserPassword());
    } catch (IOException exception){
        throw new InputNotFoundException();
    }
    setDetails(request, authRequest);
    return this.getAuthenticationManager().authenticate(authRequest);
}

SpringAuthenticationFilter는 입력받은 id와 password를 기반으로 UsernamePasswordAuthenticationToken을 발급합니다.

이렇게 발급한 token은 authenticationManager를 통해 provider로 전달되어 인증절차를 수행하며 검증합니다.

 

 

이번에는 SpringAuthenticationProvider.java에 대해서 이야기 해보겠습니다.

public class SpringAuthenticationProvider implements AuthenticationProvider {

해당 code를 보시면 AuthenticationProvider를 상속하고 있습니다.

AuthenticationProvider를 상속하게 되면 authenticate method를 override하여 사용할 수 있습니다.

 

@Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
    final UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;

    // AuthenticaionFilter에서 생성된 토큰으로부터 아이디와 비밀번호를 조회함
    final String userId = token.getName();
    final String userPassword = (String) token.getCredentials();

    // UserDetailsService를 통해 DB에서 아이디로 사용자 조회
    final MyUserDetails userDetails = (MyUserDetails) userDetailsService.loadUserByUsername(userId);
    if (!passwordEncoder.matches(userPassword, userDetails.getPassword())) {
        throw new BadCredentialsException(userDetails.getUsername() + "Invalid password");
    }
    log.info("this is success");

    return new UsernamePasswordAuthenticationToken(userDetails, userPassword, userDetails.getAuthorities());
}

provider의 authenticate는 실제 인증과정을 처리합니다.

authenticateManager를 통해 전달받은 UsernamePasswordAuthenticationToken의 아이디와 비밀번호들을

userDetailsService를 통해 DB에서 아이디로 사용자를 조회하여 검증하는 절차를 가집니다.

 

여기서 userDetails의 경우

@RequiredArgsConstructor
@Getter
public class MyUserDetails implements UserDetails {

    @Delegate
    private final User user;
    //private final User user;
    private final Collection<? extends GrantedAuthority> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }


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

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


    @Override
    public boolean isAccountNonExpired() {
        return user.getIsEnable();
    }

    @Override
    public boolean isAccountNonLocked() {
        return user.getIsEnable();
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return user.getIsEnable();
    }

    @Override
    public boolean isEnabled() {
        return user.getIsEnable();
    }
}

 

이러한 code로 구성되어 있는데 user entity를 기반으로 생성되어 있다고 생각하시면 됩니다.

 

또한 만들어진 MyUserDetails를 바탕으로 UserDeatilServiceImpl.java를 생성합니다.

@Service
public class UserDetailServiceImpl implements UserDetailsService {
    private final UserRepository userRepository;

    public UserDetailServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }


    @Override
    public MyUserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
        List<User> userList = userRepository.findByUserId(userId);
        if (userList.isEmpty()) {
            throw new UserNotFoundException(userId);
        }
        User user = userList.get(0);

        // Get the authorities/roles for the user
        Collection<? extends GrantedAuthority> authorities = getAuthorities(user);

        return new MyUserDetails(user, authorities);
    }

    private Collection<? extends GrantedAuthority> getAuthorities(User user) {

        String Name = user.getUserName();
        String Email = user.getUserEmail();
        Integer Age = user.getUserAge();

        //GrantedAuthority authority = new SimpleGrantedAuthority(Email);

        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority(Name));
        authorities.add(new SimpleGrantedAuthority(Email));
        authorities.add(new SimpleGrantedAuthority(Age.toString()));

        return authorities;

        //return Collections.singleton(authority);
    }

}

이렇게 만들어진 MyUserDetails.java와 UserDetailServiceImpl.java를 이용하여

SpringAuthenticationProvider.java에서 token에 대한 검증을 한다고 생각하시면 됩니다.

 

추가적으로 provider code에서는 

@Override
public boolean supports(Class<?> authentication) {
    return authentication.equals(UsernamePasswordAuthenticationToken.class);
}

이러한 method를 사용하는데 

해당 method의 경우 AuthenticationProvider 표시된 Authentication객체를 지원하는지에 대한 여부를 반환 합니다.

 

이제 남은 소개드릴 file은 

TokenUtils.java, HeaderFilter.java, WebMvcConfig.java, JwtTokenIntercepter.java, SpringLoginSuccessHandler.java

그리고 실질적인 로그인을 위한 service와 controller 정도 남았는데 차차 또 게시물을 올리도록 하겠습니다.

 

 

반응형