1.准備工作
移動應用微信登錄是基於OAuth2.0協議標准 構建的微信OAuth2.0授權登錄系統。
在進行微信OAuth2.0授權登錄接入之前,在微信開放平台注冊開發者帳號,並擁有一個已審核通過的移動應用,並獲得相應的AppID和AppSecret,申請微信登錄且通過審核后,可開始接入流程。
2.授權流程說明
第三方發起微信授權登錄請求,微信用戶允許授權第三方應用后,微信會拉起應用或重定向到第三方網站,並且帶上授權臨時票據code參數;
通過code參數加上AppID和AppSecret等,通過API換取access_token;
通過access_token進行接口調用,獲取用戶基本數據資源或幫助用戶實現基本操作。
3. 獲取access_token時序圖:
4. maven依賴
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>
5.在application.yml文件中配置你的
#第三方微信登錄(用你自己的)
#appID App的ID
#appSecret
weixinconfig:
weixinappID: wxf7865421a3c4d5f
weixinappSecret: 6cdbe6d4ce6sbcf0593c913d8a0ce12
創建配置類
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix="weixinconfig")
public class WeixinLoginProperties {
private String weixinappID; // 商戶appid
private String weixinappSecret; // 私鑰 pkcs8格式的
public String getWeixinappID() {
return weixinappID;
}
public void setWeixinappID(String weixinappID) {
this.weixinappID = weixinappID;
}
public String getWeixinappSecret() {
return weixinappSecret;
}
public void setWeixinappSecret(String weixinappSecret) {
this.weixinappSecret = weixinappSecret;
}
}
6.第一步:請求CODE
這一步客戶端會把code傳過來 ,不用你操心
7.第二步:通過code獲取access_token
package io.renren.api.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.configurationprocessor.json.JSONException;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import io.renren.api.dao.TpAccesstokenMapper;
import io.renren.api.dao.TpUsersMapper;
import io.renren.api.entity.TpAccesstoken;
import io.renren.api.entity.TpAccesstokenExample;
import io.renren.api.entity.TpAccumulativeAward;
import io.renren.api.entity.TpUsers;
import io.renren.api.entity.TpUsersExample;
import io.renren.api.properties.WeixinLoginProperties;
import io.renren.api.service.TpAccesstokenService;
import io.renren.api.service.TpAccumulativeAwardService;
import io.renren.api.service.TpUsersService;
import io.renren.common.utils.R;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.URI;
import java.util.List;
import javax.annotation.Resource;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
/**
* 第三方微信登錄
* @author Administrator
*
*/
@SuppressWarnings("deprecation")
@Controller
@RequestMapping("/api")
public class WeXinController {
//微信公眾平台申請
//應用唯一標識,在微信開放平台提交應用審核通過后獲得 appID
//應用密鑰AppSecret,在微信開放平台提交應用審核通過后獲得 appSecret
//TpAccesstoken 用來保存微信返回的用戶信息oppid等
@Resource
private WeixinLoginProperties weixinLoginProperties;
@Autowired
TpUsersService tpUsersService;
@Autowired
TpUsersMapper tpUsersMapper;
@Autowired
TpAccesstokenService tpAccesstokenService;
@Autowired
TpAccesstokenMapper tpAccesstokenMapper;
@Autowired
TpAccumulativeAwardService tpAccumulativeAwardService;
/**
* 獲取accessToken,該步驟返回的accessToken期限為一個月
*
* @param code
* @return
* @throws Exception
*/
@SuppressWarnings("all")
@RequestMapping("weixincallback")
@ResponseBody
public R getAccessToken(String code) throws Exception {
String appID = weixinLoginProperties.getWeixinappID();
String appSecret = weixinLoginProperties.getWeixinappSecret();
String accesstoken;
String openid = null;
String refreshtoken;
int expiresIn;
String unionid;//可通過獲取用戶基本信息中的unionid來區分用戶的唯一性,因為只要是同一個微信開放平台帳號下的移動應用、網站應用和公眾帳號,
//用戶的unionid是唯一的。換句話說,同一用戶,對同一個微信開放平台下的不同應用,unionid是相同的。
if (code != null) {
System.out.println(code);
}
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appID+"&secret="+appSecret+"&code="+code+"&grant_type=authorization_code";
URI uri = URI.create(url);
org.apache.http.client.HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(uri);
HttpResponse response;
try {
response = client.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
StringBuilder sb = new StringBuilder();
for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
sb.append(temp);
}
JSONObject object = new JSONObject(sb.toString().trim());
System.out.println("object:"+object);
accesstoken = object.getString("access_token");
System.out.println("accesstoken:"+accesstoken);
openid = object.getString("openid");
System.out.println("openid:"+openid);
refreshtoken = object.getString("refresh_token");
System.out.println("refreshtoken:"+refreshtoken);
expiresIn = (int) object.getLong("expires_in");
unionid = object.getString("unionid");
// 將用戶信息保存到數據庫
//1.先查詢用戶是否是第一次第三方登錄如果是第一次那么是將用戶信息添加到數據庫 如果不是那么是更新到數據庫
TpUsers userInfo = getUserInfo(accesstoken,openid);
Integer userId = userInfo.getUserId();
TpAccesstokenExample example = new TpAccesstokenExample();
example.createCriteria().andOpenidEqualTo(openid);
List<TpAccesstoken> list = tpAccesstokenMapper.selectByExample(example);
if(list!=null&&list.size()>0) {
//那么該用戶不是第一次 執行更新操作
TpAccesstoken tpAccesstoken = list.get(0);
tpAccesstoken.setAccesstoken(accesstoken);
tpAccesstoken.setUserId(userId);
tpAccesstoken.setExpiresIn(expiresIn);
tpAccesstoken.setOpenid(openid);
tpAccesstoken.setRefreshtoken(refreshtoken);
tpAccesstokenService.save(tpAccesstoken);
}else {
TpAccesstoken tpAccesstoken=new TpAccesstoken();
tpAccesstoken.setUserId(userId);
tpAccesstoken.setAccesstoken(accesstoken);
tpAccesstoken.setExpiresIn(expiresIn);
tpAccesstoken.setOpenid(openid);
tpAccesstoken.setRefreshtoken(refreshtoken);
tpAccesstokenService.save(tpAccesstoken);
//tpAccesstokenService.insertAccesstoken(userId,openid, accesstoken, expiresIn, refreshtoken);
}
//refreshAccessToken(openid);
System.out.println("Openid"+userInfo.getOpenid());
return R.ok().put("userInfo", userInfo).put("openid", openid);
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return R.ok().put("openid", openid);
}
/*
*
* 1 { 2 "access_token":"ACCESS_TOKEN", 3 "expires_in":7200, 4
* "refresh_token":"REFRESH_TOKEN", 5 "openid":"OPENID", 6 "scope":"SCOPE", 7
* "unionid":"o6_bmasdasdsad6_2sgVt7hMZOPfL" 8 } 復制代碼 復制代碼 參數 說明 access_token
* 接口調用憑證 expires_in access_token 接口調用憑證超時時間,單位(秒) refresh_token
* 用戶刷新access_token openid 授權用戶唯一標識 scope 用戶授權的作用域,使用逗號(,)分隔 unionid
* 只有在用戶將公眾號綁定到微信開放平台帳號后,才會出現該字段。
*
*/
/**
* 刷新token
*
* @param openID
* @return
*/
@SuppressWarnings({ "unused", "resource" })
private void refreshAccessToken(String openid) {
String refreshtoken=null;
TpAccesstoken tpAccesstoken=new TpAccesstoken();
String appID = weixinLoginProperties.getWeixinappID();
String appSecret = weixinLoginProperties.getWeixinappSecret();
TpAccesstokenExample example = new TpAccesstokenExample();
example.createCriteria().andOpenidEqualTo(openid);
List<TpAccesstoken> list = tpAccesstokenMapper.selectByExample(example);
if(list!=null&&list.size()>0) {
tpAccesstoken = list.get(0);
refreshtoken = tpAccesstoken.getRefreshtoken();
}
String uri = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="+appID+"&grant_type=refresh_token&refresh_token="+refreshtoken;
org.apache.http.client.HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(URI.create(uri));
try {
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
BufferedReader reader = new BufferedReader(
new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
StringBuilder builder = new StringBuilder();
for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
builder.append(temp);
}
JSONObject object = new JSONObject(builder.toString().trim());
String accessToken = object.getString("access_token");
String refreshToken = object.getString("refresh_token");
openid = object.getString("openid");
int expires_in = (int) object.getLong("expires_in");
tpAccesstoken.setAccesstoken(accessToken);
tpAccesstoken.setExpiresIn(expires_in);
tpAccesstoken.setOpenid(openid);
tpAccesstoken.setRefreshtoken(refreshToken);
tpAccesstokenService.save(tpAccesstoken);
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 根據accessToken獲取用戶信息
*
* @param accessToken
* @param openID
* @return
* @throws Exception
*/
@SuppressWarnings({ "unused", "resource" })
public TpUsers getUserInfo(String accessToken, String openID) throws Exception {
String appID = weixinLoginProperties.getWeixinappID();
String appSecret = weixinLoginProperties.getWeixinappSecret();
String uri = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openID;
org.apache.http.client.HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(URI.create(uri));
try {
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
BufferedReader reader = new BufferedReader(
new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
StringBuilder builder = new StringBuilder();
for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
builder.append(temp);
}
JSONObject object = new JSONObject(builder.toString().trim());
String country = object.getString("country");
String nikeName = object.getString("nickname");
String unionid = object.getString("unionid");
String province = object.getString("province");
String city = object.getString("city");
String openid = object.getString("openid");
String sex = object.getString("sex");
String headimgurl = object.getString("headimgurl");
String language = object.getString("language");
BigDecimal bigDecimal=new BigDecimal(0.0);
TpUsersExample example=new TpUsersExample();
example.createCriteria().andOpenidEqualTo(openid);
List<TpUsers> list = tpUsersMapper.selectByExample(example);
if(list!=null&&list.size()>0) {
TpUsers tpUsers = list.get(0);
System.out.println("---------");
return tpUsers;
}else {
TpUsers tpUsers=new TpUsers();
tpUsers.setOauth("wx");
tpUsers.setOpenid(openid);
tpUsers.setUnionid(unionid);
tpUsers.setUserName(nikeName);
tpUsers.setUserMoney(bigDecimal);
tpUsersService.save(tpUsers);
System.out.println("+++++++");
return tpUsers;
}
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
@RequestMapping("/isaccesstoken")
@SuppressWarnings({ "resource" })
private boolean isAccessTokenIsInvalid(String accessToken,String openID) {
String url = "https://api.weixin.qq.com/sns/auth?access_token=" + accessToken + "&openid=" + openID;
URI uri = URI.create(url);
org.apache.http.client.HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(uri);
HttpResponse response;
try {
response = client.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
StringBuilder sb = new StringBuilder();
for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
sb.append(temp);
}
JSONObject object = new JSONObject(sb.toString().trim());
/* {
"errcode":0,"errmsg":"ok"
}
錯誤的Json返回示例:
{
"errcode":40003,"errmsg":"invalid openid"
}*/
int errorCode = object.getInt("errcode");
if (errorCode == 0) {
return true;
}
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return false;
}
}
獲取第一步的code后,請求以下鏈接進行refresh_token:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
參數說明
參數 是否必須 說明
appid 是 應用唯一標識
grant_type 是 填refresh_token
refresh_token 是 填寫通過access_token獲取到的refresh_token參數
返回說明
正確的返回:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
參數 說明
access_token 接口調用憑證
expires_in access_token接口調用憑證超時時間,單位(秒)
refresh_token 用戶刷新access_token
openid 授權用戶唯一標識
scope 用戶授權的作用域,使用逗號(,)分隔
錯誤返回樣例:
{"errcode":40030,"errmsg":"invalid refresh_token"}
刷新或續期access_token使用
接口說明
access_token是調用授權關系接口的調用憑證,由於access_token有效期(目前為2個小時)較短,當access_token超時后,可以使用refresh_token進行刷新,access_token刷新結果有兩種:
1.若access_token已超時,那么進行refresh_token會獲取一個新的access_token,新的超時時間;
2.若access_token未超時,那么進行refresh_token不會改變access_token,但超時時間會刷新,相當於續期access_token。
refresh_token擁有較長的有效期(30天)且無法續期,當refresh_token失效的后,需要用戶重新授權后才可以繼續獲取用戶頭像昵稱。
請求方法
使用/sns/oauth2/access_token接口獲取到的refresh_token進行以下接口調用:
http請求方式: GET
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
參數說明
參數 是否必須 說明
appid 是 應用唯一標識
grant_type 是 填refresh_token
refresh_token 是 填寫通過access_token獲取到的refresh_token參數
返回說明
正確的返回:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
參數 說明
access_token 接口調用憑證
expires_in access_token接口調用憑證超時時間,單位(秒)
refresh_token 用戶刷新access_token
openid 授權用戶唯一標識
scope 用戶授權的作用域,使用逗號(,)分隔
錯誤返回樣例:
{
"errcode":40030,"errmsg":"invalid refresh_token"
}
獲取用戶個人信息(UnionID機制)
接口說明
此接口用於獲取用戶個人信息。開發者可通過OpenID來獲取用戶基本信息。特別需要注意的是,如果開發者擁有多個移動應用、網站應用和公眾帳號,可通過獲取用戶基本信息中的unionid來區分用戶的唯一性,因為只要是同一個微信開放平台帳號下的移動應用、網站應用和公眾帳號,用戶的unionid是唯一的。換句話說,同一用戶,對同一個微信開放平台下的不同應用,unionid是相同的。請注意,在用戶修改微信頭像后,舊的微信頭像URL將會失效,因此開發者應該自己在獲取用戶信息后,將頭像圖片保存下來,避免微信頭像URL失效后的異常情況。
請求說明
http請求方式: GET
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
參數說明
參數 是否必須 說明
access_token 是 調用憑證
openid 是 普通用戶的標識,對當前開發者帳號唯一
lang 否 國家地區語言版本,zh_CN 簡體,zh_TW 繁體,en 英語,默認為zh-CN
返回說明
正確的Json返回結果:
{
"openid":"OPENID",
"nickname":"NICKNAME",
"sex":1,
"province":"PROVINCE",
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
"privilege":[
"PRIVILEGE1",
"PRIVILEGE2"
],
"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
參數 說明
openid 普通用戶的標識,對當前開發者帳號唯一
nickname 普通用戶昵稱
sex 普通用戶性別,1為男性,2為女性
province 普通用戶個人資料填寫的省份
city 普通用戶個人資料填寫的城市
country 國家,如中國為CN
headimgurl 用戶頭像,最后一個數值代表正方形頭像大小(有0、46、64、96、132數值可選,0代表640*640正方形頭像),用戶沒有頭像時該項為空
privilege 用戶特權信息,json數組,如微信沃卡用戶為(chinaunicom)
unionid 用戶統一標識。針對一個微信開放平台帳號下的應用,同一用戶的unionid是唯一的。
建議:
開發者最好保存unionID信息,以便以后在不同應用之間進行用戶信息互通。
錯誤的Json返回示例:
{
"errcode":40003,"errmsg":"invalid openid"
}
工具類
package io.renren.common.utils;
import java.util.HashMap;
import java.util.Map;
/**
* 返回數據
*
* @author chenshun
* @email sunlightcs@gmail.com
* @date 2016年10月27日 下午9:59:27
*/
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public R() {
put("code", 0);
put("msg", "success");
}
public static R error() {
return error(500, "未知異常,請聯系管理員");
}
public static R error(String msg) {
return error(500, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok() {
return new R();
}
@Override
public R put(String key, Object value) {
super.put(key, value);
return this;
}
}
————————————————
原文鏈接:https://blog.csdn.net/weixin_42694286/java/article/details/84344786