jwt 설명
대칭키, RSA
package shop.mtcoding.bank.config.jwt;
public interface JwtValueObject {
public static final String SECRET = "메타코딩"; // HS256 (대칭키), 환경변수로 해야됨, 서버만 알아야하는 키
public static final int EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 7; // 일주일
public static final String TOKEN_PREFIX = "Bearer "; // 조심
public static final String HEADER = "Authorization";
}
package shop.mtcoding.bank.config.jwt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JwtProcess {
private final Logger log = LoggerFactory.getLogger(getClass());
// 토큰 생성
public static String create() {
}
// 토큰 검증
public static void verify() {
}
}
2개의 메소드 필요
package shop.mtcoding.bank.config.auth;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import shop.mtcoding.bank.domain.user.User;
@RequiredArgsConstructor
@Getter
public class LoginUser implements UserDetails {
private final User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(() -> "ROLE_" + user.getRole());
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
package shop.mtcoding.bank.config.auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import shop.mtcoding.bank.domain.user.User;
import shop.mtcoding.bank.domain.user.UserRepository;
@Service // IOC 등록
public class LoginService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
// 시큐리티로 로그인할 때, 시큐리티가 loadUserByUsername() 실행해서 username 체크
// user가 없으면 오류
// user가 있으면 정상적으로 시큐리티 컨텍스트 내부 세션에 로그인된 세션이 만들어진다.
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User userPS = userRepository.findByUsername(username).orElseThrow(
() -> new InternalAuthenticationServiceException("인증 실패"));
return new LoginUser(userPS);
}
}
InternalAuthenticationServiceException로 터트려야하는 이유 : 시큐리티가 돌때, 개발자에게 제어권이 없기 때문에
코드 추가
package shop.mtcoding.bank.config.jwt;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import shop.mtcoding.bank.config.auth.LoginUser;
import shop.mtcoding.bank.domain.user.User;
import shop.mtcoding.bank.domain.user.UserEnum;
public class JwtProcess {
private final Logger log = LoggerFactory.getLogger(getClass());
// 토큰 생성
public static String create(LoginUser loginUser) {
String jwtToken = JWT.create()
.withSubject("bank") // 토큰 제목
.withExpiresAt(new Date(System.currentTimeMillis() + JwtValueObject.EXPIRATION_TIME)) // 만료시간
.withClaim("id", loginUser.getUser().getId())
.withClaim("role", loginUser.getUser().getRole() + "")
.sign(Algorithm.HMAC512(JwtValueObject.SECRET)); // 단방향 암호화
return JwtValueObject.TOKEN_PREFIX + jwtToken;
}
// 토큰 검증
public static LoginUser verify(String token) {
DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512(JwtValueObject.SECRET)).build().verify(token);
Long id = decodedJWT.getClaim("id").asLong();
String role = decodedJWT.getClaim("role").asString();
User user = User.builder().id(id).role(UserEnum.valueOf(role)).build();
LoginUser loginUser = new LoginUser(user); // JWT에 들어있는 것으로 객체 생성
return loginUser; // 강제로 시큐리티 세션에 직접 주입(강제로그인)
}
}
생성과 검증을 한 곳에서 -> 대칭키
시큐리티 세션에 강제 주입하는 이유는 나중에