Unified certification center Oauth2 certification pit

In the previous article Springcloud Oauth2 HA In this paper, the authentication and authorization of unified authentication based on Oauth2 are realized. In the configuration, we can see:

cas-server-url: http://CAS server service # is configured as HA address here

security:
  oauth2: #Configuration corresponding to CAS server
    client:
      client-id: admin-web
      client-secret: admin-web-123
      user-authorization-uri: ${cas-server-url}/oauth/authorize #It is required by the authorization code authentication method
      access-token-uri: ${cas-server-url}/oauth/token #It is the interface for obtaining token required by password mode
    resource:
      loadBalanced: true
      id: admin-web
      user-info-uri: ${cas-server-url}/api/user #Specifies the URI of user info
      prefer-token-info: false

The URL configuration here is based on k8s Service to achieve load balancing and high availability. But let's next analyze the user info URI. The principle of user info URI is to obtain user information through URL by binding the authentication information Principal through formal parameters after authorization server authentication. Of course, it also has supporting UserInfoTokenService and so on.

However, there are some problems when the client obtains user permissions. For example, the Web side requests an interface of the consumer side:

/**
 * Returns all discovered services
 * @author Damon 
 * @date 2021 November 2, 2008 8:18:44 PM
 * @return
 *
 */
@PreAuthorize("hasRole('admin')")
    @GetMapping(value = "/getService")
    public String getService(){
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        HttpEntity<String> formEntity = new HttpEntity<String>(null, headers);
        String body = "";
        try {
            ResponseEntity<String> responseEntity = restTemplate.exchange("http://cas-server/api/v1/user",
                    HttpMethod.GET, formEntity, String.class);
            if (responseEntity.getStatusCodeValue() == 200) {
                return "ok";
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return body;
    }

In this interface, we control permissions by adding @ PreAuthorize("hasRole('admin ')). Only admin users can access the interface.

Let's first request the login interface of the authentication center to obtain the token:

After we get the token, we request this interface, and we will find:

Note that it is not authenticated. Let's look again: it is found that when requesting this interface, the consumer then requests the interface of the authentication center:

2021-11-03 15:59:09.385 DEBUG 127896 --- [io2-2001-exec-4] org.springframework.web.HttpLogging      : HTTP GET http://cas-server/auth/user
2021-11-03 15:59:09.389 DEBUG 127896 --- [io2-2001-exec-4] org.springframework.web.HttpLogging      : Accept=[application/json, application/*+json]
2021-11-03 15:59:09.427 DEBUG 127896 --- [io2-2001-exec-4] org.springframework.web.HttpLogging      : Response 404 NOT_FOUND
2021-11-03 15:59:09.446 DEBUG 127896 --- [io2-2001-exec-4] o.s.w.c.HttpMessageConverterExtractor    : Reading to [org.springframework.security.oauth2.common.exceptions.OAuth2Exception]
2021-11-03 15:59:09.456  WARN 127896 --- [io2-2001-exec-4] o.s.b.a.s.o.r.UserInfoTokenServices      : Could not fetch user details: class org.springframework.web.client.HttpClientErrorException$NotFound, 404 : [{"timestamp":"2021-11-03T07:59:09.423+00:00","status":404,"error":"Not Found","message":"","path":"/auth/user"}]
2021-11-03 15:59:09.457 ERROR 127896 --- [io2-2001-exec-4] c.l.h.CustomAuthenticationEntryPoint     : invalid token,Please re authenticate access
{"data":"b34841b4-61fa-4dbb-9e2b-76496deb27b4","result":{"code":20202,"msg":"Not certified","status":401}}

However, for the 404 status code returned by the certification center, the unified exception EntryPoint will be prompted and an error will be reported: invalid token, please re authenticate and access. Thus, the information body is returned: {"data":"b34841b4-61fa-4dbb-9e2b-76496deb27b4","result":{"code":20202,"msg": "unauthenticated", "status":401}}.

Next analysis: why does the certification center return 404? See the certification center log:

2021-11-03 15:59:09.407 DEBUG 54492 --- [o2-2000-exec-15] o.s.web.servlet.DispatcherServlet        : GET "/auth/user", parameters={}
2021-11-03 15:59:09.409 DEBUG 54492 --- [o2-2000-exec-15] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"]
2021-11-03 15:59:09.413 DEBUG 54492 --- [o2-2000-exec-15] o.s.w.s.r.ResourceHttpRequestHandler     : Resource not found
2021-11-03 15:59:09.414 DEBUG 54492 --- [o2-2000-exec-15] o.s.web.servlet.DispatcherServlet        : Completed 404 NOT_FOUND
2021-11-03 15:59:09.422 DEBUG 54492 --- [o2-2000-exec-15] o.s.web.servlet.DispatcherServlet        : "ERROR" dispatch for GET "/error", parameters={}
2021-11-03 15:59:09.423 DEBUG 54492 --- [o2-2000-exec-15] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2021-11-03 15:59:09.424 DEBUG 54492 --- [o2-2000-exec-15] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Using 'application/json', given [application/json] and supported [application/json, application/*+json, application/json, application/*+json, application/json, application/*+json]
2021-11-03 15:59:09.424 DEBUG 54492 --- [o2-2000-exec-15] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Writing [{timestamp=Wed Nov 03 15:59:09 CST 2021, status=404, error=Not Found, message=, path=/auth/user}]
2021-11-03 15:59:09.426 DEBUG 54492 --- [o2-2000-exec-15] o.s.web.servlet.DispatcherServlet        : Exiting from "ERROR" dispatch, status 404

It is found that Oauth2 does not have such an interface: / auth/user. Finally, it is decided to write an interface to replace the native interface:

@GetMapping("/api/v1/user")
    public Authentication user(Map map, Principal user, Authentication auth) {
        //Get current user information
        logger.info("cas-server provide user: " + JSON.toJSONString(auth));
        return auth;
    }

After encapsulation and coverage, relevant configurations are directly configured on the consumer side:

cas-server-url: http://cas-server

security:
  path:
    ignores: /,/index,/static/**,/css/**, /image/**, /favicon.ico, /js/**,/plugin/**,/avue.min.js,/img/**,/fonts/**
  oauth2:
    client:
      client-id: rest-service
      client-secret: rest-service-123
      user-authorization-uri: ${cas-server-url}/oauth/authorize
      access-token-uri: ${cas-server-url}/oauth/token
    resource:
      loadBalanced: true
      id: rest-service
      prefer-token-info: false
      user-info-uri: ${cas-server-url}/api/v1/user
    authorization:
      check-token-access: ${cas-server-url}/oauth/check_token

At the same time, start the authentication center and the consumer. After continuing to obtain the token, request the interface:

At this time, it is found that 403 has no permission. Now we can add this permission to the user:

"authorities": [ { "authority": "ROLE_admin" }, { "authority": "admin" }

After adding, we found that the interface can be requested successfully:

{ "authorities": [ { "authority": "ROLE_admin" }, { "authority": "admin" } ], "details": { "remoteAddress": "0:0:0:0:0:0:0:1", "sessionId": null, "tokenValue": "b34841b4-61fa-4dbb-9e2b-76496deb27b4", "tokenType": "bearer", "decodedDetails": null }, "authenticated": true, "userAuthentication": { "authorities": [ { "authority": "ROLE_admin" }, { "authority": "admin" } ], "details": { "authorities": [ { "authority": "ROLE_admin" }, { "authority": "admin" } ], "details": { "remoteAddress": "169.254.200.12", "sessionId": null, "tokenValue": "b34841b4-61fa-4dbb-9e2b-76496deb27b4", "tokenType": "Bearer", "decodedDetails": null }, "authenticated": true, "userAuthentication": { "authorities": [ { "authority": "ROLE_admin" }, { "authority": "admin" } ],
...

Here is a simple test. Directly write the interface that returns the current user's permissions. It is found that the permissions are "ROLE_admin" and "admin".

summary

Sometimes there is little source code analysis on the official website. We must look at the source code and take practical actions to accurately analyze its intention. Therefore, when it does not exist or does not meet our needs, we can choose to override its source code logic and implement custom patterns, which will avoid a lot of unnecessary trouble. Because the source code analysis is different for different versions, the corresponding source code is also different.

Keywords: Kubernetes Load Balance Container

Added by battlesiege on Wed, 12 Jan 2022 20:57:06 +0200