diff --git a/backend/src/main/java/com/twtw/backend/config/security/SecurityConfig.java b/backend/src/main/java/com/twtw/backend/config/security/SecurityConfig.java index f8c981d5..71cb93dc 100644 --- a/backend/src/main/java/com/twtw/backend/config/security/SecurityConfig.java +++ b/backend/src/main/java/com/twtw/backend/config/security/SecurityConfig.java @@ -18,6 +18,14 @@ @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { + + private static final String[] AUTH_WHITELIST = { + "/auth/refresh", + "/auth/save", + "/auth/login", + "/member/duplicate/**", + "/location/**", + "/actuator/**"}; private final JwtFilter jwtFilter; private final JwtAccessDeniedHandler jwtAccessDeniedHandler; private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @@ -30,14 +38,7 @@ public SecurityFilterChain configure(HttpSecurity http) throws Exception { .formLogin(f -> f.disable()) .authorizeHttpRequests( x -> - x.requestMatchers( - "/auth/refresh", - "/auth/save", - "/auth/login", - "/member/duplicate/**", - "/location/**", - "/actuator/**", - "/member/test/**") + x.requestMatchers(AUTH_WHITELIST) .permitAll() .anyRequest() .authenticated()) diff --git a/backend/src/main/java/com/twtw/backend/config/security/jwt/JwtFilter.java b/backend/src/main/java/com/twtw/backend/config/security/jwt/JwtFilter.java index db47c486..3931c8f8 100644 --- a/backend/src/main/java/com/twtw/backend/config/security/jwt/JwtFilter.java +++ b/backend/src/main/java/com/twtw/backend/config/security/jwt/JwtFilter.java @@ -14,6 +14,7 @@ import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; +import java.util.Optional; @Component @RequiredArgsConstructor @@ -25,24 +26,29 @@ public class JwtFilter extends OncePerRequestFilter { @Override protected void doFilterInternal( - HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException { - String jwt = resolveToken(request); + Optional jwt = resolveToken(request); + + jwt.ifPresent(this::setAuthentication); - if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) { - Authentication authentication = tokenProvider.getAuthentication(jwt); - SecurityContextHolder.getContext().setAuthentication(authentication); - } filterChain.doFilter(request, response); } - private String resolveToken(HttpServletRequest request) { + private void setAuthentication(final String token) { + if (tokenProvider.validateToken(token)) { + Optional authentication = tokenProvider.getAuthentication(token); + authentication.ifPresent(SecurityContextHolder.getContext()::setAuthentication); + } + } + + private Optional resolveToken(final HttpServletRequest request) { String bearerToken = request.getHeader(AUTHORIZATION_HEADER); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) { - return bearerToken.substring(7); + return Optional.of(bearerToken.substring(BEARER_PREFIX.length())); } - return null; + return Optional.empty(); } } diff --git a/backend/src/main/java/com/twtw/backend/config/security/jwt/KeyConfig.java b/backend/src/main/java/com/twtw/backend/config/security/jwt/KeyConfig.java new file mode 100644 index 00000000..1397c5ca --- /dev/null +++ b/backend/src/main/java/com/twtw/backend/config/security/jwt/KeyConfig.java @@ -0,0 +1,19 @@ +package com.twtw.backend.config.security.jwt; + +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.security.Key; + +@Configuration +public class KeyConfig { + + @Bean + public Key key(@Value("${jwt.secret}") final String secretKey) { + byte[] keyBytes = Decoders.BASE64.decode(secretKey); + return Keys.hmacShaKeyFor(keyBytes); + } +} diff --git a/backend/src/main/java/com/twtw/backend/config/security/jwt/TokenProvider.java b/backend/src/main/java/com/twtw/backend/config/security/jwt/TokenProvider.java index e8085648..bc24d8cf 100644 --- a/backend/src/main/java/com/twtw/backend/config/security/jwt/TokenProvider.java +++ b/backend/src/main/java/com/twtw/backend/config/security/jwt/TokenProvider.java @@ -2,14 +2,10 @@ import com.twtw.backend.domain.member.dto.response.TokenDto; import com.twtw.backend.domain.member.entity.Member; +import com.twtw.backend.domain.member.entity.Role; import com.twtw.backend.global.exception.AuthorityException; - import io.jsonwebtoken.*; -import io.jsonwebtoken.io.Decoders; -import io.jsonwebtoken.security.Keys; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Value; +import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -17,31 +13,18 @@ import org.springframework.stereotype.Component; import java.security.Key; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; @Component -public class TokenProvider implements InitializingBean { +@RequiredArgsConstructor +public class TokenProvider { - private Key key; - private final String secretKey; + private final Key key; private static final String AUTHORITIES_KEY = "auth"; private static final Long ACCESS_TOKEN_EXPIRE_LENGTH = 60L * 60 * 24 * 1000; // 1 Day private static final Long REFRESH_TOKEN_EXPIRE_LENGTH = 60L * 60 * 24 * 14 * 1000; // 14 Days - public TokenProvider(@Value("${jwt.secret}") String secretKey) { - this.secretKey = secretKey; - } - - @Override - public void afterPropertiesSet() throws Exception { - byte[] keyBytes = Decoders.BASE64.decode(secretKey); - this.key = Keys.hmacShaKeyFor(keyBytes); - } - public TokenDto createToken(Authentication authentication) { String authorities = authentication.getAuthorities().stream() @@ -69,23 +52,30 @@ public TokenDto createToken(Authentication authentication) { return new TokenDto(accessToken, refreshToken); } - public Authentication getAuthentication(String accessToken) { + public Optional getAuthentication(String accessToken) { Claims claims = parseClaims(accessToken); - if (claims.get(AUTHORITIES_KEY) == null) { - throw new AuthorityException(); - } + return Optional.ofNullable(claims.get(AUTHORITIES_KEY)) + .map( + auth -> { + Collection authorities = new ArrayList<>(); + String role = claims.get(AUTHORITIES_KEY).toString(); - Collection authorities = new ArrayList<>(); - String role = claims.get(AUTHORITIES_KEY).toString(); + addRole(role, authorities); - if (role.equals("ROLE_ADMIN")) { - authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); - } else if (role.equals("ROLE_USER")) { - authorities.add(new SimpleGrantedAuthority("ROLE_USER")); - } + return new UsernamePasswordAuthenticationToken(claims.getSubject(), "", authorities); + } + ); + } - return new UsernamePasswordAuthenticationToken(claims.getSubject(), "", authorities); + private void addRole(final String role, final Collection authorities) { + if (role.equals(Role.ROLE_ADMIN.name())) { + authorities.add(new SimpleGrantedAuthority(Role.ROLE_ADMIN.name())); + return; + } + if (role.equals(Role.ROLE_USER.name())) { + authorities.add(new SimpleGrantedAuthority(Role.ROLE_USER.name())); + } } public boolean validateToken(String token) { @@ -102,24 +92,19 @@ public boolean validateToken(String token) { private Claims parseClaims(String accessToken) { try { - Claims claims = - Jwts.parserBuilder() + return Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(accessToken) .getBody(); - return claims; } catch (ExpiredJwtException e) { - return e.getClaims(); + throw new AuthorityException(); } } public UsernamePasswordAuthenticationToken makeCredit(Member member) { - List role = new ArrayList<>(); - role.add(new SimpleGrantedAuthority(member.getRole().toString())); - UsernamePasswordAuthenticationToken credit = - new UsernamePasswordAuthenticationToken(member.getId().toString(), "", role); + List role = List.of(new SimpleGrantedAuthority(member.getRole().toString())); - return credit; + return new UsernamePasswordAuthenticationToken(member.getId().toString(), "", role); } } diff --git a/backend/src/main/java/com/twtw/backend/domain/member/dto/response/AfterLoginResponse.java b/backend/src/main/java/com/twtw/backend/domain/member/dto/response/AfterLoginResponse.java index 04fae01a..85e604ea 100644 --- a/backend/src/main/java/com/twtw/backend/domain/member/dto/response/AfterLoginResponse.java +++ b/backend/src/main/java/com/twtw/backend/domain/member/dto/response/AfterLoginResponse.java @@ -10,6 +10,13 @@ @Builder @AllArgsConstructor public class AfterLoginResponse { + + private static final AfterLoginResponse SIGNUP = AfterLoginResponse.builder() + .status(AuthStatus.SIGNUP).build(); private AuthStatus status; private TokenDto tokenDto; + + public static AfterLoginResponse ofSignup() { + return SIGNUP; + } } diff --git a/backend/src/main/java/com/twtw/backend/domain/member/service/AuthService.java b/backend/src/main/java/com/twtw/backend/domain/member/service/AuthService.java index 03e8f6e1..5520bfc7 100644 --- a/backend/src/main/java/com/twtw/backend/domain/member/service/AuthService.java +++ b/backend/src/main/java/com/twtw/backend/domain/member/service/AuthService.java @@ -17,6 +17,7 @@ import com.twtw.backend.domain.member.mapper.MemberMapper; import com.twtw.backend.domain.member.repository.MemberRepository; import com.twtw.backend.domain.member.repository.RefreshTokenRepository; +import com.twtw.backend.global.exception.AuthorityException; import com.twtw.backend.global.exception.EntityNotFoundException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -86,14 +87,14 @@ public AfterLoginResponse getTokenByOAuth(OAuthRequest request) { Optional member = memberRepository.findByOAuthIdAndAuthType(clientId, request.getAuthType()); - if (member.isPresent()) { - Member curMember = member.get(); - UsernamePasswordAuthenticationToken credit = tokenProvider.makeCredit(curMember); - TokenDto tokenDto = saveRefreshToken(credit, curMember.getId().toString()); - return new AfterLoginResponse(AuthStatus.SIGNIN, tokenDto); - } + return member.map(this::getAfterLoginResponse) + .orElseGet(AfterLoginResponse::ofSignup); + } - return new AfterLoginResponse(AuthStatus.SIGNUP, null); + private AfterLoginResponse getAfterLoginResponse(final Member member) { + UsernamePasswordAuthenticationToken credit = tokenProvider.makeCredit(member); + TokenDto tokenDto = saveRefreshToken(credit, member.getId().toString()); + return new AfterLoginResponse(AuthStatus.SIGNIN, tokenDto); } /* @@ -109,16 +110,19 @@ public TokenDto refreshToken(TokenRequest tokenRequest) { throw new RefreshTokenValidationException(); } - Authentication authentication = + Optional authentication = tokenProvider.getAuthentication(tokenRequest.getAccessToken()); - String userName = authentication.getName(); + return authentication.map(auth -> getTokenDto(auth, refreshToken)) + .orElseThrow(AuthorityException::new); + } + private TokenDto getTokenDto(final Authentication auth, final String refreshToken) { + String userName = auth.getName(); if (!getRefreshTokenValue(userName).equals(refreshToken)) { throw new RefreshTokenInfoMismatchException(); } - - return saveRefreshToken(authentication, userName); + return saveRefreshToken(auth, auth.getName()); } public String getRefreshTokenValue(String tokenKey) {