Skip to content

Commit

Permalink
Merge branch 'dev' into feature/withdraw_user
Browse files Browse the repository at this point in the history
  • Loading branch information
5upportPark authored Sep 27, 2024
2 parents 25c6983 + 179b090 commit e655516
Show file tree
Hide file tree
Showing 14 changed files with 267 additions and 84 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/pjw/retry_view/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public FilterRegistrationBean<JWTVerifyFilter> jwtVerifyFilterBean(){
FilterRegistrationBean<JWTVerifyFilter> jwtVerifyFilterBean = new FilterRegistrationBean<>();
jwtVerifyFilterBean.setFilter(jwtverifyFilter);
jwtVerifyFilterBean.setOrder(1);
jwtVerifyFilterBean.setUrlPatterns(List.of("/users","/users/info","/users/withdraw"));
jwtVerifyFilterBean.setUrlPatterns(List.of("/users","/users/info","/logout","/users/withdraw"));
return jwtVerifyFilterBean;
}

Expand Down
20 changes: 12 additions & 8 deletions src/main/java/com/pjw/retry_view/controller/LoginController.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.pjw.retry_view.controller;

import com.pjw.retry_view.response.JWToken;
import com.pjw.retry_view.dto.UserDTO;
import com.pjw.retry_view.dto.UserInfo;
import com.pjw.retry_view.request.LoginRequest;
import com.pjw.retry_view.response.LoginResponse;
import com.pjw.retry_view.service.JWTService;
import com.pjw.retry_view.service.UserService;
import com.pjw.retry_view.util.JWTUtil;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -13,9 +16,6 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/login")
public class LoginController {
Expand All @@ -31,13 +31,17 @@ public LoginController(UserService userService, JWTService jwtService){
public ResponseEntity<LoginResponse> userLogin(@RequestBody @Valid LoginRequest loginReq){
UserDTO user = userService.userLogin(loginReq);

Map<String, Object> userInfo = new HashMap<>();
userInfo.put("name", user.getName());
userInfo.put("loginId", user.getLoginId());
UserInfo userInfo = new UserInfo(user.getName(), user.getLoginId());

LoginResponse response = new LoginResponse();
response.setAccessToken(jwtService.createAccessToken(userInfo));
response.setRefreshToken(jwtService.createRefreshToken());
String refreshToken = JWTUtil.createRefreshToken();
JWToken token = JWToken.getJWT(JWTUtil.createAccessToken(userInfo), refreshToken);

response.setToken(token);

user.changeRefereshToken(refreshToken);
userService.saveUser(user);

return new ResponseEntity<LoginResponse>(response, HttpStatus.OK);
}
}
24 changes: 24 additions & 0 deletions src/main/java/com/pjw/retry_view/controller/LogoutController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.pjw.retry_view.controller;

import com.pjw.retry_view.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Controller
@RequestMapping("/logout")
public class LogoutController {
private final UserService userService;

public LogoutController(UserService userService) {
this.userService = userService;
}

@PostMapping
public void logout(HttpServletRequest req){
String loginId = req.getAttribute("loginId").toString();
userService.logout(loginId);
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/pjw/retry_view/controller/TokenController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.pjw.retry_view.controller;

import com.pjw.retry_view.response.JWToken;
import com.pjw.retry_view.service.JWTService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/token")
public class TokenController {
private final JWTService jwtService;

public TokenController(JWTService jwtService) {
this.jwtService = jwtService;
}

@PostMapping
public JWToken renewAccessToken(@RequestBody JWToken token){
return jwtService.renewAccessToken(token.getRefreshToken());
}
}
5 changes: 5 additions & 0 deletions src/main/java/com/pjw/retry_view/dto/UserDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public class UserDTO implements Serializable {
private String updatedBy;
private ZonedDateTime updatedAt;

public void changeRefereshToken(String token){
this.refreshToken = token;
this.updatedAt = ZonedDateTime.now();
}

public User toEntity(){
return User.builder()
.id(id)
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/pjw/retry_view/dto/UserInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.pjw.retry_view.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class UserInfo {
private String name;
private String loginId;

public UserInfo(){}
public UserInfo(String name, String loginId){
this.name = name;
this.loginId = loginId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.pjw.retry_view.exception;

import org.springframework.http.HttpStatus;

public class InvalidTokenException extends BusinessException{
@Override
public HttpStatus getHttpStatus() {
return HttpStatus.UNAUTHORIZED;
}

public InvalidTokenException(){}
public InvalidTokenException(String msg){
super(msg);
}
public InvalidTokenException(Exception e){
super(e);
}
}
10 changes: 4 additions & 6 deletions src/main/java/com/pjw/retry_view/filter/JWTVerifyFilter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.pjw.retry_view.filter;

import com.pjw.retry_view.service.JWTService;
import com.pjw.retry_view.util.JWTUtil;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
Expand All @@ -12,10 +13,6 @@

@Component
public class JWTVerifyFilter implements Filter {
private final JWTService jwtService;
public JWTVerifyFilter(JWTService jwtService){
this.jwtService = jwtService;
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
Expand All @@ -24,11 +21,12 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
String jwt = httpServletRequest.getHeader("Authorization");

System.out.println("Method: "+method+", JWT: "+jwt);
if(isAllowMethod(method) && jwtService.validateToken(jwt)){
String loginId = jwtService.getClaims(jwt).get("loginId").toString();
if(isAllowMethod(method) && JWTUtil.isValidateToken(jwt)){
String loginId = JWTUtil.getClaims(jwt).get("loginId").toString();
servletRequest.setAttribute("loginId", loginId);
filterChain.doFilter(servletRequest,servletResponse);
}else{
// accessToken 재발급받게 하기
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
httpServletResponse.sendError(HttpStatus.UNAUTHORIZED.value(), "권한이 없습니다.");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.pjw.retry_view.repository;

import com.pjw.retry_view.entity.User;
import com.pjw.retry_view.request.LoginRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

Expand All @@ -13,5 +12,6 @@ public interface UserRepository extends JpaRepository<User, Long> {
public List<User> findAll();
public Optional<User> findByLoginId(String loginId);
public Optional<User> findByLoginIdAndPassword(String loginId, String password);
public Optional<User> findByRefreshToken(String refreshToken);
public User save(User user);
}
21 changes: 21 additions & 0 deletions src/main/java/com/pjw/retry_view/response/JWToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.pjw.retry_view.response;

import io.micrometer.common.util.StringUtils;
import lombok.Getter;
import lombok.Setter;

@Getter
public class JWToken {
private String accessToken;
private String refreshToken;

private JWToken(){}
private JWToken(String accessToken, String refreshToken){
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}

public static JWToken getJWT(String accessToken, String refreshToken){
return new JWToken(accessToken, refreshToken);
}
}
4 changes: 2 additions & 2 deletions src/main/java/com/pjw/retry_view/response/LoginResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
@Getter
@Setter
public class LoginResponse {
private String accessToken;
private String refreshToken;
private String msg;
private JWToken token;
}
95 changes: 29 additions & 66 deletions src/main/java/com/pjw/retry_view/service/JWTService.java
Original file line number Diff line number Diff line change
@@ -1,82 +1,45 @@
package com.pjw.retry_view.service;

import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecurityException;
import org.springframework.beans.factory.annotation.Value;
import com.pjw.retry_view.response.JWToken;
import com.pjw.retry_view.dto.UserDTO;
import com.pjw.retry_view.dto.UserInfo;
import com.pjw.retry_view.entity.User;
import com.pjw.retry_view.exception.InvalidTokenException;
import com.pjw.retry_view.exception.UserNotFoundException;
import com.pjw.retry_view.repository.UserRepository;
import com.pjw.retry_view.util.JWTUtil;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;

import javax.crypto.SecretKey;
import java.time.Instant;
import java.util.Date;
import java.util.Map;

@Service
public class JWTService {
private final UserRepository userRepository;

private final SecretKey secretKey;
//private static final String AUTH_KEY = "auth";
private static final String BEARER_TYPE = "Bearer";
private static final long ACCESS_TOKEN_EXPIRED = 1000 * 60 * 60 * 24; // 1day
private static final long REFRESH_TOKEN_EXPIRED = 1000 * 60 * 60 * 24 * 7; // 7day

public JWTService(@Value("${jwt.key}")String key){
this.secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(key));
}

public String createAccessToken(Map<String, Object> claims){
long currentTimeMillis = System.currentTimeMillis();
return Jwts.builder()
.claims(claims)
.issuer("issuer")
.issuedAt(new Date(currentTimeMillis))
.expiration(new Date(currentTimeMillis+ACCESS_TOKEN_EXPIRED))
.signWith(secretKey, Jwts.SIG.HS512)
.compact();
public JWTService(UserRepository userRepository){
this.userRepository = userRepository;
}

public String createRefreshToken(){
long currentTimeMillis = System.currentTimeMillis();
return Jwts.builder()
.issuedAt(new Date(currentTimeMillis))
.expiration(new Date(currentTimeMillis+REFRESH_TOKEN_EXPIRED))
.signWith(secretKey, Jwts.SIG.HS512)
.compact();
}

public Claims getClaims(String token){
token = tokenSplit(token);
return Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(token)
.getPayload();
}

public boolean validateToken(String token){
if(token == null) return false;
token = tokenSplit(token);
try {
Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token);
return true;
} catch (SecurityException | MalformedJwtException e) {
System.out.println("잘못된 JWT 서명입니다.");
} catch (ExpiredJwtException e) {
System.out.println("만료된 JWT 토큰입니다.");
} catch (UnsupportedJwtException e) {
System.out.println("지원되지 않는 JWT 토큰입니다.");
} catch (IllegalArgumentException e) {
System.out.println("JWT 토큰이 잘못되었습니다.");
@Transactional
public JWToken renewAccessToken(String refreshToken) throws InvalidTokenException {
if (JWTUtil.isValidateToken(refreshToken)) {
throw new InvalidTokenException();
}
return false;
}

private String tokenSplit(String token){
if(token.startsWith(BEARER_TYPE)){
return token.split(" ")[1];
} else{
return token;
UserDTO user = userRepository.findByRefreshToken(refreshToken).map(User::toDTO).orElseThrow(UserNotFoundException::new);
UserInfo userInfo = new UserInfo(user.getName(), user.getLoginId());

boolean isExpired = JWTUtil.isTokenExpired(refreshToken);
if (isExpired) {
refreshToken = JWTUtil.createRefreshToken();
user.setRefreshToken(refreshToken);
userRepository.save(user.toEntity());
return JWToken.getJWT(JWTUtil.createAccessToken(userInfo), refreshToken);
}

return JWToken.getJWT(JWTUtil.createAccessToken(userInfo), null);
}


}
7 changes: 7 additions & 0 deletions src/main/java/com/pjw/retry_view/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,11 @@ public void withdrawUser(String loginId) {
user.withdraw();
userRepository.save(user);
}

@Transactional
public void logout(String loginId){
User user = userRepository.findByLoginId(loginId).orElseThrow(UserNotFoundException::new);
user.setRefreshToken(null);
userRepository.save(user);
}
}
Loading

0 comments on commit e655516

Please sign in to comment.