TokenRequiredAspect.java

package no.ntnu.idatt2105.l4.demo.aop;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import no.ntnu.idatt2105.l4.demo.service.SecurityService;
import no.ntnu.idatt2105.l4.demo.service.SecurityServiceImpl;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.server.ResponseStatusException;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;

@Aspect
@Component
public class TokenRequiredAspect {

    @Autowired
    SecurityService securityService;

//    @Before("execution(* no.ntnu.idatt2105.l4.demo.web.Lesson4Controller.openEndpoint())")
//    public void tokenRequiredWithoutAnnoation() throws Throwable{
//        System.out.println("Before tokenRequiredWithExecution");
//    }

    @Before("@annotation(tokenRequired)")
    public void tokenRequiredWithAnnotation(TokenRequired tokenRequired) throws Throwable {

        System.out.println("Before tokenRequiredWithAnnotation");

        ServletRequestAttributes reqAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = reqAttributes.getRequest();
        // checks for token in request header
        String tokenInHeader = request.getHeader("Authorization");
        if (tokenInHeader.startsWith("Bearer")) {
            tokenInHeader = tokenInHeader.substring("Bearer ".length());
        }
        if(ObjectUtils.isEmpty(tokenInHeader)){
//            throw new IllegalArgumentException("Empty token");

            // Instead of using a "more correct" exception here, like IllegalArgumentException,
            // we cheat a little and directly throw a ResponseStatusException. This is due
            // to the fact that otherwise Spring Boot will return a "500 - Internal Server
            // Error". Directly returning the ResponseStatusException here is
            // something I consider OK, even if it's not optimal, given that this advice
            // is exclusively meant to be used in conjunction with Controllers.
            System.out.println("Empty token received");
            throw new ResponseStatusException(HttpStatus.I_AM_A_TEAPOT, "Empty token");
        }
        Claims claims = Jwts.parser()
                            .setSigningKey(DatatypeConverter.parseBase64Binary(SecurityServiceImpl.secretKey))
                            .parseClaimsJws(tokenInHeader).getBody();

        if(claims == null || claims.getSubject() == null){
//            throw new IllegalArgumentException("Token Error : Claim is null");
            System.out.println("Claim is null");
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Token Error : Claim is null");
        }
        if (!securityService.validToken(tokenInHeader)) {
            System.out.println("Could not find token among valid ones");
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Token error: could not find token among valid ones");
//            throw new IllegalArgumentException("Token error: could find token among valid ones");
        }
        // Here we probably should have checked the expiration, as well, and if it's too close to
        // expiry, refresh it. If it's after expiry, call SecurityService and tell it to
        // remove the token from the list of valid tokens.
    }
}