diff --git a/kcsc-back-end/build.gradle b/kcsc-back-end/build.gradle index c6d24c2..e069284 100644 --- a/kcsc-back-end/build.gradle +++ b/kcsc-back-end/build.gradle @@ -45,6 +45,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-validation' developmentOnly 'org.springframework.boot:spring-boot-devtools' diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java index 3ead837..d3c9b3e 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java @@ -3,19 +3,25 @@ package com.dbnt.kcscbackend.config.jwt; import com.dbnt.kcscbackend.auth.entity.UserInfo; import com.dbnt.kcscbackend.config.egov.EgovProperties; +import com.dbnt.kcscbackend.config.jwt.redis.RefreshToken; +import com.dbnt.kcscbackend.config.jwt.redis.RefreshTokenRepository; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.UUID; /** * @@ -44,9 +50,11 @@ public class EgovJwtTokenUtil implements Serializable{ private static final long serialVersionUID = -5180902194184255251L; //public static final long JWT_TOKEN_VALIDITY = 24 * 60 * 60; //하루 - public static final long JWT_TOKEN_VALIDITY = (long) ((1 * 60 * 60) / 60) * 60; //토큰의 유효시간 설정, 기본 60분 - + public static final long JWT_ACCESS_TOKEN_VALIDITY = (30); //토큰의 유효시간 설정, 기본 60분 60*60 public static final String SECRET_KEY = EgovProperties.getProperty("Globals.jwt.secret"); + @Autowired + private RefreshTokenRepository refreshTokenRepository; + //retrieve username from jwt token public String getUserIdFromToken(String token) { @@ -74,17 +82,12 @@ public class EgovJwtTokenUtil implements Serializable{ } //generate token for user - public String generateToken(UserInfo loginVO) { - return doGenerateToken(loginVO); - } - //while creating the token - //1. Define claims of the token, like Issuer, Expiration, Subject, and the ID //2. Sign the JWT using the HS512 algorithm and secret key. //3. According to JWS Compact Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1) // compaction of the JWT to a URL-safe string - private String doGenerateToken(UserInfo loginVO) { - + public String generateAccessToken(UserInfo loginVO) { Map claims = new HashMap<>(); claims.put("id", loginVO.getUserId() ); claims.put("userSe", loginVO.getUserSe() ); @@ -97,10 +100,18 @@ public class EgovJwtTokenUtil implements Serializable{ .setClaims(claims) .setSubject("Authorization") .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)) + .setExpiration(new Date(System.currentTimeMillis() + JWT_ACCESS_TOKEN_VALIDITY * 1000)) .signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8)), SignatureAlgorithm.HS512); return builder.compact(); } + @Transactional + public String generateRefreshTokenToken(UserInfo loginVO){ + RefreshToken refreshToken = new RefreshToken(); + refreshToken.setRefreshToken(UUID.randomUUID().toString()); + refreshToken.setUserSeq(loginVO.getUserSeq()); + refreshTokenRepository.save(refreshToken); + return refreshToken.getRefreshToken(); + } } diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/redis/RefreshToken.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/redis/RefreshToken.java new file mode 100644 index 0000000..310c680 --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/redis/RefreshToken.java @@ -0,0 +1,20 @@ +package com.dbnt.kcscbackend.config.jwt.redis; + +import lombok.*; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + + + +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@RedisHash(value="refreshToken", timeToLive = 60) // *60*24*14 +public class RefreshToken { + + @Id + private String refreshToken; + private Integer userSeq; +} diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/redis/RefreshTokenRepository.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/redis/RefreshTokenRepository.java new file mode 100644 index 0000000..497f940 --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/redis/RefreshTokenRepository.java @@ -0,0 +1,8 @@ +package com.dbnt.kcscbackend.config.jwt.redis; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RefreshTokenRepository extends CrudRepository { +} diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomUrlAuthenticationSuccessHandler.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomUrlAuthenticationSuccessHandler.java index fcb12ae..68eb6ad 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomUrlAuthenticationSuccessHandler.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomUrlAuthenticationSuccessHandler.java @@ -2,6 +2,7 @@ package com.dbnt.kcscbackend.config.security; import com.dbnt.kcscbackend.auth.entity.UserInfo; import com.dbnt.kcscbackend.config.jwt.EgovJwtTokenUtil; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -49,12 +50,14 @@ public class CustomUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticati MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); MediaType jsonMimeType = MediaType.APPLICATION_JSON; - String jwtToken = jwtTokenUtil.generateToken(securityUser); + String refreshToken = jwtTokenUtil.generateRefreshTokenToken(securityUser); + String jwtToken = jwtTokenUtil.generateAccessToken(securityUser); securityUser.setPassword(null); securityUser.setUserSeq(null); HashMap resultMap = new HashMap(); resultMap.put("resultCode", "200"); resultMap.put("resultVO", (UserDetails)securityUser); + resultMap.put("refreshToken", refreshToken); resultMap.put("jToken", jwtToken); // String userName = jwtTokenUtil.getUserSeFromToken(jwtToken); response.addHeader("Authorization", "BEARER "+jwtToken); diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/JsonUsernamePasswordAuthenticationFilter.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/JsonAuthenticationFilter.java similarity index 87% rename from kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/JsonUsernamePasswordAuthenticationFilter.java rename to kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/JsonAuthenticationFilter.java index 17c5802..7fa72c6 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/JsonUsernamePasswordAuthenticationFilter.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/JsonAuthenticationFilter.java @@ -20,7 +20,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; @Slf4j -public class JsonUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { +public class JsonAuthenticationFilter extends AbstractAuthenticationProcessingFilter { private static final String DEFAULT_LOGIN_REQUEST_URL = "/auth/login"; // /login/oauth2/ + ????? 로 오는 요청을 처리할 것이다 private static final String HTTP_METHOD = "POST"; //HTTP 메서드의 방식은 POST 이다. @@ -30,9 +30,9 @@ public class JsonUsernamePasswordAuthenticationFilter extends AbstractAuthentica private final ObjectMapper objectMapper; - public JsonUsernamePasswordAuthenticationFilter(ObjectMapper objectMapper, - AuthenticationSuccessHandler authenticationSuccessHandler, // 로그인 성공 시 처리할 핸들러 - AuthenticationFailureHandler authenticationFailureHandler // 로그인 실패 시 처리할 핸들러 + public JsonAuthenticationFilter(ObjectMapper objectMapper, + AuthenticationSuccessHandler authenticationSuccessHandler, // 로그인 성공 시 처리할 핸들러 + AuthenticationFailureHandler authenticationFailureHandler // 로그인 실패 시 처리할 핸들러 ) { super(DEFAULT_LOGIN_PATH_REQUEST_MATCHER); // 위에서 설정한 /oauth2/login/* 의 요청에, GET으로 온 요청을 처리하기 위해 설정한다. diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/SecurityConfig.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/SecurityConfig.java index 25a5f6d..4ea66e4 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/SecurityConfig.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/SecurityConfig.java @@ -1,7 +1,5 @@ package com.dbnt.kcscbackend.config.security; -import com.dbnt.kcscbackend.auth.entity.UserInfo; -import com.dbnt.kcscbackend.auth.service.EgovLoginService; import com.dbnt.kcscbackend.config.jwt.EgovJwtTokenUtil; import com.dbnt.kcscbackend.config.jwt.JwtAuthenticationEntryPoint; import com.dbnt.kcscbackend.config.jwt.JwtAuthenticationFilter; @@ -16,28 +14,20 @@ import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.security.web.savedrequest.HttpSessionRequestCache; -import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.RegexRequestMatcher; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import javax.annotation.Resource; -import javax.servlet.http.HttpSession; import java.util.Arrays; /** @@ -55,8 +45,6 @@ import java.util.Arrays; @RequiredArgsConstructor public class SecurityConfig { - @Autowired - private EgovJwtTokenUtil jwtTokenUtil; @Resource(name = "loginService") private UserDetailsService loginService; private final ObjectMapper objectMapper; @@ -150,10 +138,10 @@ public class SecurityConfig { } @Bean - public JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter() { - JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter = new JsonUsernamePasswordAuthenticationFilter(objectMapper, new CustomUrlAuthenticationSuccessHandler(), loginFailureHandler()); - jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager()); - return jsonUsernamePasswordAuthenticationFilter; + public JsonAuthenticationFilter jsonUsernamePasswordAuthenticationFilter() { + JsonAuthenticationFilter jsonAuthenticationFilter = new JsonAuthenticationFilter(objectMapper, new CustomUrlAuthenticationSuccessHandler(), loginFailureHandler()); + jsonAuthenticationFilter.setAuthenticationManager(authenticationManager()); + return jsonAuthenticationFilter; } @Bean diff --git a/kcsc-back-end/src/main/resources/application.properties b/kcsc-back-end/src/main/resources/application.properties index 1319570..a072886 100644 --- a/kcsc-back-end/src/main/resources/application.properties +++ b/kcsc-back-end/src/main/resources/application.properties @@ -1,8 +1,14 @@ + + spring.mvc.pathmatch.matching-strategy=ant_path_matcher # Page Config Globals.pageUnit=10 Globals.pageSize=10 -#JWT secret key +#JWT +# redisConfig +spring.redis.host=localhost +spring.redis.port=6379 +# secret key Globals.jwt.secret = qWwMroux3QtiIJcPSIZARNTZEBBnWVH0jZ2Lx7tfFChCYi0ViZllo1bekZdiU0B3FRjJI7g90n0ha120dwlz8JZU8rOkmNCe9Uq0