Spring Security - 정수원 8회차

머어조·2022년 10월 27일
0

Spring Security

목록 보기
8/8
post-custom-banner

4. DB연동 인증 처리 : CustomUserDetailService

  1. UserDetailService Interface 를 상속 받는 CustomUserDetailService클래스를 만든다.

  2. UserDetails(인터페이스)를 반환하는 loadUserByUsername 함수를 오버라이드 한다.

  3. UserDetails 인터페이스를 직접 구현해서 커스텀하게 만들 수 있지만 Spring에서 UserDetails를 구현한 클래스 User를 제공한다

  4. UserDetails를 구현한 User클래스를 상속 받는 AccountContext를 만든다

  5. loadUserByUsername 함수에서 DB에서 유저 불러와서 가져온다음, AccountContext에 유저정보와 권한 정보를 넣어준다.

    List<GrantedAuthority> roles = new ArrayList<>();
    roles.add(new SimpleGrantedAuthority(account.getRole()));
@Service("userDetailsService")
public class CustomUserDetailService implements UserDetailsService {

    private final UserRepository userRepository;

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

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Account account = userRepository.findByUsername(username);

        if (account == null) {
            throw new UsernameNotFoundException("UsernameNotFoundException");
        }

        List<GrantedAuthority> roles = new ArrayList<>();
        roles.add(new SimpleGrantedAuthority(account.getRole()));

        AccountContext accountContext = new AccountContext(account, roles);

        return accountContext;
    }
}

정리

  • 시큐리티로 만든 로그인 처리를 할 때 디비에서 정보를 가져와서 그 사용자가 맞는지 체크해주는 것.
  • 로그인 → 디비에서 사용자 정보 가져와 확인 → 권한도 확인 → 인증 처리

5. DB연동 인증 처리 : CustomAuthenticationProvider

  • 위에서 반환 했던 AccountContext를 가지고 추가 적인 인증 처리를 한다
  1. AuthenticationProvider 인터페이스를 상속 받는다

  2. authenticatesupports 메소드를 오버라이드 한다.

  3. supports 메소드로 UsernamePasswordAuthenticationToken 이랑 파라미터로 전달 받은 Authentication 클래스와 같은 클래스 종류인지 판별하고

  4. authenticate 에서 UserDetailService를 주입 받고 AccountContext객체를 가져온다음 검수 하고

    UsernamePasswordAuthenticationToken 객체를 만들어 줘서 반환 시킨다.

public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String username = authentication.getName();
        String password = (String) authentication.getCredentials();

        AccountContext accountContext = (AccountContext) userDetailsService.loadUserByUsername(username);

        if (!passwordEncoder.matches(password, accountContext.getAccount().getPassword())) {
            throw new BadCredentialsException("BadCredentialsException");
        }

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(accountContext.getAccount(), null, accountContext.getAuthorities());

        return authenticationToken;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
  • 따라서 기존에 CustomUserDetailService 에서는 DB에 있는 정보를 가져와서 있는지 없는지 여부만 체크해 줬고 AccountContext를 반환 하였고, CustomAuthenticationProvider 에서는 CustomUserDetailService 를 활용해서 UsernamePasswordAuthentication에서 주는 Authentication 객체와 DB에서 조회하였던 AccountContext를 비교하여 로그인 처리를 해준다

6. 커스텀 로그인 페이지

.formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login_proc")
                .defaultSuccessUrl("/")
                .permitAll();

7. 로그아웃 및 인증에 따른 화면 보안 처리

로그아웃 방법

  1. 태그를 사용해서 POST로 요청 - 이때 LogoutFilter가 동작해서 실제 로그아웃을 진행
  2. 태그를 사용해서 GET으로 요청 - SecurityContextLogoutHandler 활용
    • 이때는 LogoutFilter가 동작안하고 SecurityContextLogoutHandler 커스텀하여 처리해야됨
  • 실질 적으로 LogoutFilter 도 SecurityContextLogoutHandler 를 활용하여 로그아웃 처리함
<li class="nav-item" sec:authorize="isAnonymous()">
	<a class="nav-link text-light" th:href="@{/login}">로그인</a>
</li>

<li class="nav-item" sec:authorize="isAuthenticated()">
	<a class="nav-link text-light" th:href="@{/logout}">로그아웃</a>
</li>

// LoginController.class
@GetMapping("/logout")
public String logout(HttpServletRequest request, HttpServletResponse response) {

    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

    if (authentication != null) {
        new SecurityContextLogoutHandler().logout(request, response, authentication);
    }

    return "redirect:/login";
}
  • SecurityContextLogoutHandler 는 파라미터가 3개 필요하고, 로그인 되었던 정보는 SecurityContextHolder.getContext().getAuthentication() 에서 가져온다

8. Form 인증 - WebAuthenticationDetails, AuthenticationDetailsSource

  1. 사용자가 인증 요청
  2. 기본적으로 username과 password를 담고 있다. AuthenticationFilter
    • 추가적으로 정보를 저장하고 싶으면 WebAuthenticationDetails를 활용하여 함, 그리고 WebAuthenticationDetailsAuthenticationDetailsSource가 생성한다.
  3. AuthenticationFilterAuthentication을 생성하고 기본적으로 id와 password가 저장된다. Authentication 는 내부적으로 details라는 속성을 가지고 있고 Object 타입이다.(타입과 상관없이 어느 타입이든 저장 가능)
  4. details라는 속성에 WebAuthenticationDetails 객체를 만들어서 저장
  5. WebAuthenticationDetails 는 request라는 파라미터를 받아서 parameter값을 저장하고 details라는 속성에 저장된다(remoteAddress와 SessionId는 Security가 처리함)
    • 참고로 AuthenticationDetailsSource 가 만든다 WebAuthenticationDetails
profile
너어무 조오아
post-custom-banner

0개의 댓글