Spring Boot Keycloak - Bearer: How to solve NOT_AT

2019-04-14 11:53发布

问题:

  • Angular v. v4.0.2
  • Spring Boot v. 1.5.2.RELEASE
  • Keycloak v.2.4.0.Final (will upgrade later)

I read this mail converstion about the same problem: http://keycloak-user.88327.x6.nabble.com/keycloak-user-NOT-ATTEMPTED-bearer-only-error-while-trying-to-access-server-from-client-td927.html and this http://slackspace.de/articles/authentication-with-spring-boot-angularjs-and-keycloak/

I use the following http service for making authorized requests:

@Injectable()
export class AuthHttpService extends Http {
  constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private authService: AuthService) {
    super(backend, defaultOptions);
  }

  private setToken(options: RequestOptionsArgs) {
    if (options == null || AuthService.auth == null || AuthService.auth.authz == null || AuthService.auth.authz.token == null) {
      console.log("Need a token, but no token is available, not setting bearer token.");
      return;
    }
    console.log(AuthService.auth.authz.token);
    options.headers.set('Authorization', 'Bearer ' + AuthService.auth.authz.token);
  }

  private configureRequest(f:Function, url:string | Request, options:RequestOptionsArgs, body?: any):Observable<Response> {
    let tokenPromise:Promise<string> = this.authService.getToken();
    let tokenObservable:Observable<string> = Observable.fromPromise(tokenPromise);
    let tokenUpdateObservable:Observable<any> = Observable.create((observer) => {
      if (options == null) {
        let headers = new Headers();
        options = new RequestOptions({ headers: headers });
      }

      this.setToken(options);
      observer.next();
      observer.complete();
    });
    let requestObservable:Observable<Response> = Observable.create((observer) => {
      let result;
      if (body) {
        result = f.apply(this, [url, body, options]);
      } else {
        result = f.apply(this, [url, options]);
      }

      result.subscribe((response) => {
        observer.next(response);
        observer.complete();
      }, (err) => observer.error(err));
    });

    return <Observable<Response>>Observable
      .merge(tokenObservable, tokenUpdateObservable, requestObservable, 1) 
      .filter((response) => response instanceof Response);
  }

...

Application.properties

The token is correctly logged.

server.port = 8081

keycloak.realm = apprealm
keycloak.auth-server-url = http://localhost:8080/auth
keycloak.ssl-required = external
keycloak.resource = appbackend
keycloak.bearer-only = true
keycloak.credentials.secret = ...

keycloak.securityConstraints[0].securityCollections[0].name = secure
keycloak.securityConstraints[0].securityCollections[0].authRoles[0]=frontenduser
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /r/s/*

logging.level.org.keycloak=DEBUG

The user which I use in the frontend has that role.

Error in backend

2017-04-22 15:40:00.517 DEBUG 14088 --- [nio-8081-exec-1] o.k.adapters.PreAuthActionsHandler       : adminRequest http://localhost:8081/r/s/e/p/m
2017-04-22 15:40:00.540 DEBUG 14088 --- [nio-8081-exec-1] o.k.a.a.ClientCredentialsProviderUtils   : Using provider 'secret' for authentication of client 'appbackend'
2017-04-22 15:40:00.543 DEBUG 14088 --- [nio-8081-exec-1] o.k.a.a.ClientCredentialsProviderUtils   : Loaded clientCredentialsProvider secret
2017-04-22 15:40:00.545 DEBUG 14088 --- [nio-8081-exec-1] o.k.a.a.ClientCredentialsProviderUtils   : Loaded clientCredentialsProvider jwt
2017-04-22 15:40:00.552 DEBUG 14088 --- [nio-8081-exec-1] o.k.a.a.ClientCredentialsProviderUtils   : Loaded clientCredentialsProvider secret
2017-04-22 15:40:00.553 DEBUG 14088 --- [nio-8081-exec-1] o.k.a.a.ClientCredentialsProviderUtils   : Loaded clientCredentialsProvider jwt
2017-04-22 15:40:00.625 DEBUG 14088 --- [nio-8081-exec-1] o.keycloak.adapters.KeycloakDeployment   : resolveUrls
2017-04-22 15:40:00.631 DEBUG 14088 --- [nio-8081-exec-1] o.k.adapters.KeycloakDeploymentBuilder   : Use authServerUrl: http://localhost:8080/auth, tokenUrl: http://localhost:8080/auth/realms/apprealm/protocol/openid-connect/token, relativeUrls: NEVER
2017-04-22 15:40:00.662 DEBUG 14088 --- [nio-8081-exec-1] o.k.adapters.RequestAuthenticator        : NOT_ATTEMPTED: bearer only
2017-04-22 15:40:00.681  INFO 14088 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-04-22 15:40:00.681  INFO 14088 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-04-22 15:40:00.723  INFO 14088 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 42 ms
2017-04-22 15:40:08.560 DEBUG 14088 --- [nio-8081-exec-2] o.k.adapters.PreAuthActionsHandler       : adminRequest http://localhost:8081/r/s/e/p/m
2017-04-22 15:40:08.560 DEBUG 14088 --- [nio-8081-exec-2] o.k.adapters.RequestAuthenticator        : NOT_ATTEMPTED: bearer only

Edit http

HTTP/1.1 401
Cache-Control: private
Expires: Thu, 01 Jan 1970 01:00:00 CET
WWW-Authenticate: Bearer realm="apprealm"
Access-Control-Allow-Origin: http://localhost:4200
Vary: Origin
Access-Control-Allow-Methods: GET,POST,PUT,DELETE
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1800
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Content-Length: 0
Date: Sun, 23 Apr 2017 17:04:07 GMT

Edit 2: http raw request

OPTIONS http://localhost:8081/r/p/main HTTP/1.1
Host: localhost:8081
Connection: keep-alive
Access-Control-Request-Method: PUT
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Access-Control-Request-Headers: authorization,content-type
Accept: */*
Referer: http://localhost:4200/b
Accept-Encoding: gzip, deflate, sdch
Accept-Language: nl-NL,nl;q=0.8,en-US;q=0.6,en;q=0.4

What could be the problem?