관리 메뉴

JUNGKIMHOON

[Spring] JWT를 활용한 인증 (feat. vue) 본문

Spring

[Spring] JWT를 활용한 인증 (feat. vue)

JUNGKIMHOON 2020. 9. 30. 20:45

 

Spring Boot

 

기존 Spring을 활용한 MVC 프로젝트에서는 한 서버안에서 작업을 처리하면서 session으로 로그인을 유지시켜주면 만사가 편했다. Vue.js와 Spring boot 프로젝트를 진행하면서 로그인 처리에 JWT를 활용하는 방법을 많이 사용하는 것을 알 수 있었다.

JWT Procedure

뉴캣 & 고돌의 통신

  1.  프론트 측에서 로그인을 시도하여 서버로 데이터 넘김
  2.  서버에서 데이터 검증 및 JWT 생성
  3.  JWT 프론트로 전달
  4.  서버에 데이터요청 (JWT헤더 + 요청값)  
  5.  JWT 유효성 검증
  6.  요청 데이터 전송

 

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 Boot - REST API 인증 - 2 (Refresh Token이란?)

jwttoken 이번 포스팅에서는, JWT Token을 이용한 REST API 인증 후, Token이 만료된 경우의 처리 방법에 대해 알아보겠습니다. REST API 인증 1 - https://galid1.tistory.com/638 위의 링크를 통해 REST API 인..

galid1.tistory.com

 

'Spring' 카테고리의 다른 글

[Spring] CORS 오류 해결  (0) 2020.09.19
Glory of REST  (0) 2020.08.29
Comments