Получить метод, сопоставленный с запросом в Spring Security - PullRequest
0 голосов
/ 19 февраля 2019

Мне нужно использовать некоторую пользовательскую логику авторизации ролей с Spring Security и Jaxrs.Это означает, что я хочу использовать мою собственную аннотацию.У меня проблема с получением метода и класса, сопоставленных с текущим запросом, и они мне нужны, чтобы я мог извлечь аннотации к ним.Я пытался ввести ResourceInfo, но это всегда кажется нулевым.Есть идеи, как получить эти данные в фильтре?(см. метод getAuthentication)


package com.concentric.scenarios.rest.auth.jwt;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper;

import com.auth0.jwt.interfaces.DecodedJWT;
import com.concentric.scenarios.domain.user.AppRole;
import com.concentric.scenarios.security.Authorized;
import com.concentric.scenarios.security.SecurityConstants;
import com.concentric.scenarios.util.JWTUtil;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {

    @SuppressWarnings("unused")
    private AuthenticationManager authManager;
    private SecurityConstants securityConstants;


    public JWTAuthorizationFilter(AuthenticationManager authManager, SecurityConstants securityConstants) {
        super(authManager);
        this.securityConstants = securityConstants;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
            throws IOException, ServletException {

        String header = req.getHeader(securityConstants.getHeaderKey());
        if (header == null || !header.startsWith(securityConstants.getTokenPrefix())) {
            if(securityConstants.isMultipleProviders()) {
                log.info("Missing or malformed JWT token. Continuing security authorization filtering.");
            } else {
                log.error("Missing or malformed JWT token. JWT is only registered security provider.");
            }
            chain.doFilter(req, res);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        chain.doFilter(req, res);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String authorizationHeader = request.getHeader(securityConstants.getHeaderKey());
        if (authorizationHeader != null) {

            DecodedJWT jwt = JWTUtil.verifyToken(
                    authorizationHeader,
                    securityConstants.getSecret(),
                    securityConstants.getTokenPrefix());

            String principalRoleStr = jwt.getClaim("role").asString();

            Class<?> resourceClass = // somehow obtain the class mapped to this request by jax-rs
            Method resourceMethod = // somehow obtain the method mapped to this request by jax-rs
            // no authentication is needed
            if(!isAnnotationPresent(Authorized.class, resourceClass) && !isAnnotationPresent(Authorized.class, resourceMethod)) {
                return null;
            }

            // extract roles from class
            List<AppRole> classRoles = extractRoles(resourceClass);

            // extract roles from method
            List<AppRole> methodRoles = extractRoles(resourceMethod);

            AppRole principalRole = AppRole.valueOf(principalRoleStr);
            // Check if the user is allowed to execute the method
            if (methodRoles.isEmpty()) {
                checkPermissions(classRoles, principalRole);
            } else {
                checkPermissions(methodRoles, principalRole);
            }

            return new UsernamePasswordAuthenticationToken(jwt.getSubject(), null, new ArrayList<>());
        }

        return null;
    }

    private void checkPermissions(List<AppRole> allowedRoles, AppRole userRole) {
        boolean hasRole = false;

        for(AppRole allowed : allowedRoles) {
            if(userRole.hasRights(allowed)) {
                hasRole = true;
                break;
            }
        }

        if(hasRole == false) {
            throw new NotAuthorizedException("User is not authorized to access resource with JWT auth!");
        }
    }

    private List<AppRole> extractRoles(AnnotatedElement annotatedElement) {
        if (annotatedElement == null) {
            return new ArrayList<>();
        } else {
            Authorized secured = annotatedElement.getAnnotation(Authorized.class);
            if (secured == null) {
                return new ArrayList<AppRole>();
            } else {
                AppRole[] allowedRoles = secured.value();
                return Arrays.asList(allowedRoles);
            }
        }
    }

    private boolean isAnnotationPresent(Class<? extends Annotation> annotation, AnnotatedElement annotatedElement) {
        if (annotatedElement == null) {
            return false;
        } else if(annotatedElement.isAnnotationPresent(annotation)) {
            return true;
        }
        return false;
    }
}

@Component
@ApplicationPath("/api")
public class ScenariosRestConfig extends ResourceConfig {

    public ScenariosRestConfig() {
        packages(ScenariosRestConfig.class.getPackageName());
    }

}
...