大家好!今天让小编来大家介绍下关于hzero微服务平台06: 代码分析之token生成、校验、获取信息、传递的问题,以下是酷知号的小编对此问题的归纳整理,让我们一起来看看吧。
目录概述从oauth服务获取token过程oauth服务校验token的过程如果快过期, 自动延长有效时间;服务间token的传递过程gateway获取用户信息(principal)的过程oauth服务创建用户信息(principal)的过程client_credentials模式password模式业务服务从jwt_token获取用户信息的过程
概述
功能介绍: 认证服务
oauth2是开放的标准协议, spring security oauth提供了实现, 授权中心(authorization server)用@enableauthorizationserver
及相关配置实现, 资源服务(resource server)用@enableresourceserver
及相关配置实现;
授权中心提供授权(/oauth/authorize)、获取token(/oauth/token)等接口, 资源服务实现对token的校验、信息提取; hzero的oauth服务即是授权中心也是资源服务;
资料:
oauth2.0的rfc文档: rfc 6749 - the oauth 2.0 authorization framework
spring官方开发文档: oauth 2 developers guide
调用过程阅读方法: 如何以纯文本方式简单快速记录java代码的调用过程
从oauth服务获取token过程
调试技巧: 在最内层的方法上打断点, 看调用堆栈;
post /oauth/token
tokenendpoint#postaccesstoken
oauth2accesstoken token = gettokengranter().grant(tokenrequest.getgranttype(), tokenrequest); //责任链模式, 每种授权模式对应一个granter
abstracttokengranter#grant
clientdetails client = clientdetailsservice.loadclientbyclientid(clientid);
abstracttokengranter#getaccesstoken
return tokenservices.createaccesstoken(getoauth2authentication(client, tokenrequest));
abstracttokengranter#getoauth2authentication //这个方法会被子类granter覆写
return new oauth2authentication(storedoauth2request, null);
defaulttokenservices#createaccesstoken(oauth2authentication) //hzero修改版
defaulttokenservices#createaccesstoken(oauth2authentication , oauth2refreshtoken) //hzero修改版
return accesstokenenhancer != null ? accesstokenenhancer.enhance(token, authentication) : token;
"hzero修改版": hzero直接把spring的某些代码保留包名、类名复制到了项目里, 相当于直接替换了源码, 一种不太好的hack方法;
abstracttokengranter
的子类authorizationcodetokengranter、implicittokengranter、clientcredentialstokengranter、resourceownerpasswordtokengranter
分别对应四种授权模式, 可以增加新的granter, 优雅的实现新的认证方式.
oauth服务校验token的过程
oauth2authenticationprocessingfilter#dofilter
authentication authresult = authenticationmanager.authenticate(authentication);
oauth2authenticationmanager#authenticate
oauth2authentication auth = tokenservices.loadauthentication(token);
defaulttokenservices#loadauthentication
oauth2accesstoken accesstoken = tokenstore.readaccesstoken(accesstokenvalue);
customredistokenstore#readaccesstoken //从redis里读取、反序列化
如果快过期, 自动延长有效时间;
defaulttokenservices#loadauthentication
//如果快过期, 自动增加有效时间;
if (accesstoken.getexpiresin() < 3600) {
long deltams = 4 * 3600 * 1000l; //4小时, 单位是毫秒;
((defaultoauth2accesstoken) accesstoken).setexpiration(new date(system.currenttimemillis() deltams));
tokenstore.storeaccesstoken(accesstoken, result);
}
服务间token的传递过程
前端使用oauth2流程获取token, 之后的请求必须携带token, token在传递示意图:
前端获取的uuid格式的token(相当于sessionid), 传递给网关;
网关使用uuid token获取用户信息, 把用户信息转换jwt token, 并添加到jwt_token
header里, 传递到后端服务; 如果获取用户信息失败, 直接返回401(认证失败);
后端服务从jwt_token里解析、获取用户信息;
gateway获取用户信息(principal)的过程
重点:
gateway把uuid转换为jwt是在addjwtfilter
用户信息最终是oauth服务从customredistokenstore
里读取的;
gateway服务:
从gateway调用非public的任意接口时:
getuserdetailsfilter#run
customuserdetailswithresult result = this.getuserdetailsservice.getuserdetails(accesstoken);
getuserdetailsserviceimpl#getuserdetails //调用oauth服务的/oauth/api/user
注意: oauth/api/user
接口是within接口, 直接从网关调用会报错: error.permission.withinforbidden
permission_with_in error.permission.withinforbidden
no access to within interface
oauth服务:
// oauth/api/user
oauthcontroller#user
return principal;
principal来自securitycontext, securitycontext来自oauth2authenticationprocessingfilter:
oauth2authenticationprocessingfilter#dofilter
authentication authresult = authenticationmanager.authenticate(authentication);
oauth2authenticationmanager#authenticate
oauth2authentication auth = tokenservices.loadauthentication(token);
securitycontextholder.getcontext().setauthentication(authresult);
关于additioninfo
字段:
defaulttokenservices#loadauthentication的返回结果包含additioninfo, 但序列化的之后不包含, 因为spring添加了ignore注解;
principal序列化把additioninfo字段里信息, 放到了和client_id同级的位置;
oauth服务创建用户信息(principal)的过程
principal来自object securitycontext.getauthentication().getprincipal()
, object具体是什么类型需要看authenticationtoken
设置了什么值;
client_credentials模式
principal是customclientdetails
类型:
...
clientcredentialstokengranter#grant
abstracttokengranter#grant
clientdetails client = clientdetailsservice.loadclientbyclientid(clientid);
customclientdetailsservice#loadclientbyclientid
clientdetailswrapper.warp(clientdetails, client.getid(), client.getorganizationid()); //角色、租户等信息来自这里
return getaccesstoken(client, tokenrequest);
abstracttokengranter#getaccesstoken
return tokenservices.createaccesstoken(getoauth2authentication(client, tokenrequest));
clientcredentialstokengranter#getoauth2authentication //hzero修改版
return new clientoauth2authentication(storedoauth2request, new clientauthenticationtoken(client)); //new clientauthenticationtoken(client)的入参client是principal, 是customclientdetails
password模式
principal是customuserdetails
类型:
...
resourceownerpasswordtokengranter#getoauth2authentication
userauth = authenticationmanager.authenticate(userauth);
providermanager#authenticate
abstractuserdetailsauthenticationprovider#authenticate
user = retrieveuser(username,(usernamepasswordauthenticationtoken) authentication);
customauthenticationprovider#retrieveuser
return getuserdetailsservice().loaduserbyusername(username);
customuserdetailsservice#loaduserbyusername
return createsuccessauthentication(principaltoreturn, authentication, user);
return new oauth2authentication(storedoauth2request, userauth);
业务服务从jwt_token获取用户信息的过程
调试思路: 给jwttokenextractor
打断点, 看调用堆栈;
业务服务里hzero没有用spring oauth的@enableresourceserver
, 自定义了jwttokenfilter
, 相当于oauth2authenticationprocessingfilter
的功能:
jwttokenfilter#dofilter
authentication authentication = this.tokenextractor.extract(httprequest);
authentication authresult = this.authenticate(authentication);
jwttokenfilter#authenticate
this.tokenservices.loadauthentication(token);
securitycontextholder.getcontext().setauthentication(authresult);
使用方法: 封装好的方法:
detailshelper.getuserdetails()
以上就是小编对于hzero微服务平台06: 代码分析之token生成、校验、获取信息、传递问题和相关问题的解答了,hzero微服务平台06: 代码分析之token生成、校验、获取信息、传递的问题希望对你有用!