Skip to content

Commit

Permalink
feat: 로그인 성공 시 JWT 생성 및 front로 redirect 기능 추
Browse files Browse the repository at this point in the history
- login 및 회원가입 후 로직 실행되는OAuth2AthenticationSuccessHandler 추가
- JwtProvider에 JWT생성 기능 추가
  • Loading branch information
ah9mon committed Jul 31, 2023
1 parent 46f9af5 commit 9ff56ae
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 25 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.2'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
Expand Down
18 changes: 16 additions & 2 deletions src/main/java/com/anywayclear/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.anywayclear.config;

import com.anywayclear.config.oauth.CustumOAuth2UserService;
import com.anywayclear.config.oauth.OAuth2AuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.filter.CorsFilter;

import javax.servlet.Filter;

@Configuration
@EnableWebSecurity
Expand All @@ -17,15 +20,26 @@ public class SecurityConfig {
@Autowired
private CustumOAuth2UserService custumOAuth2UserService;

@Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;

@Autowired
private CorsFilter corsFilter;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors(Customizer.withDefaults()) // 기본 CORS 설정 적용

httpSecurity
.addFilterBefore(corsFilter, CorsFilter.class) // CorsFilter를 SecurityFilterChain 앞에 추가
.csrf().disable() // CSRF 비활성화
.formLogin().disable() // spring security에서 제공하는 login form 비활성화
.authorizeHttpRequests(authorize -> authorize
.antMatchers(HttpMethod.OPTIONS).permitAll() // OPTIONS 메서드는 모두 허용
.anyRequest().permitAll() // 모든 요청 권한 허용 (추후 권한 설정해야함)
)
.oauth2Login(oauth2Login -> oauth2Login
// .loginPage("/frontLoginPage") // OAuth 2.0 로그인을 처리할 때, 인증되지 않은 사용자를 전달할 로그인 페이지를 지정
.successHandler(oAuth2AuthenticationSuccessHandler)
.userInfoEndpoint()
.userService(custumOAuth2UserService)
);
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/com/anywayclear/config/jwt/JwtProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.anywayclear.config.jwt;

public interface JwtProperties {
int EXPIRATION_TIME = 864000000; // 10일 (1/1000초)
String TOKEN_PREFIX = "Bearer ";
String HEADER_STRING = "Authorization";
}
43 changes: 43 additions & 0 deletions src/main/java/com/anywayclear/config/jwt/JwtProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.anywayclear.config.jwt;

import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Date;
import io.jsonwebtoken.Jwts;

import static com.anywayclear.config.jwt.JwtProperties.*;

@Component
public class JwtProvider {
private static final Logger logger = LoggerFactory.getLogger(JwtProvider.class);
private Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS512);

// 인증 정보를 기반으로 JWT 토큰 생성하는 메서드
public String createToken(Authentication authentication) {
System.out.println(">>>>>>>>>>>> ");

// 사용자 정보 가져오기
OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
System.out.println("Provider ================== ");
System.out.println("oAuth2User.getAttributes() = " + oAuth2User.getAttributes());

// 현재 시간과 토큰 만료 시간 설정
Date now = new Date();
Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);

// JWT 토큰 생성
return Jwts.builder()
.setSubject((String) oAuth2User.getAttributes().get("userId")) // userId를 토큰의 subject로 설정
.setIssuedAt(new Date()) // 토큰 발행 시간 설정
.setExpiration(expiryDate) // 토큰 만료 시간 설정
.signWith(SECRET_KEY, SignatureAlgorithm.HS512) // 토큰 서명에 사용할 비밀키 설정
.compact(); // 최종적으로 JWT 토큰 문자열로 변환
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.anywayclear.config.oauth;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import com.anywayclear.entity.Member;
import com.anywayclear.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -12,10 +15,13 @@
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@Slf4j
@RequiredArgsConstructor
@Service
public class CustumOAuth2UserService extends DefaultOAuth2UserService {

Expand All @@ -25,39 +31,52 @@ public class CustumOAuth2UserService extends DefaultOAuth2UserService {
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
Map<String, Object> attributes = oAuth2User.getAttributes();
Map<String, Object> kakao_account = (Map<String, Object>) attributes.get("kakao_account");
Map<String, Object> kakao_account = (Map<String, Object>) oAuth2User.getAttributes().get("kakao_account");
Map<String, Object> profile = (Map<String, Object>) kakao_account.get("profile");

String emailAddress = (String) kakao_account.get("email");
String nickname = (String) profile.get("nickname");
String image = (String) profile.get("profile_image_url");
String id = String.valueOf(UUID.randomUUID()); // UUID Version 4를 생성하여 반환
String userId = String.valueOf(UUID.randomUUID());

// email로 회원가입 여부 판단
Member member = getOrCreateMember(emailAddress, nickname, image);

String oauth2UserRole = member.getRole();
String oauth2UserId = member.getUserId();

Map<String, Object> userAttributes = new HashMap<>();
userAttributes.put("role", oauth2UserRole);
userAttributes.put("userId", oauth2UserId);

// Spring Security의 세션에 OAuth2User객체 저장됨
return new DefaultOAuth2User(Collections.singleton(new SimpleGrantedAuthority(oauth2UserRole)), userAttributes, "userId");
}

private Member getOrCreateMember(String emailAddress, String nickname, String image) {
Optional<Member> memberOptional = memberRepository.findByEmailAddress(emailAddress);
Member member;

if (memberOptional.isEmpty()) {
// 회원가입
member = Member.builder()
.id(id)
.userId(userId)
.emailAddress(emailAddress)
.image(image)
.nickname(nickname)
.role("ROLE_CUSTOMER")
.build();
} else {
member = memberOptional.get();
if (memberOptional.isPresent()) {
System.out.println("이미 회원입니다");
Member member = memberOptional.get();
member.setImage(image);
return memberRepository.save(member);
} else {
System.out.println("회원가입합니다");
return createMember(emailAddress, nickname, image);
}
}

memberRepository.save(member);
String oauth2UserRole = member.getRole();
private Member createMember(String emailAddress, String nickname, String image) {
String id = UUID.randomUUID().toString();
String userId = UUID.randomUUID().toString();

Member member = Member.builder()
.id(id)
.userId(userId)
.emailAddress(emailAddress)
.image(image)
.nickname(nickname)
.role("ROLE_CUSTOMER")
.build();

// Handler로 로그인된 유저객체 보내기
return new DefaultOAuth2User(Collections.singleton(new SimpleGrantedAuthority(oauth2UserRole)), attributes, "id");
return memberRepository.save(member);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.anywayclear.config.oauth;

import com.anywayclear.config.jwt.JwtProperties;
import com.anywayclear.config.jwt.JwtProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

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

@Component
public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

@Autowired
private JwtProvider jwtProvider;

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {

String jwt = jwtProvider.createToken(authentication);
String redirectUrl = "http://localhost:3000/oauth2/redirect";

response.setHeader(JwtProperties.HEADER_STRING, JwtProperties.TOKEN_PREFIX + jwt);
response.setStatus(HttpServletResponse.SC_OK);

if (response.isCommitted()) {
logger.debug(redirectUrl + "로 리다이렉트 불가");
return;
}

getRedirectStrategy().sendRedirect(request, response, redirectUrl);

}
}

0 comments on commit 9ff56ae

Please sign in to comment.