2024. 3. 7. 10:48ㆍSpring
코드 버전
Spring boot: 3.2.1
Java: JDK 17.0.9
Gradle - Groovy
의존성
//security
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-security'
//jwt
implementation 'io.jsonwebtoken:jjwt:0.12.3'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
진행 방식
클라이언트에서 로그인 요청을 보내면 서버에서는 로그인 폼을 카카오 서버에서 받아 클라이언트에게 반환한다.
로그인 후 서버에서는 Redirect URL을 통해 Token 값을 반환 받는다.
반환 받은 토큰 값을 가지고 다시 카카오 서버에 사용자 정보를 요청
Spring Security 이용한 인증/인가
전달 받은 사용자 정보를 이용해 회원 가입과 Jwt 토큰 발급을 통해 Spring Security 세션에 사용자 정보를 저장
Jwt 토큰을 클라이언트에 전달한다.
동작 방식별 작성 코드
Spring Security - 1편
- SecurityConfig
- CorsMvcConfig
Jwt - 2편
- JWTFilter
- JWTUtil
- + LoginFilter
- OAuth2UserDetailsService
OAuth2UserDetails→ dto (패키지) PrincipalDetail (OAuth2, Local 통합 구현) 생성- OAuth2SuccessHandler
- OAuth2FailHandler
- OAuth2Response
Spring Security applicaion.yml 환경변수 설정
기존 application.yml 파일
spring:
security:
oauth2:
client:
registration:
kakao:
client-authentication-method: client_secret_post
client-name: kakao
client-id: ${Kakao_Client_ID}
client-secret: ${Kakao_Client_SECRET}
authorization-grant-type: authorization_code
provider: kakao
redirect-uri: ${Kakao_Redirect-URI}
scope:
- profile_nickname
- account_email
provider:
kakao:
authorization-uri: https://kauth.kakao.com/oauth/authorize
token-uri: https://kauth.kakao.com/oauth/token
user-info-uri: https://kapi.kakao.com/v2/user/me
user-name-attribute: id
git에 업로드되지 않는 API-KEY.properties
Kakao_Client_ID = ....
Kakao_Client_SECRET = ....
Kakao_Redirect-URI = ....
SECRET_KEY = ....
별도의 application-API-KEY.properties 파일을 생성해 클라우드 서버에 올리면 안되는 값을 작성 후 호출
Kakao_Client_ID = REST API 키
Kakao_Client_SECRET = 보안 -> 카카오 로그인 활성화 -> Client Secret 발급
Kakao_Redirect-URI = 카카오 로그인 -> Redirect URL 작성
SECRET_KEY = JWT 암호화키
client-authentication-method: client_secret_post = 일반 POST (Google, Naver) 의 경우 카카오 로그인에서는 Cors 오류가 발생해 정상적인 결과를 얻을 수없다.
https://devtalk.kakao.com/t/spring-security-oauth2-401-no-body/127960/6
Spring security OAuth2 카카오 로그인 401 [no body] 에러 질문드립니다
좀더 빨리 찾았으면 좋았을텐데 다 해결하고 찾았네요. Spring security 5.6 이후로 변경되었나 봅니다. (POST → client_secret_post) 로 변경됐네요. https://docs.spring.io/spring-security/reference/5.6.0-RC1/servlet/oauth2/
devtalk.kakao.com
SecurityConfig
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final CustomOAuth2UserService customOAuth2UserService;
private final JwtUtil jwtUtil;
private final CustomSuccessHandler customSuccessHandler;
private final CustomFailHandler customFailHandler;
@Bean
public SecurityFilterChain securityFilterChains(HttpSecurity http) throws Exception {
http
.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setMaxAge(3600L);
configuration.setExposedHeaders(Collections.singletonList("Set-Cookie"));
configuration.setExposedHeaders(Collections.singletonList("Authorization"));
return configuration;
}
}));
//csrf disable
http
.csrf(AbstractHttpConfigurer::disable);
//From 로그인 방식 disable
http
.formLogin(AbstractHttpConfigurer::disable);
//HTTP Basic 인증 방식 disable
http
.httpBasic(AbstractHttpConfigurer::disable);
//JWTFilter 추가
http
.addFilterAfter(new JwtFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class);
//oauth2
http
.oauth2Login((oauth2) -> oauth2
.userInfoEndpoint((userInfoEndpointConfig) -> userInfoEndpointConfig
.userService(customOAuth2UserService))
.successHandler(customSuccessHandler)
.failureHandler(customFailHandler)
);
//경로별 인가 작업
http
.authorizeHttpRequests((auth) -> auth
.requestMatchers("/", "/user/**").permitAll()
.anyRequest().authenticated());
//세션 설정 : STATELESS
http
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
}
JWT를 이용해 사용자 정보를 처리하기 때문에 csrf 비활성화 세션 또한 stateless 설정을 해줬다. 각각의 설정은 사용자의 조건에 맞춰서 수정 해야한다.
configuration.setExposedHeaders(Collections.singletonList("Set-Cookie")); 쿠키를 헤더에 저장
configuration.setExposedHeaders(Collections.singletonList("Authorization")); 쿠키 name 설정
Spring boot 버전별 구현 방식
스프링 부트 2.X.X ~ 2.6.X
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").authenticated()
.anyRequest().permitAll();
}
}
WebSecurityConfig를 이용해 구현하였다.
스프링 부트 2.7.X ~ 3.0.X
public class SpringSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers("/")
.anyRequest().authenticated();
return http.build();
}
}
WebSecurityConfig -> SecurityFilterChain으로 바뀜
스프링 부트 3.1.X ~
public class SpringSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((auth) -> auth
.requestMatchers("/login", "/join").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}
SecurityFilterChain 람다식 선택에서 필수로 변경
CorsMvcConfig
@Configuration
public class CorsMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry corsRegistry) {
corsRegistry.addMapping("/**")
.exposedHeaders("Set-Cookie")
.allowedOrigins("http://localhost:3000");
}
}
로컬 환경에서 개발시 클라이언트 서버와 백엔드 서버 사이 웹브라우저에서 데이터를 요청하면 데이터가 보여지지 않는 CORS 오류를 허용해줘야 된다.
추가로 React에서 axios를 이용한 api 통신을 할 경우
// 1. axios 전역 설정
axios.defaults.withCredentials = true; // withCredentials 전역 설정
해당 코드를 추가하지 않으면 쿠키 데이터가 서버로 정상적으로 넘어오지 않는 문제가 발생한다
🍪 CORS 쿠키 전송하기 (withCredentials 옵션)
🤬 CORS를 허용했는데도 쿠키가 넘어가지 않는 현상 보통 웹을 구성할때 리액트(React)나 뷰(Vue)와 같은 라이브러리 / 프레임워크를 사용한다면 따로 프론트 서버를 실행하여 개발하게 된다. 만일
inpa.tistory.com
'Spring' 카테고리의 다른 글
Github Action 이용한 CI/CD Spring Server 자동화 배포 (feat: Synology Nas) (0) | 2024.06.05 |
---|---|
[Spring - Security] OAuth2 클라이언트와 Security 기초 인증 / 인가 처리 (Feat - Kakao Login && Local Login) - 2 (0) | 2024.05.19 |
[Spring] JWT 토큰이란 무엇일까? (0) | 2024.01.31 |
[Spring] SSR, CSR HTTP API 이용한 렌더링 방식의 차이점 (0) | 2024.01.31 |
[Spring] 양방향 매핑으로 인한 순환 참조 (0) | 2024.01.15 |