весенняя загрузка jwt запрещена для POST / DELETE, но работает для GET - PullRequest
0 голосов
/ 14 октября 2019

У меня есть API отдыха, который я хочу защитить с помощью Spring Security. Я получаю jwt с моего сервера аутентификации, но когда я нажимаю rest api, он отвечает запрещенным для операции POST / DELETE и прекрасно работает для операций GET. Мои классы конфигурации:

package com.mycompany.restapi.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;

@Configuration
public class RestAPISecurityConfig extends WebSecurityConfigurerAdapter {

    private final String publicKeyValue;
    private final UserDetailsService userDetailsService;
    @Autowired
    public RestAPISecurityConfig(@Value("${environment.publicKey}") String publicKeyValue, UserDetailsService userDetailsService) {
        this.publicKeyValue = publicKeyValue;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers(getPermitAllMatchers()).permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilter(new JwtAuthorizationFilter(authenticationManager(), publicKeyValue, userDetailsService))
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    /*
    Array of all paths that should be accessible without authentication/authorization
     */
    public static String[] getPermitAllMatchers() {
        return new String[] {
            "/user/forgetPassword", // URL that is needed to generate the forgot password mail
            "/user/resetPassword",   // URL where the one time key is posted to, to set a new password
            "/"
        };
    }

}

и фильтр:

package com.blupa.hb.restapi.security;

import com.mycompany.user.HBUserDetails;
import com.mycompany.user.User;
import com.mycompany.user.UserRepository;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.SecurityException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;


public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    private static final Logger log = LoggerFactory.getLogger(JwtAuthorizationFilter.class);

    private final PublicKey publicKey;
    private final UserDetailsService userDetailsService;
    public JwtAuthorizationFilter(AuthenticationManager authenticationManager, String publicKeyValue, UserDetailsService userDetailsService) {
        super(authenticationManager);
        this.publicKey = getKey(publicKeyValue);
        this.userDetailsService = userDetailsService;
    }

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

        // Is requested path accessible to everyone? -> Don't check the access token, i.e. continue the filter chain
        String requestedPath = request.getServletPath();
        String[] publicPaths = RestAPISecurityConfig.getPermitAllMatchers();

        //

        boolean isPublicPath = Arrays.asList(publicPaths).contains(requestedPath);
        if (isPublicPath) {
            chain.doFilter(request, response);
            return;
        }

        // Check Access Token
        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
        if (authentication == null) {
            chain.doFilter(request, response);
            return;
        }

        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {

        String token = request.getHeader("Authorization");

        if (token != null && !token.isEmpty() && token.startsWith("Bearer")) {

            try {

                Jws<Claims> parsedToken = Jwts.parser()
                    .setSigningKey(publicKey)
                    .parseClaimsJws(token.replace("Bearer ", ""));


                // Check for the "ati" attribute in the jwt
                // Only refresh tokens have it, so we should not grant access for it
                String refreshTokenAti = (String) parsedToken.getBody().get("ati");
                if (refreshTokenAti != null) {
                    return null;
                }

                String username = (String) parsedToken
                    .getBody()
                    .get("user_name");

                List<GrantedAuthority> authorities = ((List<?>) parsedToken.getBody().get("authorities"))
                    .stream()
                    .map(authority -> new SimpleGrantedAuthority((String) authority))
                    .collect(Collectors.toList());

                if (!username.isEmpty()) {
                    UserDetails user = userDetailsService.loadUserByUsername(username);
                    return new UsernamePasswordAuthenticationToken(user, null, authorities);
                }
            } catch (ExpiredJwtException exception) {
                log.warn("Request to parse expired JWT : {} failed : {}", token, exception.getMessage());
            } catch (UnsupportedJwtException exception) {
                log.warn("Request to parse unsupported JWT : {} failed : {}", token, exception.getMessage());
            } catch (MalformedJwtException exception) {
                log.warn("Request to parse invalid JWT : {} failed : {}", token, exception.getMessage());
            } catch (SecurityException exception) {
                log.warn("Request to parse JWT with invalid signature : {} failed : {}", token, exception.getMessage());
            } catch (IllegalArgumentException exception) {
                log.warn("Request to parse empty or null JWT : {} failed : {}", token, exception.getMessage());
            }
        }
        return null;
    }

    // create PublicKey instance from publicKey string : https://stackoverflow.com/a/24502245
    public static PublicKey getKey(String key){
        try{
            String parsedKey = key.replaceAll("\\n", "").replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
            byte[] parsedBytes = Base64.getDecoder().decode(parsedKey);
            X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(parsedBytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePublic(X509publicKey);
        }
        catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }

}

Я использую открытый ключ, сгенерированный в классе фильтра. когда я нажимаю на запрос get, он работает нормально, но не работает для запроса POST / DELETE.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...