这里我使用SsoUser来进行演示
属性 | 注释 |
---|---|
userId | 用户ID |
userCode | 用户编码 |
userNum | 警号ID |
idCard | 身份证ID |
假设就只有这个4个值的封装成为的对象,用来接受其他Api请求端给我们传过来的参数。
@ApiOperation(value = "001-通过身份证获取用户信息", notes = "001-通过身份证获取用户信息")
@PostResource(value = "/user/get", name = "001-通过身份证获取用户信息")
public RwResponse<SsoUser> getUserByIdCard(@RequestParam(name = "idcard") String idcard) {
SsoUser ssoUser = sysUserApplicationService.getSsoUserByIdCard(idcard);
return RwResponse.build(ssoUser);
}
其中,@PostResource是一个自定义注解,用于标记某个方法为一个RESTful API的POST请求
name可以用于生成API文档或在日志中记录请求的详细信息,方便调试和维护。
RwResponse是一个封装好的响应体,这里不是本次的重点。
public SsoUser getSsoUserByIdCard(String idcard) {
SsoUser ssoUser = ssoUserApi.getSsoUserByIdCard(idcard);
if (ssoUser == null) {
throw new ServiceException(CommCode.DATA_NOT_FOUND, "未找到身份证号[" + idcard + "]的用户信息!");
}
String deptCode = DeptLocalCahce.getDeptCodeByCodeInOtherSys(ssoUser.getOrgNum());
if (StringUtils.isBlank(deptCode)) {
deptCode = DeptLocalCahce.getDeptCodeByDeptName(ssoUser.getOrgName());
}
ssoUser.setDeptCode(StringUtils.isBlank(deptCode) ? "0101" : deptCode);
return ssoUser;
}
在这一层中,封装了大量调用其他方法API的接口,但是还是有几个方面可以展开来说说。
在DeptLocalCache中,如果无法通过orgNum来获取到用户的部门的信息的话,则通过另外的一个属性来来获取deptCode,然后进行再次的判断,如果仍然为空,则通过赋值默认值的方式进行赋值。最后将对象进行返回。
UserInfoQueryParam param = UserInfoQueryParam.builder()
.idCard(idCard)
.build();
List<SsoUser> list = this.getUserInfo(param);
list = list.stream().filter(item -> YesOrNoEnum.no(item.getIsDeleted())).collect(Collectors.toList());
if (CollectionUtil.isEmpty(list)) {
return null;
}
return list.stream().max(Comparator.comparing(SsoUser::getUpdatedSeq)).get();
在这个方法中,我们封装了查询的请求体,然后将得到的数据以流的方式进行过滤然后得到需要的数据,这里的思路则是,将每个对象中已经删除的对象进行标记并且抛弃,然后把剩下的再次合并成为一个集合,然后再进一步进行选择,而后面的一个比较容器中,目的是为了挑选出一个最大更新频率的用户,并封装成为SsoUser进行返回。
public List<SsoUser> getUserInfo(UserInfoQueryParam param) {
HttpRequest request = HttpUtil.createPost(ssoUserUrl + "/aa/bb/.userInfo");
request.method(Method.POST);
request.headerMap(this.getHeaders().toSingleValueMap(), true);
request.body(JSON.toJSONString(param), ContentType.JSON.getValue());
log.info("调用统一用户接口:{},入参:{}", "/aa/bb/.userInfo", JSON.toJSON(param).toString());
HttpResponse response = request.execute();
List<SsoUser> result = null;
if (response.getStatus() == 200) {
JSONObject jsonObject = JSON.parseObject(response.body());
JSONObject payloadObject = jsonObject.getJSONObject("payload");
if (payloadObject != null && payloadObject.containsKey("data")) {
result = JSONObject.parseArray(payloadObject.getString("data"), SsoUser.class);
}
} else {
return Collections.EMPTY_LIST;
}
return result == null ? Collections.EMPTY_LIST : result;
在这个方法中,就是一个请求url的基本套路,包括参数、参数请求头的相关转换【Json对象】和设置。然后在响应处理中,如果状体码为200时,则对json对象进行处理,提取payload对象中的data字段,并返回泛型集合,反之则返回空集合
private MultiValueMap<String, String> getHeaders() {
SsoToken ssoToken = ssoAuthApi.getToken();
log.info("调用统一认证接口获取token:{}", ssoToken.getAccessToken());
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add("Authorization", Joiner.on(" ").join("Bearer", ssoToken.getAccessToken()));
headers.add("Content-Type", ContentType.JSON.getValue());
return headers;
}
在这个方法中,将获取到的token,先记录到日志文件中去,然后新建立一个Map集合,并指定类型,然后在对获取到token进行加工和封装
public SsoToken getToken() {
if (StringUtils.isBlank(this.clientId) || StringUtils.isBlank(this.clientSecret)) {
throw new ServiceException(CommCode.ERROR, "缺少配置[sso.client-id]或[sso.client-secret]");
}
MultiValueMap<String, String> headers = new HttpHeaders();
String clientBase64 = Base64.encode(Joiner.on(":").join(clientId, clientSecret));
headers.put("Authorization", Collections.singletonList("Basic " + clientBase64));
headers.add("Content-Type", ContentType.FORM_URLENCODED.getValue());
SsoToken ssoToken = ssoAuthApi.getToken("client_credentials", headers);
if (ssoToken == null || StringUtils.isBlank(ssoToken.getAccessToken())) {
throw new ServiceException(CommCode.USER_LOGIN_TOKEN_EMPTY, "获取token失败!");
}
return ssoToken;
}
这段代码中,首先检查id和secret是否为空,如果有任一个为空,则抛出异常体现缺少配置,紧接着创建请求头,对id和secret进行编码,并且设置到Authorization中。添加Content-Type,值为application/x-www-form-urlencoded。如果获取成功,则返回SsoToken对象
@FeignClient(value = "gz-sso-auth", url = "${sso.auth.url}")
public interface ISsoAuthApi {
@PostMapping("/sso/oauth2/token")
SsoToken getToken(@RequestParam("grant_type") String grantType
, @RequestHeader MultiValueMap<String, String> headers);
}
这两个代码片段虽然实现方式不同,但都实现了与远程服务的通信。选择哪种方式取决于具体的应用场景和需求。Feign 客户端适用于简单的、规则固定的服务调用,而手动构建 HTTP 请求则提供了更多的灵活性和控制。