Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
YoungGyo-00 committed Jan 30, 2024
2 parents 1acbf01 + 38cd450 commit c529826
Show file tree
Hide file tree
Showing 31 changed files with 592 additions and 610 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.reddiserver.auth.filter;

import jakarta.servlet.http.HttpServletRequest;
import lombok.Getter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

@Getter
public class CustomAuthenticationDetails extends WebAuthenticationDetails {

private final Long userId;

public CustomAuthenticationDetails(Long userId, HttpServletRequest request) {
super(request);
this.userId = userId;
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.example.reddiserver.auth.filter;

import com.example.reddiserver.auth.service.JwtTokenProvider;
import com.example.reddiserver.entity.enums.Authority;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.MalformedJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.h2.engine.Role;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Component
@Slf4j
@RequiredArgsConstructor
public class JwtRequestFilter extends OncePerRequestFilter {

private final JwtTokenProvider jwtTokenProvider;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String accessTokenHeader = request.getHeader("Authorization");
String refreshTokenHeader = request.getHeader("RefreshToken");

// 헤더에 토큰 안 담기는 경우는 인가 안하고 그대로 필터 종료
if (accessTokenHeader == null) {
filterChain.doFilter(request, response);
return;
}

// 헤더에 토큰 담기는 경우
if (accessTokenHeader.startsWith("Bearer ")) {
String token = accessTokenHeader.substring(7);

try {
// 토큰이 유효한 경우에 수행할 작업
jwtTokenProvider.validateToken(token);

Map<String, String> payload = jwtTokenProvider.getPayload(token);

Long userId = Long.valueOf(payload.get("userId"));

System.out.println("필터 userId = " + userId);


// 권한 부여
log.info("=====권한 부여=====");
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userId, null, List.of(new SimpleGrantedAuthority(Authority.ROLE_USER.name())));


// Detail을 넣어줌
// userId와 uuid를 추가로 넣어줌
authenticationToken.setDetails(new CustomAuthenticationDetails(userId, request));

SecurityContextHolder.getContext().setAuthentication(authenticationToken);

log.info("[+] Token in SecurityContextHolder");
filterChain.doFilter(request, response);

} catch (ExpiredJwtException e) {
// 토큰이 만료된 경우에 수행할 작업
// refresh 토큰이 있으면 검증하고 access 토큰 재발급
log.error("=====만료된 토큰입니다=====", e);
throw e;

// errorResponse(response, "jwt필터 - 만료된 토큰입니다", HttpServletResponse.SC_UNAUTHORIZED);

} catch (MalformedJwtException e) {
// 토큰 형식이 잘못된 경우에 수행할 작업
log.error("=====잘못된 형식의 토큰=====", e);
throw e;
// errorResponse(response, "jwt필터 - 잘못된 형식의 토큰입니다", HttpServletResponse.SC_BAD_REQUEST);

} catch (JwtException e) {
// 기타 예외 처리
log.error("=====유효하지 않은 토큰입니다=====", e);
throw e;
// errorResponse(response, "jwt필터 - 유효하지 않은 토큰입니다", HttpServletResponse.SC_FORBIDDEN);
}


}

}


// private boolean userHasPermission(String userId) {
// // 여기에서 해당 사용자의 권한을 확인하는 로직 구현
// // userId를 기반으로 데이터베이스 등에서 해당 사용자의 권한 정보를 가져와서 확인
// // 권한이 있다면 true를 반환, 권한이 없다면 false를 반환
// return true; // 또는 false
// }
//}


private void errorResponse(HttpServletResponse response, String errMsg, int httpStatus) throws IOException {
response.setStatus(httpStatus);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
response.getWriter().write("{\"error\": \"" + errMsg + "\"}");
}
}
68 changes: 68 additions & 0 deletions src/main/java/com/example/reddiserver/auth/oauth/GoogleOAuth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.example.reddiserver.auth.oauth;

import com.example.reddiserver.dto.auth.response.GoogleTokenResponseDto;
import com.example.reddiserver.dto.auth.response.GoogleUserResponseDto;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.Map;

@Component
@RequiredArgsConstructor
@Getter
public class GoogleOAuth {

private final String GOOGLE_LOGIN_URL = "https://accounts.google.com/o/oauth2/v2/auth";
private final String GOOGLE_TOKEN_REQUEST_URL = "https://oauth2.googleapis.com/token";
private final String GOOGLE_USERINFO_REQUEST_URL = "https://www.googleapis.com/oauth2/v3/userinfo";

@Value("${spring.security.oauth2.client.registration.google.redirectUri}")
private String REDIRECT_URI;

@Value("${spring.security.oauth2.client.registration.google.client-id}")
private String CLIENT_ID;

@Value("${spring.security.oauth2.client.registration.google.client-secret}")
private String CLIENT_SECRET;

private final WebClient webClient;

public GoogleTokenResponseDto requestToken(String code) {
Map<String, Object> params = Map.of(
"code", code,
"client_id", CLIENT_ID,
"client_secret", CLIENT_SECRET,
"redirect_uri", REDIRECT_URI,
"grant_type", "authorization_code"
);

return webClient.post()
.uri(GOOGLE_TOKEN_REQUEST_URL)
.bodyValue(params)
.retrieve()
.bodyToMono(GoogleTokenResponseDto.class)
.block();
}


public GoogleUserResponseDto requestUserInfo(GoogleTokenResponseDto googleToken) {

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + googleToken.getAccess_token());

GoogleUserResponseDto googleUserResponse = webClient.get()
.uri(GOOGLE_USERINFO_REQUEST_URL)
.headers(httpHeaders -> httpHeaders.setBearerAuth(googleToken.getAccess_token()))
.retrieve()
.bodyToMono(GoogleUserResponseDto.class)
.block();

System.out.println("requestUserInfo response = " + googleUserResponse);

return googleUserResponse;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.reddiserver.security.oauth.provider;
package com.example.reddiserver.auth.oauth.provider;

import com.example.reddiserver.entity.enums.ProviderType;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.reddiserver.security.oauth.provider;
package com.example.reddiserver.auth.oauth.provider;

import com.example.reddiserver.entity.enums.ProviderType;
import lombok.AllArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.example.reddiserver.auth.service;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
@RequiredArgsConstructor
public class JwtTokenProvider implements InitializingBean {

@Value("${spring.jwt.secret}")
private String JWT_SECRET;

private Key key;

private final Long accessTokenExpiredTime = 1000 * 60L * 60L * 24L; // 유효시간 24시간 (임시 변경)
private final Long refreshTokenExpiredTime = 1000 * 60L * 60L * 24L * 14L; // 유효시간 14일

private final Long shortAccessTokenExpiredTime = 1000 * 60L * 5; // 유효시간 5분

@Override
public void afterPropertiesSet() throws Exception {
Base64.Decoder decoders = Base64.getDecoder();
byte[] keyBytes = decoders.decode(JWT_SECRET);
this.key = Keys.hmacShaKeyFor(keyBytes);
}

public String createAccessToken(Map<String, String> payload) {
return createJwtToken(payload, accessTokenExpiredTime);
}

public String createRefreshToken(Map<String, String> payload) {
return createJwtToken(payload, refreshTokenExpiredTime);
}

public String createJwtToken(Map<String, String> payload, long expireLength) {
Date now = new Date();
Date validity = new Date(now.getTime() + expireLength);

try {
return Jwts.builder()
.setClaims(payload)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(key, SignatureAlgorithm.HS512)
.compact();
} catch (Exception e) {
throw new RuntimeException(e);
}

}

public Map<String, String> getPayload(String token) {
try {
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();

// 원하는 클레임 값 추출하여 Map으로 저장
Map<String, String> payload = new HashMap<>();
payload.put("userId", claims.get("userId", String.class));

return payload;

} catch (JwtException e) {
System.err.println("Error Type: " + e.getClass().getName());
System.err.println("Error Message: " + e.getMessage());
throw new JwtException("유효하지 않은 토큰 입니다");
}
}

public void validateToken(String token) throws JwtException {
try {
Jws<Claims> claimsJws = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token);

Date expiration = claimsJws.getBody().getExpiration();
Date now = new Date();

if (expiration.before(now)) {
throw new ExpiredJwtException(null, null, "Token expired");
}
} catch (ExpiredJwtException e) {
// 토큰이 만료된 경우 처리
log.error("===Token expired: {}===", e.getMessage());
throw e;
} catch (MalformedJwtException e) {
// 토큰 형식이 잘못된 경우 처리
log.error("====Malformed token: {}===", e.getMessage());
throw e;
} catch (JwtException | IllegalArgumentException e) {
// 기타 예외 처리
log.error("===Invalid token: {}===", e.getMessage());
throw e;
}
}



}
Loading

0 comments on commit c529826

Please sign in to comment.