Skip to content

Commit

Permalink
Merge pull request #29 from team-REDDI/dev
Browse files Browse the repository at this point in the history
dev to main
  • Loading branch information
itsme-shawn authored Jan 26, 2024
2 parents 248e4ac + 416c180 commit bfee704
Show file tree
Hide file tree
Showing 15 changed files with 174 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;

@OpenAPIDefinition(servers = {@Server(url = "https://api.reddi.kr", description = "배포서버 URL"), @Server(url = "http://localhost:8080", description = "로컬서버 URL")})
//@OpenAPIDefinition(servers = {@Server(url = "https://api.reddi.kr", description = "배포서버 URL"), @Server(url = "http://localhost:8080", description = "로컬서버 URL")})
@EnableJpaAuditing
@EnableScheduling
@SpringBootApplication
Expand All @@ -16,5 +16,4 @@ public class ReddiServerApplication {
public static void main(String[] args) {
SpringApplication.run(ReddiServerApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.reddiserver.security.config;
package com.example.reddiserver.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down
40 changes: 29 additions & 11 deletions src/main/java/com/example/reddiserver/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,49 @@
package com.example.reddiserver.config;

import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import java.util.List;


@Configuration
@SecurityScheme(
name = "Authorization",
type = SecuritySchemeType.HTTP,
bearerFormat = "JWT",
scheme = "bearer"
)
public class SwaggerConfig {
private static final String SECURITY_SCHEME_NAME = "authorization"; // 추가

@Bean
@Profile("local")
public OpenAPI localOpenAPIBuilder() {
return new OpenAPI().addServersItem(new Server().url("http://localhost:8080"));
}

@Bean
@Profile("dev")
public OpenAPI devOpenAPIBuilder() {
return new OpenAPI().addServersItem(new Server().url("https://api.reddi.kr"));
}
@Bean
public OpenAPI openAPI() {
Info info = new Info()
.title("API Document")
.version("v0.0.1")
.description("REDDI API 명세서");
.title("API Document")
.version("v0.0.1")
.description("REDDI API 명세서");
return new OpenAPI()
.components(new Components()
.addSecuritySchemes(SECURITY_SCHEME_NAME, new SecurityScheme()
.name(SECURITY_SCHEME_NAME)
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")))
.addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME))
.components(new Components())
.info(info);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.reddiserver.security.config;
package com.example.reddiserver.config;

import com.example.reddiserver.security.jwt.JwtAuthenticationFilter;
import com.example.reddiserver.security.jwt.JwtExceptionHandlerFilter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,56 @@

import com.example.reddiserver.common.ApiResponse;
import com.example.reddiserver.dto.auth.request.ReissueRequestDto;
import com.example.reddiserver.dto.auth.response.UserInfoResponseDto;
import com.example.reddiserver.dto.security.response.TokenDto;
import com.example.reddiserver.entity.Member;
import com.example.reddiserver.repository.MemberRepository;
import com.example.reddiserver.security.jwt.TokenProvider;
import com.example.reddiserver.service.AuthService;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/auth")
public class AuthController {
private final AuthService authService;
private final TokenProvider tokenProvider;

@SecurityRequirement(name = "Authorization") // 인증 필요한 엔드포인트에 설정
@PostMapping("/reissue")
public ApiResponse<TokenDto> reissue(@RequestBody ReissueRequestDto reissueRequestDto) {
return ApiResponse.successResponse(authService.reissue(reissueRequestDto), "Access Token 재발급 성공");
}

@SecurityRequirement(name = "Authorization") // 인증 필요한 엔드포인트에 설정
@GetMapping("/info")
public ApiResponse<?> getUserInfo(HttpServletRequest request) {
// Extract Access Token from the request
String accessToken = tokenProvider.resolveToken(request);

Map<String, Object> userInfo = authService.getUserInfo(accessToken);

if (userInfo != null) {
UserInfoResponseDto userInfoResponseDto = UserInfoResponseDto.from(Long.valueOf(userInfo.get("userId").toString()),
userInfo.get("username").toString(),
userInfo.get("email").toString(),
userInfo.get("profileImageUrl").toString());

return ApiResponse.successResponse(userInfoResponseDto);
}
return ApiResponse.errorResponse("유효하지 않은 Access Token 입니다.");
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.reddiserver.dto.auth.response;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class UserInfoResponseDto {
private Long userId;
private String name;
private String email;
private String profileImageUrl;

public static UserInfoResponseDto from(Long userId, String name, String email, String profileImageUrl) {
UserInfoResponseDto userInfoResponseDto = new UserInfoResponseDto();
userInfoResponseDto.setUserId(userId);
userInfoResponseDto.setName(name);
userInfoResponseDto.setEmail(email);
userInfoResponseDto.setProfileImageUrl(profileImageUrl);
return userInfoResponseDto;
}
}
10 changes: 7 additions & 3 deletions src/main/java/com/example/reddiserver/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ public class Member extends BaseTimeEntity {
@Column(nullable = false)
private String name;

@Column(nullable = false)
private String email;

@Column
private String imageUrl;
private String profileImageUrl;

@Column(nullable = false)
@Enumerated(EnumType.STRING)
Expand All @@ -46,10 +49,11 @@ public class Member extends BaseTimeEntity {
private Authority authority;

@Builder
public Member(String providerId, String name, String imageUrl, ProviderType providerType, Authority authority) {
public Member(String providerId, String name, String email, String profileImageUrl, ProviderType providerType, Authority authority) {
this.providerId = providerId;
this.name = name;
this.imageUrl = imageUrl;
this.email = email;
this.profileImageUrl = profileImageUrl;
this.providerType = providerType;
this.authority = authority;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,13 @@
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BEARER_PREFIX = "Bearer";
private final TokenProvider tokenProvider;

private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);

if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
return bearerToken.substring(7);
}
private final TokenProvider tokenProvider;

return null;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String jwt = resolveToken(request);
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, RuntimeException {
String jwt = tokenProvider.resolveToken(request);

if (StringUtils.hasText(jwt) && tokenProvider.validateAccessToken(jwt)) {
Authentication authentication = tokenProvider.getAuthentication(jwt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.example.reddiserver.repository.RefreshTokenRepository;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
Expand All @@ -16,6 +17,7 @@
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.security.Key;
import java.util.*;
Expand All @@ -25,6 +27,9 @@
@Component
@RequiredArgsConstructor
public class TokenProvider implements InitializingBean {

private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BEARER_PREFIX = "Bearer";
private static final String AUTHORITIES_KEY = "auth";
private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30L;
private static final long REFRESH_TOKEN_EXPIRE_TIME = 60 * 60 * 24 * 3L;
Expand Down Expand Up @@ -110,18 +115,32 @@ public Authentication getAuthentication(String accessToken) {
return new UsernamePasswordAuthenticationToken(userDetails, "", authorities);
}

public boolean validateAccessToken(String accessToken) {
public String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);

if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
return bearerToken.substring(7);
}

return null;
}

public boolean validateAccessToken(String accessToken){
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(accessToken);
return true;
} catch (io.jsonwebtoken.security.SignatureException | MalformedJwtException e) {
log.error("잘못된 JWT 서명입니다");
String msg = "잘못된 JWT 서명입니다";
log.error(msg);
} catch (ExpiredJwtException e) {
log.error("만료된 JWT 토큰입니다");
String msg = "만료된 JWT 토큰입니다";
log.error(msg);
} catch (UnsupportedJwtException e) {
log.error("지원되지 않는 JWT 토큰입니다");
String msg = "지원되지 않는 JWT 토큰입니다";
log.error(msg);
} catch (IllegalArgumentException e) {
log.error("Jwt 토큰이 잘못되었습니다");
String msg = "JWT 토큰이 잘못되었습니다";
log.error(msg);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public String getAuthority() {
}

@Override
public String getName() {
public String getName() { // principal.getName() 에서 getName() 은 providerId 를 의미
return member.getProviderId();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ protected OAuth2User processOAuth2User(OAuth2UserRequest userRequest, OAuth2User
String registrationId = userRequest.getClientRegistration().getRegistrationId().toUpperCase();
OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(registrationId, oAuth2User.getAttributes());

System.out.println("oAuth2User.getAttributes() = " + oAuth2User.getAttributes());

Member member = memberRepository.findByProviderId(oAuth2UserInfo.getProviderId()).orElse(null);
if (member == null) {
member = signup(oAuth2UserInfo);
Expand All @@ -39,6 +41,8 @@ private Member signup(OAuth2UserInfo oAuth2UserInfo) {
Member member = Member.builder()
.providerId(oAuth2UserInfo.getProviderId())
.name(oAuth2UserInfo.getName())
.email(oAuth2UserInfo.getEmail())
.profileImageUrl(oAuth2UserInfo.getProfileImage())
.providerType(oAuth2UserInfo.getProviderType())
.authority(Authority.ROLE_USER)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ public ProviderType getProviderType() {
public String getEmail() {
return (String) attributes.get("email");
}

public String getProfileImage() {
return (String) attributes.get("picture");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ public abstract class OAuth2UserInfo {
public abstract String getName();
public abstract ProviderType getProviderType();
public abstract String getEmail();
public abstract String getProfileImage();
}
34 changes: 34 additions & 0 deletions src/main/java/com/example/reddiserver/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
package com.example.reddiserver.service;

import com.example.reddiserver.common.ApiResponse;
import com.example.reddiserver.dto.auth.request.ReissueRequestDto;
import com.example.reddiserver.dto.security.response.TokenDto;
import com.example.reddiserver.entity.Member;
import com.example.reddiserver.repository.MemberRepository;
import com.example.reddiserver.security.jwt.TokenProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class AuthService {
private final TokenProvider tokenProvider;
private final MemberRepository memberRepository;

@Transactional
public TokenDto reissue(ReissueRequestDto reissueRequestDto) {
Expand All @@ -24,4 +33,29 @@ public TokenDto reissue(ReissueRequestDto reissueRequestDto) {

return tokenProvider.createAccessToken(authentication);
}

// user info 조회 서비스
public Map<String, Object> getUserInfo(String accessToken) {

// Validate the Access Token
if (StringUtils.hasText(accessToken) && tokenProvider.validateAccessToken(accessToken)) {
// Retrieve user information from the token
Authentication authentication = tokenProvider.getAuthentication(accessToken);
UserDetails userDetails = (UserDetails) authentication.getPrincipal();

String providerId = userDetails.getUsername();

Member member = memberRepository.findByProviderId(providerId)
.orElseThrow(() -> new RuntimeException("User not found"));

Map<String, Object> userInfo = new HashMap<>();
userInfo.put("userId", member.getId());
userInfo.put("username", member.getName());
userInfo.put("email", member.getEmail());
userInfo.put("profileImageUrl", member.getProfileImageUrl());
return userInfo;
}
return null;
}

}
Loading

0 comments on commit bfee704

Please sign in to comment.