Herval NGANYA
Herval NGANYA

Reputation: 303

How to allow anonymous access to endpoint

I have a spring cloud architecture and I can't allow anonymous access to an endpoint.

Here is my code:

Gateway =============================

Application:
    @SpringBootApplication
    @EnableZuulProxy
    @EnableEurekaClient
    @EnableResourceServer
    public class GatewayApplication {

        public static void main(String[] args) {
            SpringApplication.run(GatewayApplication.class, args);
        }

        @Bean
        public FilterRegistrationBean<?> corsFilter() {
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            CorsConfiguration config = new CorsConfiguration();
            config.setAllowCredentials(true);
            config.addAllowedOrigin("*");
            config.addAllowedHeader("*");
            config.addAllowedMethod("*");
            source.registerCorsConfiguration("/**", config);
            FilterRegistrationBean<?> bean = new FilterRegistrationBean<>(new CorsFilter(source));
            bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
            return bean;
        }
    }

Pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.geminiald</groupId>
    <artifactId>gateway</artifactId>
    <version>1.0.0</version>
    <name>gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

bootstrap.properties:

spring.cloud.config.name=gateway
spring.cloud.config.discovery.service-id=config
spring.cloud.config.discovery.enabled=true

eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/

application.properties:

security.oauth2.resource.user-info-uri=http://localhost:8083/user

Furthermore, I have an auth-service ==================== Application:

@SpringBootApplication
@EnableEurekaClient
@EnableAuthorizationServer
@EnableResourceServer
@EntityScan(basePackages = { "com.geminiald.authservice.models" })
@EnableJpaRepositories(basePackages = { "com.geminiald.authservice.repositories" })
public class AuthServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(AuthServiceApplication.class, args);
    }
}

@Configuration
public class AuthorizationServerConfig
                extends AuthorizationServerConfigurerAdapter {

    private BCryptPasswordEncoder passwordEncoder;

    @Autowired
    public AuthorizationServerConfig(
                    @Lazy BCryptPasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    @Autowired
    private AuthSettings settings;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security)
        throws Exception {
        security.checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients)
        throws Exception {
        clients.inMemory().withClient(settings.getClient())
                        .authorizedGrantTypes(
                                        settings.getAuthorizedGrantTypes())
                        .authorities(settings.getAuthorities())
                        .scopes(settings.getScopes())
                        .resourceIds(settings.getResourceIds())
                        .accessTokenValiditySeconds(settings
                                        .getAccessTokenValiditySeconds())
                        .secret(passwordEncoder.encode(settings.getSecret()));
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
        throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }
}

@Configuration
public class AuthenticationMananagerProvider
                extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    private BCryptPasswordEncoder encoder;

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Autowired
    public void authenticationManager(AuthenticationManagerBuilder builder,
        UserRepository repository) throws Exception {

        builder.userDetailsService(userDetailsService).passwordEncoder(encoder);
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        BCryptPasswordEncoder bCryptPasswordEncoder =
                        new BCryptPasswordEncoder();
        return bCryptPasswordEncoder;
    }

    @Bean
    public FilterRegistrationBean<?> corsFilter() {
        UrlBasedCorsConfigurationSource source =
                        new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<?> bean =
                        new FilterRegistrationBean<>(new CorsFilter(source));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }

}

application.properties:

# H2 Database configuration
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.initialization-mode=always

bootstrap.properties:

spring.cloud.config.name=auth-service
spring.cloud.config.discovery.service-id=config
spring.cloud.config.discovery.enabled=true

eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/

I have a Dc-tool-box-service: Application ==========

@SpringBootApplication
@EnableEurekaClient
@EnableResourceServer
@EntityScan(basePackages = { "com.geminiald.dctoolbox.models" })
@EnableJpaRepositories(basePackages = { "com.geminiald.dctoolbox.repositories" })
public class DcToolBoxServiceApplication {

    public static void main(String[] args) throws IOException {
        SpringApplication.run(DcToolBoxServiceApplication.class, args);
    }
}

bootstrap.properties:

spring.cloud.config.name=dc-tool-box-service
spring.cloud.config.discovery.service-id=config
spring.cloud.config.discovery.enabled=true

eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/

application.properties:

# H2 Database configuration
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.initialization-mode=always

spring.servlet.multipart.max-file-size=-1
spring.servlet.multipart.max-request-size=-1

security.oauth2.resource.user-info-uri=http://localhost:8083/user

and there, you can see all *.properties file from the configuration service: auth-service:

spring.application.name=auth-service
server.port=8083

eureka.client.region = default
eureka.client.registryFetchIntervalSeconds = 5
eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/

gateway:

spring.application.name=gateway
server.port=8000

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000
zuul.host.connect-timeout-millis= 15000
zuul.host.socket-timeout-millis= 60000
ribbon.ReadTimeout= 60000
ribbon.ConnectTimeout= 60000

eureka.client.region = default
eureka.client.registryFetchIntervalSeconds = 5

zuul.routes.discovery.path=/discovery/**
zuul.routes.discovery.sensitive-headers=Set-Cookie,Authorization
zuul.routes.discovery.url=http://localhost:8082
hystrix.command.discovery.execution.isolation.thread.timeoutInMilliseconds=600000

zuul.routes.auth-service.path=/auth-service/**
zuul.routes.auth-service.sensitive-headers=Set-Cookie
hystrix.command.auth-service.execution.isolation.thread.timeoutInMilliseconds=600000

zuul.routes.dc-tool-box-service.path=/dc-tool-box-service/**
zuul.routes.dc-tool-box-service.sensitive-headers=Set-Cookie
hystrix.command.dc-tool-box-service.execution.isolation.thread.timeoutInMilliseconds=600000

dc-tool-box-service:

spring.application.name=dc-tool-box-service
server.port=8086

eureka.client.region = default
eureka.client.registryFetchIntervalSeconds = 5
eureka.client.serviceUrl.defaultZone=http://localhost:8082/eureka/

In order to do that, I have two endpoints in the dc-tool-box-service: /persons/signup and /dossiers. I would like to keep the security on /dossiers, but /persons/signup should be anonymous. So anybody can access without authentication.

That is what I in gateway:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
        .anyRequest().authenticated()
        .antMatchers("/dc-tool-box-service/persons/signup").anonymous();
    }
}

In in my Postman, I can access the /dossiers using my token, but I get the message:

{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource"
}

when I try to access /persons/signup without providing a property Authorization in my header.

Could someone help me please?! I would be thankful.

Upvotes: 3

Views: 1881

Answers (1)

Vijay Nandwana
Vijay Nandwana

Reputation: 2634

You need to use permitAll() instead of anonymous().

Replace: .antMatchers("/dc-tool-box-service/persons/signup").anonymous();

With: .antMatchers("/dc-tool-box-service/persons/signup").permitAll();

This will authorize all users, anonymous and logged in.

Problem with anonymous() is that, only users that have ROLE_ANONYMOUS would able to access that endpoint.

EDIT: Security order matters: It still doesn't work because your first security constraint is that any request to your application should be authenticated, then you have configured to allow /signup request. Change the order of these permissions.

http.authorizeRequests()
    .antMatchers("/dc-tool-box-service/persons/signup").permitAll()
    .and()
    .authorizeRequests()
    .anyRequest().authenticated();

Upvotes: 2

Related Questions