这里服务之间调用使用的是RestTemplate,因为在某些特殊的场景下RestTemplate相比Feign和Dubbo来说也是有它的方便之处的,这里我就不细说了,知道这里用的RestTemplate来调用上游微服务就可以了
我们在调用上游服务时大多数情况是需要认证的,这时我们是需要把认证信息(这里是token)放到请求头header里。但是我们肯定不能把token字符串写死,因为token一般都是有过期时间的。
那我们该怎么办,每次向上游服务请求时都先获取一下新的token?
这样token确实动态了,但是每次请求都获取一次token、生成一个新的token,每生成一次token都会把新的token放到Redis服务器上的,这样一来就有点浪费Redis的内存了,虽然token都会过期、会移除,但那也挡不住你每次向上游服务都发送请求都生成新的token往Redis里塞得快呀,这样太浪费Redis内存空间了。
我们可以自定义一个XxxRestTemplate类继承一下RestTemplate,重写RestTemplate的exchange方法。
@Component
public class FireFlyRestTemplate extends RestTemplate {
public static final String CODE_401 = "401";
private static final Logger log = LoggerFactory.getLogger(FireFlyRestTemplate.class);
@Autowired
private TokenProvider tokenProvider;
@Override
public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
Class<T> responseType, Object... uriVariables) throws RestClientException {
log.info("【FireFlyRestTemplate.exchange】");
ResponseEntity<T> result = super.exchange(url, method, requestEntity, responseType, uriVariables);
T body = result.getBody();
// 强转成JSONObject类型
JSONObject jsonResult = (JSONObject) body;
boolean isUnAuthentication = CODE_401.equals(jsonResult.getString("code"));
// 如果返回的code是401
if (isUnAuthentication) {
log.info("【401:token过期了】");
// 刷新本地缓存里的token
tokenProvider.refreshToken(1000*60*10L);
// 利用新的token发送请求
return super.exchange(url, method, tokenProvider.createNewEntity(requestEntity), responseType, uriVariables);
}
return result;
}
}
请看重写的exchange的代码,有如下几个步骤:
接下来看一下TokenProvider这个工具类:
@Component
public class TokenProvider {
private static final Logger log = LoggerFactory.getLogger(TokenProvider.class);
private static final String TOKEN_STR = "token";
@Autowired
private RestTemplate restTemplate;
@Value("${api.firefly.url}")
private String BASE_URL;
@Value("${api.firefly.auth-username}")
private String autUsername;
@Value("${api.firefly.auth-password}")
private String authPassword;
@Value("${auth.defaultTokenExpireTime}")
private Long defaultTokenExpireTime;
/** * 获取新的token * @return */
public String getNewToken() {
log.info("【getNewToken】");
// 先拿到 公钥
String publicKey = this.getPublicKey();
String encryptedPassword = "";
// 对密码进行加密
try {
// 利用公钥 使用RSA算法为密码进行加密
encryptedPassword = RSAUtils.encrypt(authPassword, publicKey);
} catch (Exception e) {
log.info("密码加密失败");
e.printStackTrace();
}
// 向上游服务发送请求、获取token。 参数:用户名、加密后的密码
JSONObject loginResult = this.login(autUsername, encryptedPassword);
return loginResult.getString(TOKEN_STR);
}
/** * 请求上游服务获取 公钥 * @return */
private String getPublicKey() {
String FULL_URL = BASE_URL + "/getPublicKey";
ResponseEntity<JSONObject> result = restTemplate.exchange(FULL_URL, HttpMethod.GET, null, JSONObject.class);
return result.getBody().getString("msg");
}
/** * 真正发起请求获取token的方法 * @param username * @param password * @return */
public JSONObject login(String username, String password) {
log.info("【login】");
String FULL_URL = BASE_URL + "/login";
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("username", username);
requestBody.put("password", password);
HttpEntity<Map<String, Object>> httpEntity = new HttpEntity<>(requestBody, null);
ResponseEntity<JSONObject> result = restTemplate.exchange(FULL_URL, HttpMethod.POST, httpEntity, JSONObject.class);
return result.getBody();
}
/** * 1、请求获取新的token * 2、并将其设置到LocalCacheUtil中 */
public void refreshToken(Long expireTime) {
log.info("【refreshToken】");
// 获取新token
String newToken = getNewToken();
// 将获取的新token设置到缓存类(LocalCacheUtil)中
if (expireTime == 0) expireTime = defaultTokenExpireTime;
LocalCacheUtil.set(TOKEN_STR, newToken, expireTime);
}
/** * 从缓存中获取token,这个方法只能保重可以从本地缓存中拿到token,但是获取的token可能对上游服务来说是过期的。 * @return */
public String getTokenFromCache() {
log.info("【getTokenFromCache】");
Object token = LocalCacheUtil.get(TOKEN_STR);
// 如果拿到的数据为null 则说明cache中的token已经过期不存在了,要重新请求一次token放到缓存中,再从缓存中获取token
if (token == null) {
refreshToken(1000*60*10L);
// 再次从缓存中获取token
token = LocalCacheUtil.get(TOKEN_STR);
}
return (String)token;
}
/** * 根据老的HttpEntity 获取新的 HttpEntity * @param requestEntity * @return */
public HttpEntity<Object> createNewEntity(HttpEntity<?> requestEntity) {
log.info("【createNewEntity】");
Object body = requestEntity.getBody();
HttpHeaders headers = new HttpHeaders();
String token = (String)LocalCacheUtil.get(TOKEN_STR);
headers.add("Authorization", token);
HttpEntity<Object> newHttpEntity = new HttpEntity<>(body, headers);
return newHttpEntity;
}
}
TokenProvider这个工具类的主要有如下功能:
上述代码直接拷贝下来是不能直接执行的,我只是提供一个方案,希望这篇文章能帮到你。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_45464560/article/details/120890903
内容来源于网络,如有侵权,请联系作者删除!