Skip to content

Commit

Permalink
[#5] fix: add AuthController and refreshToken api
Browse files Browse the repository at this point in the history
- userController에서 authController 분리
- jwtToken -> accessToken으로 명칭 변경
- /refresh api 추가
  • Loading branch information
raae7742 committed May 28, 2022
1 parent fffe97b commit eecd325
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 211 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.answer.notinote.Auth.controller;

import com.answer.notinote.Auth.service.AuthService;
import com.answer.notinote.User.dto.JoinRequestDto;
import com.answer.notinote.User.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@RestController
@RequiredArgsConstructor
public class AuthController {

private final AuthService authService;

/**
* oauth2 로그인을 진행합니다.
* @param token
* @return
*/
@GetMapping("/login/oauth2")
public ResponseEntity<?> oauthLogin(HttpServletResponse response, @RequestHeader("Authorization") String token) {
return ResponseEntity.ok(authService.oauthLogin(response, token));
}

/**
* 회원가입 폼 정보를 받아 유저의 권한을 USER로 바꾸고 로그인을 진행합니다.
* @param requestDto
* @return
*/
@PostMapping("/join")
public ResponseEntity<?> join(HttpServletResponse response, @RequestBody JoinRequestDto requestDto) {
return ResponseEntity.ok(authService.join(response, requestDto));
}

@PostMapping("/refresh")
public ResponseEntity<?> refresh(HttpServletRequest request, HttpServletResponse response) {

return ResponseEntity.ok(authService.refreshToken(request, response));
}

/**
* 회원을 로그아웃합니다.
* @param request
* @return
*/
@DeleteMapping("/logout")
public ResponseEntity<?> logout(HttpServletRequest request) {
return ResponseEntity.ok(authService.logout(request));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,10 @@ public class JwtAuthenticationFilter extends GenericFilterBean {

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String jwtToken = jwtTokenProvider.resolveToken((HttpServletRequest) request);
String refreshToken = jwtTokenProvider.resolveRefreshToken((HttpServletRequest) request);
String accessToken = jwtTokenProvider.resolveAccessToken((HttpServletRequest) request);
if (accessToken != null && jwtTokenProvider.validateToken(accessToken))
setAuthentication(accessToken);

if (jwtToken != null) {
if (jwtTokenProvider.validateToken(jwtToken)) {
// jwt token이 유효한 경우
setAuthentication(jwtToken);
}
else {
if (refreshToken != null) {
if (jwtTokenProvider.validateToken(refreshToken) && jwtTokenProvider.existsRefreshToken(refreshToken)) {
// jwt token이 만료되고, refresh token이 유효한 경우
String email = jwtTokenProvider.getUserEmail(refreshToken);
String newToken = jwtTokenProvider.createToken(email);
jwtTokenProvider.setHeaderToken((HttpServletResponse) response, newToken);

setAuthentication(newToken);
}
}
}
}
chain.doFilter(request, response);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.answer.notinote.Auth.service;

import com.answer.notinote.Auth.data.ProviderType;
import com.answer.notinote.Auth.data.RoleType;
import com.answer.notinote.Auth.repository.RefreshTokenRepository;
import com.answer.notinote.Auth.token.RefreshToken;
import com.answer.notinote.Auth.token.provider.JwtTokenProvider;
import com.answer.notinote.Auth.userdetails.GoogleUser;
import com.answer.notinote.Child.service.ChildService;
import com.answer.notinote.Exception.CustomException;
import com.answer.notinote.Exception.ErrorCode;
import com.answer.notinote.User.domain.entity.User;
import com.answer.notinote.User.domain.repository.UserRepository;
import com.answer.notinote.User.dto.JoinRequestDto;
import com.answer.notinote.User.dto.UserResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Service
@RequiredArgsConstructor
public class AuthService {
private final OAuth2Service oAuthService;
private final ChildService childService;
private final UserRepository userRepository;
private final RefreshTokenRepository refreshTokenRepository;
private final JwtTokenProvider jwtTokenProvider;

public UserResponseDto oauthLogin(HttpServletResponse response, String token) {
ResponseEntity<String> userInfoResponse = oAuthService.createGetRequest(token);
GoogleUser googleUser = oAuthService.getUserInfo(userInfoResponse);

User user = userRepository.findByUemail(googleUser.getEmail()).orElse(null);
if (user == null) {
user = User.builder()
.uemail(googleUser.getEmail())
.username(googleUser.getName())
.uroleType(RoleType.GUEST)
.uproviderType(ProviderType.GOOGLE)
.build();
userRepository.save(user);
}
else {
issueToken(response, user);
}

return new UserResponseDto(user);
}

@Transactional
public UserResponseDto join(HttpServletResponse response, JoinRequestDto requestDto) {
User user = userRepository.findById(requestDto.getUid()).orElseThrow(
() -> new CustomException(ErrorCode.USER_NOT_FOUND)
);

requestDto.getUchildren().forEach( childDto -> childService.create(childDto, user));

if (user.getUroleType() == RoleType.GUEST) {
user.setUsername(requestDto.getUsername());
user.setUlanguage(requestDto.getUlanguage());
user.setUroleType(RoleType.USER);
user.setUprofileImg(requestDto.getUprofileImg());
userRepository.save(user);

issueToken(response, user);
return new UserResponseDto(user);
}
else {
throw new CustomException(ErrorCode.USER_DUPLICATED);
}
}

private void issueToken(HttpServletResponse response, User user) {
String accessToken = jwtTokenProvider.createToken(user);
String refreshToken = jwtTokenProvider.createRefreshToken(user);

jwtTokenProvider.setAccessToken(response, accessToken);
jwtTokenProvider.setRefreshToken(response, refreshToken);
}

@Transactional
public String refreshToken(HttpServletRequest request, HttpServletResponse response) {
String accessToken = jwtTokenProvider.resolveAccessToken(request);
String refreshToken = jwtTokenProvider.resolveRefreshToken(request);

boolean validateRefreshToken = jwtTokenProvider.validateToken(refreshToken) && jwtTokenProvider.existsRefreshToken(refreshToken);
if (jwtTokenProvider.validateTokenExpired(accessToken) && validateRefreshToken) {
String email = jwtTokenProvider.getUserEmail(refreshToken);
User user = findUserByEmail(email);

String newAccessToken = jwtTokenProvider.createToken(user);
jwtTokenProvider.setAccessToken(response, newAccessToken);

return "ok";
} else {
throw new CustomException(ErrorCode.TOKEN_NOT_EXPIRED);
}
}

@Transactional
public Long logout(HttpServletRequest request) {
String token = jwtTokenProvider.resolveAccessToken(request);
String email = jwtTokenProvider.getUserEmail(token);

User user = findUserByEmail(email);

RefreshToken refreshToken = refreshTokenRepository.findByUser(user).orElseThrow(
() -> new CustomException(ErrorCode.TOKEN_NOT_FOUND)
);
refreshTokenRepository.delete(refreshToken);

return user.getUid();
}

private User findUserByEmail(String email) {
return userRepository.findByUemail(email).orElseThrow(
() -> new CustomException(ErrorCode.USER_NOT_FOUND)
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

@Service
@RequiredArgsConstructor
public class OAuthService {
public class OAuth2Service {

private final ObjectMapper objectMapper;
private final RestTemplate restTemplate = new RestTemplate();
Expand All @@ -32,7 +32,7 @@ public ResponseEntity<String> createGetRequest(String oAuthToken) {

return restTemplate.exchange(url, HttpMethod.GET, request, String.class);
} catch (Exception e) {
throw new CustomException(ErrorCode.TOKEN_INVALID);
throw new CustomException(ErrorCode.TOKEN_EXPIRED);
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.answer.notinote.Auth.token.provider;

import com.answer.notinote.Auth.data.RoleType;
import com.answer.notinote.Auth.repository.RefreshTokenRepository;
import com.answer.notinote.Auth.token.RefreshToken;
import com.answer.notinote.Exception.CustomException;
import com.answer.notinote.Exception.ErrorCode;
import com.answer.notinote.User.domain.entity.User;
Expand Down Expand Up @@ -33,14 +33,12 @@ public class JwtTokenProvider {
@Value("${jwt.secret}")
private String secretKey;

@Value("${jwt.refresh}")
private String refreshKey;
private long accessTokenValidTime = 60 * 60 * 1000L;
private long refreshTokenValidTime = 300 * 60 * 1000L;

private long tokenValidTime = 60 * 60 * 1000L;
private long refreshValidTime = 300 * 60 * 1000L;
private String accessTokenHeader = "Access-Token";

private String tokenHeader = "JWT_TOKEN";
private String refreshHeader = "REFRESH_TOKEN";
private String refreshTokenHeader = "Refresh-Token";

private final UserDetailsService userDetailsService;
private final RefreshTokenRepository refreshTokenRepository;
Expand All @@ -51,15 +49,17 @@ public class JwtTokenProvider {
@PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
refreshKey = Base64.getEncoder().encodeToString(refreshKey.getBytes());
}

public String createToken(String email) {
return convertToToken(email, tokenValidTime, secretKey);
public String createToken(User user) {
return convertToToken(user.getUemail(), accessTokenValidTime, secretKey);
}

public String createRefreshToken(String email) {
return convertToToken(email, refreshValidTime, refreshKey);
public String createRefreshToken(User user) {
String refreshToken = convertToToken(user.getUemail(), refreshTokenValidTime, secretKey);
refreshTokenRepository.save(new RefreshToken(user, refreshToken));

return refreshToken;
}

// 토큰에서 인증 정보 조회
Expand All @@ -68,25 +68,24 @@ public Authentication getAuthentication(String token) {
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}

// 토큰에서 회원 정보 추출
public String getUserEmail(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}

public String resolveToken(HttpServletRequest request) {
return request.getHeader(tokenHeader);
public String resolveAccessToken(HttpServletRequest request) {
return request.getHeader(accessTokenHeader);
}

public String resolveRefreshToken(HttpServletRequest request) {
return request.getHeader(refreshHeader);
public void setAccessToken(HttpServletResponse response, String token) {
response.setHeader(accessTokenHeader, token);
}

public void setHeaderToken(HttpServletResponse response, String token) {
response.setHeader(tokenHeader, token);
public String resolveRefreshToken(HttpServletRequest request) {
return request.getHeader(refreshTokenHeader);
}

public void setHeaderRefreshToken(HttpServletResponse response, String token) {
response.setHeader(refreshHeader, token);
public void setRefreshToken(HttpServletResponse response, String token) {
response.setHeader(refreshTokenHeader, token);
}

// 토큰의 유효성 & 만료일자 확인
Expand All @@ -108,6 +107,17 @@ public boolean validateToken(String token) {
return false;
}

public boolean validateTokenExpired(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
return claims.getBody().getExpiration().before(new Date());
} catch(ExpiredJwtException e) {
return true;
} catch (Exception e) {
return false;
}
}

private String convertToToken(String email, Long validTime, String key) {
User user = userRepository.findByUemail(email).orElseThrow(
() -> new CustomException(ErrorCode.USER_NOT_FOUND)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
@RequiredArgsConstructor
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

private final JwtTokenProvider jwtTokenProvider;

@Override
Expand All @@ -35,7 +34,7 @@ protected void configure(HttpSecurity http) throws Exception {
.and()
// 모두 접근 가능한 URL
.authorizeRequests()
.antMatchers("/","/login/oauth2","/login", "/join",
.antMatchers("/","/login/oauth2","/login", "/join", "/refresh",
"/swagger-ui.html", "/swagger/**", "/swagger-resources/**", "/webjars/**", "/v2/api-docs").permitAll()
.and()
// USER만 접근 가능한 URL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ public enum ErrorCode {
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 계정이 존재하지 않습니다."),
USER_DUPLICATED(HttpStatus.CONFLICT, "이미 해당 계정의 유저가 존재합니다."),
TOKEN_NOT_FOUND(HttpStatus.NOT_FOUND, "토큰이 존재하지 않습니다."),
TOKEN_INVALID(HttpStatus.BAD_REQUEST, "액세스 토큰이 만료되었습니다."),
NOT_SUPPORTED_TYPE(HttpStatus.CONFLICT, "지원하지 않는 로그인 형식입니다"),
TOKEN_EXPIRED(HttpStatus.BAD_REQUEST, "액세스 토큰이 만료되었습니다."),
TOKEN_NOT_EXPIRED(HttpStatus.BAD_REQUEST, "액세스 토큰이 만료되지 않았습니다."),
NOT_FOUND(HttpStatus.NOT_FOUND, "해당 객체가 존재하지 않습니다."),
;

Expand Down
Loading

0 comments on commit eecd325

Please sign in to comment.