一、jwt令牌在网关处的过期时间校验
二、refresh-token接口缺少用户信息
三、新建business-server模块作为web容器
四、前后端分离
1、关闭认证服务表单登录
2、前后端代码
五、测试
六、源代码地址
历史文章
Spring Security OAuth2.0认证授权一:框架搭建和认证测试
Spring Security OAuth2.0认证授权二:搭建资源服务
Spring Security OAuth2.0认证授权三:使用JWT令牌
Spring Security OAuth2.0认证授权四:分布式系统认证授权
Spring Security OAuth2.0认证授权五:用户信息扩展到jwt
本篇文章将会解决上一篇文章《Spring Security OAuth2.0认证授权五:用户信息扩展到jwt 》中遗留的问题,并在原有的项目中新增模块business-server用来充当前端页面的web容器并转发登录请求和更换token的请求等,以模拟前后端分离下的登录以及更换token操作。
一、jwt令牌在网关处的过期时间校验
上一篇文章中讲了在网关处解析token并转发到目标服务的操作,因为使用了jwt令牌的原因,所以省了一步到认证服务器认证的操作,只要验签成功,就认为令牌有效。这实际上留下了一个bug:服务端无法主动取消jwt令牌,所以这个令牌只要客户端保存下来,如果不调用认证服务器的令牌验证接口,这个jwt令牌将永远有效。因此需要在网关处加上对过期时间的校验。
在TokenFilter中添加以下代码逻辑
//取出exp字段,判断token是否已经过期
try {
Map<String, Object> map = objectMapper.readValue(payLoad, new TypeReference<Map<String, Object>>() {
});
long expiration = ((Integer) map.get(“exp”)) * 1000L;
if (expiration < new Date().getTime()) {
return unAuthorized(exchange, “未认证的请求:token存在,但是已经失效”,WrapperResult.TOKEN_EXPIRE);
}
} catch (IOException e) {
log.error("", e);
return unAuthorized(exchange, “未认证的请求:错误的token”,null);
}
二、refresh-token接口缺少用户信息
refresh-token在access_token过期,但是refresh-token未过期的时候使用,目的是使用refresh_token更新已经过期的access_token,这样理论上来说,客户端只要能在refresh_token过期之前进行任意操作,就可以避免重新登录了。
上一篇文章中将用户信息放到了jwt token中并返回给客户端,但是如果使用refresh_token更新token,后端会报错,前端取到的token中则缺少了用户信息。究其原因,和JwtAccessTokenConverter有关系,关于这个类的实例,当初创建的方法如下
@Bean
public JwtAccessTokenConverter accessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);//对称秘钥,资源服务器使用该秘钥来验证
return jwtAccessTokenConverter;
}
这里的new操作省了很多默认参数的指定,且先看下为啥会缺少用户信息,扩展用户信息的关键在于方法com.kdyzm.spring.security.auth.center.service.MyUserDetailsServiceImpl#loadUserByUsername,这里扩展了用户信息,使其从单纯的username字符串变成了UserDetailsExpand对象,然后在增强方法com.kdyzm.spring.security.auth.center.enhancer.CustomTokenEnhancer#enhance中将扩展信息取出来放到Token中。
经过debug,发现
2021-01-29_162556.jpg
最终发现是如下代码的问题org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter#extractAuthentication
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME))