在前面文章 Springcloud Oauth2 HA篇
cas-server-url: http://cas-server-service #这里配置成HA地址
security:
oauth2: #与cas-server对应的配置
client:
client-id: admin-web
client-secret: admin-web-123
user-authorization-uri: ${cas-server-url}/oauth/authorize #是授权码认证方式需要的
access-token-uri: ${cas-server-url}/oauth/token #是密码模式需要用到的获取 token 的接口
resource:
loadBalanced: true
id: admin-web
user-info-uri: ${cas-server-url}/api/user #指定user info的URI
prefer-token-info: false
这里的 url 配置是基于k8s的 Service,实现负载均衡,从而实现高可用。但我们接下来分析 user-info-uri。user-info-uri 的原理是在授权服务器认证后将认证信息 Principal 通过形参绑定的方法通过URL的方式获取用户信息。当然它也有配套的 UserInfoTokenService 等。
但这个在客户端获取用户权限时候,是存在一定问题的。譬如 Web端请求消费端的某个接口:
/**
* 返回发现的所有服务
* @author Damon
* @date 2021年11月2日 下午8:18:44
* @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 formEntity = new HttpEntity(null, headers);
String body = "";
try {
ResponseEntity 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;
}
在这个接口中,我们通过添加@PreAuthorize("hasRole('admin')")
来控制权限,只要是admin的用户才能访问改接口。
我们先来请求认证中心登录接口,获取token:
image.png在拿到token之后,我们请求这个接口,我们会发现:
image.png说明未认证,我们再看看:发现原来当请求这个接口时,消费端后去请求认证中心的接口:
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 : 无效的token,请重新认证访问
{"data":"b34841b4-61fa-4dbb-9e2b-76496deb27b4","result":{"code":20202,"msg":"未认证","status":401}}
但认证中心给返回的404
状态码,此时会走统一异常EntryPoint提示报错:无效的token,请重新认证访问
。从而返回信息体:{"data":"b34841b4-61fa-4dbb-9e2b-76496deb27b4","result":{"code":20202,"msg":"未认证","status":401}}
。
接下来分析:为什么认证中心会返回404
呢?看认证中心日志:
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
发现原来Oauth2没有此类接口:/auth/user
。最后决定自写一个接口来替换原生:
@GetMapping("/api/v1/user")
public Authentication user(Map map, Principal user, Authentication auth) {
//获取当前用户信息
logger.info("cas-server provide user: " + JSON.toJSONString(auth));
return auth;
}
在封装、覆盖后,在消费端直接配置相关配置:
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
同时启动认证中心、消费端,继续获取token后,请求接口:
image.png此时,发现是403
,没有权限了,这下我们可以对用户添加这种权限即可:
"authorities": [ { "authority": "ROLE_admin" }, { "authority": "admin" }
添加完之后,我们发现可以请求接口成功:
{ "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" } ],
...
这里简单测试,直接写的返回当前用户权限的接口,发现权限就是"ROLE_admin、"admin"。
有时候官网的源码解析很少,我们必须看源码,结合实际行动才能准确的分析其用意。所以当其不存在、或者不满足我们的需求时,可以选择覆盖其源码逻辑,实现自定义模式,这样会避免很多不必要的麻烦。因为源码解析毕竟不同版本,对应的源码也是不同的。
实践项目代码开源:https://gitee.com/damon_one/microservice-k8s
欢迎大家star、fork,欢迎联系我,一起学习。
云原生社区合肥站
云原生社区合肥站正式启动啦,欢迎Base合肥、关注云原生、长期从事云原生的同志们踊跃加入,云原生社区合肥站会因为你们的加入而变得更加美好~
详情参见Issue:https://github.com/cloudnativeto/community/issues/107
欢迎关注个站
微服务自动化部署CI/CD
如何利用k8s拉取私有仓库镜像
个站建设基础教程
ArrayList、LinkedList 你真的了解吗?
大佬整理的mysql规范,分享给大家
如果张东升是个程序员
微服务架构设计之解耦合
浅谈负载均衡
Oauth2的认证实战-HA篇
Oauth2的授权码模式《上》
浅谈开发与研发之差异
浅谈 Java 集合 | 底层源码解析
基于 Sentinel 作熔断 | 文末赠资料
基础设施服务k8s快速部署之HA篇
今天被问微服务,这几点,让面试官刮目相看
Spring cloud 之多种方式限流(实战)
Spring cloud 之熔断机制(实战)
面试被问finally 和 return,到底谁先执行?
Springcloud Oauth2 HA篇
Spring Cloud Kubernetes之实战一配置管理
Spring Cloud Kubernetes之实战二服务注册与发现
Spring Cloud Kubernetes之实战三网关Gateway
关注公众号,回复入群,获取更多惊喜!公众号(程序猿Damon)里回复 ES、Flink、Java、Kafka、MQ、ML、监控、大数据、k8s 等关键字可以查看更多关键字对应的文章。
点击 "damon8.cn" 获取更好的阅读体验!
❤️给个「在看」,是对我最大的支持❤️