广东省统一身份认证平台对接笔记
2022年11月12日
对接首先需要有申请好秘钥,也就是clientId和clientSecret,设置好登录成功跳转的地址。其他平台也是类似的方案,Oauth2.0。
sso:
# 用于用户登录成功后重定向
vue-url: http://localhost:9200/corp/sso
# vue-url: http://localhost:9200/corp/#/sso/
# 申请到的id
client-id: xxx
# 申请到的秘钥
client-secret: xxx
# 对接平台的baseUrl
tyrz-url: https://tyrztest.gd.gov.cn
我们先要在前端页面,跳转到统一身份认证平台,进行认证,再认证成功后跳转回我们的前端地址。步骤如下:
检查用户是否已登录我们的系统(检查cookie或localSessionStorage)
用户未登录则进行跳转,跳转到:
https://tyrztest.gd.gov.cn/tif/sso/connect/page/oauth2/authorize?service=initService&response_type=code&client_id=申请的id&scope=all&redirect_uri=http%3A%2F%2Flocalhost%3A9200%2Fcorp%2Fsso
需要注意的是,这里的redirect_url是配置好的,如果url不对是会报错的。并且要进行encodeURL
登录成功后,会进行跳转,并且会携带一个参数,code
跳转到我们的前端页面后,我们需要把这个code传给我们的后端服务器由服务器再次发送请求到平台获取token,前端向后端发请求时可以携带上自己的业务参数。
请求到达服务器,服务器使用http工具,向平台发送请求:
获取到token以后,用这个token,调用平台接口获取用户基本信息,进行判断来校验用户是否可用
可用则将用户信息存到我们自己的系统,生成本系统的token返回给前端,保存到cookie
工具类:
/**
* sso工具
*
* @author 赖卓成
* @date 2023/02/21
*/
@Component
@Slf4j
public class SsoUtil {
private final ObjectMapper OBJECTMAPPER = new ObjectMapper();
@Autowired
private ApplicationProperties applicationProperties;
/**
* 获取省厅令牌
*
* @param code 代码
* @param path 跳转路径
* @return {@link ProvinceTokenDto}
*/
public ProvinceTokenDto getProvinceToken(String code, String path) {
// 跳转地址encode
path = null == path ? "" : path;
String redirectUri = "";
try {
redirectUri = URLEncoder.encode(applicationProperties.getSso().getSsoVueUrl()+ "?type=" + path + "&ywlx=" + "path", "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new BusinessException(BusinessExceptionEnum.CAN_NOT_GET_JUMP_ADDRESS);
}
// 调用接口地址拼接
String serviceUrl = applicationProperties.getSso().getTyrzUrl()
+ "/tif/sso/connect/page/oauth2/access_token?client_id="
+ applicationProperties.getSso().getClientId()+ "&code=" + code + "&scope=all&client_secret="
+ applicationProperties.getSso().getClientSecret() + "&grant_type=authorization_code&redirect_uri="
+ redirectUri;
// 发起GET请求
String json = HttpUtil.get(serviceUrl);
log.info("登录返回信息:{}",json);
ProvinceTokenDto provinceTokenDto = null;
try {
provinceTokenDto = OBJECTMAPPER.readValue(json, ProvinceTokenDto.class);
} catch (JsonProcessingException e) {
throw new BusinessException(BusinessExceptionEnum.AUTHENTICATION_FAILURE);
}
if (StringUtils.isBlank(provinceTokenDto.getAccessToken())){
throw new BusinessException(BusinessExceptionEnum.AUTHENTICATION_FAILURE);
}
return provinceTokenDto;
}
public ProvinceUserResultDto getProvinceUserDto(String accessToken) {
if (StringUtils.isBlank(accessToken)){
throw new BusinessException(BusinessExceptionEnum.NOT_FIND_PROVINCE_ACCESS_TOKEN);
}
// 调用接口地址拼接
String userInfoUrl = applicationProperties.getSso().getTyrzUrl() + "/tif/sso/connect/page/oauth2/tokeninfo?access_token=" + accessToken;
// 发起GET请求
String json = HttpUtil.get(userInfoUrl);
ProvinceUserResultDto provinceUserResultDto = null;
try {
provinceUserResultDto = OBJECTMAPPER.readValue(json, ProvinceUserResultDto.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
// 处理额外属性 转为java对象
List<String> extProperties = provinceUserResultDto.getUserObj().getExtProperties();
Map<String, String> map = extProperties.stream().map(s -> s.split("=")).collect(Collectors.toMap(s -> s[0], s -> s[1]));
ProvinceUserExtDataDto provinceUserExtDataDto = new ProvinceUserExtDataDto(map);
provinceUserResultDto.getUserObj().setProvinceUserExtDataDto(provinceUserExtDataDto);
provinceUserResultDto.getUserObj().setExtProperties(null);
return provinceUserResultDto;
}
}
实现类:
@Override
public BasLoginVo clientSsoLogin(String code, String path) {
// 登录-》通过身份证查询,优先查询已入库的记录(始终保持只有一条已入库的,且id不能变),有已入库的则直接用已入库的id生成token
// 没有已入库的,则拿未入库的生成token(始终保持只有一条未入库的)
if (StringUtils.isBlank(code)) {
throw new BusinessException(BusinessExceptionEnum.OBTAIN_PROVINCE_PARAMETER_FAILURE);
}
// 通过授权码获取省厅访问令牌
ProvinceTokenDto provinceTokenDto = ssoUtil.getProvinceToken(code, path);
// 通过令牌获取用户信息
ProvinceUserResultDto provinceUserResultDto = ssoUtil.getProvinceUserDto(provinceTokenDto.getAccessToken());
ProvinceUserDataDto userObj = provinceUserResultDto.getUserObj();
String creditableLevelOfAccount = userObj.getCreditableLevelOfAccount();
String userType = userObj.getUserType();
String idCardType = userObj.getIdCardType();
String idCardNumber = userObj.getIdCardNumber();
boolean real = userObj.isReal();
// TODO 移动端可能会用到以下校验,可以封装工具类 暂时先这样写
// 校验身份信息
if (StringUtils.isBlank(userType) || StringUtils.isBlank(idCardType) || StringUtils.isBlank(idCardNumber)) {
throw new BusinessException(BusinessExceptionEnum.ID_INFORMATION_IS_NOT_COMPLETE);
}
// 个人用户、已实名认证、用户等级为L2才行,否则不让使用
if (!ProvinceUserLevelEnum.USER_LEVEL_L2.getValue().equals(creditableLevelOfAccount)) {
throw new BusinessException(BusinessExceptionEnum.PARAMETERS_ERROR);
}
if (!ProvinceUserTypeEnum.USER_TYPE_INDIVIDUAL_USER.getValue().equals(userType)){
throw new BusinessException(BusinessExceptionEnum.LEGAL_USERS_CANT_USE);
}
if (!real){
throw new BusinessException(BusinessExceptionEnum.NOT_REAL_NAME_AUTHENTICATION);
}
// 校验专家是否已经注册 已注册返回token,未注册返回信息,提示需要同意注册保留信息致本系统
boolean registerStatus = bizExpertService.getExpertRegisterStatus(idCardNumber);
BasLoginVo basLoginVo = new BasLoginVo();
basLoginVo.setRegisterStatus(registerStatus);
if (registerStatus){
// 已注册,则返回已入库信息,如还未入库,则返回未入库的信息
BizExpert bizExpert=bizExpertService.getValidExpert(idCardNumber);
if (null==bizExpert){
bizExpert = bizExpertService.getNotInStoreExpertByIdCardNumber(idCardNumber);
}
String id = bizExpert.getId();
try {
String token = jwtUtil.createToken(id);
basLoginVo.setAccessToken(token);
} catch (Exception e) {
throw new BusinessException(BusinessExceptionEnum.CANT_CREATE_TOKEN);
}
}
// else {
// basLoginVo.setProvinceUserResultDto(provinceUserResultDto);
// }
basLoginVo.setProvinceUserResultDto(provinceUserResultDto);
basLoginVo.setExpertName(provinceUserResultDto.getUserObj().getName());
return basLoginVo;
}
传输对象:
/**
* 省厅返回的用户信息-返回结果最外层对象
*
* @author 赖卓成
* @date 2023/02/21
*/
@Data
public class ProvinceUserResultDto implements Serializable {
private static final long serialVersionUID = 6790808153472792218L;
/**
* 签名
*/
@JsonProperty("signdata")
private String signData;
/**
* 用户信息
*/
@JsonProperty("userobj")
private ProvinceUserDataDto userObj;
/**
* pareObj
*/
@JsonProperty("pareobj")
private ProvinceUserDataDto pareObj;
/**
* 用户等级信息
*/
@JsonProperty("user_creditable_level")
private ProvinceUserCreditableLevelDto userCreditableLevel;
}
/**
* 省用户数据dto
*
* @author 赖卓成
* @date 2023/02/21
*/
@Data
public class ProvinceUserDataDto implements Serializable {
private static final long serialVersionUID = 6169661489111021236L;
/**
* 用户id
*/
@JsonProperty("uid")
private String userId;
/**
* 核验方 如YSS 粤省事
*/
@JsonProperty("realtype")
private String realType;
/**
* 名字
*/
@JsonProperty("cn")
private String name;
/**
* 令牌标识
*/
@JsonProperty("tokenid")
private String tokenId;
/**
* 用户类型
*/
@JsonProperty("usertype")
private String userType;
/**
* 实名状态
*/
@JsonProperty("isreal")
private boolean isReal;
/**
* 电话号码
*/
@JsonProperty("telephonenumber")
private String phoneNumber;
/**
* 邮件
*/
@JsonProperty("mail")
private String mail;
/**
* 身份证类型
*/
@JsonProperty("idcardtype")
private String idCardType;
/**
* 创建时间
*/
@JsonProperty("createtime")
private String createTime;
/**
* ext属性
*/
@JsonProperty("extproperties")
private List<String> extProperties;
/**
* 省用户ext数据
*/
private ProvinceUserExtDataDto provinceUserExtDataDto;
/**
* 身份证号码
*/
@JsonProperty("idcardnumber")
private String idCardNumber;
/**
* 用户id代码
*/
@JsonProperty("useridcode")
private String userIdCode;
/**
* 身份验证代码行
*/
@JsonProperty("authloc")
private String authLoc;
/**
* 信誉级别账户
*/
@JsonProperty("creditable_level_of_account_way")
private String creditableLevelOfAccountWay;
/**
* 信誉级别账户
*/
@JsonProperty("creditable_level_of_account")
private String creditableLevelOfAccount;
/**
* flag
*/
@JsonProperty("anon_flag")
private boolean anonFlag;
/**
* 联系人名字
*/
@JsonProperty("link_person_name")
private String linkPersonName;
}
/**
* 省用户可信dto
*
* @author 赖卓成
* @date 2023/02/22
*/
@Data
public class ProvinceUserCreditableLevelDto {
/**
* 账户可信级别以及核验方式字符串
*/
@JsonProperty("creditable_level_of_account_way")
private String creditableLevelOfAccountWay;
/**
* 账户当前最高的可信等级
*/
@JsonProperty("creditable_level_of_account")
private String creditableLevelOfAccount;
/**
* 账户可信等级的核验方式明细
*/
@JsonProperty("creditable_level_of_account_way_list")
private List<ProvinceCreditableLevelOfAccountWayListDto> creditableLevelOfAccountWayList;
}
/**
* 诚信信息
*
* @author 赖卓成
* @date 2023/02/22
*/
public class ProvinceCreditableLevelOfAccountWayListDto {
/**
* 认证时间
*/
@JsonProperty("auth_time")
private String authTime;
/**
* 用户名
*/
@JsonProperty("user_name")
private String userName;
/**
* 认证标识
*/
@JsonProperty("auth_identification")
private String authIdentification;
/**
* 身份级别
*/
@JsonProperty("identity_level")
private String identityLevel;
/**
* 证书号
*/
@JsonProperty("credential_no")
private String credentialNo;
/**
* 方法代码
*/
@JsonProperty("way_code")
private String wayCode;
/**
* 惟一id
*/
@JsonProperty("uniqueid")
private String uniqueId;
}
/**
* 省厅认证获取令牌返回对象
*
* @author 赖卓成
* @date 2023/02/21
*/
@Data
public class ProvinceTokenDto implements Serializable {
private static final long serialVersionUID = 8083867889121871276L;
/**
* 有效期
*/
@JsonProperty("expires_in")
public String expiresIn;
/**
* 令牌类型
*/
@JsonProperty("token_type")
public String tokenType;
/**
* 访问令牌
*/
@JsonProperty("access_token")
public String accessToken;
/**
* 刷新令牌
*/
@JsonProperty("refresh_token")
public String refreshToken;
/**
* 范围
*/
@JsonProperty("scope")
public String scope;
}
Loading...