鉴权
ACE项目是一个比较完整的使用了spring cloud框架的开源项目,可以用它来快速搭建分布式系统。本次着重看的部分是其服务以及用户鉴权的部分。ACE有一个鉴权服务,负责微服务群的鉴权。启动ACE项目依次启动center,auth-server,admin,gateway即可。
模块架构
鉴权模块分为服务端和客户端(另有一个common模块,可忽略,是一些鉴权模块公用代码的提取)。应用程序依赖客户端,客户端提供了两个拦截器,工具类,并和服务端通信的feign。服务端则负责token生成,验证和更新。
代码分析
程序启动
auth-server启动时初始化KeyConfiguration,获得userSecret和serviceSecret.并在启动结束的钩子类AuthServerRunner中,分别设置User和Server的pri/puk.User用于用户认证,Server用于微服务认证。
admin和gateway在启动的时候,会调用auth-client的自动配置,生成ServiceAuthConfig和UserAuthConfig的实例。同时auth-client的AuthClientRunner会请求pubkey到ServiceAuthConfig和UserAuthConfig的实例。同时在应用各自的WebConfiguration中添加UserAuthRestInterceptor和ServiceAuthRestInterceptor拦截器
运行阶段
用户认证,由UserAuthRestInterceptor处理。
在用户登录的时候,调用jwtTokenUtil.generateToken生成token。这个token的生成规则稍后再论。在浏览器记录一个Admin-Token.
--auth-server
public String login(JwtAuthenticationRequest authenticationRequest) throws Exception {
UserInfo info = userService.validate(authenticationRequest);
if (!StringUtils.isEmpty(info.getId())) {
return jwtTokenUtil.generateToken(new JWTInfo(info.getUsername(), info.getId() + "", info.getName()));
}
throw new UserInvalidException("用户不存在或账户密码错误!");
}
登录之后的请求都会经过UserAuthRestInterceptor,在解析token的时候同时会进行过期,签名合法等验证。并把这些信息加到threadLocal中去。
--UserAuthRestInterceptor
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
......
IJWTInfo infoFromToken = userAuthUtil.getInfoFromToken(token);
BaseContextHandler.setUsername(infoFromToken.getUniqueName());
BaseContextHandler.setName(infoFromToken.getName());
BaseContextHandler.setUserID(infoFromToken.getId());
return super.preHandle(request, response, handler);
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
BaseContextHandler.remove();
super.afterCompletion(request, response, handler, ex);
}
服务认证由ServiceAuthRestInterceptor处理。service的鉴权也是通过user的token。通过serviceAuthConfig.getTokenHeader()来解析service的信息。
--ServiceAuthRestInterceptor
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
......
String token = request.getHeader(serviceAuthConfig.getTokenHeader());
IJWTInfo infoFromToken = serviceAuthUtil.getInfoFromToken(token);
String uniqueName = infoFromToken.getUniqueName();
for(String client:serviceAuthUtil.getAllowedClient()){
if(client.equals(uniqueName)){
return super.preHandle(request, response, handler);
}
}
throw new ClientForbiddenException("Client is Forbidden!");
}
一些方法解读(待补充)
jwtTokenUtil.generateToken:生成用户token
userService.validate:验证用户的合法性
userAuthUtil.getInfoFromToken(token):从token中获得用户名称和ID等信息。