小言_互联网的博客

浙里办微信小程序上架

1370人阅读  评论(0)

一、概述

本指南旨在为“浙里办”单点登录组件提供接入指南,“浙里办”单点登陆组件,上架在IRS,为上架在IRS的应用,提供统一的单点登录解决方案,现阶段仅支持微信端的接入。

二、服务创建

IRS 应用管理员在 IRS 应用发布服务侧进行“应用发布注册”。
注意事项
1、IRS 应用系统查询没有应用信息,可通过 IRS 应用编目查询该应用的 IRS 应用管理员
信息。
2、IRS 应用管理员添加的开发商信息需要有浙政钉账号,由服务侧应用建设单位提供,
用于 IRS 应用发布开发商工作台进行服务部署。

3、应用接入“浙里办”单点登录组件前,需要先获取AK&SK。

三、接入说明

接入前需要服务侧应用建设单位已完成在 IRS 应用编目工作。从开发部署到服务上架,需
要经历以下主要阶段:

四、接入规范

  1. 接入浙里办微信小程序的 H5 应用(以下简称应用), 应当符合同源发布及无障碍适老化等要求。
  2. 应用应当在浙江省一体化数字资源系统(以下简称 IRS)发布,并使用统一的域名 https://mapi.zjzwfw.gov.cn/,作为接入微信小程序的前置条件。应用上架 IRS,应遵循 IRS 相关规范。
  3. 应用接入浙里办微信小程序,应当按照本指南操作步骤与注意事项,进行微信端的兼容适配。

五、操作步骤

1、单点登录适配 

政务服务网法人用户单点登录
支持范围
“浙里办”APP、浙江政务服务网
登录入口
https://esso.zjzwfw.gov.cn/opensso/spsaehandler/metaAlias/sp?spappurl=回调地址
浙江政务服务网登出地址
https://esso.zjzwfw.gov.cn/opensso/UI/Logout?goto=https://oauth.zjzwfw.gov.cn/
oauth/logout.do?redirect=登录地址
API 概览
API
接口说明
访问地址
atg.biz.userquery
验证令牌并
获取用户的
登录信息
政务外网地址:
https://bcdsg.zj.gov.cn:8443/restapi/prod/IC
33000020220309000001/rest/user/query
互联网地址:
https://ibcdsg.zj.gov.cn:8443/restapi/prod/I
C33000020220309000001/rest/user/query
atg.biz.callb
ackurl
业务系统回
调地址添加
政务外网地址:
https://bcdsg.zj.gov.cn:8443/restapi/prod/IC
33000020220309000002/rest/callbackUrl
互联网地址: https://ibcdsg.zj.gov.cn:8443/restapi/prod/I
C33000020220309000002/rest/callbackUrl

  1. IRS 应用管理员在 IRS 申请【浙江政务服务网个人单点登录】组件,前端通过调用登录地址获取 ticket 票据后,服务端可通过 ticketvalidation 和 getuserinfo接口。
  2. 登录地址 spappurl 参数回调地址用于接收 ssotoken 的信息。
注意事项
“浙里办”APP 没有支持浙江政务服务网法人单点登录测试环境
https://essotest.zjzwfw.gov.cn 地址免登能力;

Java代码案例:

(1)Constants  定义所有常量


  
  1. /**
  2. * @author jie.chen
  3. * @date 2022-03-30 15:24
  4. */
  5. public interface Constants {
  6. /**
  7. * 单点登录 ticketId换token的地址
  8. */
  9. // String ACCESS_TOKEN_URL = "https://bcdsg.zj.gov.cn:8443/restapi/prod/IC33000020220329000007/uc/sso/access_token";政务外网
  10. //互联网
  11. String ACCESS_TOKEN_URL = "https://ibcdsg.zj.gov.cn:8443/restapi/prod/IC33000020220329000007/uc/sso/access_token";
  12. /**
  13. * 单点登录 token获取用户信息地址
  14. */
  15. // String GET_USER_INFO_URL = "https://bcdsg.zj.gov.cn:8443/restapi/prod/IC33000020220329000008/uc/sso/getUserInfo";政务外网
  16. //互联网
  17. String GET_USER_INFO_URL = "https://ibcdsg.zj.gov.cn:8443/restapi/prod/IC33000020220329000008/uc/sso/getUserInfo";
  18. /**
  19. * IRS请求携带的请求头
  20. */
  21. String X_BG_HMAC_ACCESS_KEY = "X-BG-HMAC-ACCESS-KEY";
  22. String X_BG_HMAC_SIGNATURE = "X-BG-HMAC-SIGNATURE";
  23. String X_BG_HMAC_ALGORITHM = "X-BG-HMAC-ALGORITHM";
  24. String X_BG_DATE_TIME = "X-BG-DATE-TIME";
  25. /**
  26. * IRS签名算法
  27. */
  28. String DEFAULT_HMAC_SIGNATURE = "hmac-sha256";
  29. /**
  30. * 应用ID
  31. */
  32. String APP_ID = "20******33";
  33. /**
  34. * 微信端固定值为weixin
  35. */
  36. String WEIXIN_ENDPOINT_TYPE = "weixin";
  37. /**
  38. * IRS 申请组件生成的AK
  39. */
  40. String IRS_AK = "********************************";
  41. /**
  42. * IRS 申请组件生成的SK
  43. */
  44. String IRS_SK = "********************************";
  45. String TOKEN_SESSION_KEY = "sessionAccessToken";
  46. String USER_INFO_KEY = "sessionUserInfo";
  47. }

(2)IrsUtils


  
  1. /**
  2. * @author jie.chen
  3. * @date 2022-03-30 15:28
  4. */
  5. public class IrsUtils {
  6. @SneakyThrows
  7. public static IrsSignRes sign (String url, String method) {
  8. UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(url).build();
  9. uriComponents = uriComponents.encode();
  10. List<String> queryArr = new ArrayList<>();
  11. MultiValueMap<String, String> queryParams = uriComponents.getQueryParams();
  12. for (Map.Entry<String, List<String>> next : queryParams.entrySet()) {
  13. for (String va : next.getValue()) {
  14. if (va == null) {
  15. queryArr.add(next.getKey() + "=");
  16. } else {
  17. queryArr.add(next.getKey() + "=" + va);
  18. }
  19. }
  20. }
  21. //按照字典排序
  22. Collections.sort(queryArr);
  23. ///Tue, 09 Nov 2021 08:49:20 GMT
  24. DateFormat dateFormat = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
  25. dateFormat.setTimeZone(TimeZone.getTimeZone( "GMT"));
  26. String dateTime = dateFormat.format( new Date());
  27. String signStr = method.toUpperCase() + "\n" +
  28. //拼接url path
  29. uriComponents.getPath() + "\n" +
  30. //拼接url query
  31. String.join( "&", queryArr) + "\n" +
  32. Constants.IRS_AK + "\n" +
  33. dateTime + "\n";
  34. String sign = hmacSha256Base64(signStr, Constants.IRS_SK);
  35. IrsSignRes res = new IrsSignRes();
  36. res.setSignature(sign);
  37. res.setAccessKey(Constants.IRS_AK);
  38. res.setDateTime(dateTime);
  39. res.setAlgorithm(Constants.DEFAULT_HMAC_SIGNATURE);
  40. return res;
  41. }
  42. @SneakyThrows
  43. private static String hmacSha256Base64 (String content, String key) {
  44. Mac hmacSHA256 = Mac.getInstance( "HmacSHA256");
  45. SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
  46. hmacSHA256.init(secretKey);
  47. byte[] bytes = hmacSHA256.doFinal(content.getBytes(StandardCharsets.UTF_8));
  48. return Base64.getEncoder().encodeToString(bytes);
  49. }
  50. public static void main (String[] args) {
  51. System.out.println(sign( "https://bcdsg.zj.gov.cn:8443/restapi/prod/IC33000020220329000007/uc/sso/getUserInfo", "POST"));
  52. }
  53. }

(3)IrsSignRes


  
  1. /**
  2. * @author jie.chen
  3. * @date 2022-03-30 15:28
  4. */
  5. @Data
  6. public class IrsSignRes {
  7. private String accessKey;
  8. private String signature;
  9. private String algorithm;
  10. private String dateTime;
  11. }

(4)AuthService 业务实现类


  
  1. /**
  2. * @author jie.chen
  3. * @date 2022-03-30 15:49
  4. */
  5. @Component
  6. public class AuthService {
  7. @Autowired
  8. private RestTemplateBuilder restTemplateBuilder;
  9. private RestTemplate restTemplate;
  10. @PostConstruct
  11. void init () {
  12. restTemplate = restTemplateBuilder.build();
  13. }
  14. public String getTokenByTicketId (String ticketId) {
  15. HttpHeaders headers = getHttpHeaders(Constants.ACCESS_TOKEN_URL);
  16. JSONObject body = new JSONObject();
  17. body.put( "appId", Constants.APP_ID);
  18. body.put( "ticketId", ticketId);
  19. HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);
  20. ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(Constants.ACCESS_TOKEN_URL, request, String.class);
  21. return checkResponse(stringResponseEntity).getJSONObject( "data").getString( "accessToken");
  22. }
  23. public JSONObject getUserInfoByToken (String accessToken) {
  24. HttpHeaders headers = getHttpHeaders(Constants.GET_USER_INFO_URL);
  25. JSONObject body = new JSONObject();
  26. body.put( "token", accessToken);
  27. HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);
  28. ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(Constants.GET_USER_INFO_URL, request, String.class);
  29. return checkResponse(stringResponseEntity).getJSONObject( "data");
  30. }
  31. private JSONObject checkResponse (ResponseEntity<String> stringResponseEntity) {
  32. if (!stringResponseEntity.getStatusCode().is2xxSuccessful()) {
  33. //请求失败
  34. throw new RuntimeException( "status:" + stringResponseEntity.getStatusCodeValue() + " " + stringResponseEntity.getBody());
  35. }
  36. JSONObject result = JSON.parseObject(stringResponseEntity.getBody());
  37. if (result.containsKey( "errorCode") && result.getString( "errorCode") != null && !result.getBooleanValue( "success")) {
  38. //业务错误
  39. throw new RuntimeException(result.toString());
  40. }
  41. return result;
  42. }
  43. private HttpHeaders getHttpHeaders (String url) {
  44. IrsSignRes res = IrsUtils.sign(url, "POST");
  45. HttpHeaders headers = new HttpHeaders();
  46. headers.add(Constants.X_BG_HMAC_ACCESS_KEY, res.getAccessKey());
  47. headers.add(Constants.X_BG_HMAC_ALGORITHM, res.getAlgorithm());
  48. headers.add(Constants.X_BG_HMAC_SIGNATURE, res.getSignature());
  49. headers.add(Constants.X_BG_DATE_TIME, res.getDateTime());
  50. return headers;
  51. }

(5)LoginController 接口测试


  
  1. /**
  2. * @author hejun
  3. * @since 2022-02-22 10:46:11
  4. */
  5. @RestController
  6. @RequestMapping("/user")
  7. @Api(tags="用户登录")
  8. @Slf4j
  9. public class LoginController extends ProBaseController {
  10. @GetMapping(value = "zlbWxLoginTest")
  11. @ApiOperation(value = "测试浙里办微信小程序登录接口", notes = "测试浙里办微信小程序登录接口后端接口")
  12. public String zlbWxLoginTest (@RequestParam @ApiParam(name = "st", value = "浙里办 ticketId", required = true)String st) {
  13. try {
  14. return buildResultStr(buildSuccessResultData(userService.getUserBeanByTicketId(st)));
  15. } catch (Exception e) {
  16. logError(log, e);
  17. return buildResultStr(buildErrorResultData(e.getMessage()));
  18. }
  19. }
  20. }

(6) UserServiceImpl 浙里办用户体系转换


  
  1. /**
  2. * 用户表(User)表服务实现类
  3. *
  4. * @author hejun
  5. * @since 2022-02-22 10:02:16
  6. */
  7. @Service("userService")
  8. @Slf4j
  9. public class UserServiceImpl implements UserService {
  10. @Autowired
  11. private AuthService authService;
  12. @Override
  13. public UserBean getUserBeanByTicketId (String ticketId){
  14. UserBean userBean = new UserBean();
  15. //1. 通过ticketId 换取 accessToken
  16. String token = authService.getTokenByTicketId(ticketId);
  17. //3. 通过accessToken 获取用户信息
  18. JSONObject userInfo = authService.getUserInfoByToken(token);
  19. JSONObject personInfo = userInfo.getJSONObject( "personInfo");
  20. String phone = personInfo.get( "phone").toString();
  21. userBean.setMobile(phone);
  22. userBean.setUsername(personInfo.get( "userName").toString());
  23. userBean.setIdnum(personInfo.get( "idNo").toString());
  24. userBean.setUserid(personInfo.get( "userId").toString());
  25. String login = null;
  26. if (StringUtils.isNotNullString(phone)){
  27. login = this.login(phone);
  28. userBean.setToken(login);
  29. log.info( "token------------------------------", login);
  30. }
  31. return userBean;
  32. }
  33. /**
  34. * 通过手机号登录
  35. *
  36. * @return token
  37. */
  38. @Override
  39. public String login (String phone) {
  40. User user = this.getUserByPhone(phone);
  41. if (user == null){
  42. user = new User();
  43. user.setMobile(phone);
  44. this.insert(user);
  45. }
  46. //生成token
  47. String token = getUserRsid(phone);
  48. token = token.replaceAll( "/", "_");
  49. //token放入缓存
  50. JedisUtils.setObject(token,user,portalRsidCacheSeconds);
  51. //返回token
  52. return token;
  53. }
  54. }

六、接口调用方式

请以POST方式提交请求参数以application/json形式提交

    1. 鉴权参数说明

“浙里办”单点登录,HTTP请求都必须在请求头(HTTP Header)中设置如下4个参数:

参数名

是否必填

类型

说明

X-BG-HMAC-SIGNATURE

string

API输入参数签名结果

X-BG-HMAC-ALGORITHM

string

签名的摘要算法,当前仅支持hmac-sha256。

X-BG-HMAC-ACCESS-KEY

string

分配给应用的accessKey,例如:12345678。

X-BG-DATE-TIME

string

时间戳,时区为GMT+8,格式为:Tue, 09 Nov 2021 08:49:20 GMT。服务端允许客户端请求最大时间误差为100秒。

其中X-BG-HMAC-SIGNATURE的计算公式为:

signature = HMAC-SHA256-HEX(secret_key,signing_string)

各字段解释如下:

  1. secret_key为接口申请完成后获取到的secret_key
  2. signing_string由请求方法、URI、请求参数等拼接获得,具体如下:

HTTP_METHOD+\n+HTTP_URI+\n+QUERY_STREAM+\n+X-BG-HMAC-ACCESS-KEY+\n+X-BG-DATE+\n

参数解释如下图,详细代码可参考签名计算代码

参数名

说明

HTTP METHOD

指 HTTP 协议中定义的 GET、PUT、POST 等请求方法,必须使用全大写的形式。

HTTP URI

请求路径,要求必须以“/”开头,不以“/”开头的需要补充上,空路径为“/”

X-BG-DATE

请求头中的 Date ( GMT 格式 )格式为:“Tue, 09 Nov 2021 08:49:20 GMT”

QUERY_STREAM

是对于 URL 中的 query( query 即 URL 中?后面的 key1=valve1&key2=valve2 字符串)进行编码后的结果。以 key 按照字典顺序( ASCII 码由小到大)排序,并使用 & 符号连接起来,生成相应的query_string。

  • 接口说明
    1. 基于单点登录票据换取请求token
    1. 请求地址

 /uc/sso/access_token

    1. 入参

参数

类型

描述

ticketId

String

单点登录票据

appId

String

AppId

    1. 出参

参数

类型

描述

errorCode

String

错误码

errorMsg

String

错误信息

success

Boolean

请求是否成功

data

Object

响应体

|- accessToken

String

获取用户信息token

    1. 错误码

错误码

描述

C-USER-SSO-TICKET-INVALID

ticket非法

    1. 基于token获取用户信息
  1. 请求地址

/uc/sso/getUserInfo

  1. 入参

参数

类型

描述

token

String

获取用户信息token

  1. 出参

参数

类型

描述

success

Boolean

请求是否成功

errorCode

String

错误码

errorMsg

String

错误信息

data

Object

响应体

|- userType

String

用户类型,PERSON

个人/LEGAL_PERSON

法人

|- personInfo

Object

个人用户信息,当前登陆自然人的信息

|-- userId

String

主键

|-- userName

String

个人姓名

|-- idType

String

ID_CARD:身份证,PASSPORT:护照,OFFICER_CARD:军官证,MAINLAND_TRAVEL_PERMIT_FOR_HONGKONG_AND_MACAO_RESIDENTS:港澳居民来往内地通行证,MAINLAND_TRAVEL_PERMIT_FOR_TAIWAN_RESIDENTS:台湾居民来往大陆通行证,FOREIGN_PERMANENT_RESIDENT_ID_CARD:外国人永久居留身份证,FOREIGN_PASSPORT:外籍人士护照,DIPLOMACY_PASSPORT:外交护照,OFFICIAL_PASSPORT:公务护照,SOLDIER_CARD:士兵证,OFFICER_RETIRE_CARD:军官离退休证,GANG_AO_TAI_RESIDENCE_CART:港澳台居民居住证,GANG_AO_ID_CART:港澳居民身份证,UNIFIED_SOCIAL_ID:统一社会信用代码,OTHER:其他

|-- outerIdType

String

外部证件类型

|-- idNo

String

证件编号

|-- attnUserType

String

法人经办人时用户类型,评级

|-- phone

String

手机号

|-- email

String

邮箱

|-- nation

String

民族

|-- gender

String

性别

|-- birthday

String

生日

|-- certKey

String

身份散列值

|-- attributes

Object

额外属性

|- legalPersonInfo

Object

法人用户信息,比如公司相关的信息

|-- name

String

法人名称

|-- unifiedSocialId

String

社会统一信用代码

|-- orgType

String

法人类型

|-- attnName

String

经办人姓名

|-- attnPhone

String

经办人手机号

|-- attnIdType

String

经办人证件类型

|-- attnIdNo

String

经办人证件号码

|-- attnUserType

String

经办人用户等级

|-- principal

String

法人代表人姓名

|-- gender

Integer

法人代表人性别

|-- nation

Integer

法人代表人民族

|-- idType

Integer

法人代表人证件类型

|-- outerIdType

String

法人代表人外部证件类型

|-- idNo

String

法人代表人证件号码

|-- principalUserId

String

法人代表唯一键

|-- corpId

String

法人唯一键

|-- attributes

Object

额外属性

|- organizationInfoList

Array

所属组织信息

|-- orgId

String

组织主键

|-- oid

String

Alias for orgId

|-- parentId

String

父组织主键

|-- pid

String

Alias for parentId

|-- name

String

组织机构简称

|--fullName

String

组织机构全称

|--devCoding

String

组织后缀

|--leafFlag

Boolean

是否叶子标志

|--orderBy

Integer

排序号,从小到大

  1. 错误码

错误码

描述

C-USER-SSO-TOKEN-INVALID

token非法

C-USER-SSO-USER-EMPTY

用户信息为空


转载:https://blog.csdn.net/Zyw907155124/article/details/128022335
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场