Modify 비밀키 환경변수로 숨김 처리

This commit is contained in:
Daeil Choi
2023-02-08 12:20:14 +09:00
parent 09f1e3d07f
commit eb7f9974a2
7 changed files with 68 additions and 28 deletions

View File

@@ -19,6 +19,7 @@ import com.example.springsecuritystudy.filter.StopwatchFilter;
import com.example.springsecuritystudy.jwt.JwtAuthenticationFilter;
import com.example.springsecuritystudy.jwt.JwtAuthorizationFilter;
import com.example.springsecuritystudy.jwt.JwtProperties;
import com.example.springsecuritystudy.jwt.JwtUtils;
import com.example.springsecuritystudy.user.UserRepository;
import lombok.RequiredArgsConstructor;
@@ -31,6 +32,7 @@ import lombok.RequiredArgsConstructor;
public class SecurityConfig {
private final UserRepository userRepository;
private final JwtUtils jwtUtils;
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
@@ -47,10 +49,11 @@ public class SecurityConfig {
);
// JWT filter
http.addFilterBefore(
new JwtAuthenticationFilter(authenticationManager(http.getSharedObject(AuthenticationConfiguration.class))),
new JwtAuthenticationFilter(authenticationManager(http.getSharedObject(AuthenticationConfiguration.class))
, jwtUtils),
UsernamePasswordAuthenticationFilter.class
).addFilterBefore(
new JwtAuthorizationFilter(userRepository),
new JwtAuthorizationFilter(userRepository, jwtUtils),
BasicAuthenticationFilter.class
);
http

View File

@@ -19,9 +19,12 @@ import com.example.springsecuritystudy.user.User;
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final JwtUtils jwtUtils;
public JwtAuthenticationFilter(
AuthenticationManager authenticationManager) {
AuthenticationManager authenticationManager, JwtUtils jwtUtils) {
super(authenticationManager);
this.jwtUtils = jwtUtils;
}
/**
@@ -46,7 +49,7 @@ public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilte
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
User user = (User)authResult.getPrincipal();
String token = JwtUtils.createToken(user);
String token = jwtUtils.createToken(user);
// 쿠키 생성
Cookie cookie = new Cookie(JwtProperties.COOKIE_NAME, token);
cookie.setMaxAge(JwtProperties.EXPIRATION_TIME); // 쿠키 만료 시간

View File

@@ -18,16 +18,17 @@ import com.example.springsecuritystudy.common.UserNotFoundException;
import com.example.springsecuritystudy.user.User;
import com.example.springsecuritystudy.user.UserRepository;
import lombok.RequiredArgsConstructor;
/**
* JWT를 이용한 인증
*/
@RequiredArgsConstructor
public class JwtAuthorizationFilter extends OncePerRequestFilter {
private final UserRepository userRepository;
private final JwtUtils jwtUtils;
public JwtAuthorizationFilter(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
@@ -55,7 +56,7 @@ public class JwtAuthorizationFilter extends OncePerRequestFilter {
}
private Authentication getUsernamePasswordAuthenticationToken(String token) {
String username = JwtUtils.getUsername(token);
String username = jwtUtils.getUsername(token);
if (username != null) {
User user = userRepository.findByUsername(username).orElseThrow(UserNotFoundException::new);
return new UsernamePasswordAuthenticationToken(

View File

@@ -6,6 +6,11 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.annotation.PostConstruct;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.security.Keys;
import javafx.util.Pair;
@@ -13,27 +18,39 @@ import javafx.util.Pair;
* JWT Key를 제공하고 조회한다.
* Key Rolling을 지원한다.
*/
@Component
public class JwtKey {
/**
* Kid-Key List 외부로 절대 유출되어서는 안된다.
*/
private static final Map<String, String> SECRET_KEY_SET = new HashMap<String, String>() {
private final Environment env;
public JwtKey(Environment env) {
this.env = env;
}
private Map<String, String> SECRET_KEY_SET;
private String[] KID_SET;
private Random randomIndex;
@PostConstruct
public void init() {
SECRET_KEY_SET = new HashMap<String, String>() {
{
put("key1", "SpringSecurityJWTPracticeProjectIsSoGoodAndThisProjectIsSoFunSpringSecurityJWTPracticeProjectIsSoGoodAndThisProjectIsSoFun");
put("key2", "GoodSpringSecurityNiceSpringSecurityGoodSpringSecurityNiceSpringSecurityGoodSpringSecurityNiceSpringSecurityGoodSpringSecurityNiceSpringSecurity");
put("key3", "HelloSpringSecurityHelloSpringSecurityHelloSpringSecurityHelloSpringSecurityHelloSpringSecurityHelloSpringSecurityHelloSpringSecurityHelloSpringSecurity");
put("key1", env.getProperty("jwt.secret-key1"));
put("key2", env.getProperty("jwt.secret-key2"));
put("key3", env.getProperty("jwt.secret-key3"));
}
};
private static final String[] KID_SET = SECRET_KEY_SET.keySet().toArray(new String[0]);
private static Random randomIndex = new Random();
KID_SET = SECRET_KEY_SET.keySet().toArray(new String[0]);
randomIndex = new Random();
}
/**
* SECRET_KEY_SET 에서 랜덤한 KEY 가져오기
*
* @return kid와 key Pair
*/
public static Pair<String, Key> getRandomKey() {
public Pair<String, Key> getRandomKey() {
String kid = KID_SET[randomIndex.nextInt(KID_SET.length)];
String secretKey = SECRET_KEY_SET.get(kid);
return new Pair<>(kid, Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)));
@@ -45,7 +62,7 @@ public class JwtKey {
* @param kid kid
* @return Key
*/
public static Key getKey(String kid) {
public Key getKey(String kid) {
String key = SECRET_KEY_SET.getOrDefault(kid, null);
if (key == null) {
return null;

View File

@@ -3,24 +3,31 @@ package com.example.springsecuritystudy.jwt;
import java.security.Key;
import java.util.Date;
import org.springframework.stereotype.Component;
import com.example.springsecuritystudy.user.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwts;
import javafx.util.Pair;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
public class JwtUtils {
private final JwtKey jwtKey;
/**
* 토큰에서 username 찾기
*
* @param token 토큰
* @return username
*/
public static String getUsername(String token) {
public String getUsername(String token) {
// jwtToken에서 username을 찾는다.
return Jwts.parserBuilder()
.setSigningKeyResolver(SigningKeyResolver.instance)
.setSigningKeyResolver(new SigningKeyResolver(jwtKey))
.build()
.parseClaimsJws(token)
.getBody()
@@ -36,10 +43,10 @@ public class JwtUtils {
* @param user 유저
* @return jwt token
*/
public static String createToken(User user) {
public String createToken(User user) {
Claims claims = Jwts.claims().setSubject(user.getUsername());
Date now = new Date();
Pair<String, Key> key = JwtKey.getRandomKey();
Pair<String, Key> key = jwtKey.getRandomKey();
return Jwts.builder()
.setClaims(claims) // 토큰에 담을 정보 설정
.setIssuedAt(now) // 토큰 발행 시간 설정

View File

@@ -5,18 +5,20 @@ import java.security.Key;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.SigningKeyResolverAdapter;
import lombok.RequiredArgsConstructor;
/**
* JwsHeader를 통해 Signature 검증에 필요한 Key를 가져오는 코드를 구현합니다.
*/
@RequiredArgsConstructor
public class SigningKeyResolver extends SigningKeyResolverAdapter {
public static SigningKeyResolver instance = new SigningKeyResolver();
private final JwtKey jwtKey;
@Override
public Key resolveSigningKey(JwsHeader jwsHeader, Claims claims) {
String kid = jwsHeader.getKeyId();
if (kid == null)
return null;
return JwtKey.getKey(kid);
return jwtKey.getKey(kid);
}
}

View File

@@ -13,3 +13,10 @@ spring:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
show-sql: true
jwt:
secret-key1: SpringSecurityJWTPracticeProjectIsSoGoodAndThisProjectIsSoFunSpringSecurityJWTPracticeProjectIsSoGoodAndThisProjectIsSoFun
secret-key2: GoodSpringSecurityNiceSpringSecurityGoodSpringSecurityNiceSpringSecurityGoodSpringSecurityNiceSpringSecurityGoodSpringSecurityNiceSpringSecurity
secret-key3: HelloSpringSecurityHelloSpringSecurityHelloSpringSecurityHelloSpringSecurityHelloSpringSecurityHelloSpringSecurityHelloSpringSecurityHelloSpringSecurity
expiration-time: 86400 #60 * 60 * 24
remember-me-expiration-time: 2592000 #60 * 60 * 24 * 30