跳到主要内容

支付宝账号信任登录

概述

  1. 支付宝账号信任登录包含:PC网站应用登录和H5网站应用登录
  2. 支付宝信任登录与支付配置参数一致,无需再单独配置,直接使用支付配置参数即可
  3. 总体逻辑为:发起登录 -> 用户授权 -> 获取用户信息 -> 根据用户信息进行登录

PC网站应用登录

逻辑概述

  1. 按照规则拼接授权页面的 URL 链接,并且引导用户跳转至该链接
  2. 用户在授权页面上确认授权后,将跳转到开发者指定的回调页,并且带上 auth_code
  3. 开发者通过 alipay.system.oauth.token(换取授权访问令牌接口),使用 auth_code 换取 access_token 及用户的 user_id
  4. 如果需要除 user_id 以外的其他信息,则使用 access_token 调用 alipay.user.info.share(支付宝会员授权信息查询接口)获得用户信息

详细逻辑可参考官方文档:PC 网页内获取用户信息

流程图

image-20230804163433014

重点步骤说明:

  • 步骤3:构建的授权URL格式如下:

    https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?appid=APPID&redirect_uri=REDIRECT_URI&scope=SCOPE&state=STATE

    接口权限值scope目前只支持 auth_user 和 auth_base 两个值,我们使用的是auth_user

    image-20230804164142670

  • 步骤10-11:

    调用的接口为 alipay.system.oauth.token(换取授权访问令牌接口)

    Javashop系统集成了支付宝的SDK,利用SDK中已经集成好的方法进行接口调用即可,详情可参考下面的代码展示

    接口返回的数据格式如下:

    {
    "alipay_system_oauth_token_response": {
    "user_id": "2088102150477652",
    "access_token": "20120823ac6ffaa4d2d84e7384bf983531473993",
    "expires_in": "3600",
    "refresh_token": "20120823ac6ffdsdf2d84e7384bf983531473993",
    "re_expires_in": "3600",
    "auth_start": "2010-11-11 11:11:11"
    },
    "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
    }

    返回参数说明:

    image-20230804164629405

  • 步骤12:根据user_id查询是否已经绑定注册了会员信息(查询es_connect表,此表存放的是第三方账号和系统会员的关联信息),如果未查询到数据,需要调用会员注册业务接口注册一条会员数据

  • 步骤13:会员登录后会创建会员token信息,由于是服务端进行的重定向跳转页面,因此跳转之前要将token信息存入cookie中方便前端读取

代码展示

Controller代码展示:

@Tag(name = "支付宝统一登陆接口")
@RestController
@RequestMapping("/buyer/connect/alipay")
@Validated
public class LoginByAlipayController {

@Operation(summary = "PC发起信任登录")
@GetMapping("/pc/login")
public void pcLogin(){
//信任登录插件基类
AbstractConnectLoginPlugin connectionLogin = connectManager.getConnectionLogin(ConnectTypeEnum.ALIPAY);
String loginUrl = connectionLogin.getLoginUrl(ClientTypeEnum.PC.name());
try {
ThreadContextHolder.getHttpResponse().sendRedirect(loginUrl);
} catch (IOException e) {
this.logger.error(e.getMessage(), e);
throw new ServiceException(MemberErrorCode.E131.name(), "联合登录失败");
}
}

}

构建授权登录URL代码展示:

@Component
public class AlipayAbstractConnectLoginPlugin extends AbstractConnectLoginPlugin {

/**
* 获取支付宝授权登录页面链接
* @param clientTypeEnum 客户端类型 {@link ClientTypeEnum}
* @return
*/
@Override
public String getLoginUrl(String clientTypeEnum) {
Map map = initConnectSetting(ThirdPlatformEnum.ALIPAY,ClientTypeEnum.valueOf(clientTypeEnum));

String uuid = UUID.randomUUID().toString();
debugger.log("生成uuid" , uuid);

String callBack = this.getCallBackUrl(ConnectTypeEnum.ALIPAY.value(), clientTypeEnum);
return "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?" +
"app_id=" + map.get("app_id").toString() +
"&redirect_uri=" + callBack+
"&scope=auth_user"+
"&state="+uuid;
}

}

回调获取支付宝用户accessToken和userId代码展示:

@Component
public class AlipayAbstractConnectLoginPlugin extends AbstractConnectLoginPlugin {

/**
* 登录成功后的回调方法
*
* @param client 客户端类型 {@link ClientTypeEnum}
* @return
*/
@Override
public Auth2Token loginCallback(String client) {

debugger.log("进入 AlipayAbstractConnectLoginPlugin 回调");

Map map = initConnectSetting(ThirdPlatformEnum.ALIPAY,ClientTypeEnum.valueOf(client));
HttpServletRequest req = ThreadContextHolder.getHttpRequest();

//获取code
String code = req.getParameter("auth_code");

//根据支付宝配置参数创建AlipayClient实例
AlipayClient alipayClient = buildClient(map);

debugger.log("向支付宝发出请求");

AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setCode(code);
request.setGrantType("authorization_code");
try {
AlipaySystemOauthTokenResponse oauthTokenResponse = alipayClient.certificateExecute(request);
String accessToken = oauthTokenResponse.getAccessToken();
String openid = oauthTokenResponse.getUserId();

debugger.log("返回的accessToken及openid为:",accessToken,openid);

//如果返回值为null
if (accessToken == null || openid == null) {
throw new ServiceException(MemberErrorCode.E131.name(),"联合登录失败");
}

Auth2Token auth2Token = new Auth2Token();
auth2Token.setUnionid(openid);
auth2Token.setAccessToken(accessToken);
return auth2Token;
} catch (AlipayApiException e) {
//处理异常
this.logger.error(e.getMessage(), e);
throw new ServiceException(MemberErrorCode.E131.name(),"联合登录失败");
}
}

}

H5网站应用登录

逻辑概述

  1. 前端调用API获取支付宝H5授权地址
  2. 用户确认授权跳转至回调页面后,获取auth_code
  3. 通过auth_code进行接口调用获取access_token和获取用户基本数据资源或帮助用户实现基本操作

具体逻辑可参考官方文档:H5内获取用户信息

流程图

image-20230804170900976

重点步骤说明:

  • 步骤2:获取的授权URL格式如下:

    https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?appid=APPID&redirect_uri=REDIRECT_URI&scope=SCOPE&state=STATE

    接口权限值scope目前只支持 auth_user 和 auth_base 两个值,我们使用的是auth_user

    image-20230804164142670

  • 步骤7:在步骤5跳转至回调页面时,回调地址上会携带auth_code

  • 步骤9-10:

    调用的接口为 alipay.system.oauth.token(换取授权访问令牌接口)

    Javashop系统集成了支付宝的SDK,利用SDK中已经集成好的方法进行接口调用即可,详情可参考下面的代码展示

    接口返回的数据格式如下:

    {
    "alipay_system_oauth_token_response": {
    "user_id": "2088102150477652",
    "access_token": "20120823ac6ffaa4d2d84e7384bf983531473993",
    "expires_in": "3600",
    "refresh_token": "20120823ac6ffdsdf2d84e7384bf983531473993",
    "re_expires_in": "3600",
    "auth_start": "2010-11-11 11:11:11"
    },
    "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
    }

    返回参数说明:

    image-20230804164629405

  • 步骤11-12:

    调用的接口为 alipay.user.info.share(支付宝会员授权信息查询接口)

    Javashop系统集成了支付宝的SDK,利用SDK中已经集成好的方法进行接口调用即可,详情可参考下面的代码展示

    接口返回的数据格式如下:

    {
    "alipay_user_info_share_response":{
    "msg":"Success",
    "code":"10000",
    "province":"安徽省",
    "gender":"F",
    "user_id":"2088102104794936",
    "city":"安庆",
    "nick_name":"支付宝小二",
    "avatar":"http://tfsimg.alipay.com/images/partner/T1uIxXXbpXXXXXXXX"
    },
    "sign":"ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
    }

    返回参数说明:

    image-20230804172624210

代码展示

Controller代码展示:

@Tag(name = "支付宝统一登陆接口")
@RestController
@RequestMapping("/buyer/connect/alipay")
@Validated
public class LoginByAlipayController {

@Operation(summary = "获取授权页地址")
@Parameter(name = "redirectUri", description = "授权成功跳转地址(需要urlEncode整体加密)", required = true, in = ParameterIn.QUERY)
@GetMapping("/h5/getLoginUrl")
public String getLoginUrl(@RequestParam("redirectUri") String redirectUri) {
String loginUrl = loginAlipayManager.getLoginUrl(redirectUri);
return loginUrl;
}

@Operation(summary = "网页登陆")
@GetMapping("/h5/login")
public Map h5Login(String code, String uuid) {
return loginAlipayManager.wapLogin(code, uuid);
}

}

业务接口代码展示:

@Service
public class LoginAlipayManagerImpl implements LoginAlipayManager {

/**
* 获取支付宝授权登录页面地址链接
*
* @param redirectUri 回调页面地址链接
* @return
*/
@Override
public String getLoginUrl(String redirectUri) {
//支付宝无需单独的信任登录配置,因此从支付宝收款方式配置参数中获取即可
Map<String, String> map = this.thirdPlatformManager.getConfig(ClientTypeEnum.H5.value(), "alipayDirectPlugin");

String appId = map.get("app_id");
StringBuffer loginBuffer = new StringBuffer("https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?");
loginBuffer.append("app_id=").append(appId);
loginBuffer.append("&scope=auth_user");
loginBuffer.append("&redirect_uri=").append(redirectUri);
loginBuffer.append("&state=init");
StringBuffer openBuffer = new StringBuffer("alipays://platformapi/startapp?appId=20000067&url=");
try {
openBuffer.append(URLEncoder.encode(loginBuffer.toString(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return openBuffer.toString();
}

/**
* 获取支付宝登录access_token
*
* @param code 获取token的凭证
* @param uuid 请求唯一标识
* @return
*/
@Override
public Map wapLogin(String code,String uuid){
AlipaySystemOauthTokenResponse alipaySystemOauthTokenResponse = getAccessToken(code);
AlipayUserInfoShareResponse alipayUserInfoShareResponse = getUserInfo(alipaySystemOauthTokenResponse.getAccessToken());
LoginUserDTO loginUserDTO = new LoginUserDTO();
loginUserDTO.setUuid(uuid);
loginUserDTO.setTokenOutTime(null);
loginUserDTO.setRefreshTokenOutTime(null);
loginUserDTO.setUnionType(ConnectTypeEnum.ALIPAY);
loginUserDTO.setUnionid(alipayUserInfoShareResponse.getUserId());
loginUserDTO.setHeadimgurl(alipayUserInfoShareResponse.getAvatar());
loginUserDTO.setNickName(alipayUserInfoShareResponse.getNickName());
loginUserDTO.setSex("F".equals(alipayUserInfoShareResponse.getGender())?0:1);
loginUserDTO.setProvince(alipayUserInfoShareResponse.getProvince());
loginUserDTO.setCity(alipayUserInfoShareResponse.getCity());
return loginManager.loginByUnionId(loginUserDTO);
}

/**
* 获取access_token
*
* @param code 获取token的凭证
* @return
*/
private AlipaySystemOauthTokenResponse getAccessToken(String code){
//支付宝无需单独的信任登录配置,因此从支付宝收款方式配置参数中获取即可
Map<String, String> map = this.thirdPlatformManager.getConfig(ClientTypeEnum.H5.value(), "alipayDirectPlugin");
//根据支付宝配置参数创建AlipayClient实例
AlipayClient alipayClient = this.buildClient(map);
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setCode(code);
request.setGrantType("authorization_code");
AlipaySystemOauthTokenResponse oauthTokenResponse = null;
try {
oauthTokenResponse = alipayClient.execute(request);
if (!oauthTokenResponse.isSuccess()){
throw new ServiceException("403",oauthTokenResponse.getSubMsg());
}
} catch (AlipayApiException e) {
//处理异常
e.printStackTrace();
}
return oauthTokenResponse;
}

/**
* 获取支付宝用户信息
*
* @param accessToken 接口调用凭证
* @return
*/
private AlipayUserInfoShareResponse getUserInfo(String accessToken){
//支付宝无需单独的信任登录配置,因此从支付宝收款方式配置参数中获取即可
Map<String, String> map = this.thirdPlatformManager.getConfig(ClientTypeEnum.H5.value(), "alipayDirectPlugin");
//根据支付宝配置参数创建AlipayClient实例
AlipayClient alipayClient = this.buildClient(map);
AlipayUserInfoShareRequest request = new AlipayUserInfoShareRequest();
AlipayUserInfoShareResponse response = null;
try {
response = alipayClient.execute(request,accessToken);
if(!response.isSuccess()){
throw new ServiceException("403",response.getSubMsg());
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return response;
}

}