Skip to content

Commit

Permalink
Merge pull request #125 from HandTris/#120
Browse files Browse the repository at this point in the history
Spring Security 관련 기능
  • Loading branch information
thun0514 authored Jul 12, 2024
2 parents 906a407 + 9a18ace commit aafa8da
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public Pair<Member, String> signin (MemberRequest memberRequest) {
}

String accessToken = jwtUtil.createAccessToken(member.getNickname());
String refreshToken = jwtUtil.createRefreshToken();
String refreshToken = jwtUtil.createRefreshToken(member.getNickname());

member.updateRefreshToken(refreshToken);
memberRepository.save(member);
Expand Down Expand Up @@ -78,13 +78,13 @@ public void signup(MemberRequest memberRequest) {

@Override
public void signout(HttpServletRequest request) {
String accessToken = jwtUtil.resolveAccessToken(request);
String refreshToken = jwtUtil.resolveRefreshToken(request);

if (jwtUtil.isExpired(accessToken)) {
throw new AccessTokenExpiredException();
if (jwtUtil.isExpired(refreshToken)) {
throw new RefreshTokenExpiredException();
}

String nickname = jwtUtil.getNickname(accessToken);
String nickname = jwtUtil.getNickname(refreshToken);

memberRepository.findByNickname(nickname)
.ifPresent(Member::deleteRefreshToken);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package jungle.HandTris.application.impl;

import jakarta.servlet.http.HttpServletRequest;
import jungle.HandTris.application.service.ReissueService;
import jungle.HandTris.domain.Member;
import jungle.HandTris.domain.exception.InvalidTokenFormatException;
import jungle.HandTris.domain.exception.RefreshTokenExpiredException;
import jungle.HandTris.domain.exception.UnauthorizedAccessException;
import jungle.HandTris.domain.repo.MemberRepository;
import jungle.HandTris.global.jwt.JWTUtil;
import jungle.HandTris.presentation.dto.response.ReissueTokenRes;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class ReissueServiceImpl implements ReissueService {

private final JWTUtil jwtUtil;
private final MemberRepository memberRepository;

public ReissueTokenRes reissue (HttpServletRequest request, String requestUsername) {
String refreshToken = jwtUtil.resolveRefreshToken(request);

//토큰 소멸 시간 검증
if (jwtUtil.isExpired(refreshToken)) {
throw new RefreshTokenExpiredException();
}

String subject = jwtUtil.getSubject(refreshToken);

if(!subject.equals("RefreshToken")) {
throw new InvalidTokenFormatException();
}

String nickname = jwtUtil.getNickname(refreshToken);
Member member = memberRepository.findByUsername(requestUsername);

if(!member.getRefreshToken().equals(refreshToken)) {
throw new UnauthorizedAccessException();
}

String newAccessToken = jwtUtil.createAccessToken(nickname);
String newRefreshToken = jwtUtil.createRefreshToken(nickname);

member.updateRefreshToken(newRefreshToken);
memberRepository.save(member);

ReissueTokenRes token = new ReissueTokenRes(newAccessToken, newRefreshToken);

return token;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package jungle.HandTris.application.service;

import jakarta.servlet.http.HttpServletRequest;
import jungle.HandTris.presentation.dto.response.ReissueTokenRes;

public interface ReissueService {
ReissueTokenRes reissue (HttpServletRequest request, String requestUsername);
}
4 changes: 2 additions & 2 deletions src/main/java/jungle/HandTris/global/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public WebMvcConfigurer corsConfigurer() {
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000", "https://handtris.vercel.app")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("content-type", "authorization", "x-requested-with")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")
.allowedHeaders("content-type", "Authorization", "Authorization-Refresh", "x-requested-with")
.exposedHeaders("Set-Cookie")
// .exposedHeaders("content-type", "x-requested-with")
.allowCredentials(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.httpBasic((auth) -> auth.disable()
)
.authorizeHttpRequests((auth) -> auth
.anyRequest().permitAll()
.requestMatchers("/", "/auth/signin", "/auth/signup","/reissue/**", "/oauth2/loginSuccess").permitAll()
.anyRequest().authenticated()
)
.cors(Customizer.withDefaults()
)
Expand All @@ -74,8 +75,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000", "https://handtris.vercel.app"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("content-type", "authorization", "x-requested-with"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
configuration.setAllowedHeaders(Arrays.asList("content-type", "Authorization", "Authorization-Refresh", "x-requested-with"));
configuration.setAllowCredentials(true);
configuration.setExposedHeaders(Collections.singletonList("Set-Cookie"));
configuration.setExposedHeaders(Collections.singletonList("Authorization"));
Expand Down
14 changes: 11 additions & 3 deletions src/main/java/jungle/HandTris/global/filter/JWTFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jungle.HandTris.domain.Member;
import jungle.HandTris.domain.exception.AccessTokenExpiredException;
import jungle.HandTris.domain.exception.InvalidTokenFormatException;
import jungle.HandTris.global.jwt.JWTUtil;
import jungle.HandTris.presentation.dto.response.CustomMemberDetails;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -33,11 +35,17 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse

//토큰 소멸 시간 검증
if (jwtUtil.isExpired(token)) {
filterChain.doFilter(request, response);
return;
throw new AccessTokenExpiredException();
}

// 토큰이 access인지 확인 (발급시 페이로드에 명시)
String subject = jwtUtil.getSubject(token);

if (!subject.equals("AccessToken")) {
throw new InvalidTokenFormatException();
}

//토큰에서 username과 role 획득
//토큰에서 nickname 획득
String nickname = jwtUtil.getNickname(token);

//userEntity를 생성하여 값 set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public class OAuth2FailureHandler implements AuthenticationFailureHandler{
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
// 인증 실패시 메인 페이지로 이동
response.sendRedirect(redirectUrl);
// 인증 실패시 메인 (로그인) 페이지로 이동
response.sendRedirect(redirectUrl + "/");
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package jungle.HandTris.global.handler;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jungle.HandTris.domain.Member;
import jungle.HandTris.domain.repo.MemberRepository;
import jungle.HandTris.global.jwt.JWTUtil;
import jungle.HandTris.presentation.dto.response.CustomOAuth2Member;
import lombok.RequiredArgsConstructor;
Expand All @@ -24,6 +24,7 @@
public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

private final JWTUtil jwtUtil;
private final MemberRepository memberRepository;

@Value("${spring.security.oauth2.redirect-uri}")
String redirectUrl;
Expand All @@ -37,10 +38,15 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
String nickname = member.getNickname();

String accessToken = jwtUtil.createAccessToken(nickname);
System.out.println("accessToken = " + accessToken);
String refreshToken = jwtUtil.createRefreshToken(nickname);

String targetUrl = UriComponentsBuilder.fromUriString(redirectUrl + "oauth2/loginSuccess")
member.updateRefreshToken(refreshToken);
memberRepository.save(member);


String targetUrl = UriComponentsBuilder.fromUriString(redirectUrl + "/oauth2/loginSuccess")
.queryParam("access", accessToken)
.queryParam("refresh", refreshToken)
.build()
.encode(StandardCharsets.UTF_8)
.toUriString();
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/jungle/HandTris/global/jwt/JWTUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,18 @@ public String createAccessToken(String nickname) {
}

// Refresh 토큰 생성
public String createRefreshToken() {
public String createRefreshToken(String nickname) {
Date now = new Date();

return Jwts.builder()
.header()
.type("JWT")
.and()
.subject(refreshSubject)
.claim("nickname", nickname)
.issuedAt(now)
.expiration(new Date(now.getTime() + validityInMillisecondsRefresh))
.issuer(issuer)
.signWith(secretKey)
.compact();
}
Expand All @@ -113,6 +115,16 @@ public String getNickname(String token) {
.get("nickname", String.class);
}

public String getSubject(String token) {

return Jwts.parser().
verifyWith(secretKey)
.build()
.parseSignedClaims(token)
.getPayload()
.getSubject();
}

public Boolean isExpired(String token) {

return Jwts.parser()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public ResponseEnvelope<MemberDetailResWithTokenRes> signin(@RequestBody MemberR
return ResponseEnvelope.of(memberDetailResWithTokenRes);
}

@GetMapping("/signout")
@PostMapping("/signout")
public ResponseEnvelope<String> signout(HttpServletRequest request) {
memberService.signout(request);

Expand Down
24 changes: 24 additions & 0 deletions src/main/java/jungle/HandTris/presentation/ReissueController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package jungle.HandTris.presentation;

import jakarta.servlet.http.HttpServletRequest;
import jungle.HandTris.application.service.ReissueService;
import jungle.HandTris.global.dto.ResponseEnvelope;
import jungle.HandTris.presentation.dto.response.ReissueTokenRes;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class ReissueController {

private final ReissueService reissueService;

@PostMapping("/reissue/{username}")
public ResponseEnvelope<ReissueTokenRes> reissue (HttpServletRequest request, @PathVariable("username") String requestUsername) {
ReissueTokenRes token = reissueService.reissue(request, requestUsername);

return ResponseEnvelope.of(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package jungle.HandTris.presentation.dto.response;

public record ReissueTokenRes (String access,
String refresh) {
}
2 changes: 1 addition & 1 deletion src/main/resources/application-oauth-dev.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
spring:
security:
oauth2:
redirect-uri: http://localhost:8080/
redirect-uri: http://localhost:3000
client:
temppassword: ENC(kof7KM9iTyWvb6wVueqmEGZjOyPFRA7+mwS7ioUOOd5fGyahPdVrFA==)
registration:
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application-oauth-prod.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
spring:
security:
oauth2:
redirect-uri: https://api.checkmatejungle.shop/
redirect-uri: https://handtris.vercel.app
client:
temppassword: ENC(kof7KM9iTyWvb6wVueqmEGZjOyPFRA7+mwS7ioUOOd5fGyahPdVrFA==)
registration:
Expand Down

0 comments on commit aafa8da

Please sign in to comment.