Skip to content

Commit

Permalink
refactor : Token 만료 시간을 포함한 Property를 TokenPropertyHolder를 통해 객체로 의존성…
Browse files Browse the repository at this point in the history
… 역전하여 관리 (#112)
  • Loading branch information
binary-ho committed Mar 12, 2024
1 parent b6ecacb commit ef23d2a
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@


import gdsc.binaryho.imhere.core.member.infrastructure.MemberRepository;
import gdsc.binaryho.imhere.security.oauth.CustomOAuth2SuccessHandler;
import gdsc.binaryho.imhere.security.oauth.CustomOAuth2UserService;
import gdsc.binaryho.imhere.security.filter.JwtAuthorizationFilter;
import gdsc.binaryho.imhere.security.jwt.TokenPropertyHolder;
import gdsc.binaryho.imhere.security.jwt.TokenService;
import gdsc.binaryho.imhere.security.oauth.CustomOAuth2SuccessHandler;
import gdsc.binaryho.imhere.security.oauth.CustomOAuth2UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -41,6 +42,7 @@ public class SecurityConfig {
private final CustomOAuth2UserService customOAuth2UserService;

private final TokenService tokenService;
private final TokenPropertyHolder tokenPropertyHolder;

@Value("${actuator.username}")
private String ACTUATOR_USERNAME;
Expand Down Expand Up @@ -94,7 +96,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.httpBasic().disable()

.oauth2Login(configurer -> {
configurer.userInfoEndpoint(endpoint -> endpoint.userService(customOAuth2UserService));
configurer.userInfoEndpoint(
endpoint -> endpoint.userService(customOAuth2UserService));
configurer.successHandler(customOAuth2SuccessHandler);
configurer.failureHandler(setStatusUnauthorized());
}
Expand All @@ -114,7 +117,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.anyRequest().authenticated();

http.addFilterBefore(new JwtAuthorizationFilter(
authenticationManager(authenticationConfiguration), tokenService, memberRepository), BasicAuthenticationFilter.class);
authenticationManager(authenticationConfiguration),
tokenService, memberRepository, tokenPropertyHolder),
BasicAuthenticationFilter.class);

return http.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import gdsc.binaryho.imhere.security.jwt.Token;
import gdsc.binaryho.imhere.security.jwt.TokenPropertyHolder;
import gdsc.binaryho.imhere.security.jwt.TokenService;
import java.io.IOException;
import javax.servlet.FilterChain;
Expand All @@ -25,10 +26,10 @@
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

private static final String HEADER_STRING = HttpHeaders.AUTHORIZATION;
private static final String ACCESS_TOKEN_PREFIX = "Token ";

private final AuthenticationManager authenticationManager;
private final TokenService tokenService;
private final TokenPropertyHolder tokenPropertyHolder;

@Override
public Authentication attemptAuthentication(
Expand Down Expand Up @@ -68,8 +69,9 @@ public void successfulAuthentication(HttpServletRequest request,

Token jwtToken = tokenService.createToken(authResult.getPrincipal().toString(), grantedAuthority);

String accessTokenPrefix = tokenPropertyHolder.getAccessTokenPrefix();
response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.AUTHORIZATION);
response.addHeader(HEADER_STRING, ACCESS_TOKEN_PREFIX + jwtToken.getAccessToken());
response.addHeader(HEADER_STRING, accessTokenPrefix + jwtToken.getAccessToken());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import gdsc.binaryho.imhere.core.auth.exception.MemberNotFoundException;
import gdsc.binaryho.imhere.core.member.Member;
import gdsc.binaryho.imhere.core.member.infrastructure.MemberRepository;
import gdsc.binaryho.imhere.security.jwt.TokenPropertyHolder;
import gdsc.binaryho.imhere.security.jwt.TokenService;
import gdsc.binaryho.imhere.security.principal.PrincipalDetails;
import java.io.IOException;
Expand All @@ -21,38 +22,43 @@
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

private static final String TOKEN_HEADER_STRING = HttpHeaders.AUTHORIZATION;
private static final String ACCESS_TOKEN_PREFIX = "Token ";

private final TokenService tokenService;
private final MemberRepository memberRepository;
private final TokenPropertyHolder tokenPropertyHolder;

public JwtAuthorizationFilter(
AuthenticationManager authenticationManager,
TokenService tokenService, MemberRepository memberRepository) {
TokenService tokenService, MemberRepository memberRepository,
TokenPropertyHolder tokenPropertyHolder) {
super(authenticationManager);
this.tokenService = tokenService;
this.memberRepository = memberRepository;
this.tokenPropertyHolder = tokenPropertyHolder;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain)
throws ServletException, IOException {
String jwtToken = request.getHeader(TOKEN_HEADER_STRING);
if (isTokenNullOrInvalidate(jwtToken)) {
chain.doFilter(request, response);
return;
}

String tokenValue = jwtToken.replace(ACCESS_TOKEN_PREFIX, "");
String accessTokenPrefix = tokenPropertyHolder.getAccessTokenPrefix();
String tokenValue = jwtToken.replace(accessTokenPrefix, "");
if (tokenService.validateTokenExpirationTimeNotExpired(tokenValue)) {
setAuthentication(tokenValue);
}
chain.doFilter(request, response);
}

private boolean isTokenNullOrInvalidate(String token) {
String accessTokenPrefix = tokenPropertyHolder.getAccessTokenPrefix();
return Objects.isNull(token)
|| (!token.startsWith(ACCESS_TOKEN_PREFIX));
|| (!token.startsWith(accessTokenPrefix));
}

private void setAuthentication(String jwtToken) {
Expand All @@ -62,7 +68,8 @@ private void setAuthentication(String jwtToken) {

PrincipalDetails principalDetails = new PrincipalDetails(member);
Authentication authentication =
new UsernamePasswordAuthenticationToken(principalDetails, "", principalDetails.getAuthorities());
new UsernamePasswordAuthenticationToken(principalDetails, "",
principalDetails.getAuthorities());

SecurityContextHolder.getContext().setAuthentication(authentication);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package gdsc.binaryho.imhere.security.jwt;

import java.time.Duration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ImhereTokenPropertyHolder implements TokenPropertyHolder {

@Value("${token.secret}")
private String secret;

@Value("${token.access-token-expire-minute}")
private Integer accessTokenExpireMinute;

@Value("${token.access-token-prefix}")
private String accessTokenPrefix;

@Override
public String getSecret() {
return secret;
}

@Override
public Duration getAccessTokenExpiration() {
return Duration.ofMinutes(accessTokenExpireMinute);
}

@Override
public String getAccessTokenPrefix() {
return accessTokenPrefix;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package gdsc.binaryho.imhere.security.jwt;

import java.time.Duration;

public interface TokenPropertyHolder {

String getSecret();

Duration getAccessTokenExpiration();

String getAccessTokenPrefix();
}
16 changes: 10 additions & 6 deletions src/main/java/gdsc/binaryho/imhere/security/jwt/TokenService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
@RequiredArgsConstructor
public class TokenService {

private final SecretHolder secretHolder;
private final SeoulDateTimeHolder seoulDateTimeHolder;
private final TokenPropertyHolder tokenPropertyHolder;

private static final String ROLE_KEY = "role";
private static final Duration ACCESS_TOKEN_EXPIRATION_TIME = Duration.ofMinutes(30);

// TODO : 기존 토큰 생성 방식은 삭제할 예정
public Token createToken(String univId, String roleKey) {
Expand Down Expand Up @@ -52,8 +51,9 @@ public boolean validateTokenExpirationTimeNotExpired(String token) {
}

public Long getId(String token) {
String tokenSecret = tokenPropertyHolder.getSecret();
String subject = Jwts.parser()
.setSigningKey(secretHolder.getSecret())
.setSigningKey(tokenSecret)
.parseClaimsJws(token)
.getBody()
.getSubject();
Expand All @@ -75,17 +75,21 @@ private Boolean isNullOrEmpty(String token) {

private String buildJwtToken(Claims claims) {
long seoulTimeNow = seoulDateTimeHolder.getSeoulMilliseconds();
Duration accessTokenExpiration = tokenPropertyHolder.getAccessTokenExpiration();
String tokenSecret = tokenPropertyHolder.getSecret();

return Jwts.builder()
.setClaims(claims)
.setIssuedAt(new Date(seoulTimeNow))
.setExpiration(new Date(seoulTimeNow + ACCESS_TOKEN_EXPIRATION_TIME.toMillis()))
.signWith(SignatureAlgorithm.HS256, secretHolder.getSecret())
.setExpiration(new Date(seoulTimeNow + accessTokenExpiration.toMillis()))
.signWith(SignatureAlgorithm.HS256, tokenSecret)
.compact();
}

private void parseToValidateToken(String token) {
String tokenSecret = tokenPropertyHolder.getSecret();
Jwts.parser()
.setSigningKey(secretHolder.getSecret())
.setSigningKey(tokenSecret)
.parseClaimsJws(token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import gdsc.binaryho.imhere.security.SignUpProcessRedirectionPath;
import gdsc.binaryho.imhere.security.jwt.Token;
import gdsc.binaryho.imhere.security.jwt.TokenPropertyHolder;
import gdsc.binaryho.imhere.security.jwt.TokenService;
import gdsc.binaryho.imhere.util.ClientUrlUtil;
import java.io.IOException;
Expand All @@ -18,10 +19,10 @@
public class CustomOAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

private static final String HEADER_STRING = HttpHeaders.AUTHORIZATION;
private static final String ACCESS_TOKEN_PREFIX = "Token ";

private final TokenService tokenService;
private final ClientUrlUtil clientUrlUtil;
private final TokenPropertyHolder tokenPropertyHolder;

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Expand All @@ -39,8 +40,10 @@ private void setRedirectUrl(HttpServletRequest request, HttpServletResponse resp
}

private void setAccessToken(HttpServletResponse response, CustomOAuth2User oAuthUser) {
Token jwtToken = tokenService.createToken(oAuthUser.getMemberId(), oAuthUser.getRole());
response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.AUTHORIZATION);
response.addHeader(HEADER_STRING, ACCESS_TOKEN_PREFIX + jwtToken.getAccessToken());

String accessTokenPrefix = tokenPropertyHolder.getAccessTokenPrefix();
Token jwtToken = tokenService.createToken(oAuthUser.getMemberId(), oAuthUser.getRole());
response.addHeader(HEADER_STRING, accessTokenPrefix + jwtToken.getAccessToken());
}
}
3 changes: 2 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ spring:
client-id: ENC(18FNZ6cjSHBH889DgERn3ZscMgAY8R02b6DBg9l47Xs=)
client-secret: ENC(hUvBA0g45hgzXQ2Ia/ovO8dQWDR67BsVt6Um0h1cSh/qrwKaWdVND06XApXIJMSaZy5Kem6nJx4=)

jwt:
token:
secret: ENC(Wi5BD1R9VNidIJj2xI10Ww==)
prefix: "Bearer "
header-string: "Authorization"
access-token-prefix: "Token "
access-token-expire-minute: 30

# SMTP
mail:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package gdsc.binaryho.imhere.mock;

import gdsc.binaryho.imhere.security.jwt.TokenPropertyHolder;
import java.time.Duration;

public class FakeTokenPropertyHolder implements TokenPropertyHolder {

private final String secret;
private final Duration accessTokenExpiration;


public FakeTokenPropertyHolder(String secret, Duration accessTokenExpiration) {
this.secret = secret;
this.accessTokenExpiration = accessTokenExpiration;
}

@Override
public String getSecret() {
return secret;
}

@Override
public Duration getAccessTokenExpiration() {
return accessTokenExpiration;
}

@Override
public String getAccessTokenPrefix() {
return "prefix";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
public class FixedSeoulTimeHolder implements SeoulDateTimeHolder {

public static final LocalDateTime FIXED_LOCAL_DATE_TIME = LocalDateTime.now();
// LocalDateTime
// .of(2023, Month.MAY, 6, 11, 0, 0);

public static final Long FIXED_MILLISECONDS = FIXED_LOCAL_DATE_TIME
.toInstant(ZoneOffset.UTC).toEpochMilli();
Expand Down
17 changes: 0 additions & 17 deletions src/test/java/gdsc/binaryho/imhere/mock/TestSecretHolder.java

This file was deleted.

Loading

0 comments on commit ef23d2a

Please sign in to comment.