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;
|
package com.bpgroup.poc.admin.domain.base.admin.entity;
|
||||||
|
|
||||||
|
import com.bpgroup.poc.admin.domain.base.BaseEntity;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
@ -9,23 +10,53 @@ import java.time.LocalDateTime;
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "admin_token")
|
@Table(name = "admin_token")
|
||||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||||
public class AdminToken {
|
public class AdminToken extends BaseEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "ip", nullable = false)
|
||||||
|
private String ip;
|
||||||
|
|
||||||
@Column(name = "access_token", nullable = false)
|
@Column(name = "access_token", nullable = false)
|
||||||
private String accessToken;
|
private String accessToken;
|
||||||
|
|
||||||
@Column(name = "refresh_token", length = 32, nullable = false)
|
@Column(name = "refresh_token", nullable = false)
|
||||||
private String refreshToken;
|
private String refreshToken;
|
||||||
|
|
||||||
@Column(name = "expired_refresh_token", nullable = false)
|
@Column(name = "expired_refresh_token_date_time", nullable = false)
|
||||||
private LocalDateTime expiredRefreshToken;
|
private LocalDateTime expiredRefreshTokenDateTime;
|
||||||
|
|
||||||
|
@Column(name = "state", nullable = false)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private TokenState state;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "admin_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), nullable = false)
|
@JoinColumn(name = "admin_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), nullable = false)
|
||||||
private Admin admin;
|
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 org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface AdminTokenRepository extends JpaRepository<AdminToken, Long> {
|
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;
|
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.security.authentication.*;
|
||||||
import com.bpgroup.poc.admin.app.authentication.AuthenticationService;
|
import com.bpgroup.poc.admin.app.authentication.AuthenticationService;
|
||||||
import com.bpgroup.poc.admin.security.authorization.CustomAccessDeniedHandler;
|
import com.bpgroup.poc.admin.security.authorization.CustomAccessDeniedHandler;
|
||||||
|
|
@ -37,6 +38,7 @@ public class SecurityConfig {
|
||||||
|
|
||||||
private final AuthenticationService authenticationService;
|
private final AuthenticationService authenticationService;
|
||||||
private final AuthorizationService authorizationService;
|
private final AuthorizationService authorizationService;
|
||||||
|
private final JwtTokenService jwtTokenService;
|
||||||
|
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
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.app.authentication.AuthenticationResponse;
|
||||||
import com.bpgroup.poc.admin.security.jwt.JwtTokenProvider;
|
import com.bpgroup.poc.admin.security.jwt.JwtTokenProvider;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import jakarta.servlet.http.Cookie;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
@ -23,11 +22,7 @@ public class CustomAuthenticationSuccessHandler implements AuthenticationSuccess
|
||||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
|
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
|
||||||
String username = (String) authentication.getPrincipal();
|
String username = (String) authentication.getPrincipal();
|
||||||
|
|
||||||
Cookie accessTokenCookie = jwtTokenProvider.createCookieWithToken(username, JwtTokenProvider.JwtTokenType.ACCESS);
|
jwtTokenProvider.generateToken(request, response, username);
|
||||||
response.addCookie(accessTokenCookie);
|
|
||||||
|
|
||||||
Cookie refreshTokenCookie = jwtTokenProvider.createCookieWithToken(username, JwtTokenProvider.JwtTokenType.REFRESH);
|
|
||||||
response.addCookie(refreshTokenCookie);
|
|
||||||
|
|
||||||
response.setStatus(HttpServletResponse.SC_OK);
|
response.setStatus(HttpServletResponse.SC_OK);
|
||||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,56 @@
|
||||||
package com.bpgroup.poc.admin.security.jwt;
|
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.Claims;
|
||||||
import io.jsonwebtoken.ExpiredJwtException;
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
import jakarta.servlet.http.Cookie;
|
import jakarta.servlet.http.Cookie;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class JwtTokenProvider {
|
public class JwtTokenProvider {
|
||||||
|
|
||||||
|
private final JwtTokenService jwtTokenService;
|
||||||
|
|
||||||
// TODO: 추후 Key 별도 관리 필요
|
// TODO: 추후 Key 별도 관리 필요
|
||||||
private static final SecretKey JWT_KEY = Keys.hmacShaKeyFor(JwtTokenConstants.KEY.getBytes(StandardCharsets.UTF_8));
|
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 {
|
public enum JwtTokenType {
|
||||||
ACCESS, REFRESH
|
ACCESS, REFRESH
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cookie createCookieWithToken(String username, JwtTokenType type) {
|
private Cookie createCookieWithToken(String username, JwtTokenType type) {
|
||||||
String tokenName;
|
String tokenName;
|
||||||
String subject;
|
String subject;
|
||||||
long expirationTime;
|
long expirationTime;
|
||||||
|
|
@ -45,16 +74,6 @@ public class JwtTokenProvider {
|
||||||
return tokenCookie;
|
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) {
|
private String generateToken(String issuer, String subject, String username, long expirationTime) {
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.issuer(issuer)
|
.issuer(issuer)
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,7 @@ public class JwtTokenValidateFilter extends OncePerRequestFilter {
|
||||||
Claims claims = jwtTokenProvider.getClaims(refreshToken);
|
Claims claims = jwtTokenProvider.getClaims(refreshToken);
|
||||||
String username = claims.get("username", String.class);
|
String username = claims.get("username", String.class);
|
||||||
|
|
||||||
Cookie accessTokenCookie = jwtTokenProvider.createCookieWithToken(username, JwtTokenProvider.JwtTokenType.ACCESS);
|
jwtTokenProvider.generateToken(request, response, username);
|
||||||
response.addCookie(accessTokenCookie);
|
|
||||||
|
|
||||||
Cookie refreshTokenCookie = jwtTokenProvider.createCookieWithToken(username, JwtTokenProvider.JwtTokenType.REFRESH);
|
|
||||||
response.addCookie(refreshTokenCookie);
|
|
||||||
|
|
||||||
setSecurityContext(username);
|
setSecurityContext(username);
|
||||||
} catch (JwtTokenExpiredException e) {
|
} catch (JwtTokenExpiredException e) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue