kakawait / uaa-behind-zuul-sample

Spring AuthorizationServer load balanced behind Zuul
320 stars 156 forks source link

A question about using curl to access resource server? #13

Open wldandan opened 7 years ago

wldandan commented 7 years ago

Hi kakawait Thanks for the awesome sample. It helps me a lot about understanding zuul with security.

Now I have one problem about the sample, could you help me? thank you very much.

a). I can get token from zuul server by using curl, that is result expected.

curl --insecure -H "Authorization: Basic $(echo -n 'acme:acmesecret' | base64)" http://localhost:8765/uaa/oauth/token -d grant_type=password -d username=user -d password=password -v the token is eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Nzc4NzEwMDAsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIiwiUk9MRV9VU0VSIl0sImp0aSI6IjhiODdhNTNhLWU0MmEtNDkyYS05ZWMxLTY2YWE4NmU4ZDkxOCIsImNsaWVudF9pZCI6ImFjbWUiLCJzY29wZSI6WyJvcGVuaWQiXX0.TWef-agKnnKaWDiW44ROxIseL0tVG7TXZWYTiMIV4RoKVLXXCFsFb5l24gRjxRCZAhmyB-5N6l-axLflgvKEbLrPHYJj054nEE9bi_I5mi9b2y4Z2mII6sfMkjeTAlrlyGKN-s-oU-nO2tws-vKCAYtycKmTL_xsNMH3Qmm1-4haG9SZIFq4YJkC93RvBDCGxit7jV62XrEyDj1atPWQ8QJeJmp0I8z04sVKlxulzRYpkkFMKjvWAx5lgnx5B6m-0jhXgi8g4kejUpZK9yojDTNRKMkYJuhPyTVCLsakpTl3vwOBZ5xpO-o2mgd9yn-zvjo7P1TK1rFys9xvREu2mw

b). then, when I request zuul server by the command

curl -v -H 'Content-type: application/json' -H 'Authorization: Bearer ACCESS_TOKEN' http://localhost:8765/dummy the result always show " X-Frame-Options: DENY < Location: http://localhost:8765/login"

for the case, how can I access the http://localhost:8765/dummy by curl with token rather than using browser? suppose that I have a client which can send api request to zuul server.

wldandan commented 7 years ago
login-prompt
kakawait commented 7 years ago

@wldandan good point. By using @EnableOauth2Sso on Zuul (here api-gateway) you transform api-gateway as token relay but not as resource server.

So when fetching microservices using Authorization header api-gateway will not try to read this header, so it will consider you as not auth.

You should transform your api-gateway as ResourceServer (@EnableResourceServer) in order to add OAuth2AuthenticationProcessingFilter (in addition to OAuth2ClientAuthenticationProcessingFilter used by SSO) that will handle Authorization header.

I just created a branch to POC your needs. I have to polish it before merging on master so be careful if you choose to copy past.


In addition you could up-vote http://stackoverflow.com/questions/37059128/spring-1-3-3-enableresourceserver-and-enableoauth2sso-at-the-same-time (not my question) and up-vote https://github.com/spring-projects/spring-security-oauth/issues/683 because there is a trick to do for combining @EnableResourceServer and @EnableOauth2Sso

kakawait commented 7 years ago

@wldandan does the solution works for you ?

wldandan commented 7 years ago

@kakawait Hey kakawait, thanks for your solution. I did not get chance to verify it. I will give it a try and respond you as soon as possible. :) Thanks a lot.

wldandan commented 7 years ago

@kakawait Hey kakawait, I have verified the solution in 'resource-server' branch. It does work, thanks for your work, it is awesome :)

kakawait commented 7 years ago

Cool, I will keep issue open until I polish the code

mavogel commented 7 years ago

@kakawait first of all: awesome work! It helps me a lot.

Problem It have the same issue like wldadan with adding a client who uses the zuul proxy, but I'd like to have multiple users accessing the resourced by the grand_type=password

  1. I removed the user and password property from application.yml of the uaa-service
  2. I added more users for authentication
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
    .inMemoryAuthentication() 
    .withUser("user").password("password").roles("USER")
    .and()
    .withUser("admin").password("admin").roles("ADMIN");
    //  auth.parentAuthenticationManager(authenticationManager);
    }
  3. It works fine in the browser with admin and user credentials. So authorization is fine there.

But a call from another client like

curl --insecure -H "Authorization: Basic $(echo -n 'acme:acmesecret' | base64)" http://localhost:8765/uaa/oauth/token -d grant_type=password -d username=admin -d password=admin -v

results in

*   Trying ::1...
* Connected to localhost (::1) port 8765 (#0)
> POST /uaa/oauth/token HTTP/1.1
> Host: localhost:8765
> User-Agent: curl/7.43.0
> Accept: */*
> Authorization: Basic YWNtZTphY21lc2VjcmV0
> Content-Length: 49
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 49 out of 49 bytes
< HTTP/1.1 400
< Set-Cookie: JSESSIONID=85A5C7AD4F55891EF9F0EA56BCF4087A;path=/;HttpOnly
< Set-Cookie: XSRF-TOKEN=2b264f92-1cb6-4a1c-9f99-6fc25f66546c;path=/;Secure
< X-Application-Context: api-gateway:docker:8765
< Date: Mon, 09 Jan 2017 12:12:13 GMT
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Connection: close
<
* Closing connection 0
{"error":"invalid_grant","error_description":"Bad credentials"}

With the log message:

o.s.s.a.dao.DaoAuthenticationProvider   : User 'admin' not found

The same happens with user and password.

Possible idea to solve: My idea now (which I didn't manage to implement) to share the authentication manager of the WebSecurityAdapter with the AuthorizationServerConfigurerAdapter.

 @Override
 public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {           
  endpoints
  // TODO here: use the authenticationManager from the other class 
    .authenticationManager(authenticationManager)
    .accessTokenConverter(jwtAccessTokenConverter());
}

I made a branch for it in my fork.

UPDATE: Added a question on stackoverflow as well.

=> Do you know how to do that? Or is there another (better) way? Thanks in advance! (maybe it's a easy task, but I'm new to the Spring/Cloud world :) )

UPDATE2: Got it working on my fork =) I'll send you a PR later

mavogel commented 7 years ago

The solution was

  1. Exposing the AuthenticationManager from LoginConfiguration

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
    }
  2. Injecting it in the AuthorizationServerConfiguration

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;
  3. I also added a protected resource only accessible for the ROLE_ADMIN in the DummyServiceApplication which gets an ACCESS_DENIED for the user:

    @PreAuthorize("#oauth2.hasScope('openid') and hasRole('ROLE_ADMIN')")
    @RequestMapping(value = "secret", method = RequestMethod.GET)
    @ResponseBody
    public String helloSecret(Principal principal) {
    return principal == null ? "Hello anonymous" : "S3CR3T  - Hello " + principal.getName();
    }

Props to PiggyMetrics who brought me up to the solution and you guys for this awesome work!

kakawait commented 7 years ago

Sorry I didn't find time to read your question. I will check your question and solution

But thank you for the solution

kentoj commented 7 years ago

This solution in the resource-server branch worked for me also. Is there a reason it wouldn't be merged to master? I will submit a PR for this if it is wanted in master.

mavogel commented 7 years ago

Yep @kakawait, I think you could merge it into the master branch because the resource-server branch is an enhancement of the master atm.

kentoj commented 7 years ago

@mavogel He accepted my PR: https://github.com/kakawait/uaa-behind-zuul-sample/pull/23