Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[REFACTOR] 멤버 및 시큐리티 리팩토링 #92

Merged
merged 4 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Optional;

@Component
@RequiredArgsConstructor
Expand All @@ -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<String> 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> authentication = tokenProvider.getAuthentication(token);
authentication.ifPresent(SecurityContextHolder.getContext()::setAuthentication);
}
}

private Optional<String> 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();
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,29 @@

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;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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()
Expand Down Expand Up @@ -69,23 +52,30 @@ public TokenDto createToken(Authentication authentication) {
return new TokenDto(accessToken, refreshToken);
}

public Authentication getAuthentication(String accessToken) {
public Optional<Authentication> 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<GrantedAuthority> authorities = new ArrayList<>();
String role = claims.get(AUTHORITIES_KEY).toString();

Collection<GrantedAuthority> 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<GrantedAuthority> 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) {
Expand All @@ -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<GrantedAuthority> role = new ArrayList<>();
role.add(new SimpleGrantedAuthority(member.getRole().toString()));
UsernamePasswordAuthenticationToken credit =
new UsernamePasswordAuthenticationToken(member.getId().toString(), "", role);
List<GrantedAuthority> role = List.of(new SimpleGrantedAuthority(member.getRole().toString()));

return credit;
return new UsernamePasswordAuthenticationToken(member.getId().toString(), "", role);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -86,14 +87,14 @@ public AfterLoginResponse getTokenByOAuth(OAuthRequest request) {
Optional<Member> 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);
}

/*
Expand All @@ -109,16 +110,19 @@ public TokenDto refreshToken(TokenRequest tokenRequest) {
throw new RefreshTokenValidationException();
}

Authentication authentication =
Optional<Authentication> 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) {
Expand Down
Loading