-
TIL 76일 - JWT 토큰 검증공부/Spring 사용한 프로젝트 2022. 3. 15. 21:48
jwt 토큰으로 인증하는 걸 해보자....
참고글에 있는 코드랑은 조금 다르다.
package com.example.ical.Infrastructure; import com.example.ical.Domain.User; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @Component public class JwtTokenUtil implements Serializable { private static final long serialVersionUID = -2550185165626007488L; public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60; @Value("${jwt.secret}") private String secret; //retrieve username from jwt token public String getRoleFromToken(String token) { return getClaimRoleFromToken(token); } //retrieve expiration date from jwt token public Date getExpirationDateFromToken(String token) { return getClaimFromToken(token, Claims::getExpiration); } public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) { final Claims claims = getAllClaimsFromToken(token); return claimsResolver.apply(claims); } public String getClaimRoleFromToken(String token) { final Claims claims = getAllClaimsFromToken(token); return claims.get("role").toString(); } //for retrieveing any information from token we will need the secret key private Claims getAllClaimsFromToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } // //check if the token has expired private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date()); } //generate token for user public String generateToken(User user) { Map<String, Object> claims = new HashMap<>(); claims.put("role", user.getRole()); return doGenerateToken(claims, "i-cal"); } //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(Map<String, Object> claims, String subject) { return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)) .signWith(SignatureAlgorithm.HS512, secret).compact(); } // validate token public Boolean validateToken(String token, User user) { final String userRole = getRoleFromToken(token); return (userRole.equals(user.getRole()) && !isTokenExpired(token)); } }
참고글에서는 getSubject를 사용해서 username을 가져왔는데, username이 아니라 role이 필요하기 때문에 Claim으로부터 role을 가져오는 메서드를 만들었다.
그리고 그 메서드로 validateToken 메서드 쪽의 로직을 수정했다. (일단 되는지는 봐야함)
JwtRequestFilter
/* 다른 코드들은 더 필요한지 모르겠는데 얘는 필요해 보여서 해봄 */
일단 OncePerRequestFilter 클래스를 상속할 예정이다. 요것을 구현하고 나면 모든 요청에 대해 필터 클래스가 실행된가고 한다.
이 요청에 유효한 JWT 토큰이 있는지 확인한다고 한다. 유효할 경우 인증된 것으로!
package com.example.ical.Infrastructure; import com.example.ical.Domain.User; import com.example.ical.Service.UserService; import io.jsonwebtoken.ExpiredJwtException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class JwtRequestFilter extends OncePerRequestFilter { private UserService userService; private JwtTokenUtil jwtTokenUtil; public JwtRequestFilter(UserService userService, JwtTokenUtil jwtTokenUtil) { this.userService = userService; this.jwtTokenUtil = jwtTokenUtil; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String requestTokenHeader = request.getHeader("Authorization"); String jwtToken = null; Integer userId = null; // JWT Token is in the form "Bearer token". Remove Bearer word and get // only the Token if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { jwtToken = requestTokenHeader.substring(7); try { userId = jwtTokenUtil.getIdFromToken(jwtToken); } catch (IllegalArgumentException e) { System.out.println("Unable to get JWT Token"); } catch (ExpiredJwtException e) { System.out.println("JWT Token has expired"); } } else { logger.warn("JWT Token does not begin with Bearer String"); } // Once we get the token validate it. System.out.println(request.getRequestURI()); System.out.println("/login"); if (request.getMethod().equalsIgnoreCase("POST")) { User user = this.userService.getUserById(userId); // if token is valid configure Spring Security to manually set // authentication System.out.println(jwtTokenUtil.validateToken(jwtToken, user)); if (!jwtTokenUtil.validateToken(jwtToken, user)) { throw new RuntimeException("권한이 없습니다."); } } filterChain.doFilter(request, response); } }
일단 이렇게 하니까 POST 요청일 때는 role을 확인하고, admin인 경우에만 요청에 성공하도록 하는 것은 됐다.
다만... 뭔가... 코드가 이런식이지는 않았는데 음... 일단 동작은 정상적으로 하는 그런... 이상한... 상황이라고 해야하나.... 근데 filter의 역할이 뭔지는 조금 알 거 같음.
OncePerRequestFilter?
한번의 요청에서 한번만 필터가 동작하도록 하는 것
필터는 서블릿 전후로 동작하도록 할 수 있는데, 한번의 요청에서 여러 번의 서블릿을 거칠 경우 필터가 여러번 동작할 수 있는 걸 막을 수 있다고 한다.
참고글
https://www.javainuse.com/spring/boot-jwt
'공부 > Spring 사용한 프로젝트' 카테고리의 다른 글
TIL 78일 - mvc test (0) 2022.03.17 TIL 75일 - 로그인 시 jwt 반환 (0) 2022.03.14 TIL 74일 - Filters (0) 2022.03.13 TIL 73일 - 디스패쳐 서블릿 (0) 2022.03.12 TIL 71일 - 스프링 자잘자잘 (0) 2022.03.10