-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from Kusitms-29th-Meetup-TeamE/feat/14/oauth
Feat: 카카오 oauth 로그인 구현
- Loading branch information
Showing
19 changed files
with
581 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 15 additions & 1 deletion
16
src/main/java/com/meetup/teame/backend/domain/auth/config/SecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
src/main/java/com/meetup/teame/backend/domain/auth/jwt/JwtFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package com.meetup.teame.backend.domain.auth.jwt; | ||
|
||
import com.meetup.teame.backend.global.exception.CustomException; | ||
import com.meetup.teame.backend.global.exception.ExceptionContent; | ||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.util.StringUtils; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
import java.io.IOException; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class JwtFilter extends OncePerRequestFilter { | ||
private static final String AUTHORIZATION = "Authorization"; | ||
private static final String BEARER = "Bearer "; | ||
|
||
private final JwtProvider jwtProvider; | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { | ||
final String accessToken = getAccessTokenFromHttpServletRequest(request); | ||
jwtProvider.validateAccessToken(accessToken); | ||
final Long userId = jwtProvider.getSubject(accessToken); | ||
setAuthentication(request, userId); | ||
filterChain.doFilter(request, response); | ||
} | ||
|
||
private String getAccessTokenFromHttpServletRequest(HttpServletRequest request) { | ||
String accessToken = request.getHeader(AUTHORIZATION); | ||
if (StringUtils.hasText(accessToken) && accessToken.startsWith(BEARER)) { | ||
return accessToken.substring(BEARER.length()); | ||
} | ||
throw new CustomException(ExceptionContent.INVALID_TOKEN); | ||
} | ||
|
||
private void setAuthentication(HttpServletRequest request, Long userId) { | ||
UserAuthentication authentication = new UserAuthentication(userId, null, null); | ||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | ||
SecurityContextHolder.getContext().setAuthentication(authentication); | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
src/main/java/com/meetup/teame/backend/domain/auth/jwt/JwtProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package com.meetup.teame.backend.domain.auth.jwt; | ||
|
||
import com.meetup.teame.backend.global.exception.CustomException; | ||
import com.meetup.teame.backend.global.exception.ExceptionContent; | ||
import io.jsonwebtoken.*; | ||
import io.jsonwebtoken.security.Keys; | ||
import lombok.Getter; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.security.Key; | ||
import java.util.Base64; | ||
import java.util.Date; | ||
|
||
@Getter | ||
@Component | ||
public class JwtProvider { | ||
|
||
@Value("${jwt.secret}") | ||
private String secretKey; | ||
|
||
public void validateAccessToken(String accessToken) { | ||
try { | ||
getJwtParser().parseClaimsJws(accessToken); | ||
} catch (ExpiredJwtException e) { | ||
throw new CustomException(ExceptionContent.EXPIRED_TOKEN); | ||
} catch (Exception e) { | ||
throw new CustomException(ExceptionContent.INVALID_TOKEN); | ||
} | ||
} | ||
|
||
public Long getSubject(String token) { | ||
return Long.valueOf(getJwtParser().parseClaimsJws(token) | ||
.getBody() | ||
.getSubject()); | ||
} | ||
|
||
public String generateToken(Long userId, long expireTime) { | ||
final Date now = new Date(); | ||
final Date expiration = new Date(now.getTime() + expireTime); | ||
return Jwts.builder() | ||
.setHeaderParam(Header.TYPE, Header.JWT_TYPE) | ||
.setSubject(String.valueOf(userId)) | ||
.setIssuedAt(now) | ||
.setExpiration(expiration) | ||
.signWith(getSigningKey(), SignatureAlgorithm.HS256) | ||
.compact(); | ||
} | ||
|
||
private JwtParser getJwtParser() { | ||
return Jwts.parserBuilder() | ||
.setSigningKey(getSigningKey()) | ||
.build(); | ||
} | ||
|
||
private Key getSigningKey() { | ||
String encoded = Base64.getEncoder().encodeToString(secretKey.getBytes()); | ||
return Keys.hmacShaKeyFor(encoded.getBytes()); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
src/main/java/com/meetup/teame/backend/domain/auth/jwt/UserAuthentication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.meetup.teame.backend.domain.auth.jwt; | ||
|
||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.GrantedAuthority; | ||
|
||
import java.util.Collection; | ||
|
||
public class UserAuthentication extends UsernamePasswordAuthenticationToken { | ||
public UserAuthentication(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { | ||
super(principal, credentials, authorities); | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
src/main/java/com/meetup/teame/backend/domain/auth/jwt/dto/CustomUserDetails.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package com.meetup.teame.backend.domain.auth.jwt.dto; | ||
|
||
import com.meetup.teame.backend.domain.user.entity.User; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
|
||
@RequiredArgsConstructor | ||
public class CustomUserDetails implements UserDetails { | ||
|
||
private final User user; | ||
|
||
@Override | ||
public Collection<? extends GrantedAuthority> getAuthorities() { | ||
|
||
Collection<GrantedAuthority> collection = new ArrayList<>(); | ||
|
||
collection.add(new GrantedAuthority() { | ||
@Override | ||
public String getAuthority() { | ||
return user.getEmail(); | ||
} | ||
}); | ||
return collection; | ||
} | ||
|
||
@Override | ||
public String getUsername() { | ||
return user.getName(); | ||
} | ||
|
||
@Override | ||
public String getPassword() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonExpired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonLocked() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isCredentialsNonExpired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isEnabled() { | ||
return true; | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/com/meetup/teame/backend/domain/auth/jwt/dto/TokenInfo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.meetup.teame.backend.domain.auth.jwt.dto; | ||
|
||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
@Getter | ||
public class TokenInfo { | ||
private String accessToken; | ||
private String refreshToken; | ||
|
||
public static TokenInfo of(String accessToken, String refreshToken) { | ||
return new TokenInfo(accessToken, refreshToken); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/com/meetup/teame/backend/domain/auth/jwt/service/CustomUserDetailsService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.meetup.teame.backend.domain.auth.jwt.service; | ||
|
||
import com.meetup.teame.backend.domain.auth.jwt.dto.CustomUserDetails; | ||
import com.meetup.teame.backend.domain.user.entity.User; | ||
import com.meetup.teame.backend.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; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class CustomUserDetailsService implements UserDetailsService { | ||
|
||
private final UserRepository userRepository; | ||
|
||
@Override | ||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | ||
|
||
//DB에서 조회 | ||
User userData = userRepository.findByName(username); | ||
|
||
if (userData != null) { | ||
|
||
//UserDetails에 담아서 return하면 AutneticationManager가 검증 함 | ||
return new CustomUserDetails(userData); | ||
} | ||
|
||
return null; | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
src/main/java/com/meetup/teame/backend/domain/auth/oauth/controller/KakaoController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package com.meetup.teame.backend.domain.auth.oauth.controller; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.meetup.teame.backend.domain.auth.oauth.dto.CreateOauthUserRequest; | ||
import com.meetup.teame.backend.domain.auth.oauth.dto.CreateUserRequest; | ||
import com.meetup.teame.backend.domain.auth.oauth.service.KakaoService; | ||
import com.meetup.teame.backend.domain.user.entity.User; | ||
import com.meetup.teame.backend.domain.user.service.UserService; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.*; | ||
import org.springframework.web.bind.annotation.*; | ||
import java.util.Optional; | ||
|
||
|
||
@RequiredArgsConstructor | ||
@RestController | ||
@RequestMapping("/api") | ||
@Tag(name = "oauth", description = "로그인 관련 api") | ||
public class KakaoController { | ||
|
||
private final UserService userService; | ||
private final KakaoService kakaoService; | ||
|
||
@Operation(summary = "카카오 로그인", description = """ | ||
카카오 소셜 로그인을 진행합니다. | ||
이미 등록된 사용자면 "login"이 출력되고 | ||
등록되지 않은 사용자면 "/api/sign-up"을 요청해서 거주지 정보를 추가로 받아줘야 합니다. | ||
""") | ||
@GetMapping("/login/kakao") | ||
public ResponseEntity<Object> kakaoLogin(@RequestParam String code) throws JsonProcessingException { | ||
String kakaoAccessToken = kakaoService.getKakaoAccessToken(code); //인가코드로 카카오 엑세스 토큰 받아오기 | ||
CreateOauthUserRequest request = kakaoService.getKakaoInfo(kakaoAccessToken); //엑세스 토큰으로 카카오 사용자 정보 받아오기 | ||
boolean checkExist = kakaoService.userExists(request.getEmail()); | ||
if(checkExist) { //이미 가입된 회원 | ||
/*Optional<User> userOptional*/User user = userService.findByEmail(request.getEmail()); | ||
//User user = userOptional.get(); | ||
HttpHeaders headers = kakaoService.getLoginHeader(user); | ||
|
||
return ResponseEntity.ok().headers(headers).body("login"); | ||
//로그인 처리하기 | ||
} else { //신규 회원 | ||
return ResponseEntity.ok(request); | ||
} | ||
} | ||
|
||
@Operation(summary = "사용자 등록", description = """ | ||
사용자 정보 등록을 진행합니다. | ||
거주지까지 입력을 마친 정보로 DB에 사용자 정보를 등록합니다. | ||
Jwt 토큰을 헤더에 넣어서 "OK" 메세지와 함께 반환합니다. | ||
""") | ||
@PostMapping("/sign-up") | ||
public ResponseEntity<Object> signup(@RequestBody CreateUserRequest request) { //이미 있는 회원인지 확인해야됨 | ||
User user = userService.createUser(request); | ||
Long userId = userService.save(user); | ||
HttpHeaders headers = kakaoService.getLoginHeader(userService.findById(userId)); | ||
return ResponseEntity.ok().headers(headers).body("OK"); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/main/java/com/meetup/teame/backend/domain/auth/oauth/dto/CreateOauthUserRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.meetup.teame.backend.domain.auth.oauth.dto; | ||
|
||
import lombok.*; | ||
|
||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
@Getter | ||
public class CreateOauthUserRequest { | ||
private String name; // 필수 동의 | ||
private String email; // 선택 동의 | ||
private String gender; // 필수 동의 | ||
private String birthYear; // 필수 동의 | ||
private String profileImage; // 선택 동의 | ||
} |
27 changes: 27 additions & 0 deletions
27
src/main/java/com/meetup/teame/backend/domain/auth/oauth/dto/CreateUserRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.meetup.teame.backend.domain.auth.oauth.dto; | ||
|
||
import com.meetup.teame.backend.domain.user.entity.Gender; | ||
import jakarta.persistence.EnumType; | ||
import jakarta.persistence.Enumerated; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import org.hibernate.annotations.Comment; | ||
|
||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
@Getter | ||
public class CreateUserRequest { | ||
|
||
private String name; | ||
|
||
private String email; | ||
|
||
private String imageUrl; | ||
|
||
private String gender; | ||
|
||
private String birthyear; | ||
|
||
private String location; | ||
} |
Oops, something went wrong.