JUNGKIMHOON
[Spring] JWT를 활용한 인증 (feat. vue) 본문
기존 Spring을 활용한 MVC 프로젝트에서는 한 서버안에서 작업을 처리하면서 session으로 로그인을 유지시켜주면 만사가 편했다. Vue.js와 Spring boot 프로젝트를 진행하면서 로그인 처리에 JWT를 활용하는 방법을 많이 사용하는 것을 알 수 있었다.
JWT Procedure
- 프론트 측에서 로그인을 시도하여 서버로 데이터 넘김
- 서버에서 데이터 검증 및 JWT 생성
- JWT 프론트로 전달
- 서버에 데이터요청 (JWT헤더 + 요청값)
- JWT 유효성 검증
- 요청 데이터 전송
JWT Structure
- 헤더 : 토큰 타입, 해싱 알고리즘
- 페이로드 : 토큰에 담을 정보 (클레임)
- 시그니처 : 비밀키 해싱값
JWT 구현
POM.xml
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
Controller
@PostMapping("/user")
public void login(@RequestBody HashMap<String, String> map, HttpServletResponse response){
System.out.println(map.get("email"));
UserDTO loginUser = userRepository.findMember(map.get("email"), map.get("password"));
if(loginUser == null) new UnauthorizedException();
else{
String token = jwtService.createToken(loginUser.getId()+"", (60 * 1000 * 60));
System.out.println(token);
response.setHeader("authorization", token);
}
}
data 정보가 유효하면 id를 정보로 담은 token 발행 (시간은 1시간), 발행한 토큰은 헤더로 전송
Service
@Slf4j
@Service("jwtService")
public class JwtServiceImpl implements JwtService{
private static final String SECRET_KEY = "accessToken";
// 토큰 발행
@Override
public String createToken(String subject, long time) {
if (time <= 0) {
throw new RuntimeException("Expiry time must be greater than Zero : ["+time+"] ");
}
// 토큰을 서명하기 위해 사용해야할 알고리즘 선택
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] secretKeyBytes = DatatypeConverter.parseBase64Binary(SECRET_KEY);
Key signingKey = new SecretKeySpec(secretKeyBytes, signatureAlgorithm.getJcaName());
JwtBuilder builder = Jwts.builder()
.setSubject(subject)
.signWith(signatureAlgorithm, signingKey);
long nowTime = System.currentTimeMillis();
builder.setExpiration(new Date(nowTime + time));
return builder.compact();
}
// 토큰 해독
@Override
public String getSubject(String token) {
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(SECRET_KEY))
.parseClaimsJws(token).getBody();
return claims.getSubject();
}
@Override
public boolean isUsable(String jwt) {
try{
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(SECRET_KEY))
.parseClaimsJws(jwt).getBody();
return true;
}catch (Exception e) {
throw new UnauthorizedException();
}
}
}
- createToken : controller에서 정보를 받아 토큰을 발행
- getSubject : 토큰을 받아 정보 해독
- isUsable : 유효한 토큰인지 확인
Interceptor
@Component
public class JwtInterceptor implements HandlerInterceptor {
private static final String HEADER_AUTH = "authorization";
@Autowired
private JwtService jwtService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final String token = request.getHeader(HEADER_AUTH);
System.out.println(token);
if(token != null && jwtService.isUsable(token)){
return true;
}else{
throw new UnauthorizedException();
}
}
}
프런트 측에서 서버에 요청이 있을 때 interceptor로 요청의 header를 인터셉트해서 isUsable 체크를 진행한다.
Config
@Configuration
public class WebConfig implements WebMvcConfigurer {
private static final String[] EXCLUDE_PATHS = {
"/movie/**",
"/movie/**/**",
"/user",
"/users",
"/"
};
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(EXCLUDE_PATHS);
}
}
Config에서 interceptor의 path를 적용시킨다. exclude또한 설정한다.
Vue에서는 axios로 서버와 통신을 시도할때 헤더로 저장하고 있는 jwt값을 헤더로 보내준다.
위 내용은 refresh 토큰 없이 인증 토큰만 생성하여 작업하였다.
refresh 토큰의 아이디어는 galid1.tistory.com/755
'Spring' 카테고리의 다른 글
[Spring] CORS 오류 해결 (0) | 2020.09.19 |
---|---|
Glory of REST (0) | 2020.08.29 |
Comments