Spring/Security

Spring Security + JWT를 이용한 자체 Login & OAuth2 Login(구글, 네이버, 카카오) API 구현 (4) - 자체 JSON 로그인 커스텀하기

BE_성하 2022. 11. 27. 15:14
반응형

이전 챕터에서 JWT 관련 클래스와 인증 로직을 살펴봤습니다.

 

이번 챕터에서는 자체 JSON 로그인 관련 클래스들을 커스텀해보겠습니다.

 

저는 자체 로그인 시 일반 Form Login이 아닌,

아이디와 비밀번호를 JSON 형식으로 RequestBody로 보내는 로그인 방식을 구현할 것입니다.

{
    "email" : "aaa@bbb.com"
    "password" : "test123"
}

이런 식으로, JSON으로 Email, Password를 보내 로그인을 하는 로그인 방식을 구현할 것입니다.

 

 

Spring Security에서는 일반 Form Login을 기본적으로 제공하지만,

JSON 형식의 RequestBody로 하는 로그인 방식은 기본적으로 제공하지 않습니다.

 

따라서, JSON 로그인 방식을 사용하기 위해 커스텀 필터를 구현해야합니다.

이때, Form Login 시 기본적으로 사용되는

UsernamePasswordAuthenticationFilter의 코드를 참고하여 구현합니다.

 

기본 원리들은 아래의 블로그에 잘 설명되어 있으니 참고해주시면 감사하겠습니다.

https://ttl-blog.tistory.com/268

 

스프링부트 게시판 API 만들기 (3) - JSON 로그인 (2) - 시큐리티 로그인 동작원리

이번에는 지난 포스팅에 이어서 스프링 시큐리티를 이용하여 로그인을 진행하는 코드를 작성하기 전에 스프링 시큐리티의 로그인 진행방식을 살펴보겠습니다. 이번 포스팅은 조금 길기 때문에

ttl-blog.tistory.com


0. 패키지 구조

JSON 로그인 관련 클래스 패키지 구조


1. 커스텀 JSON 로그인 필터 구현

 

Form Login 시 기본적으로 사용되는

UsernamePasswordAuthenticationFilter에서

AbstractAuthenticationProcessingFilter를 상속받아 구현하기 때문에,

커스텀 JSON 필터에서도 AbstractAuthenticationProcessingFilter를 상속받아 구현했습니다.

 

 

전체 코드를 먼저 보여드리고, 그 후에 부분적으로 설명드리겠습니다.

 

🎯 CustomJsonUsernamePasswordAuthenticationFilter 전체 코드

package login.oauthtest4.global.login.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StreamUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * 스프링 시큐리티의 폼 기반의 UsernamePasswordAuthenticationFilter를 참고하여 만든 커스텀 필터
 * 거의 구조가 같고, Type이 Json인 Login만 처리하도록 설정한 부분만 다르다. (커스텀 API용 필터 구현)
 * Username : 회원 아이디 -> email로 설정
 * "/login" 요청 왔을 때 JSON 값을 매핑 처리하는 필터
 */
public class CustomJsonUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private static final String DEFAULT_LOGIN_REQUEST_URL = "/login"; // "/login"으로 오는 요청을 처리
    private static final String HTTP_METHOD = "POST"; // 로그인 HTTP 메소드는 POST
    private static final String CONTENT_TYPE = "application/json"; // JSON 타입의 데이터로 오는 로그인 요청만 처리
    private static final String USERNAME_KEY = "email"; // 회원 로그인 시 이메일 요청 JSON Key : "email"
    private static final String PASSWORD_KEY = "password"; // 회원 로그인 시 비밀번호 요청 JSon Key : "password"
    private static final AntPathRequestMatcher DEFAULT_LOGIN_PATH_REQUEST_MATCHER =
            new AntPathRequestMatcher(DEFAULT_LOGIN_REQUEST_URL, HTTP_METHOD); // "/login" + POST로 온 요청에 매칭된다.

    private final ObjectMapper objectMapper;

    public CustomJsonUsernamePasswordAuthenticationFilter(ObjectMapper objectMapper) {
        super(DEFAULT_LOGIN_PATH_REQUEST_MATCHER); // 위에서 설정한 "login" + POST로 온 요청을 처리하기 위해 설정
        this.objectMapper = objectMapper;
    }

    /**
     * 인증 처리 메소드
     *
     * UsernamePasswordAuthenticationFilter와 동일하게 UsernamePasswordAuthenticationToken 사용
     * StreamUtils를 통해 request에서 messageBody(JSON) 반환
     * 요청 JSON Example
     * {
     *    "email" : "aaa@bbb.com"
     *    "password" : "test123"
     * }
     * 꺼낸 messageBody를 objectMapper.readValue()로 Map으로 변환 (Key : JSON의 키 -> email, password)
     * Map의 Key(email, password)로 해당 이메일, 패스워드 추출 후
     * UsernamePasswordAuthenticationToken의 파라미터 principal, credentials에 대입
     *
     * AbstractAuthenticationProcessingFilter(부모)의 getAuthenticationManager()로 AuthenticationManager 객체를 반환 받은 후
     * authenticate()의 파라미터로 UsernamePasswordAuthenticationToken 객체를 넣고 인증 처리
     * (여기서 AuthenticationManager 객체는 ProviderManager -> SecurityConfig에서 설정)
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException {
        if(request.getContentType() == null || !request.getContentType().equals(CONTENT_TYPE)  ) {
            throw new AuthenticationServiceException("Authentication Content-Type not supported: " + request.getContentType());
        }

        String messageBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);

        Map<String, String> usernamePasswordMap = objectMapper.readValue(messageBody, Map.class);

        String email = usernamePasswordMap.get(USERNAME_KEY);
        String password = usernamePasswordMap.get(PASSWORD_KEY);

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(email, password);//principal 과 credentials 전달

        return this.getAuthenticationManager().authenticate(authRequest);
    }
}

🎯 CustomJsonUsernamePasswordAuthenticationFilter 부분 코드 설명

 

💻  CustomJsonUsernamePasswordAuthenticationFilter 생성자 부분

public CustomJsonUsernamePasswordAuthenticationFilter(ObjectMapper objectMapper) {
    super(DEFAULT_LOGIN_PATH_REQUEST_MATCHER); // 위에서 설정한 "login" + POST로 온 요청을 처리하기 위해 설정
    this.objectMapper = objectMapper;
}

super()를 통해 부모 클래스인 AbstractAuthenticationProcessingFilter()의 

생성자 파라미터로 위에서 상수로 선언한 "/login" URL을 설정해줬습니다.

이렇게 설정함으로써 우리가 만든 필터는 "/login" URL이 들어올 시 작동하게 됩니다.

 

또한, 생성자의 파라미터로 ObjectMapper를 받아 ObjectMapper를 생성자 주입해줬습니다.

 

 

💻  AbstractAuthenticationProcessingFilter()의 Override 메소드

/**
 * 인증 처리 메소드
 *
 * UsernamePasswordAuthenticationFilter와 동일하게 UsernamePasswordAuthenticationToken 사용
 * StreamUtils를 통해 request에서 messageBody(JSON) 반환
 * 요청 JSON Example
 * {
 *    "email" : "aaa@bbb.com"
 *    "password" : "test123"
 * }
 * 꺼낸 messageBody를 objectMapper.readValue()로 Map으로 변환 (Key : JSON의 키 -> email, password)
 * Map의 Key(email, password)로 해당 이메일, 패스워드 추출 후
 * UsernamePasswordAuthenticationToken의 파라미터 principal, credentials에 대입
 *
 * AbstractAuthenticationProcessingFilter(부모)의 getAuthenticationManager()로 AuthenticationManager 객체를 반환 받은 후
 * authenticate()의 파라미터로 UsernamePasswordAuthenticationToken 객체를 넣고 인증 처리
 * (여기서 AuthenticationManager 객체는 ProviderManager -> SecurityConfig에서 설정)
 */
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException {
    if(request.getContentType() == null || !request.getContentType().equals(CONTENT_TYPE)  ) {
        throw new AuthenticationServiceException("Authentication Content-Type not supported: " + request.getContentType());
    }

    String messageBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);

    Map<String, String> usernamePasswordMap = objectMapper.readValue(messageBody, Map.class);

    String email = usernamePasswordMap.get(USERNAME_KEY);
    String password = usernamePasswordMap.get(PASSWORD_KEY);

    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(email, password);//principal 과 credentials 전달

    return this.getAuthenticationManager().authenticate(authRequest);
}

기본적으로 AbstractAuthenticationProcessingFilter

attemptAuthentication()은 인증 처리 메소드입니다.

이를 Override하여 커스텀 필터에서도 인증 처리를 구현하였습니다.

 

 

if(request.getContentType() == null || 

!request.getContentType().equals(CONTENT_TYPE)

요청의 ContentType이 null이거나 우리가 설정한 application/json이 아니라면,

AuthenticationServiceException 예외를 발생시켜 JSON으로 요청하도록 했습니다. 

 

 

 

✅ Map<String, String> usernamePasswordMap =

objectMapper.readValue(messageBody, Map.class)

JSON 요청을 String으로 변환한 messageBody를

objectMapper.readValue()를 통해 Map으로 변환하여 각각 Email, Password를 저장했습니다.

 

 

UsernamePasswordAuthenticationToken authRequest =

new UsernamePasswordAuthenticationToken(email, password)

FormLogin 필터인 UsernamePasswordAuthenticationFilter와 동일하게 

UsernamePasswordAuthenticationToken 객체를 사용하였습니다.

이 객체는 인증 처리 객체인 AuthenticationManager가 인증 시 사용할 인증 대상 객체가 됩니다.

 

파라미터로 넘겨준 email이 인증 대상 객체의 principal, password가

인증 대상 객체의 credentials이 됩니다.

여기서 만든 UsernamePasswordAuthenticationToken 인증 대상 객체를 통해

인증 처리 객체인 AuthenticationManager가 인증 성공/인증 실패 처리를 하게 됩니다.

 

※ 이때, 인증 처리 객체 AuthenticationManager로 무슨 객체를 사용할지 setter로 설정해야 하는데,

이 과정은 이후의 스프링 시큐리티 설정 클래스인 SecurtyConfig 클래스를 다룰 때 설명드리겠습니다.

 

※ 인증 처리 객체 AuthenticationManager로는 FormLogin과 동일하게 ProviderManager를 사용,

ProviderManager의 구현체로 DaoAuthenticationProvider 객체를 사용합니다.

이후에 스프링 시큐리티 설정 클래스인 SecurityConfig 클래스에서 좀 더 자세하게 다루겠습니다.

 


2. 커스텀 로그인 서비스 구현

 

이 로그인 서비스는 UserDetailsService 인터페이스를 구현합니다.

 

UserDetailsService의 loadUserByUsername() 메소드를 Override를 하여 구현합니다.

이 메소드의 역할은, UserDetails의 User 객체를 만들어서 반환하는 역할을 수행합니다.

 

 

loadUserByUsername() 메소드를 이해하려면, UserDetailsService를 이해하는 것이 필요합니다.

UserDetailsService DaoAuthenticationProvider와 협력하는 인터페이스입니다.

위에 커스텀 필터 구현 설명 시 잠깐 언급되었지만, 이후에 인증 처리 객체로 사용될 객체가

DaoAuthenticationProvider 객체입니다.

 

UserDetailsService를 이해하기 위해서는,

Spring Security의 기본 로그인 인증 흐름을 이해해야합니다.

다음 그림을 보고 로그인 인증 흐름을 살펴봅시다.

📕 Spring Security 기본 로그인 인증 흐름

출처 - Spring Security 공식 문서&nbsp;10.10.9

 

1. 위에 커스텀 필터에서 인증 대상 객체를 UsernamePasswordAuthenticationToken로 설정했었습니다.
   이 객체는 클라이언트 Request의 ID(Username), PW를 가지고 있습니다.
   이 객체를 AuthenticationManager(ProviderManager)에게 전달합니다.
   
2. ProviderManager가 전달받은 UsernamePasswordAuthenticationToken를 
   ProviderManager의 구현체인 DaoAuthenticationProvider로 전달합니다.
   
3. DaoAuthenticationProvider는 UserDetailsService의 
   loadUserByUsername(String username)을 호출하여 UserDetails 객체를 반환받습니다.
   
   이때, loadUserByUsername의 파라미터인 username은 DaoAuthenticationProvider가
   UsernamePasswordAuthenticationToken에서 username을 꺼내어 설정해줍니다.
   
   따라서, UserDetialsService의 loadUserByUsername(String username)에서
   클라이언트 Request의 username을 통해 DB에서 유저를 찾아 있다면, 
   USER Entity를 반환하고(JPA 사용 시), 그 Entity를 내부적으로 UserDetails 객체로
   만들어서 반환받습니다.
   
4. 반환받은 UserDetails 객체의 password를 꺼내어, 내부의 PasswordEncoder에서
   password가 일치하는지 검증을 수행합니다.
   
5. 비밀번호가 일치한다면, 인증 대상 객체인 UsernamePasswordAuthenticationToken에
   UserDetails 객체와 Authorities를 담아서 반환해줍니다.
   이후에 ProviderManager에서 반환된 UserDetails 객체와 Authorities가 담긴 
   UsernamePasswordAuthenticationToken으로 인증 객체를 생성하여 인증 성공 처리를 합니다.

참고로, 이는 Spring Security의 기본 Form Login 로직이지만,

만들 JSON 자체 Login 방식과도 거의 다른게 없습니다.

 

1번 과정에서 UsernamePasswordAuthenticationToken에 ID, PW가 저장될 때

클라이언트의 Request가 form-data가 아닌 application/json으로 들어오기 때문에

JSON 데이터를 받아서 UsernamePasswordAuthenticationToken을 저장한다는 것만 다르고,

나머지 로직들은 모두 동일합니다.

 

 

이 흐름에서, AuthenticationManager를 설정하는 과정들은

이후에 SecurityConfig 클래스를 다룰 때 한번 더 설명하고,

지금은 UserDetailsService를 커스텀하여 구현하는 것을 설명드리겠습니다.

 

 

본론으로 돌아와서, UserDetails 객체를 반환하는 UserDetailsService를 커스텀하여 구현해야합니다.

구현해야 하는 로직은 다음과 같습니다.

1. DaoAuthenticationProvider가 꺼내어 파라미터로 설정해준 username(email)로 
   DB의 해당하는 User 찾는 로직
   
2. 해당 유저의 username(email), password, Role을 담아서 UserDetails의 User 객체를 생성하는 로직

이 두 로직을 구현한 커스텀 LoginService 코드를 보겠습니다.

 

🎯 LoginService 전체 코드

package login.oauthtest4.global.login.service;

import login.oauthtest4.domain.user.User;
import login.oauthtest4.domain.user.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 LoginService implements UserDetailsService {

    private final UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new UsernameNotFoundException("해당 이메일이 존재하지 않습니다."));

        return org.springframework.security.core.userdetails.User.builder()
                .username(user.getEmail())
                .password(user.getPassword())
                .roles(user.getRole().name())
                .build();
    }
}

1. userRepository.findByEmail()DaoAuthenticationProvider가 설정해준 email을 가진 유저를 찾아왔습니다.

 

2. UserDetails의 User 객체의 Builder를 사용하여 username(email), password, roles를 빌드하고,

    파라미터로 DB에서 찾은 유저의 Email, Password, Role을 받아 User 객체를 생성하여 반환했습니다.

 

 

※ 여기서, User의 역할 Enum인 Role의 키에 "ROLE_"을 접두사로 붙였는지 알 수 있습니다.

 UserDetails의 User 객체를 생성할 때, UserBuilder 객체를 사용하여 build를 하는데,

roles 필드를 build할 때 코드가 다음과 같습니다.

public UserBuilder roles(String... roles) {
    List<GrantedAuthority> authorities = new ArrayList<>(roles.length);
    for (String role : roles) {
        Assert.isTrue(!role.startsWith("ROLE_"),
                () -> role + " cannot start with ROLE_ (it is automatically added)");
        authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
    }
    return authorities(authorities);
}

Assert.isTrue(!role.startsWith("ROLE_"), () -> ...) 코드를 보면,

파라미터로 들어온 role들이 "ROLE_"으로 시작하지 않으면, 예외를 발생시키는 것을 알 수 있습니다.

 

따라서, 인증 시 사용하는 UserDetails의 User 객체의 role이 "ROLE_"으로 시작해야 때문에

Role ENUM에서 키에 "ROLE_" 접두사를 붙인 것입니다.

 


3. JSON 로그인 성공 시 핸들러 구현

 

위에 커스텀 JSON 로그인 필터를 정상적으로 통과하여 인증 처리가 되었을 때(로그인 성공)

로그인 성공 핸들러가 동작하는데, 이 로그인 성공 핸들러를 구현했습니다.

 

SimpleUrlAuthenticationSuccessHandler를 상속받아 구현했습니다.

 

🎯 LoginSuccessHandler 전체 코드

package login.oauthtest4.global.login.handler;

import login.oauthtest4.domain.user.repository.UserRepository;
import login.oauthtest4.global.jwt.service.JwtService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@RequiredArgsConstructor
public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    private final JwtService jwtService;
    private final UserRepository userRepository;

    @Value("${jwt.access.expiration}")
    private String accessTokenExpiration;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) {
        String email = extractUsername(authentication); // 인증 정보에서 Username(email) 추출
        String accessToken = jwtService.createAccessToken(email); // JwtService의 createAccessToken을 사용하여 AccessToken 발급
        String refreshToken = jwtService.createRefreshToken(); // JwtService의 createRefreshToken을 사용하여 RefreshToken 발급

        jwtService.sendAccessAndRefreshToken(response, accessToken, refreshToken); // 응답 헤더에 AccessToken, RefreshToken 실어서 응답

        userRepository.findByEmail(email)
                .ifPresent(user -> {
                    user.updateRefreshToken(refreshToken);
                    userRepository.saveAndFlush(user);
                });
        log.info("로그인에 성공하였습니다. 이메일 : {}", email);
        log.info("로그인에 성공하였습니다. AccessToken : {}", accessToken);
        log.info("발급된 AccessToken 만료 기간 : {}", accessTokenExpiration);
    }

    private String extractUsername(Authentication authentication) {
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        return userDetails.getUsername();
    }
}

기본적으로 SimpleUrlAuthenticationSuccessHandler를 상속받아서 구현하기 때문에,

부모 클래스의 성공 시 처리 메소드 onAuthenticationSuccess()를 Override했습니다.

 

JSON 로그인 필터를 정상적으로 통과해서 인증처리가 됐기 때문에,

내부에서 AccessToken과 RefreshToken을 생성해서 Response에 보내줍니다.

 

유저 회원가입 시에는 RefreshToken이 없기 때문에 RefreshToken Column이 null입니다.

따라서, 로그인 성공 시 RefeshToken을 발급하고, DB에 저장해야합니다.

DB에 발급된 RefreshToken을 저장하기 위해 유저 테이블의 RefreshToken Column에 업데이트하는

updateRefreshToken()으로 업데이트 후 saveAndFlush()로 DB에 반영합니다. 


4. JSON 로그인 실패 시 핸들러 구현

 

마찬가지로 위에 커스텀 JSON 로그인 필터를 통과하여 인증 실패가 되었을 때(로그인 실패)

로그인 실패 핸들러가 동작하는데, 이 로그인 실패 핸들러를 구현했습니다.

 

SimpleUrlAuthenticationFailureHandler를 상속받아 구현했습니다.

 

🎯 LoginFailureHandler 전체 코드

package login.oauthtest4.global.login.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * JWT 로그인 실패 시 처리하는 핸들러
 * SimpleUrlAuthenticationFailureHandler를 상속받아서 구현
 */
@Slf4j
public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/plain;charset=UTF-8");
        response.getWriter().write("로그인 실패! 이메일이나 비밀번호를 확인해주세요.");
        log.info("로그인에 실패했습니다. 메시지 : {}", exception.getMessage());
    }
}

로그인이 실패하게 되면,

Response Body에 로그인 실패 메세지를 띄우고 404 ERROR 코드를 반환하도록 구현했습니다.


📖 깃허브 링크 (전체 코드)

https://github.com/KSH-beginner/oauth2WithJwtLogin

 

GitHub - KSH-beginner/oauth2WithJwtLogin

Contribute to KSH-beginner/oauth2WithJwtLogin development by creating an account on GitHub.

github.com


📕 전체 로그인 구현 목차

Spring Security + JWT를 이용한 자체 Login & OAuth2 Login API 구현 (1) - 회원(User) 관련 클래스 생성

Spring Security + JWT를 이용한 자체 Login & OAuth2 Login API 구현 (2) - JWT란?

Spring Security + JWT를 이용한 자체 Login & OAuth2 Login API 구현 (3) - JWT 관련 클래스 생성 / JWT 인증 로직

Spring Security + JWT를 이용한 자체 Login & OAuth2 Login API 구현 (4) - 자체 JSON 로그인 커스텀하기

Spring Security + JWT를 이용한 자체 Login & OAuth2 Login API 구현 (5) - OAuth란? / OAuth 2.0 인증 과정 예시

Spring Security + JWT를 이용한 자체 Login & OAuth2 Login API 구현 (6) - OAuth 2.0 로그인 구현 사전 설정

Spring Security + JWT를 이용한 자체 Login & OAuth2 Login API 구현 (7) - OAuth 2.0 로그인 관련 클래스 생성

Spring Security + JWT를 이용한 자체 Login & OAuth2 Login API 구현 (8) - SecurityConfig 설정 클래스 생성

Spring Security + JWT를 이용한 자체 Login & OAuth2 Login API 구현 (9)- JWT 자체 로그인 & OAuth2 Login 테스트


Reference

https://velog.io/@yaho1024/Spring-Security-UserDetailsService-%EA%B5%AC%ED%98%84-%ED%95%B4%EB%B3%B4%EA%B8%B0

 

Spring Security - 4. UserDetailsService 구현 해보기

안녕하세요. INCHEOL'S 입니다. 저번시간에는 UsernamePasswordFilter와 그와 관련된 AuthenticationManager, AuthenticationProvider에 대해서 말씀드렸었는데 오늘은 그 중 AuthenticationProvider와

velog.io

https://ttl-blog.tistory.com/268

 

스프링부트 게시판 API 만들기 (3) - JSON 로그인 (2) - 시큐리티 로그인 동작원리

이번에는 지난 포스팅에 이어서 스프링 시큐리티를 이용하여 로그인을 진행하는 코드를 작성하기 전에 스프링 시큐리티의 로그인 진행방식을 살펴보겠습니다. 이번 포스팅은 조금 길기 때문에

ttl-blog.tistory.com

https://docs.spring.io/spring-security/site/docs/5.4.2/reference/html5/#servlet-authentication-userdetailsservice

 

Spring Security Reference

In Spring Security 3.0, the codebase was sub-divided into separate jars which more clearly separate different functionality areas and third-party dependencies. If you use Maven to build your project, these are the modules you should add to your pom.xml. Ev

docs.spring.io

 

반응형