简体   繁体   中英

spring boot oauth2 feign allow anonymous requests

I have a client service that distribute a single page application. All the requests from the single page app pass through the client service that uses proxies (Feign) to redirect the calls.

I'd like to allow anonymous calls but I'm not able to do that with my current configuration.

So to make it simpler I have three services: a client, an oauth2 server and an oauth2 resource server. The oauth2 server is also a resource server.

The client is connected to the oauth2-server with this configuration

security:
    oauth2:
        client:
            clientId: autorisation_code_client
            clientSecret: *******
            accessTokenUri: https://localhost:****/oauth2-server/oauth/token
            userAuthorizationUri: https://localhost:****/oauth2-server/oauth/authorize
            #tokenCheckUri: https://localhost:****/oauth2-server/oauth/check_token
        resource:
            userInfoUri: https://localhost:****/oauth2-server/me

Here is the WebSecurityConfigurerAdapter class of the client, when an user try to access to the login path he's redirected to the oauth2-server to authenticate himself.

@Override
public void configure(HttpSecurity http) throws Exception {

    http.antMatcher("/**")
            .authorizeRequests()
            .antMatchers(
                    "/",
                    "/index.html",
                    "/login**",
                    "/logout**",
                    //resources
                    "/assets/**",
                    "/static/**",
                    "/*.ico",
                    "/*.js",
                    "/*.json").permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .csrf().csrfTokenRepository(csrfTokenRepository())
            .and()
            .addFilterAfter(csrfHeaderFilter(), SessionManagementFilter.class);
}

The feign proxy used by the client, I'd like to configure the oauth2-server/user/like/*** path to be accessible by anonymous users.

@RestController
@FeignClient(name = "oauth2-server", url = "https://localhost:****")
public interface ProxyOauth2Server {

    @GetMapping(value = "oauth2-server/user/like/{name}")
    ResponseEntity<?> getUserLikeName(@PathVariable("name") String name);
}

To transmit the token through Feign I have this configuration in the client Main class.

@EnableConfigurationProperties
@SpringBootApplication
@EnableFeignClients("com.tutosharing.client.proxies")
public class ClientUiApplication {

    @Autowired
    private SecurityPropertiesConfig config;

    @Bean
    protected OAuth2ProtectedResourceDetails resource() {

        AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
        resource.setAccessTokenUri(config.getAccessTokenUri());
        resource.setUserAuthorizationUri(config.getUserAuthorizationUri());
        resource.setClientId(config.getClientId());
        resource.setClientSecret(config.getClientSecret());

        return resource;
    }

    @Bean
    public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext,
                                                            OAuth2ProtectedResourceDetails resource) {
        return new OAuth2FeignRequestInterceptor(oauth2ClientContext, resource);
    }

}

Now the oauth2 server which also serves as a resource server

@SpringBootApplication
@EnableResourceServer
@EnableAuthorizationServer
@EnableConfigurationProperties
public class AuthorizationServerApplication {}

the oauth2 server WebSecurityConfigurerAdapter class

public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception { 

        http.requestMatchers()
                .antMatchers("/",
                        "/login",
                        "/login.do",
                        "/oauth/authorize**")
                .and()
                .authorizeRequests()
                .antMatchers(
                        "/",
                        "/login",
                        "/login.do")
                .permitAll()
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login.do")
                .usernameParameter("*********")
                .passwordParameter("*********")
                .and()
                .userDetailsService(userDetailsServiceBean())
                .requiresChannel()
                .anyRequest()
                .requiresSecure();
    }

}

The Rest controler method I'd like to allow to anonymous users

@RestController
public class UserRControllerRest {

    @GetMapping({"/user/like/{name}"})
    @JsonView(View.SimpleUser.class)
    @PreAuthorize("hasRole('ROLE_USER')")
    public ResponseEntity getUserLikeName(@PathVariable String name) {

        Set<AuthUser> users = this.userRepository.findByNameLike(name);

        return new ResponseEntity(users, HttpStatus.OK);
    }
}

If I configure the Rest method with @PreAuthorize("hasRole('ROLE_ANONYMOUS')")

and the WebSecurityConfigurerAdapter like this

  http.requestMatchers()
                .antMatchers(
                        ...
                        "/user/like/**",
                        ...)
                .and()
                .authorizeRequests()
                .antMatchers("/user/like/**")
                .anonymous()
                ...
        }
    } // @formatter:on

I'm able to get an answer if I contact directly the oauth2-server with Postman, but not if I pass through the client service that uses Feign, I'm always redirected to the login page.

So how can I allow anonymous request Through Feign?

I've found a solution but I'm not sure this is the Best way. So if you have another solution you are welwome.

So far I used this configuration to get the Token from the oauth2-server anytime an user made a request from the client through Feign.

@Bean
protected OAuth2ProtectedResourceDetails resource() {

    AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();

    resource.setAccessTokenUri(config.getAccessTokenUri());
    resource.setUserAuthorizationUri(config.getUserAuthorizationUri());
    resource.setClientId(config.getClientId());
    resource.setClientSecret(config.getClientSecret());

    return resource;
}

@Bean
public RequestInterceptor oauth2FeignRequestInterceptor(@Qualifier("oauth2ClientContext") OAuth2ClientContext oauth2ClientContext,
                                                        OAuth2ProtectedResourceDetails resource) {

    return new OAuth2FeignRequestInterceptor(oauth2ClientContext, resource);
}

The problem with that configuration is that anytime I made a request with Feign a request is sent to the oauth2-client to the /oauth/authorize endpoint. But if the user is not connected it fails, so an unauthenticated user cannot make any request from the client service.

So I used another RequestInterceptor.

@Bean
public RequestInterceptor requestTokenBearerInterceptor() {

    return requestTemplate -> {


         Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

      if (!principal.equals("anonymousUser")) {

            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
                    SecurityContextHolder.getContext().getAuthentication().getDetails();

            requestTemplate.header("Authorization", "bearer " + details.getTokenValue());

        }
    };
}

This way the token that the client service already has, once the user is connected, is added to the request whitout making another request to the /oauth/authorize endpoint. I think the token is sent with every request, I don't think it's a good practice for security matters.

Also in the WebSecurityConfigurerAdapter classes of the client-server I need to add the path so that it is accessible to non-connected users

 http.antMatcher("/**")
            .authorizeRequests()
            .antMatchers(
                    "/oauth2-server/user/like/**",
                   ...)
            .permitAll()
            .and()
            .authorizeRequests()
            .anyRequest()
            .authenticated()
            ...;

same for the oauth2-server

 http.antMatcher("/**")
            .authorizeRequests()
            .antMatchers(
                    "/user/like/**",
                   ...)
            .permitAll()
            .and()
            .authorizeRequests()
            .anyRequest()
            .authenticated()
            ...;

With that configuration an unauthenticated user can make a request to an unprotected endpoint.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM