Keycloak保护API试图公开swagger.json

时间:2017-07-13 05:42:43

标签: spring-boot jersey-2.0 keycloak

我有一个使用SpringBoot,Jersey2和Keycloak构建的API。我使用的是SpringBoot适配器以及SpringAdapter。一切都很好。

我现在偶然发现了这个页面https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-Jersey-2.X-Project-Setup-1.5

并开始使用swagger-core包为我的API生成swagger.json文件。 swagger-jersey2依赖项会在swagger.json这样的链接中公开http://localhost:8080/swagger.json文件:SecurityConfig。但是,我无法公开访问该网址,因为密钥泄露会阻止它。

在我的public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { //....other code above @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.csrf().disable().authorizeRequests().antMatchers("*").permitAll(); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } } 课程中,我有以下内容:

swagger.json

如何更改配置以允许访问Interface而无需传递承载令牌? (我测试了使用不记名令牌进行访问,但它工作正常,但我需要在没有持票人令牌的情况下访问它)

1 个答案:

答案 0 :(得分:1)

我最后通过以下方式解决了这个问题:

  1. 创建新的PubliResource注释。
  2. 在启动时使用PublicResource注释的类创建了一个列表,然后显式地让请求投放在这些类上,即使没有检测到令牌。
  3. import java.lang.annotation.*;
    
    /***
     * Used for marking a class accessible to a non-authorized user for
     * @GET, @PUT, @POST, and @DELETE annotations
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Inherited
    public @interface PublicResource {
    
    }
    

    然后我做了一个AuthenticationTokenProcessingFilter bean。

    import org.keycloak.KeycloakSecurityContext;
    import org.keycloak.adapters.AdapterUtils;
    import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
    import org.keycloak.representations.AccessToken;
    import org.reflections.Reflections;
    import org.reflections.util.ConfigurationBuilder;
    import org.springframework.web.filter.GenericFilterBean;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.ws.rs.*;
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.Set;
    import java.util.TreeSet;
    
    public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
    
        private static Set<String> nonAuthenticationWhiteListSet = new TreeSet<>();
    
        static {
            ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
            configurationBuilder.forPackages("com.package.code.resources"); //IMPORTANT
            Reflections reflections = new Reflections(configurationBuilder);
            Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(PublicResource.class);
            for (Class<?> annotatedClass : annotated) {
                if (!annotatedClass.isAnnotationPresent(Path.class)) {
                    continue;
                }
                String classPath = annotatedClass.getAnnotation(Path.class).value();
    
                for (Method method : annotatedClass.getDeclaredMethods()) {
                    String fullPath = classPath;
                    if (method.isAnnotationPresent(GET.class)
                            || method.isAnnotationPresent(POST.class)
                            || method.isAnnotationPresent(PUT.class)
                            || method.isAnnotationPresent(DELETE.class)) {
                        if (method.isAnnotationPresent(Path.class)) {
                            fullPath += method.getDeclaredAnnotation(Path.class).value();
                        }
    
                        fullPath = fullPath.replaceAll("\\{.*\\}", "[^/]+");
                        nonAuthenticationWhiteListSet.add(fullPath);
                    }
                }
            }
        }
    
        public AuthenticationTokenProcessingFilter() {}
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
            if (!(request instanceof HttpServletRequest)) {
                throw new RuntimeException("Expecting a HTTP request");
            }
    
            RefreshableKeycloakSecurityContext context = (RefreshableKeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
    
            if (context == null) {
                handleNoSecurityContext(request, response, chain);
                return;
            }
    
            chain.doFilter(request, response);
        }
    
        private void handleNoSecurityContext(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            String path = httpRequest.getPathInfo();
    
            if (isPublicResource(path)) {
                chain.doFilter(request, response);
                return;
            }
    
            ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
    
        private boolean isPublicResource(String path) {
            for (String regex : nonAuthenticationWhiteListSet) {
                if (path.matches(regex)) {
                    return true;
                }
            }
    
            return false;
        }
    }
    

    然后在我的主要课程中我做了这个,所以它会被注册为过滤器。

    @Configuration
    @EnableAutoConfiguration(exclude = {FallbackWebSecurityAutoConfiguration.class, SpringBootWebSecurityConfiguration.class, DataSourceAutoConfiguration.class})
    @ComponentScan
    @EnableAsync
    public class MyApi {
    
        //...other code plus the main method
    
        @Bean
        public AuthenticationTokenProcessingFilter authFilter() {
            return new AuthenticationTokenProcessingFilter();
        }
    }