前言
在往期文章中有写过SpringSecurity OAuth使用JWT替换默认Token、SpringSecurityOAuth2采用JWT生成Token的模式自定义JWT数据、这两篇关于JWT的,当然还有一篇是关于JWT拓展数据获取的问题,在SpringSecurity从数据库获取用户信息这篇文章末尾有相关代码,这里用户信息是存放到JWT里面去了,虽然之前这篇文章SpringSecurity OAuth使用JWT替换默认Token中有获取JWT中的自定义数据,但是获取感觉缺点味道!具体获取JWT中自定义数据如下
@GetMapping("/myJWT")
public Object getmyJWT(Authentication user, HttpServletRequest request) throws UnsupportedEncodingException {
String header=request.getHeader("Authorization");
String token= StringUtils.substringAfter(header,"bearer");
Claims claims= Jwts.parser().setSigningKey("yiyi".getBytes("UTF-8"))
//getBytes("UTF-8")这里设置是因为这里和JwtAccessTokenConverter生成的编码格式不一致,JwtAccessTokenConverter为UTF-8,而这里使用的不是UTF-8,所以需要设置一下
.parseClaimsJws(token).getBody();
String r=(String)claims.get("mydata");
log.info("r===>"+r);
return user;
}
或者
@Autowired
private TokenStore tokenStore;
@GetMapping("/returnUser")
public Map<String, Object> getExtraInfo(OAuth2Authentication auth) {
OAuth2AuthenticationDetails details
= (OAuth2AuthenticationDetails) auth.getDetails();
OAuth2AccessToken accessToken = tokenStore.readAccessToken(details.getTokenValue());
return accessToken.getAdditionalInformation();
}
这里实际上就是获取请求头中的携带的JWT令牌,然后使用JWT解析工具反向解析得到,这种感觉在实际写业务过程中比较繁琐,也会造成代码冗余,后期维护成本比较高。所以今天改造一下。
前提场景
在我们实际业务系统中,可能需要频繁的通过认证信息做一些操作,比如我需要知道访问这个接口的用户的余额,查询用户的余额当然可以通过SpringSecurity默认提供的Authentication中获取用户名,这个在校验用户身份信息是就会将用户名、权限等一些SpringSecurity默认的数据存放到Authentication 中,获取方法如下
@GetMapping(value = "/getDefault")
public Object getDefaultJWT(Authentication authentication) {
return authentication;
}
其中authentication对象中就有username,每次通过username查询用户信息表,然后得到用户Id,然后通过用户Id查询余额表,得到用户余额信息!,这样做中间就需要通过username查询用户id这么个操作,比较鸡肋,既然我们已经扩充了JWT模板,那我们就自己将用户id存放到JWT中,这样我们全局解析后就能在每个接口中都能直接获取用户id了!这里全局解析不是文章刚开始那样在每个接口里单独写一遍解析代码!
全局解析JWT
方式1
创建JWT模板信息转换器
/**
* @author TAO
* @description: 自定义JWTToken模板信息转换器
* @date 2021/4/11 2:25
*/
@Component
public class CustomJWTTokenConverter extends DefaultAccessTokenConverter {
/**
* @param claims 将解析好的JWT数据存放到OAuth2Authentication中的Details中
* 让所有请求都能更加方便获取JWT自定义数据
* @return
*/
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {
OAuth2Authentication authentication
= super.extractAuthentication(claims);
authentication.setDetails(claims);
return authentication;
}
}
绑定JWT模板信息转换器
@Configuration
public class TokenConfig {
@Autowired
private CustomJWTTokenConverter customAccessTokenConverter;
@Bean
public TokenStore tokenStore() {
//JWT令牌存储方案
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setAccessTokenConverter(customAccessTokenConverter);
converter.setSigningKey(SecurityConstants.SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
return converter;
}
}
获取JWT自定义数据
@GetMapping("/getCustomJWT")
public OAuth2Authentication getCustomJWT(OAuth2Authentication auth) {
return auth;
}
这样我们就能通过这种全局方式获取到id,这里还可以优化一下,就是编写一个获取id或者其他数据的工具类,这样我们实际业务开发起来就更加方便了!注意!!!这种方式必须要TokenStore为JwtTokenStore才能用!
return new JwtTokenStore(jwtAccessTokenConverter());
方式2
可以使用过滤器获取请求头中的Authorization然后解析完后加在reques中存储,这种就比较简单粗暴,而且灵活性更高
@Component
public class LindTokenAuthenticationFilter extends OncePerRequestFilter {
public static String tokenHead = "Bearer ";
public static String tokenHeader = "Authorization";
private static String USER_KEY = "userSid";
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader(this.tokenHeader);
if (authHeader != null && authHeader.startsWith(tokenHead)) {
final String authToken = authHeader.substring(tokenHead.length()); // The part after "Bearer "
String sid=都得到JWT令牌了,剩下的就是解析
//设置userId到request里,后续根据userId,获取用户信息
request.setAttribute(USER_KEY, sId);
}
}
}
filterChain.doFilter(request, response);
}
}
获取
@PostMapping("/xxxx")
public Result xxxx(@RequestAttribute("userSid")String sid){
log.info("sid===>"+sid)
return Result.ok();
}