開發前提:
擁有支付寶賬戶登錄授權業務入參pid值。擁有APPID、App支付寶登錄功能併成功簽約
一、需求
用戶在APP點擊登錄後,選擇第三方登錄中的“支付寶”,跳轉到支付寶客戶端進行授權登錄(手機安裝了支付寶客戶端),或網頁端掃碼登錄(手機未安裝支付寶客戶端)。用戶同意登錄後獲取到用戶的基本信息。
二、開發流程
APP支付寶授權登錄獲取用戶信息流程圖:
1. Android客戶端調用支付寶無線賬戶授權接口,引導用戶授權,獲取會員授權碼(auth_code)與支付寶會員ID(user_id)。若匹配user_id成功,則完成登錄;否則繼續下一步。
2. 服務端調用授權令牌接口(alipay.system.oauth.token),通過auth_code換取access_token。auth_code作爲換取access_token的票據,auth_code只能使用一次,一天未被使用自動過期。
3. 服務端調用用戶信息共享接口(alipay.user.info.share),通過access_token獲取用戶的userId、暱稱、頭像等基礎信息。access_token只能使用一次
三、開發環境及使用到的技術
1、後端採用IDEA2017 進行開發
2、前端使用Android Studio 3.1.3 進行開發
3、後端必須基於JDK7以上版本,採用JDK8開發,前端基於Android SDK4.4
4、使用fastJson對json數據進行處理
四、具體實現步驟
1.前端(Android)
目錄結構
1).Android微信授權登錄開發環境配置
I.添加支付寶sdk包
將alipaySdk-xxxxxxxx.jar包放入應用工程的libs目錄下。
將jar包添加入環境,將libs目錄下jar包導入。
Build.gradle中,導入包
dependencies {
// 支付寶相關
implementation files('libs/alipaySdk-20180601.jar')
}
II.修改Manifest文件
添加聲明:
<!--添加聲明-->
<activity
android:name="com.alipay.sdk.app.H5PayActivity"
android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:exported="false"
android:screenOrientation="behind"
android:windowSoftInputMode="adjustResize|stateHidden" >
</activity>
<activity
android:name="com.alipay.sdk.app.H5AuthActivity"
android:configChanges="orientation|keyboardHidden|navigation"
android:exported="false"
android:screenOrientation="behind"
android:windowSoftInputMode="adjustResize|stateHidden" >
</activity>
添加權限聲明:
<!--權限聲明-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
III.添加混淆規則:
在proguard-rules.pro文件中添加相關規則:
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IAlixPay$Stub{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{ public *;}
-keep class com.alipay.sdk.app.AuthTask{ public *;}
-keep class com.alipay.sdk.app.H5PayCallback {
<fields>;
<methods>;
}
-keep class com.alipay.android.phone.mrpc.core.** { *; }
-keep class com.alipay.apmobilesecuritysdk.** { *; }
-keep class com.alipay.mobile.framework.service.annotation.** { *; }
-keep class com.alipay.mobilesecuritysdk.face.** { *; }
-keep class com.alipay.tscenter.biz.rpc.** { *; }
-keep class org.json.alipay.** { *; }
-keep class com.alipay.tscenter.** { *; }
-keep class com.ta.utdid2.** { *;}
-keep class com.ut.device.** { *;}
2).引導用戶點擊的按鈕
新建login.xml.添加按鈕
<!--支付寶相關-->
<Button
android:id="@+id/authV2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:onClick="authV2"
android:text="支付寶授權登錄"
tools:ignore="HardcodedText" />
當用戶點擊後,會調用authV2方法
3).loginActivity.java
public class AliPayLoginActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
}
I.相關配置參數:
private static final int SDK_PAY_FLAG = 1;
private static final int SDK_AUTH_FLAG = 2;
II.auth2方法
商戶重要信息需要存放在服務端。
Auth2方法中需要做的就是請求我方服務端獲取參數,根據參數請求螞蟻金服開放平臺服務端。
public void authV2(View v) {
new Thread(new Runnable() {
@Override
public void run() {
final String authInfo = ApacheHttpUtil.get("http://exzg5x.natappfree.cc"+"/alipay/sign/android");
Runnable authRunnable = new Runnable() {
@Override
public void run() {
// 構造AuthTask 對象
AuthTask authTask = new AuthTask(AliPayLoginActivity.this);
// 調用授權接口,獲取授權結果
Map<String, String> result = authTask.authV2(authInfo, true);
Message msg = new Message();
msg.what = SDK_AUTH_FLAG; //
msg.obj = result;
mHandler.sendMessage(msg); // 請求成功後將會發送消息到handler處理
}
};
// 必須異步調用
Thread authThread = new Thread(authRunnable);
authThread.start();
}
}).start();
}
III.Handler屬性
如果獲取到SDK_AUTH_FLAG消息,判斷狀態碼成功後,表明授權成功,螞蟻金服開放平臺將會返回code和openid,根據這兩個參數向服務端發送請求,獲取用戶信息。
根據請求獲取到的數據進行解析處理,然後跳轉到個人信息頁。
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@SuppressWarnings("unused")
public void handleMessage(Message msg) {
switch (msg.what) {
case SDK_AUTH_FLAG: {
@SuppressWarnings("unchecked")
final AuthResult authResult = new AuthResult((Map<String, String>) msg.obj, true);
String resultStatus = authResult.getResultStatus();
// 判斷resultStatus 爲“9000”且result_code
// 爲“200”則代表授權成功,具體狀態碼代表含義可參考授權接口文檔
if (TextUtils.equals(resultStatus, "9000") && TextUtils.equals(authResult.getResultCode(), "200")) {
// 獲取alipay_open_id,調支付時作爲參數extern_token 的value
// 傳入,則支付賬戶爲該授權賬戶
Toast.makeText(AliPayLoginActivity.this, "授權成功\n" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT) show();
new Thread(new Runnable() {
@Override
public void run() {
// 向服務端發送請求,預計返回用戶信息數據,返回給前端進行顯示。
String url = "http://exzg5x.natappfree.cc" +
"/alipay/getalipayMobileUserInfo" + "?" +
"app_id=" + Constants.ALPAY_APP_ID +
"&auth_code=" + authResult.getAuthCode() +
"&scope=auth_user";
String str = ApacheHttpUtil.get(url);
JSONObject jsonObject = (JSONObject) JSONObject.parse(str);
aliPayUserInfo = (AliPayUserInfo)JSON.parseObject(jsonObject.get("data").toString(), new TypeReference<AliPayUserInfo>() {});
}
}).start();
while (true){
// TODO: 這裏處理方案不是很合理,死循環或將造成界面卡死
if (aliPayUserInfo!=null){
Intent intent = new Intent(AliPayLoginActivity.this, listviewItem.class);
/* 通過Bundle對象存儲需要傳遞的數據 */
Bundle bundle = new Bundle();
bundle.putString("alipayUserId", aliPayUserInfo.getUserId());
bundle.putString("alipayAvatar", aliPayUserInfo.getAvatar());
bundle.putString("alipayNickName", aliPayUserInfo.getNickName());
bundle.putString("alipayCity", aliPayUserInfo.getCity());
bundle.putString("alipayGender", aliPayUserInfo.getGender());
bundle.putString("alipayIsCertified", aliPayUserInfo.getIs_certified());
bundle.putString("alipayIsStudentCertified", aliPayUserInfo.getIs_student_certified());
bundle.putString("alipayProvince", aliPayUserInfo.getProvince());
bundle.putString("alipayUserStatus", aliPayUserInfo.getUser_status());
bundle.putString("alipayUserType", aliPayUserInfo.getUser_type());
/*把bundle對象assign給Intent*/
intent.putExtras(bundle);
startActivity(intent);
break;
}
}
} else {
// 其他狀態值則爲授權失敗
Toast.makeText(AliPayLoginActivity.this, "授權失敗" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT).show();
}
break;
}
default:
break;
}
}
;
};
4).AliPayUserInfoViewItem.java:
需要獲取Bundle,然後將數據設置到對應控件中。
public class listviewItem extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listview_item);
Bundle bundle = this.getIntent().getExtras();
TextView alipayUserId = (TextView) findViewById(R.id.alipayUserId);
alipayUserId.setText(bundle.getString("alipayUserId"));
TextView alipayNickName = (TextView) findViewById(R.id.alipayNickName);
alipayNickName.setText(bundle.getString("alipayNickName"));
TextView alipayCity = (TextView) findViewById(R.id.alipayCity);
alipayCity.setText(bundle.getString("alipayCity"));
TextView alipayGender = (TextView) findViewById(R.id.alipayGender);
alipayGender.setText(bundle.getString("alipayGender"));
TextView alipayIsCertified = (TextView) findViewById(R.id.alipayIsCertified);
alipayIsCertified.setText(bundle.getString("alipayIsCertified"));
TextView alipayIsStudentCertified = (TextView) findViewById(R.id.alipayIsStudentCertified);
alipayIsStudentCertified.setText(bundle.getString("alipayIsStudentCertified"));
TextView alipayProvince = (TextView) findViewById(R.id.alipayProvince);
alipayProvince.setText(bundle.getString("alipayProvince"));
TextView alipayUserStatus = (TextView) findViewById(R.id.alipayUserStatus);
alipayUserStatus.setText(bundle.getString("alipayUserStatus"));
TextView alipayUserType = (TextView) findViewById(R.id.alipayUserType);
alipayUserType.setText(bundle.getString("alipayUserType"));
}
2.服務端(Java)
1).服務端加簽
@ResponseBody
@RequestMapping("/sign/{type}")
public String sign(@PathVariable("type") String type){
if (!StringUtils.isEmpty(type)&&type.equals("android")){
Map<String, String> authInfoMap = OrderInfoUtil2_0.buildAuthInfoMap(
env.getProperty("alipay.pid"),
env.getProperty("alipay.appid"),
env.getProperty("alipay.targetId"), true);
String info = OrderInfoUtil2_0.buildOrderParam(authInfoMap);
String privateKey = env.getProperty("alipay.privatekey");
String sign = OrderInfoUtil2_0.getSign(authInfoMap, privateKey, true);
final String authInfo = info + "&" + sign;
return authInfo;
}
return null;
}
2).配置
增加配置pid以及targetId。
其中pid爲商戶pid,targetId爲商戶自定義的id
# 商戶pid
alipay.pid =
# TARGET_ID
# 支付寶賬戶登錄授權業務:入參target_id值,這個target_id參數是商戶自定義的,不需要獲取。
alipay.targetId =
3).獲取用戶信息
@ResponseBody
@RequestMapping("/getalipayMobileUserInfo")
public Result getALiPayMobileUserInfo(HttpServletRequest httpServletRequest) {
String appId = httpServletRequest.getParameter("app_id");
String scope = httpServletRequest.getParameter("scope");
String authCode = httpServletRequest.getParameter("auth_code");
if (appId == null || scope == null || authCode == null ){
Result.error(CodeMsg.PARAM_ISNULL);
}
// 判定appid是否是我們的 Android傳過來的Appid和服務單Appid有點差異
if(!appId.contains(env.getProperty("alipay.appid").trim())){
Result.error(CodeMsg.ILLEGAL_PARAM);
}
AlipayClient alipayClient = new DefaultAlipayClient(
env.getProperty("alipay.serverUrl"),
env.getProperty("alipay.appid"),
env.getProperty("alipay.privatekey"),
"json", "UTF-8",
env.getProperty("alipay.publickey"),
"RSA2"); //獲得初始化的AlipayClient
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();//創建API對應的request類
request.setCode(authCode);
request.setGrantType("authorization_code");
try {
AlipaySystemOauthTokenResponse oauthTokenResponse = alipayClient.execute(request);//通過alipayClient調用API,獲得對應的response類
log.info("AccessToken:"+oauthTokenResponse.getAccessToken());
// 獲取用戶信息
AliPayUserInfo aliPayUserInfo = getAliPayUserInfo(alipayClient,oauthTokenResponse.getAccessToken());
if (aliPayUserInfo==null){
Result.error(CodeMsg.FAIL_GETUSERINFO);
}
// 統一返回結果。
return Result.success(aliPayUserInfo);
} catch (AlipayApiException e) {
//處理異常
e.printStackTrace();
}
return Result.error(CodeMsg.UNKNOW_ERROR);
}
五、注意事項:
1.Android4.0以上版本,發送網絡請求時,必須是以線程異步的方式發送請求,否則發送請求會失敗。
附錄:
App支付寶登錄授權參數
https://docs.open.alipay.com/218/105327