写点什么

统一认证中心 Oauth2 高可用坑

作者:Xiao8
  • 2022 年 6 月 11 日
  • 本文字数:3625 字

    阅读完需:约 12 分钟

前面 (统一认证中心 Oauth2 认证坑) 我们利用user-info-uri来实现消费端的认证信息以及授权获取判断,接下来我们借助 token-info-uri 来实现认证以及授权破。具体配置见:


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: true token-info-uri: ${cas-server-url}/oauth/check_token authorization: check-token-access: ${cas-server-url}/oauth/check_token
复制代码


这里的/oauth/check_token是 Oauth2 原生自带的,这里不需要封装。接下来,我们启动服务,在拿到 token 后,通过 token 请求消费端:


2021-11-03 16:40:09.057 DEBUG 24652 --- [io2-2001-exec-4] o.s.web.client.RestTemplate              : HTTP POST http://cas-server/oauth/check_token2021-11-03 16:40:09.060 DEBUG 24652 --- [io2-2001-exec-4] o.s.web.client.RestTemplate              : Accept=[application/json, application/*+json]2021-11-03 16:40:09.062 DEBUG 24652 --- [io2-2001-exec-4] o.s.web.client.RestTemplate              : Writing [{token=[b34841b4-61fa-4dbb-9e2b-76496deb27b4]}] as "application/x-www-form-urlencoded"2021-11-03 16:40:11.332 ERROR 24652 --- [io2-2001-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://cas-server/oauth/check_token": cas-server; nested exception is java.net.UnknownHostException: cas-server at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:746) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:672) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:581) at org.springframework.security.oauth2.provider.token.RemoteTokenServices.postForMap(RemoteTokenServices.java:149)
复制代码


我们从上面的日志中,可以发现系统抛出 UnknownHostException 这种异常,无法找到cas-server,但我要说的是:我们这里用到的是 Nacos 注册中心来实现服务的注册与发现:



那说明注册的服务可以被发现,接下来我们看支持 LB 的几种服务消费方式: RestTemplate、WebClient、Feign。我们这里基于 Ribbon,RestTemplate,因为在 Oauth2 原生中,就是基于 RestTemplate 来调用远程服务:


private Map<String, Object> postForMap(String path, MultiValueMap<String, String> formData, HttpHeaders headers) {    if (headers.getContentType() == null) {      headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);    }    @SuppressWarnings("rawtypes")    Map map = restTemplate.exchange(path, HttpMethod.POST,        new HttpEntity<MultiValueMap<String, String>>(formData, headers), Map.class).getBody();    @SuppressWarnings("unchecked")    Map<String, Object> result = map;    return result;  }
复制代码


大家都知道默认的原生 Ribbon,是基于 RestTemplate 的负载均衡,所以这里配置如下:


@LoadBalanced@Beanpublic RestTemplate restTemplate() {        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();        requestFactory.setReadTimeout(env.getProperty("client.http.request.readTimeout", Integer.class, 15000));        requestFactory.setConnectTimeout(env.getProperty("client.http.request.connectTimeout", Integer.class, 3000));        RestTemplate rt = new RestTemplate(requestFactory);        return rt;}
复制代码


可以看到,在定义 RestTemplate 的时候,增加了@LoadBalanced注解,但其实在真正调用服务接口的时候,原来 host 部分是通过手工拼接 ip 和端口的,直接采用服务名的时候来写请求路径即可。在真正调用的时候,Spring Cloud 会将请求拦截下来,然后通过负载均衡器选出节点,并替换服务名为具体的 ip 和端口,从而实现基于服务名的负载均衡调用。


接下来,我们再看看负载均衡的策略是否有问题,Ribbon 默认的负载均衡策略是轮询,内置了多种负载均衡策略,内置的负载均衡的顶级接口为com.netflix.loadbalancer.IRule。具体的策略有:AvailabilityFilteringRule、RoundRobinRule、RetryRule、RandomRule、WeightedResponseTimeRule、BestAvailableRule 等。这里直接使用默认的轮询:


@Beanpublic IRule ribbonRule(IClientConfig config){            //return new AvailabilityFilteringRule();    return new RoundRobinRule();//轮询    //return new RetryRule();//重试            //return new RandomRule();//这里配置策略,和配置文件对应            //return new WeightedResponseTimeRule();//这里配置策略,和配置文件对应    //return new BestAvailableRule();//选择一个最小的并发请求的server    //return new MyProbabilityRandomRule();//自定义}
复制代码


到这里,目前都还未发现问题,那么既然实现了基于 RestTemplate 的负载均衡,为什么还是报错呢?



找了半天,最后发现在 Oauth2 源码中,注入的是这么个玩意:



这时候才发现多么的坑,于是乎,一顿猛操作:在资源检验时调用的覆盖其注入:


@Autowired(required = true)private RemoteTokenServices remoteTokenServices;
@AutowiredRestTemplate restTemplate;
复制代码


其二,直接 set RestTemplate:


@Override    public void configure(ResourceServerSecurityConfigurer resource) throws Exception {    super.configure(resource);
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { @Override // Ignore 400 public void handleError(ClientHttpResponse response) throws IOException { if (response.getRawStatusCode() != 400) { super.handleError(response); } } }); if (Objects.nonNull(remoteTokenServices)) { remoteTokenServices.setRestTemplate(restTemplate); resource.tokenServices(remoteTokenServices); }
resource //.tokenStore(tokenStore) //.tokenServices(tokenServices) .authenticationEntryPoint(customAuthenticationEntryPoint) .accessDeniedHandler(customAccessDeniedHandler) //.tokenExtractor(new BearerTokenExtractor()) ; }
复制代码


接下来,我们重启消费端,看看效果,根据之前请求的 token,直接访问消费端接口:


2021-11-03 16:57:50.476  INFO 81424 --- [io2-2001-exec-3] o.s.web.servlet.DispatcherServlet        : Completed initialization in 12 ms2021-11-03 16:57:50.522 DEBUG 81424 --- [io2-2001-exec-3] o.s.web.client.RestTemplate              : HTTP POST http://cas-server/oauth/check_token2021-11-03 16:57:50.526 DEBUG 81424 --- [io2-2001-exec-3] o.s.web.client.RestTemplate              : Accept=[application/json, application/*+json]2021-11-03 16:57:50.528 DEBUG 81424 --- [io2-2001-exec-3] o.s.web.client.RestTemplate              : Writing [{token=[b34841b4-61fa-4dbb-9e2b-76496deb27b4]}] as "application/x-www-form-urlencoded"2021-11-03 16:57:50.635 DEBUG 81424 --- [io2-2001-exec-3] o.s.web.client.RestTemplate              : Response 200 OK
复制代码


发现 ok 了,返回成功200,并且有权限访问该接口:



总结


有时候自己的代码写的已经很好了,但发现还是无法实现自己想要的:于是乎,可以大胆设想是不是官网源码出了幺蛾子,就像本文一样,如果不一步步检查,怎么也不会发现原来是源码留下如此大的坑,在前面的文章中,其实发现很多源码的不合理之处之后,都在修改,并且生成一套自己的规范返回,这样对于代码本身来说,我们会更加深刻体会、理解。Oauth2 源码本身可以只是一个带头的基础功能,后面基于大项目,需要自己对于一些系统的设计进行改造,例如:高可用、高并发鉴权方案、统一认证 SSO 等等。

发布于: 刚刚阅读数: 4
用户头像

Xiao8

关注

God bless the fighters. 2020.03.11 加入

欢迎关注公众号:程序猿Damon,长期从事Java开发,研究Springcloud的微服务架构设计。目前主要从事基于K8s云原生架构研发的工作,Golang开发,长期研究边缘计算框架KubeEdge、调度框架Volcano、容器云KubeSphere研究

评论

发布
暂无评论
统一认证中心 Oauth2 高可用坑_6月月更_Xiao8_InfoQ写作社区