신입 개발자의 기록 [11/29 ~ 12/04]
- -
JWT 토큰 조사 및 구현하기
이번에는 JWT를 조사하고 JWT 토큰의 발급 및 사용, 재발급을 구현해 보았다.
JWT 토큰은 학원에서 프로젝트 때 잠깐 써본 것밖에 없어서 살짝 어려움이 있었지만 나름 재미있던 것 같다.
먼저 JWT 사용을 위한 라이브러리를 추가해준다.
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
application.yml 에 jwt 설정도 해준다.
jwt:
secret: nOmi70nNMA6ORMsiQC2035fgrsuguUT0ccrE4mL4HwQyy1abKXUphnEiKkn5Vzg1Bfs9i9k+dq3uO3yFAIE08Q==
그 뒤 member 관련 DTO들을 생성해준다.
(MemberDTO, AuthorityDTO, MemberRoleDTO)
JWT 관련 클래스들도 생성을 해준다.
public TokenDTO generateTokenDTO(MemberDTO member) {
List<String> roles = new ArrayList<>();
for(MemberRoleDTO memberRole : member.getMemberRole()) {
roles.add(memberRole.getAuthority().getAuthorityName());
}
/* 1. 회원 아이디를 "sub"라는 클레임으로 토큰에 추가 */
Claims claims = Jwts.claims().setSubject(member.getMemberId());
/* 2. 회원의 권한들을 "auth"라는 클레임으로 토큰에 추가 */
claims.put(AUTHORITIES_KEY, roles);
long now = System.currentTimeMillis(); // 현재 시각을 ms 단위로 가져옴
Date accessTokenExpiresIn = new Date(now + ACCESS_TOKEN_EXPIRE_TIME);
Date refreshTokenExpiresIn = new Date(now + REFRESH_TOKEN_EXPIRE_TIME);
String accessToken = Jwts.builder()
.setClaims(claims)
.setExpiration(accessTokenExpiresIn) // 토큰의 만료기간을 DATE형으로 토큰에 추가
.signWith(key, SignatureAlgorithm.HS512)
.compact();
String refreshToken = Jwts.builder()
.setClaims(claims)
.setExpiration(refreshTokenExpiresIn)
.signWith(key, SignatureAlgorithm.HS512)
.compact();
return new TokenDTO(BEARER_TYPE, member.getMemberCode(), accessToken, refreshToken, accessTokenExpiresIn.getTime());
}
TokenProvider -> token 생성 메소드 설정
accessToken과 refreshToken을 둘 다 발급하고 TokenDTO에 담아 반환을 하면
Front 쪽에서 토큰에서 accessToken과 refreshToken을 꺼내 쿠키에 저장한다.
public Authentication getAuthentication(String token) {
Claims claims = parseClaims(token);
if(claims.get(AUTHORITIES_KEY) == null) {
throw new RuntimeException("권한 정보가 없는 토큰입니다.");
}
UserDetails userDetails = userDetailsService.loadUserByUsername(this.getMemberId(token));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
TokenProvider -> accessToken으로 인증 객체 추출 메소드
public TokenDTO login(MemberDTO member) {
MemberDTO mem = memberMapper.findMemberById(member.getMemberId());
TokenDTO token = new TokenDTO();
if(mem != null && passwordEncoder.matches(member.getPassword(), mem.getPassword())){
int memberCode = mem.getMemberCode();
List<MemberRoleDTO> memberRoleList = memberMapper.getAuthList(memberCode);
System.out.println(memberRoleList);
for(MemberRoleDTO memberRole : memberRoleList) {
AuthorityDTO auth = memberMapper.findAuth(memberRole.getAuthorityCode());
memberRole.setAuthority(auth);
}
member.setMemberRole(memberRoleList);
token = tokenProvider.generateTokenDTO(member);
Map<String, Object> map = new HashMap<>();
map.put("refreshToken", token.getRefreshToken());
map.put("memberCode", memberCode);
memberMapper.refreshToken(map);
token.setMemberCode(mem.getMemberCode());
}
System.out.println(token);
return token;
}
로그인 시 토큰 생성하는 로직
MemberId로 가져온 MemberDTO 객체가 null이 아니고 비밀번호가 맞으면
MemberRole을 가져와 MemberDTO에 넣어준다. 그 값을 토대로 토큰을 발급하고
refreshToken을 회원 DB에 저장한다.
public Object check(String token) {
/* 토큰의 유효성 검사 (만료되면 false 를 반환) */
boolean validation = tokenProvider.validateToken(token);
if(validation == true) {
return true;
} else {
Claims auth = tokenProvider.getAuth(token);
String refreshToken = memberMapper.findToken(auth.getSubject());
/* 리프레시 토큰의 만료상태 검사 (만료 시 false반환) */
boolean refresh = tokenProvider.validateToken(refreshToken);
if(refresh == true) {
String accessToken = tokenProvider.accessToken(refreshToken);
return accessToken;
}
}
return false;
}
토큰의 유효성 검사를 하는 메소드
accessToken의 유효성을 검사해 만료되었으면 accessToken 값으로 Claims을 추출해
refreshToken을 가져온다. refreshToken을 가져와 유효성 검사를 하고
refreshToken이 만료되지 않았을 땐 다시 accessToken을 발급하고
만료되었을 때에는 모든 토큰을 다시 재발급한다.
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MemberDTO member = memberMapper.findMemberByMemberId(username);
List<MemberRoleDTO> memberRoleList = memberMapper.getAuthList(member.getMemberCode());
System.out.println(memberRoleList);
for(MemberRoleDTO memberRole : memberRoleList) {
AuthorityDTO auth = memberMapper.findAuth(memberRole.getAuthorityCode());
memberRole.setAuthority(auth);
}
member.setMemberRole(memberRoleList);
List<GrantedAuthority> authorities = new ArrayList<>();
for(MemberRoleDTO memberRole : member.getMemberRole()) {
String authorityName = memberRole.getAuthority().getAuthorityName();
authorities.add(new SimpleGrantedAuthority(authorityName));
member.setAuthorities(authorities);
}
return member;
}
userDetails에서 권한 설정해주는 메소드
이런 식으로 구현을 해 토큰의 발급, 사용 및 재발급까지 경험해볼 수 있었다.
'My Study > Java' 카테고리의 다른 글
신입 개발자의 기록 [12/06] Apache와 Tomcat을 사용해 war 배포 (1) | 2023.12.06 |
---|---|
신입 개발자의 기록[12/05 웹 최적화] (1) | 2023.12.05 |
신입 개발자의 기록 [11/24 ~ 11/28] (0) | 2023.12.01 |
신입 개발자의 기록[11/21 ~ 11/23] (2) | 2023.12.01 |
신입 개발자의 기록 [11/20] (3) | 2023.12.01 |
소중한 공감 감사합니다