동현의 나불나불
백엔드
배포
docker, AWS, github action을 활용한 배포
github_actions_기초
prometheus_이론
kafka 공부
ch1.kafka 시작하기
언어
자바
java 인터페이스
java다형성
java예외
java추상화
java컬렉션
javaThread
프로젝트
바로그림
django-학습-핵심-정리
elasticsearch-검색과-데이터-파이프라인-정리
활용한 기술
바로그림-프로젝트-설계와-시행착오
회고록
백엔드_통합점검
오늘의 식사
Today I Learned
2026_04_15~2026_04_19
2026_04_15
2026_04_16
2026_04_17
2026_04_20~2026_04_27
2026_04_20
2026_04_21
2026_04_22
2026_04_23
2026_04_24
2026_04_27~2026_05_01
2026_04_27
2026_04_28
2026_04_29
2026_04_30
2026_05_01
2026_05_04~2026_05_08
2026_05_04
2026_05_05
2026_05_06
2026_05_07
2026_05_08
2026_05_11~2026_05_15
2026_05_11
2026_05_12
2026_05_13
2026_05_14
2026_05_15
2026_05_21~2026_05_22
2026_05_21
2026_05_22
2026_05_27~2026_05_30
2026_05_27
2026_06_TIL
2026_06_11
2026_06_15
2026_06_16
2026_06_22
2026_06_25
2026_06_26
2026_06_28
2026_07_TIL
cs
네트워크
네트워킹 하향식 접근
http 프로토콜
데이터베이스
DB_SQL기초
Lock_diffrent
웹
os
chapter1 운영체제 서론
chapter2 운영체제 구조
chapter3 프로세스 관리
Home
Contact
Copyright © 2024 |
Yankos
Home
>
Today I Learned
> 2026_05_21~2026_05_22
Now Loading ...
2026_05_21~2026_05_22
2026-05-22 일지
네이버 토큰을 가져오는 OAuth2.0 방식 실습 네이버도 카카오처럼 Spring에서 지원해주지는 않으므로 등록시 authorization-grant-type, client-authentication-method, redirect-uri등을 추가로 설정해줘야 한다. 그리고 별도로 provider를 만들고 authorization-uri, user-name-attribute, token-uri, user-info-uri을 넣어 주어야 한다. 카카오와 다소 다른점은 위 사진처럼 response 객체로 받고 response 객체 하위에 접근하여 유저정보를 받아보는 식으로 진행된다. 그래서 OAuth2.0 방식으로 유저에 관한 데이터를 받아오는 경우 공식 API문서를 참조를 하는 편이 좋을 것 같다. 스웨거 Swagger 스웨거 에디터 링크 이렇게 외부 프로그램과 연결하거나 프로그램에 관해 작성할 때 API문서 형식으로 작성을 하게 된다. 이럴 때 자주사용하는 것으로는 swagger API가 있는데 REST API를 문서화하고 테스트할 수 있게 도와주는 도구라고 볼 수가 있다. 사용 이유 코드를 기반으로 API 문서 생성 가능 브라우저에 직접 요청하여 API 테스트 가능 Request/Response 확인 프론트엔드와 백엔드 간의 협업에서 효율이 증가 implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.6' 위 의존성을 추가하면 spring이 Controller을 분석하여 API를 읽어드려 자동으로 문서를 생성한다. 그리고 Swagger Annotation을 붙여 자세한 문서를 만드는 것이 가능하다. @Operation : API 설명 @Parameter : 파라미터 설명 @Schema : DTO 설명 JWT 토큰 다루기 기존의 Spring MVC + Thymeleaf 환경에서는 주로 세션(Session) 기반 인증을 사용한다. 사용자가 로그인하면 서버는 세션을 생성하고, 브라우저는 JSESSIONID 쿠키를 저장하여 이후 요청마다 함께 전달한다. 하지만 분리된 프론트엔드 환경에서 사용할 때는 CORS 설정문제, HTTPS 인정서 문제, 등 해야 할 것이 많기 때문에 다른 인증방식이 필요하다. 이러한 환경에서 많이 사용하는 방식이 JWT 기반 인증이다. JWT는 Json Web Token의 약자로 사용자 인증 정보를 담고 있는 문자열 토큰이다. 로그인 성공시 서버는 JWT를 발급하고 클라이언트는 요청마다 헤더에 토큰을 포함하여 전달을 하게 된다. 다만 JWT는 문자열로 이루어져 있기 때문에 JWT토큰을 갖고있는 클라이언트가 있으면 보안상 문제가 된다. 이를 때문에 사용해주는 것이 Access Token과 Refresh Token 전략이다. Access Token과 Refresh Token 전략 Access Token은 실제 인증에 사용하는 토큰으로 수명이 짧고 요청 시 함께 전달한다. 보통 몇 분 ~ 몇 시간 사용하게 된다. Refresh Token은 Access Token을 재발급하기 위한 토큰으로 Access Token보다 상대적으로 수명이 길고 보안적으로 더 안전하게 관리하는 것이 가능하다. 보통 DB 또는 Redis 등에 저장하는 경우가 많다. 보통 JWT 토큰을 만들때 2개를 만들고 하나를 엑세스 토큰, 다른 하나를 리프레시 토큰으로 한다. 그리고 엑세스 토큰은 로그인하는 유저에게 맡기고 리프레시 토큰을 운영자가 관리를 하고 이 두가지 토큰을 보고 갱신할지 말지를 판단을 하게 된다. 토큰 재발급 흐름은 다음과 같다. Access Token 만료 클라이언트가 Refresh Token과 함께 재발급 요청 서버가 Refresh Token 검증 새로운 Access Token 발급 프론트에는 보통 Axios Interceptor을 사용하여 토큰 만료를 감지하고 갱신하고 이를 로그인에 사용하는 것이 가능하다. public KeyPair issueKeyPair(Long id, Role role){ String accessToken = issueAccessToken(id, role); String refreshToken = issueRefreshToken(id, role); return new KeyPair(accessToken, refreshToken); } 하나의 메서드에서 토큰 쌍을 생성되도록 하여 일관된 보안정책 유지, 생성 방식 오염 방지를 하도록 한다. 요즘은 next.js의 발전으로 자체 서버에 토큰을 관리하는 것도 가능하다. 블랙리스트 전략과 화이트리스트 전략 블랙리스트 전략 사용 금지된 토큰 목록을 관리하는 방식이다. 로그아웃시 해당 JWT를 블랙리스트에 등록하고 이후 요청시 블랙리스트에 존재하면 인증을 거부한다. 구현이 비교적 간단하고 기존의 JWT 구조를 유지가능한 장점이 있다. 화이트리스트 전략 허용된 토큰만 저장하는 방식이다. DB or Redis에 저장된 토큰만 인증을 허용한다. 기본적으로 모든 토큰을 차단하고 저장된 토큰인지를 확인하는 방식이다. 실무에서는 Redis를 사용한 화이트리스트 전략이 많이 사용된다. 빠른 조회속도, 토큰 만료 자동 처리기능, TTL지원으로 Refresh Token 관리에 매우 적합하기 때문이다. 하지만 대기업의 경우 대용량 세선의 크기를 감당가능하고 보안을 더 중요시 여기서 블랙리스트를 사용하는 곳도 있다고 한다. JJWT 라이브러리 사용하기 JWT를 쉽게 다루기 위해 JJWT를 사용해준다. 이는 서드파티이기 때문에 gradle에 끌고 오기 위해서는 버전을 기재해줄 필요가 있다. JWT는 크게 3부분으로 구성되는데 이는 Header, Payload, Signature이다. 그중 Payload는 사용자의 영역을 담는다. 그리고 위변조를 방지하기 위해 Signature를 사용한다. 이를 다루는데 Secret Key와 암호화 알고리즘을 사용한다. 이 Signature에 대한 분석을 위해 사용 알고리즘, 토큰 타입,등이 포함된다. { "sub": "1234", "name": "Jone doe", "iat": 15160423 } 위는 payload에 들어간 토큰을 decode 시켜준 것이다. 이 값들은 아래 메서드를 통해 들어가게 된다. //토큰 주제 .subject("eddie") //토큰 발급 시간 .issuedAt(new Date()) //토큰 만료 시간 .expiration(exp) @Test void it_will_issue_a_token(){ Date cur = new Date(); Date exp = new Date(cur.getTime() + 30_000L); JwtBuilder jwtBuilder = Jwts.builder() .subject("Hello World!") .claim("name","eddie") .issuedAt(new Date()) .expiration(exp); } 위는 JWT Payload 생성 예시이다. 이를 사용하기 위해서는 signature이 필요하다. .requestMatchers(CorsUtils::isPreFlightRequest) .permitAll() CORS 정책을 적용하는 환경에서는 브라우저가 실제 요청 전에 사전 검사를 하는 preflight 요청을 보낼 수가 있다. 브라우저가 먼저 서버에 허용여부를 물어보게 된다. 위 코드는 브라우저의 사전 검사 요청은 인증없이 허용하겠다는 것을 의미한다. JWT 전달 방식 로그인 성공 후 JWT를 클라이언트에 전달하는 방식은 여러가지가 존재한다. 응답 Body로 전달 Header에 넣어서 전달 Cookie로 전달 이벤트 기반 처리 Spring Security에서는 인증 성공/실패 시 이벤트가 발생하게 된다. 성공하면 oauthSuccessEvent가 발생한다. 실패 시 oauthFailureEvent가 발생하게 된다. 멤버 서비스에 있는 것이 끝나고 끝난 시점을 캐치해낸 핸들러가 있는데 이를 구현하는 것이 필요하다. 실패했다면 실패한 이벤트를 알려주는것을 구현해줘야 한다. JWT Parsing 시 발생가능한 예외 MalformedJwtException : JWT 형식 자체가 깨진 경우, 예시: abc.def처럼 정상 JWT 형식이 아닌 경우 ExpiredJwtException : 유효기간이 만료된 경우, exp 시간이 지난 JWT라고 볼 수가 있다. SignatureException : 서명이 일치하지 않는 경우, 위변조 가능성 존재한 JWT이다. JwtException : 위 JWT 관련 예외들의 상위 예외 클래스, JWT 검증 실패를 포괄적으로 처리 가능한다. IllegalArgumentException : 토큰 문자열 자체가 비어있거나 잘못된 경우 발생 가능
Today I Learned
· 2026-05-22
2026-05-21 일지
Spring Security 구현 인증된 유저를 보관하는 UserDetails @Getter @RequiredArgsConstructor public class MemberDetails implements UserDetails { private final String username; private final String password; private final String role; @Override public Collection<? extends GrantedAuthority> getAuthorities() { //GrantedAuthority의 하위 타입인 경우에만 올 수가 있다. return List.of(new SimpleGrantedAuthority(this.role)); } } 폼기반 로그인을 위한 MemberDetails는 폼 로그인 인증 과정에서 현재 로그인 한 사용자의 정보를 담는 객체의 역할을 수행한다. SpringSecurity는 내부적으로 인증이 완료되면 UserDetails 타입 객체를 통해 사용자 정보를 관련하는데 내가 만들어준 Member Entity를 Spring Security에서 사용하려면 UserDetails를 구현한 클래스를 만들어야 한다. getAuthorities에 관한 설명 이 메서드는 현재 사용자의 권한목록을 반환하게 된다. Spring Security는 사용자 인증 성공시 userDetails 조회 getAuthorities() 호출 권한 확인 URL 접근 허용 여부 결정 이렇게 과정을 거치게 되는데 여기서 GrantAuthority는 권한을 표현하는 인터페이스로 ROLE_USER, ROLE_ADMIN 이런 식으로 되어있다. 권한이 여러 개일 수도 있기 때문에 Collection 형태로 반환을 받아야 한다. UserDetailSerive 구현 @Service @RequiredArgsConstructor public class MemberService implements UserDetailsService { private final MemberRepository repository; private final PasswordEncoder passwordEncoder; @Transactional public void save(SignUpRequest request) { Member member = Member.builder() .username(request.getUsername()) .password(passwordEncoder.encode(request.getPassword())) .email(request.getEmail()) .build(); repository.save(member); } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Optional<Member> memberOptional = repository.findByUsername(username); Member member = memberOptional.orElseThrow( () -> new UsernameNotFoundException("해당 회원을 찾을 수 없습니다.") ); return new MemberDetails( member.getUsername(), member.getPassword(), member.getEmail() ); } } UserDetail에 들어갈 사용자 정보를 담기 위해 사용자 정보를 조회하는 역할을 UserDetailsService에서 시행한다. 이 UserDetailService는 로그인시 loadUserByUsername이라는 메서드로 호출을 하게 되는데 이를 구현하기 위한 재정의가 되어있어야 한다. 여기서 .password(passwordEncoder.encode(request.getPassword())) 암호화를 한 상태로 패스워드를 보관을 하게 된다. 각종 보안문제에 대한 기본적인 정책으로 시행한다. 그리고 이 passwordEncoder에서 시행하는 것은 BCrypt 방식의 단방향 암호화를 제공한다. Spring Security 보안 규칙 설정 로그인, 로그아웃, URL별 접근 권한을 설정해야 한다. SecurityFilterChain 클래스를 통해 관리를 할 수가 있다. @Configuration @RequiredArgsConstructor public class SecurityConfiguration { private final PasswordEncoder passwordEncoder; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http){ return http .cors(cors -> cors.disable()) // CORS 종료 .csrf(csrf -> csrf.disable()) // CSRF 종료 //폼 로그인 관련 설정 .formLogin(Customizer.withDefaults() // // form -> form.usernameParameter("loginId") // .passwordParameter("loginPwd") // //특정페이지를 로그인 페이지로 사용하기 위해서 사용 // .loginPage("/login-page") // .loginProcessingUrl("/login") // //로그인을 성공했을 때 다음 url로 이동하고 싶을때 영구적으로 이동하고 싶으면 default // //페이지를 이동시킨다. // //로그인이 끝나고 나서 비동기 처리를 하려 할때는 페이지만 다시 보여주고 싶을때 successForwardUrl // //화면만 바뀌게 된다. // .successForwardUr l("/") // .defaultSuccessUrl("/") // .failureUrl("/login-page?error=true") // .failureForwardUrl("/login-page?error=true") ) .logout( Customizer.withDefaults() // logoutConfig ->logoutConfig // .logoutUrl("/logout") // .invalidateHttpSession(true) // .deleteCookies("JSESSIONID") // .clearAuthentication(true) ) .authorizeHttpRequests( auth -> auth.requestMatchers("/login-page") // 인증이 안되어있는 사람만 접근 가능, 로그인페이지에 열어둔다 .anonymous() .requestMatchers("/sign-up") // 인증만 되어있으면 접근을 허용해주겠다는 것, 무조건 승인하겠다는 것 .permitAll() .requestMatchers("/user/**") .hasAnyAuthority("USER","MANAGER","ADMIN") .requestMatchers("/manager/**") // .hasAuthority("MANAGER") .hasAnyAuthority("MANAGER","ADMIN") .requestMatchers("/admin/**") .hasAuthority("ADMIN") .requestMatchers("/**") .authenticated() .anyRequest() .authenticated() .anyRequest() .denyAll() // .hasAnyRole() // //가변 인자 // .hasAnyAuthority() // //ROLE_을 붙일 필요가 없다. 내부적으로 붙이게 된다. DB에서 관리하고 운영하기 편하도록 // .hasRole() // //ROLE_을 붙일 필요가 없다. 내부적으로도 붙이지 않는다. // .hasAuthority() ) .build(); } formLogin 로그인을 시도했을 때 성공한 경우, 실패한 경우때 redirection을 위한 설정 formConfig.loginPage(“/login-page”) : 특정 페이지를 로그인 페이지로 쓰고 싶을때 .loginProcessingUrl(“/login”) : 필터로 보내서 자동으로 로그인을 시켜줄 URL 매핑 가능. 로그인이 수행되는 비밀번호를 맞춰볼 URL 지정. .successForwardUrl(“/”) : 로그인 성공 후, 다른 페이지로 임시 이동시킬때, 로그인 후 비동기 처리를 해야 할 때, 로그인 URL은 유지되고 페이지만 다시 보여주고 싶을 때 .defaultSuccessUrl(“/”) : 로그인이 성공했을 때 영구적으로 이동시키고 싶을 때 사용 .failureUrl(“/login-page?error”) : 로그인 실패시, 로그인 페이지로 돌려주고, 동사에 쿼리 파라미터를 전달해서 표시하고 싶을 때 사용. .failureForwardUrl(“/login-page?error”) : 주소창은 내버려두고, 보여줄 화면을 어떻게 보여줄지에 대한 주소를 남길때 logout 로그아웃을 했을 때 성공한 경우, 실패한 경우에서 redirection을 위한 설정 .logoutUrl(“/logout”) : 로그아웃 URL .invalidateHttpSession(true) : http세션을 쓰고 있을 때, 로그아웃을 하게 되면 세션을 무효화 시킬지에 대한 여부. .deleteCookies(“JSESSIONID”) : 로그아웃을 하게 되면, 이 쿠키를 지우겠다. .clearAuthentication(true) : 쓰레드 로컬안에 들어 있는, SpringSecurityContextHolder안에 있는 인증 객체인 Authentication 비워줄 것이냐 = 진짜 인증정보를 날릴 것이냐 authorizeHttpRequest URL별 접근하는 사람의 Role에 따라 authorize 설정 .anonymous : 아직 로그인하지 않은 사용자만 접근 가능 .permitAll : 누구나 접근가능하도록 모두 접근 허용 hasAnyAuthority: 여러 개의 권한에 관해 접근하는 것이 권한을 설정 401 : 로그인 되지 않았음 403 : 로그인 되었지만, 접근 권한 없음 404 : 해당 페이지 없음 OAuth2.0 gcp에서 OAuth2.0 가져오는법 웹 서버 애플리케이션용 OAuth 2.0 사용 Google for Developers OAuth 2.0 액세스 토큰 가져오기 파트의 시나리오를 가져오면 좋을 것 같다. kakao에서 OAuth2.0 가져오는 법 Kakao Developers 문서 구글 로그인은 스프링에서 공식적으로 지원하지만, 카카오, 네이버등 기타 로그인은 스프링에서 공식적으로 지원하지 않으므로, OAuth2.0 정보를 추가로 구현 하여야 한다. 구글은 registration에 client-id, client-secret, scope만 설정파일에 넣으면 되지만, 기타 로그인은 authorization-grant-type, client-authentication-method, redirect-uri을 추가로 넣어야 하고, 별도로 provider를 만들고 authorization-uri, user-name-attribute, token-uri, user-info-uri을 넣어 주어야 한다
Today I Learned
· 2026-05-21
<
>
Touch background to close