OpaqueToken(불투명토큰)과 JWT(Json Web Token)
OpaqueToken (불투명 토큰)
현재 앞선 강의를 토대로 애플리케이션 로그인을 통해 Spring Security 프레임워크나 백엔드 코드에서는 두 가지 유형의 토큰을 제공한다.

- JSESSIONID
- XSRF-TOKEN
이 두가지두 가지 토큰은 브라우저 내 쿠키로 저장되며 React, Vue와 같은 클라이언트에서 백엔드로 보내는 모든 추가 요청은 이 두 가지 쿠키가 백엔드 측 요청에 자동으로 첨부된다. SpringSecurity 프레임워크는 이 토큰들을 검증을 합니다.
이 토큰을 통해 얻을 수 있는 이점은
JSESSIONID 토큰은 백엔드 서버에 대한 모든 요청마다 사용자 이름과 비밀번호를 보낼 필요가 없다는 것이며 모든 요청 마다 인증을 수행할 필요가 없다.
XSRF-TOKEN의 도움은 SpringSecurity 프레임워크가 CSRF 공격으로부터 사용자르 보호하려고 한다는것이다.
이러한 토큰들은 매우 간단한 토큰으로 무작위 한 문자열 값들 뿐이다. 이러한 토큰들을 OpaqueToken 불투명 토큰이라고 부른다.
토큰자체에 고유한 의미가 없으며 사용자 세션을 위해 백엔드 서버에서 유지관리하는 무작위 문자열 값일 뿐이다.

보안 API에 액세스 하려면 동일한 Opaque Token을 보내야 하며 백그라운드에서 백엔드 서버는 주어진 토큰에 연관된 사용자 세션이 있는지 확인해 있다면 보안 API에 액세스를 허용할 것이다.
이 것은 OpaqueToken을 검증하기 위해 항상 백엔드 서버에 의존하게 된다는 것이다.
토큰 기반 인증 장점

JWT(Json Web Token)
현재 강의를 통해 배우고 있는 EazyBank라는 애플리케이션은 Opaque 토큰 형식의 불투명 토큰을 생성하고 있다.
우리는 추후에 JSESSIONID를 지우고 JWT로 대체를 해볼 것이다.
JWT란?
- JSON Web Token이라고 불리며 json 형식을 사용해 토큰을 구현하고 있다.
- JWT토큰은 특별한 기능과 장점 덕분에 요즘 많은 시스템에서 일반적이며 선호되는 토큰 유형이다.
- JWT토큰은 인증 및 권한 부여와 정보 교환에 모두 사용 될 수 있으며, 토큰 자체에 사용자 관련 데이터를 공유할 수 있음을 의미한다. 이를통해 클라이언트/서버 측에서 세션에 이러한 세부 정보를 유지하는 부담을 줄일 수 있다.
JWT토큰은 각 부분이 마침표(.)로 구분되어 있으며, 총 3개의 부분으로 구성된다.
예시로는

1. Header
토큰과 관련된 메타 데이터와 정보를 저장. 메타 데이터 정보라고 한다면 토큰의 종류가 무엇인지? 토큰 서명을 도출하는 데 사용된 알고리즘이 무엇인지? 등의 정보를 말한다. 알고리즘을 의미하는 "alg", 와 유형을 의미하는 "typ" 키 두 가지의 예시와 같다. 토큰 내부에 직접적인 값을 저장하지 않고 Base64를 사용하여 인코딩 되고 출력 결과를 jwt토큰 내부에 저장한다.

2. Payload(본문)
사용자와 그의 역할에 대한 세부 정보를 저장할 수 있으며 그 정보는 나중에 인증 및 인가에 사용될 수 있다.
JWT토큰의 내용에 무엇을 얼마나 보낼 수 있는지에 대한 제한은 없지만 이 내용 요소를 가능한 가볍게 유지하기 위해 최선을 다해야 한다. 예시로 "sub": 주제, "name": 이름, "iat" : 발행시간 같은 사용자 관련 정보를 많이 저장한다. 마찬가지로 역할, 권한, 이메일, 전화번호 같은 세부 정보도 저장할 수 있어야 한다. 하지만 최종 사용자의 비밀번호는 내용에 저장하면 안 된다. 토큰에 접근할 수 있는 누구나 비밀번호를 볼 수 있기 때문이다. 모든 내용 데이터는 Base64로 인코딩 되며 인코딩 된 값은 JWT 토큰에 저장되기 때문이다.

3. Signature(서명)
토큰의 마지막 부분으로 디지털 서명이라고 부를 수 있다. 디지털 서명은 완전 선택사항으로 이 디지털 서명의 목적은 이 애플리케이션이 이 서명만을 사용하여 토큰 값이 변조되었는지 여부를 식별하자는 것이다.
조직 내부 애플리케이션용 토큰 생성에서는 클라이언트 애플리케이션이나 신뢰할 수 있는 사용자가 토큰 변조 하지 않을 것이란 확신이 있기 때문에 디지털 서명이 필요하지 않지만, 오픈 웹에서 애플리케이션을 사용할 유저요이라면 JWT 토큰에 대해 디지털 서명이 필요하다.

토큰에서 디지털 서명을 생성하기 위해서는 해싱알고리즘 중 하나를 사용해야 하며 HMACSHA256이 가장 일반적으로 사용되는 해싱 알고리즘이다. 이 해싱 알고리즘에 입력값은 base64 URL Encode(header)로 계산되며 헤더는 또다시 base64 URL encode 형식으로 인코딩이 될 것이다. 이후에 "."을 추가하고 base64 URL Encode(payload)를 추가해야 한다. 이
base64 URL Encode(header)+"."+base64 URL Encode(payload)가 SHA256 알고리즘의 첫 번째 입력값이며 다음 입력값은 secret 비밀 키이다.
이 비밀 키는 신중하게 보관되어야 하며 백엔드 측에서 신중하게 관리된다.
- 백엔드 토큰 검증 흐름

- JWT 토큰 해독 검증 사이트
JSON Web Tokens - jwt.io
JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).
www.jwt.io
JWT 토큰 기반 인증 구현
JWT 의존성 추가 및 ProjectSecurityConfig 설정 변경
- build.gradle
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.6'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.5'
https://mvnrepository.com/artifact/io.jsonwebtoken
Maven Repository: io.jsonwebtoken
JSON Web Token support for the JVM and Android Last Release on Aug 20, 2025
mvnrepository.com
JWT라이브러를 통해 JWT 토큰을 쉽게 생성하고 검증할 수 있다.
- ProjectSecurityConfig
@Configuration
@Profile("!prod")
public class ProjectSecurityConfig{
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler();
http.cors(corsConfig -> corsConfig.configurationSource(new CorsConfigurationSource() {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request){
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Collections.singletonList("http://localhost:4200"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowCredentials(true);
config.setAllowedHeaders(Collections.singletonList("*"));
config.setExposedHeaders(Arrays.asList("Authorization"));
config.setMaxAge(3600L);
return config;
}
}))
.csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler)
.ignoringRequestMatchers("/contact", "/register", "/apiLogin")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(new RequestValidationBeforeFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(new AuthoritiesLoggingAfterFilter(), BasicAuthenticationFilter.class)
.addFilterAt(new AuthoritiesLoggingAtFilter(), BasicAuthenticationFilter.class)
.sessionManagement(smc -> smc.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/myAccount").hasRole("USER")
.requestMatchers("/myBalance").hasAnyRole("USER", "ADMIN")
.requestMatchers("/myLoans").hasRole("USER")
.requestMatchers( "myCards").hasRole("USER")
.requestMatchers("/user").authenticated()
.requestMatchers("/notices", "/contact","/error","/register", "/invalidSession", "/apiLogin").permitAll());
http.formLogin(withDefaults());
http.httpBasic(hbc -> hbc.authenticationEntryPoint(new CustomBasicAuthenticationEntryPoint()));
http.exceptionHandling(ehc -> ehc.accessDeniedHandler(new CustomAccessDeniedHandler()));
return http.build();
}
@Bean
PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public CompromisedPasswordChecker compromisedPasswordChecker(){
return new HaveIBeenPwnedRestApiPasswordChecker();
}
}
변경점들을 설명하자면
- . sessionManagement(smc -> smc.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
원래는 SpringSecurity 프레임워크에서 JSESSIONID를 항상 생성하도록 지시하고 있었지만 이제는 JWT토큰을 인증토큰으로 활용할 것이기 때문에 JSESSIONID를 SpringSecurity 프레임워크가 생성하지 않도록 지시하기 위해 SessionCreationPolicy.ALWAYS를 무상태(STATELESS)로 교체를 해주어야 한다.
최종적으로 애플리케이션을 stateless로 만들게 될 것이다.
- . securityContext(contextConfig -> contextConfig.requireExplicitSave(false)) 설정 제거
repuireExplicitSave(false)는 SecurityContext 저장방식을 제어하는 옵션으로 SpringSecurity는 SecurityContext를 세션에 저장할 때 명시적으로 저장 요청이 있어야만 저장하도록 설정한다.
하지만 false로 설정 시에는 인증 성공 시점에서 자동으로 세션에 SecurityContext가 저장이 된다. 하지만 JWT기반 인증 구현을 목표로 하고 있기에 세션을 쓰지 않게 됨으로 SecurityContext 저장자체가 불필요하게 되었다.
- config.setExposedHeaders(Arrays.asList("Authorization"));
강의에서는 Angular로 구현된 클라이언트가 있으며 프런트와 서로 다른 도메인 위치에서 서로 다른 출처 위치에 배포되었다. 이러한 환경으로 CORS 설정을 하였으며 클라이언트에서 백엔드 서버로 오는 모든 헤더를 수락하려고 한다.
즉, 클라이언트 애플리케이션은 요청 내에서 어떤 헤더 값이든 보낼 수 있지만 백엔드 응답 내에서 어떤 헤더도 보낼 수 없는 상황이기 때문에 JWT 토큰을 생성하고 토큰을 응답 내에서 클라이언트 애플리케이션 헤더로 보내고자 하기 위해
setExposedHeaders()를 호출하여"Authorization" 이름의 헤더를 노출하게 하였다.
JWTTokenGeneratorFilter - JWT 토큰 생성 필터
초기 로그인이 완료되면 JWT토큰을 생성하기 위해서 Filter 방식을 사용해 구현을 할 것이다.
이전 필터 관련 글에서 나왔던 OncePerRequestFilter를 이용할 것인데 요청의 일부로 필터가 한 번만 실행되어야 하기 때 문둥이ㅏ.
요청의 일부로 필터가 여러 번 호출이 되어 여러번 JWT 토큰을 생성하는 건 알맞지 않기 때문이다.
- ApplicationConstants - 환경변수 설정 가정
public final class ApplicationConstants {
public static final String JWT_SECRET_KEY = "JWT_SECRET";
public static final String JWT_SECRET_DEFAULT_VALUE = "$2a$12$135vzpHuJPUuxOG2sMjceeWAsUbWqjAXhWsTHitNVN9kZ8qSrLVOm";
public static final String JWT_HEADER = "Authorization";
}
이렇게 설정하면 secret 키가 하드코딩 됨으로 권장하지는 않는다.
※ secret 키 보관 권장 하는 방식
- application.properties
# application.properties
jwt.secret=$2a$12$135vzpHuJPUuxOG2sMjceeWAsUbWqjAXhWsTHitNVN9kZ8qSrLVOm
jwt.header=Authorization
- JwtProperties
@Component
public class JwtProperties {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.header}")
private String header;
// getter
}
장점
- 보안성: 코드에 직접 노출되지 않고, 환경별로 다른 값을 설정 가능.
- 유연성: 운영/개발/테스트 환경마다 다른 secret을 쉽게 적용.
- 관리성: 키를 교체할 때 코드 수정 없이 설정만 바꾸면 됨.
- JWT secret을 환경 변수 + application.properties +dockercompose 로 관리
. env 파일을 활용
JWT_SECRET=$2a$12$135vzpHuJPUuxOG2sMjceeWAsUbWqjAXhWsTHitNVN9kZ8qSrLVOm
//추가 환경변수 설정 등등
docker-compose.yml
version: "0.1"
services:
app:
image: myapp:latest
environment:
JWT_SECRET: ${JWT_SECERT}
//추가 환경변수 설정 등등
ports:
- "8080:8080"
application.properties
jwt.secret=${JWT_SECRET:$2a$12$135vzpHuJPUuxOG2sMjceeWAsUbWqjAXhWsTHitNVN9kZ8qSrLVOm}
jwt.header=Authorization
- JWTTokenGeneratorFilter
public class JWTTokenGeneratorFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
//인증 세부 정보 읽어오기
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(null!= authentication){
//환경변수 불러오기
Environment env = getEnvironment();
if(null != env) {
// JWT_SECRET 키 가져오기
String secret = env.getProperty(ApplicationConstants.JWT_SECRET_KEY,
ApplicationConstants.JWT_SECRET_DEFAULT_VALUE);
//문자열을 UTF-8바이트 배열로 변환 후, HMAC-SHA 알고리즘 용 SecretKey 객체 생성
SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
//JWT토큰 생성
String jwt = Jwts.builder().issuer("Eazy Bank").subject("JWT Token") // 발행자, 주제
.claim("username", authentication.getName()) //최종사용자 ID
.claim("authorities", authentication.getAuthorities().stream().map(
GrantedAuthority::getAuthority).collect(Collectors.joining(","))) //authorities
.issuedAt(new Date()) //발행일
.expiration(new Date((new Date()).getTime() + 30000000)) // 만료일 ms 단위
.signWith(secretKey).compact();// 토큰 내부 디지털 서명 생성
//헤더에 Authorization 이름으로 jwt 토큰 응답 전달
response.setHeader(ApplicationConstants.JWT_HEADER, jwt);
}
}
filterChain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return !request.getServletPath().equals("/user");
}
}
- shuldNotFilter 설정을 통해 리턴값이 true 일 경우 JWTTokenGeneratorFilter가 동작하지 않는다. 여기서는 ("/user)라는 로그인 API 경로와 같다면 필터를 실행하도록 설정하였다.
- ProjectSecurityConfig
.addFilterAfter(new JWTTokenGeneratorFilter(), BasicAuthenticationFilter.class)
인증 성공 후 JWT토큰을 생성하기 위해 BasicAuthenticationFilter 이후에 필터가 동작하도록 설정하였다.
JWTTokenValidatorFilter - JWT 토큰 검증 필터
- JWTTokenValidatorFilter
public class JWTTokenValidatorFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
//헤더에서 jwt토큰을 가져옴
String jwt = request.getHeader(ApplicationConstants.JWT_HEADER);
if(null != jwt) {
try{
Environment env = getEnvironment();
if(null != env) {
//환경변수에서 secretkey가져오기
String secret = env.getProperty(ApplicationConstants.JWT_SECRET_KEY,
ApplicationConstants.JWT_SECRET_DEFAULT_VALUE);
SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
if(null!= secretKey){
//JWT 토큰을 secretKey를 이용하여 파싱을 통해 payload값을 가져온다.
//parseSignedClaims(jwt)를 통해 exp(만료일) 검증까지한다.
Claims claims = Jwts.parser().verifyWith(secretKey)
.build().parseSignedClaims(jwt).getPayload();
//username과 authorities를 가져와 Authentication 객체를 생성한 뒤에 SecurityContext에 저장
String username =String.valueOf(claims.get("username"));
String authorities = String.valueOf(claims.get("authorities"));
Authentication authentication = new UsernamePasswordAuthenticationToken(username, null,
AuthorityUtils.commaSeparatedStringToAuthorityList(authorities));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}catch(Exception e){
throw new BadCredentialsException("Inavlid Token received!!");
}
}
filterChain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return request.getServletPath().equals("/user");
}
}
- shouldNotFilter 가 ("/user") 로그인 API 외에는 실행하도록 원하기 때문에 JWTTokenGeneratorFilter와 다르게! 조건을 삭제하였다.
- ProjectSecurityConfig
.addFilterBefore(new JWTTokenValidatorFilter(), BasicAuthenticationFilter.class)
SpringSecurity 프레임워크의 실제 인증 전에 매번 실행이 되어 토큰이 유효한지 유효성 검사 후에 유효하다면 이미 인증이 검증되었다는 것을 알려 다시 인증을 시도하지 않도록 한다. 따라서
BasicAuthenticationFilter 이전에 필터가 실행되도록 한다.
JWT 인증 검증 테스트
- userAPI - username, pwd 입력 인증



cookie에서 JSESSIONID 쿠키가 사라진 것과
Authorization 이름으로 JWT 토큰을 응답받은 것을 볼 수 있다. JWT 토큰은 jwt.io 사이트에서 해석을 해보면

- 응답받은 JWT 토큰으로 myAccountAPI 요청

자체 자격증명 api 로그인 구현 - apilogin
지금 까지는 httpBasic 형식을 사용하여 로그인 작업을 지원하고 있었다.
애플리케이션에 로그인하려는 사람은 /user API를 호출해야 하며 이 API에 기본 인증 자격증명(username, password)을 전달했다.
하지만 요구사항에 따라 httpBasic과 formLogin 형식의 방법이 아닌
RequsetBody 또는 RequestHeaders 내부에 자격증명을 받아야 하는 요구사항이 있을 수 있다.
이러한 시나리오에선 느 어떠한 SpringSecurityProvider도 도움이 되지 않을 것이다. 사용자가 원하는 곳에 자격증명을 수락하는
자체 REST API 작업을 구축할 것이다.
인증이 완료되면 JWT 토큰을 ResponseBody 내에 전송하고자 한다.
@PostMapping("/apiLogin")
- UserController
@RestController
@RequiredArgsConstructor
public class UserController {
private final CustomerRepository customerRepository;
private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
private final Environment env;
@PostMapping("/register")
public ResponseEntity<String> registerUser(@RequestBody Customer customer) {
try {
String hashPwd = passwordEncoder.encode(customer.getPwd());
customer.setPwd(hashPwd);
customer.setCreateDt(new Date(System.currentTimeMillis()));
Customer savedCustomer = customerRepository.save(customer);
if(savedCustomer.getId() > 0){
return ResponseEntity.status(HttpStatus.CREATED)
.body("Given user details are successfully registered");
}else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body("user register failed");
}
}catch(Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("An exception occurred: " +e.getMessage());
}
}
@RequestMapping("/user")
public Customer getUserDetailsAfterLogin(Authentication authentication){
Optional<Customer> optionalCustomer = customerRepository.findByEmail(authentication.getName());
return optionalCustomer.orElse(null);
}
@PostMapping("/apiLogin")
public ResponseEntity<LoginResponseDTO> apiLogin(@RequestBody LoginRequestDTO loginRequest) {
String jwt = "";
Authentication authentication = UsernamePasswordAuthenticationToken.unauthenticated(
loginRequest.username(), loginRequest.password());
Authentication authenticationResponse = authenticationManager.authenticate(authentication);
if(null != authenticationResponse && authenticationResponse.isAuthenticated()) {
if(null != env) {
String secret = env.getProperty(ApplicationConstants.JWT_SECRET_KEY,
ApplicationConstants.JWT_SECRET_DEFAULT_VALUE);
SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
jwt = Jwts.builder().issuer("Eazy Bank").subject("JWT Token")
.claim("username", authenticationResponse.getName())
.claim("authorities", authenticationResponse.getAuthorities().stream().map(
GrantedAuthority::getAuthority).collect(Collectors.joining(",")))
.issuedAt(new java.util.Date())
.expiration(new java.util.Date((new java.util.Date()).getTime() + 30000000))
.signWith(secretKey).compact();
}
}
return ResponseEntity.status(HttpStatus.OK).header(ApplicationConstants.JWT_HEADER,jwt)
.body(new LoginResponseDTO(HttpStatus.OK.getReasonPhrase(), jwt));
}
}
- LoginRequestDTO
public record LoginRequestDTO(
String username,
String password
) {
}
- LoginResponseDTO
public record LoginResponseDTO(
String status,
String jwtToken
) {
}
- ProjectSecurityConfig
AuthenticationManager를 @Bean을 생성해수동으로 인증 프로세스를 시작할 수 있다.
@Bean
public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
EazyBankUsernamePwdAuthenticationProvider authenticationProvider =
new EazyBankUsernamePwdAuthenticationProvider(userDetailsService, passwordEncoder);
ProviderManager providerManager = new ProviderManager(authenticationProvider);
providerManager.setEraseCredentialsAfterAuthentication(false); // authentication 객체의 비밀번호를 지우지않을 것이다.
return providerManager;
}
여기서 providerManager.setEraseCredentialsAfterAuthentication(false);라는 설정이 있는데 기본적으로 인증이 성공하면 Authentication 객체 안의 비밀번호를 지워버린다. 인증이 끝난 후에는 보안상의 이유로 민감한 정보를 계속 들고 있을 필요가 없기 때문이다. 그래서 기본값은 true로 하는 게 좋으며 만약에 비즈니스로직 내에 또 다른 유효성 검사를 위해 비밀번호를 사용하고자 한다면 false로 설정하면 된다.
- "/apiLogin" permitAll() 설정
.requestMatchers("/notices", "/contact","/error","/register", "/invalidSession", "/apiLogin").permitAll());
- CSRF "/apiLogin" 예외처리
"/apiLogin" 이 postMapping이기 때문에 CSRF 설정을 하였다면 예외 리스트에도 추가해주어야 한다.
.csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler)
.ignoringRequestMatchers("/contact", "/register", "/apiLogin")
- EazyBankUsernamePwdAuthenticationProvider
AuthenticationProvider를 이해하고 커스텀 Provider 구현하기-SpringSecurity6.x Udemy EazyBytes EazyBank
목차 시작 글 앞선 글에서 InMemoryUserDetailsManager와 JdbcUserDetailsManager를 통해 유저를 Springboot 메모리와 MySQL DB에 유저를 생성시켜 보았고 JdbcDaoImpl에서는 기본적인 유저(userTable(username , password, enabled
kiwimel0n-study.tistory.com
자체 인증 apiLogin api 테스트

Json 형식으로 username, password를 입력을 하였고 백엔드 애플리케이션에서 RequestBody를 통해 성공적으로 로그인이 되어 ResponseBody 형식으로 status 와 jwtToken을 응답받았다.


콘솔 로그에서도 자체적으로 만든 EazyBankUsernamePwdAuthenticationProvider를 통해 인증이 된것을 알 수 있다.




















































