admin: 토큰 저장 로직 추가
This commit is contained in:
parent
62952885ff
commit
898cf744ab
|
|
@ -0,0 +1,30 @@
|
|||
package com.bpgroup.poc.admin.app.jwt;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.base.admin.entity.TokenState;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class JwtTokenRequest {
|
||||
private final String ip;
|
||||
private final String accessToken;
|
||||
private final String refreshToken;
|
||||
private final LocalDateTime expiredRefreshToken;
|
||||
private final TokenState state;
|
||||
private final String loginId;
|
||||
|
||||
public static JwtTokenRequest of(
|
||||
String ip,
|
||||
String accessToken,
|
||||
String refreshToken,
|
||||
LocalDateTime expiredRefreshToken,
|
||||
TokenState state,
|
||||
String loginId
|
||||
) {
|
||||
return new JwtTokenRequest(ip, accessToken, refreshToken, expiredRefreshToken, state, loginId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.bpgroup.poc.admin.app.jwt;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.base.admin.entity.Admin;
|
||||
import com.bpgroup.poc.admin.domain.base.admin.service.AdminService;
|
||||
import com.bpgroup.poc.admin.domain.base.admin.service.AdminTokenCreateCommand;
|
||||
import com.bpgroup.poc.admin.domain.base.admin.service.AdminTokenService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class JwtTokenService {
|
||||
|
||||
private final AdminService adminService;
|
||||
private final AdminTokenService adminTokenService;
|
||||
|
||||
public void save(JwtTokenRequest request) {
|
||||
Admin findAdmin = adminService.find(request.getLoginId()).orElseThrow(() -> new IllegalArgumentException("Not found admin"));
|
||||
adminTokenService.expire(findAdmin.getId());
|
||||
|
||||
adminTokenService.create(
|
||||
AdminTokenCreateCommand.of(
|
||||
request.getIp(),
|
||||
request.getAccessToken(),
|
||||
request.getRefreshToken(),
|
||||
request.getExpiredRefreshToken(),
|
||||
request.getState(),
|
||||
findAdmin
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.bpgroup.poc.admin.domain.base.admin.entity;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.base.BaseEntity;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
|
@ -9,23 +10,53 @@ import java.time.LocalDateTime;
|
|||
@Entity
|
||||
@Table(name = "admin_token")
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
public class AdminToken {
|
||||
public class AdminToken extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "ip", nullable = false)
|
||||
private String ip;
|
||||
|
||||
@Column(name = "access_token", nullable = false)
|
||||
private String accessToken;
|
||||
|
||||
@Column(name = "refresh_token", length = 32, nullable = false)
|
||||
@Column(name = "refresh_token", nullable = false)
|
||||
private String refreshToken;
|
||||
|
||||
@Column(name = "expired_refresh_token", nullable = false)
|
||||
private LocalDateTime expiredRefreshToken;
|
||||
@Column(name = "expired_refresh_token_date_time", nullable = false)
|
||||
private LocalDateTime expiredRefreshTokenDateTime;
|
||||
|
||||
@Column(name = "state", nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TokenState state;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "admin_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), nullable = false)
|
||||
private Admin admin;
|
||||
|
||||
private AdminToken(String ip, String accessToken, String refreshToken, LocalDateTime expiredRefreshTokenDateTime, TokenState state, Admin admin) {
|
||||
this.ip = ip;
|
||||
this.accessToken = accessToken;
|
||||
this.refreshToken = refreshToken;
|
||||
this.expiredRefreshTokenDateTime = expiredRefreshTokenDateTime;
|
||||
this.state = state;
|
||||
this.admin = admin;
|
||||
}
|
||||
|
||||
public static AdminToken createOf(
|
||||
String ip,
|
||||
String accessToken,
|
||||
String refreshToken,
|
||||
LocalDateTime expiredRefreshTokenDateTime,
|
||||
TokenState state,
|
||||
Admin admin
|
||||
) {
|
||||
return new AdminToken(ip, accessToken, refreshToken, expiredRefreshTokenDateTime, state, admin);
|
||||
}
|
||||
|
||||
public void expire(TokenState state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,5 +2,8 @@ package com.bpgroup.poc.admin.domain.base.admin.entity;
|
|||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface AdminTokenRepository extends JpaRepository<AdminToken, Long> {
|
||||
List<AdminToken> findByAdminIdAndState(Long adminId, TokenState state);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package com.bpgroup.poc.admin.domain.base.admin.entity;
|
||||
|
||||
public enum TokenState {
|
||||
NORMAL, EXPIRED
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.bpgroup.poc.admin.domain.base.admin.service;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.base.admin.entity.Admin;
|
||||
import com.bpgroup.poc.admin.domain.base.admin.entity.AdminToken;
|
||||
import com.bpgroup.poc.admin.domain.base.admin.entity.TokenState;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class AdminTokenCreateCommand {
|
||||
@NotBlank
|
||||
private final String ip;
|
||||
@NotBlank
|
||||
private final String accessToken;
|
||||
@NotBlank
|
||||
private final String refreshToken;
|
||||
@NotNull
|
||||
private final LocalDateTime expiredRefreshTokenDateTime;
|
||||
@NotNull
|
||||
private final TokenState state;
|
||||
@NotNull
|
||||
private final Admin admin;
|
||||
|
||||
public static AdminTokenCreateCommand of(
|
||||
String ip,
|
||||
String accessToken,
|
||||
String refreshToken,
|
||||
LocalDateTime expiredRefreshTokenDateTime,
|
||||
TokenState state,
|
||||
Admin admin
|
||||
) {
|
||||
return new AdminTokenCreateCommand(ip, accessToken, refreshToken, expiredRefreshTokenDateTime, state, admin);
|
||||
}
|
||||
|
||||
public AdminToken toEntity() {
|
||||
return AdminToken.createOf(ip, accessToken, refreshToken, expiredRefreshTokenDateTime, state, admin);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.bpgroup.poc.admin.domain.base.admin.service;
|
||||
|
||||
import com.bpgroup.poc.admin.domain.base.admin.entity.*;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Transactional
|
||||
public class AdminTokenService {
|
||||
|
||||
private final AdminTokenRepository adminTokenRepository;
|
||||
|
||||
public void create(@NotNull @Valid AdminTokenCreateCommand command) {
|
||||
adminTokenRepository.save(command.toEntity());
|
||||
}
|
||||
|
||||
public void expire(@NotNull Long adminId) {
|
||||
List<AdminToken> adminTokens = adminTokenRepository.findByAdminIdAndState(adminId, TokenState.NORMAL);
|
||||
adminTokens.forEach(
|
||||
adminToken -> adminToken.expire(TokenState.EXPIRED)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.bpgroup.poc.admin.security;
|
||||
|
||||
import com.bpgroup.poc.admin.app.jwt.JwtTokenService;
|
||||
import com.bpgroup.poc.admin.security.authentication.*;
|
||||
import com.bpgroup.poc.admin.app.authentication.AuthenticationService;
|
||||
import com.bpgroup.poc.admin.security.authorization.CustomAccessDeniedHandler;
|
||||
|
|
@ -37,6 +38,7 @@ public class SecurityConfig {
|
|||
|
||||
private final AuthenticationService authenticationService;
|
||||
private final AuthorizationService authorizationService;
|
||||
private final JwtTokenService jwtTokenService;
|
||||
|
||||
private final JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.bpgroup.poc.admin.security.authentication;
|
|||
import com.bpgroup.poc.admin.app.authentication.AuthenticationResponse;
|
||||
import com.bpgroup.poc.admin.security.jwt.JwtTokenProvider;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
|
@ -23,11 +22,7 @@ public class CustomAuthenticationSuccessHandler implements AuthenticationSuccess
|
|||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
|
||||
String username = (String) authentication.getPrincipal();
|
||||
|
||||
Cookie accessTokenCookie = jwtTokenProvider.createCookieWithToken(username, JwtTokenProvider.JwtTokenType.ACCESS);
|
||||
response.addCookie(accessTokenCookie);
|
||||
|
||||
Cookie refreshTokenCookie = jwtTokenProvider.createCookieWithToken(username, JwtTokenProvider.JwtTokenType.REFRESH);
|
||||
response.addCookie(refreshTokenCookie);
|
||||
jwtTokenProvider.generateToken(request, response, username);
|
||||
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
|
|
|
|||
|
|
@ -1,27 +1,56 @@
|
|||
package com.bpgroup.poc.admin.security.jwt;
|
||||
|
||||
import com.bpgroup.poc.admin.app.jwt.JwtTokenRequest;
|
||||
import com.bpgroup.poc.admin.app.jwt.JwtTokenService;
|
||||
import com.bpgroup.poc.admin.domain.base.admin.entity.TokenState;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class JwtTokenProvider {
|
||||
|
||||
private final JwtTokenService jwtTokenService;
|
||||
|
||||
// TODO: 추후 Key 별도 관리 필요
|
||||
private static final SecretKey JWT_KEY = Keys.hmacShaKeyFor(JwtTokenConstants.KEY.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
public void generateToken(HttpServletRequest request, HttpServletResponse response, String username) {
|
||||
Cookie accessTokenCookie = createCookieWithToken(username, JwtTokenProvider.JwtTokenType.ACCESS);
|
||||
response.addCookie(accessTokenCookie);
|
||||
|
||||
Cookie refreshTokenCookie = createCookieWithToken(username, JwtTokenProvider.JwtTokenType.REFRESH);
|
||||
response.addCookie(refreshTokenCookie);
|
||||
|
||||
jwtTokenService.save(
|
||||
JwtTokenRequest.of(
|
||||
request.getRemoteAddr(),
|
||||
accessTokenCookie.getValue(),
|
||||
refreshTokenCookie.getValue(),
|
||||
LocalDateTime.now().plusSeconds(refreshTokenCookie.getMaxAge()),
|
||||
TokenState.NORMAL,
|
||||
username
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public enum JwtTokenType {
|
||||
ACCESS, REFRESH
|
||||
}
|
||||
|
||||
public Cookie createCookieWithToken(String username, JwtTokenType type) {
|
||||
private Cookie createCookieWithToken(String username, JwtTokenType type) {
|
||||
String tokenName;
|
||||
String subject;
|
||||
long expirationTime;
|
||||
|
|
@ -45,16 +74,6 @@ public class JwtTokenProvider {
|
|||
return tokenCookie;
|
||||
}
|
||||
|
||||
private Cookie createCookieWithToken(String tokenName, String subject, String username, long expirationTime) {
|
||||
String token = generateToken(JwtTokenConstants.ISSUER, subject, username, expirationTime);
|
||||
|
||||
Cookie tokenCookie = new Cookie(tokenName, token);
|
||||
tokenCookie.setHttpOnly(true);
|
||||
tokenCookie.setPath("/");
|
||||
|
||||
return tokenCookie;
|
||||
}
|
||||
|
||||
private String generateToken(String issuer, String subject, String username, long expirationTime) {
|
||||
return Jwts.builder()
|
||||
.issuer(issuer)
|
||||
|
|
|
|||
|
|
@ -56,11 +56,7 @@ public class JwtTokenValidateFilter extends OncePerRequestFilter {
|
|||
Claims claims = jwtTokenProvider.getClaims(refreshToken);
|
||||
String username = claims.get("username", String.class);
|
||||
|
||||
Cookie accessTokenCookie = jwtTokenProvider.createCookieWithToken(username, JwtTokenProvider.JwtTokenType.ACCESS);
|
||||
response.addCookie(accessTokenCookie);
|
||||
|
||||
Cookie refreshTokenCookie = jwtTokenProvider.createCookieWithToken(username, JwtTokenProvider.JwtTokenType.REFRESH);
|
||||
response.addCookie(refreshTokenCookie);
|
||||
jwtTokenProvider.generateToken(request, response, username);
|
||||
|
||||
setSecurityContext(username);
|
||||
} catch (JwtTokenExpiredException e) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue