robert trudel
robert trudel

Reputation: 5779

Access issue with restTemplatebuider

I use spring boot and spring security.

In my rest controller, i have one method

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
@EnableWebSecurity
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

    @Autowired
    private RESTAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private RESTAuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private RESTAuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
    }

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

        http.authorizeRequests()
                .antMatchers("/login").permitAll()
                .antMatchers("/rest/**").authenticated();

        http.csrf().disable();
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);

        http.formLogin().successHandler(authenticationSuccessHandler);
        http.formLogin().failureHandler(authenticationFailureHandler);
        http.logout().logoutUrl("/logout");
        http.logout().logoutSuccessUrl("/");

        // CSRF tokens handling
        //http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
    }

}

@RequestMapping(value = "/rest")
@RestController
public class MemberController {

    @GetMapping(value = "/members/card")
    public boolean hasCardIdValid(@RequestBody String cardId) {
        return memberService.hasCardIdValid(cardId);
    }
}

In another spring boot application, i try to call hasCreditCard method

@Autowired
    public GlobalScan(RestTemplateBuilder restTemplateBuilder,  @Value("${main.server.url}") String mainServerUrl,   @Value("${commerce.username}") String commerceUsername, @Value("${commerce.password}")String commercePassword) {
        this.restTemplate = restTemplateBuilder.basicAuthorization(commerceUsername, commercePassword).rootUri(mainServerUrl).build();
    }

I do a call with this code

Map<String, String> vars = new HashMap<String, String>();
vars.put("cardId", cardId);

boolean accessAllowed = restTemplate.getForObject("/rest/members/card/" , Boolean.class, vars);

i get this message

2016-11-02 16:20:50.601 DEBUG 7139 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/rest/members/card/'; against '/login'
2016-11-02 16:20:50.601 DEBUG 7139 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/rest/members/card/'; against '/rest/**'
2016-11-02 16:20:50.601 DEBUG 7139 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /rest/members/card/; Attributes: [authenticated]
2016-11-02 16:20:50.601 DEBUG 7139 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2016-11-02 16:20:50.602 DEBUG 7139 --- [nio-8080-exec-1] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@3d300693, returned: -1
2016-11-02 16:20:50.602 TRACE 7139 --- [nio-8080-exec-1] ationConfigEmbeddedWebApplicationContext : Publishing event in org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@2bdd8394: org.springframework.security.access.event.AuthorizationFailureEvent[source=FilterInvocation: URL: /rest/members/card/]
2016-11-02 16:20:50.606 DEBUG 7139 --- [nio-8080-exec-1] o.s.s.w.a.ExceptionTranslationFilter     : Access is denied (user is anonymous); redirecting to authentication entry point

org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]

On my main app, i use a form login to connect to the app, like you can see in the spring security config.

From my other app how to call a ws without form login?

tried to call ws with this

 final RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout * 1000).setConnectionRequestTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build();

 final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
 credentialsProvider.setCredentials(new AuthScope("http://localhost", 8080, AuthScope.ANY_REALM), new UsernamePasswordCredentials("bob", "smith"));
 final CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).setDefaultCredentialsProvider(credentialsProvider).build();

 final ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(client);
 RestTemplate restTemplate = new RestTemplate(requestFactory);

 ResponseEntity<MemberDto> member = restTemplate.getForEntity("http://localhost:8080/rest/members/1", MemberDto.class);

result: http://pastebin.com/psNKPUtM

Upvotes: 2

Views: 1271

Answers (3)

sura2k
sura2k

Reputation: 7517

  1. Add BASIC auth to your existing configuration

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            ....
        .and()
        .formLogin() // <------ Keep this
            ....
        .and()
        .httpBasic() // <------ Add BASIC Auth
        .and()
            .....;
    }
    
  2. Write a simple client using RestTemplate

    public static void main(String[] args) {
        RestTemplate rest = new RestTemplate(new ArrayList(Arrays.asList(new MappingJackson2HttpMessageConverter())));
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Basic YOUR_BASE64_ENCODED_CREDENTIALS");
        MediaType applicationJson = new MediaType("application","json");
        headers.setContentType(applicationJson);
        headers.setAccept(Collections.singletonList(applicationJson));
        ResponseEntity<YourResponseObject> resp = rest.exchange("http://URL/rest/yourendpoint", HttpMethod.GET, new HttpEntity<String>("parameters", headers), YourResponseObject.class);
    
        System.out.println(resp.getBody());
    

    }

YOUR_BASE64_ENCODED_CREDENTIALS => If use use Java 8 you can use java.util.Base64, otherwise use commons-codec to do that or something else.

Update: Spring boot reference: http://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#jc-httpsecurity

Upvotes: 0

deekay
deekay

Reputation: 681

You are configuring formLogin() but you try to use an http Basic Auth in your RestTemplate. For requests via http REST I suggest that you change your configuration to use basic auth:

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

    http.authorizeRequests()
            .antMatchers("/login").permitAll()
            .antMatchers("/rest/**").authenticated();

    http.csrf().disable();
    http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);

    http.httpBasic();
    http.logout().logoutUrl("/logout");
    http.logout().logoutSuccessUrl("/");

    // CSRF tokens handling
    //http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
}

If you need both I think you can configure both.

Upvotes: 1

Vadim Kirilchuk
Vadim Kirilchuk

Reputation: 3542

The default password in spring security is configured by the following property: security.user.password=YOUR_PASSWORD This should be done in your main app where you have security configuration and which you are trying to call.

You can change the password by providing a security.user.password. This and other useful properties are externalized via SecurityProperties (properties prefix "security").

So, if you didn't update the property to match the password in commerce.password spring will reject your authorization and you will get 401. By default it uses some random generated password it prints to the console during the start. documentation

Upvotes: 1

Related Questions