app接入第三方微信登陆功能:
移动应用微信登录是基于OAuth2.0协议标准 构建的微信OAuth2.0授权登录系统,前提你需要到微信开放平台注册开发者帐号,并拥有一个已审核通过的移动应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。
接入微信登陆授权步骤:
1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;
2. 通过code参数加上AppID和AppSecret等,通过API换取access_token;
3. 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。
注意:同一个微信开放平台下绑定的用户,在不同的应用下用户的UnionID就是相同的,openId是不相同的。
获取access_token流程:
第一步:请求CODE:
需要app调起微信
第二步:通过code获取access_token:
请求路径:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数:
appid | 是 | 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
secret | 是 | 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得 |
code | 是 | 填写第一步获取的code参数 |
grant_type | 是 | 填authorization_code |
appid与secret需要通过注册获取。
第三步:通过access_token调用接口
获取access_token后,进行接口调用,有以下前提:
代码:
请求微信工具类:
package com.maobc.util;
import com.alibaba.fastjson.JSONObject;
import com.maobc.entity.jar.Member;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.poi.ss.formula.functions.T;
import springfox.documentation.spring.web.json.Json;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
/**
* @program: maobc-small_routine
* @description: app用户登陆
* @author: z.hw
**/
public class WeixinLoginUtils {
/**
* 微信登陆通过code获取accessToken
* @param appId
* @param userAppSecret
* @param code
* @return
* @throws Exception
*/
public StringBuilder getAccessTokenBycode(String appId,String userAppSecret,String code) throws Exception{
//查看官方文档 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317853&token=&lang=
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appId+"&secret="+
userAppSecret+"&code="+code+"&grant_type=authorization_code";
URI uri = URI.create(url);
HttpClient client = HttpClients.createDefault();
HttpGet get = new HttpGet(uri);
HttpResponse response=client.execute(get);
StringBuilder sb = new StringBuilder();
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
sb.append(temp);
}
}
return sb;
}
/**
* access_token是否有效的验证
* @param accessToken
* @param openID
* @return
*/
public boolean isAccessTokenIsInvalid(String accessToken,String openID) throws Exception{
String url = "https://api.weixin.qq.com/sns/auth?access_token=" + accessToken + "&openid=" + openID;
URI uri = URI.create(url);
HttpClient client = HttpClients.createDefault();
HttpGet get = new HttpGet(uri);
HttpResponse 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 = JSONObject.parseObject(sb.toString().trim());
int errcode = object.getInteger("errcode");
if (errcode == 0) {
//未失效
return true;
}
}
return false;
}
/**
* access_token 接口调用凭证
* expires_in access_token接口调用凭证超时时间,单位(秒)
* refresh_token 用户刷新access_token
* openid 授权用户唯一标识
* scope 用户授权的作用域,使用逗号(,)分隔
* @param APP_ID
*/
public JSONObject refreshAccessToken(String APP_ID,String refreshToken) throws Exception{
/**
* 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失效的后,需要用户重新授权后才可以继续获取用户头像昵称。
*/
String uri = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=" + APP_ID + "&grant_type=refresh_token&refresh_token=" + refreshToken;
HttpClient client = HttpClients.createDefault();
HttpGet get = new HttpGet(URI.create(uri));
HttpResponse response = client.execute(get);
JSONObject object =new JSONObject();
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);
}
object = JSONObject.parseObject(builder.toString().trim());
}
return object;
}
/**
* 得到用户基本信息
* @param accessToken
* @param openId
* @param tClass
* @return
* @throws Exception
*/
public T getAppWeiXinUserInfo(String accessToken, String openId, Class tClass) throws Exception{
String uri = "https://api.weixin.qq.com/sns/userinfo?access_token="+accessToken+"&openid="+openId;
HttpClient client = HttpClients.createDefault();
HttpGet get = new HttpGet(URI.create(uri));
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()) {
System.out.println(temp);
builder.append(temp);
}
return JSONObject.parseObject(builder.toString(), tClass);
}
return null;
}
}
controller:
/**
* @Description: app微信登陆
* @Author: z.hw
*/
@RequestMapping(value = {"getUserInfoByAppCode"})
@ApiOperation(value = "getUserInfoByAppCode", notes = "不分页", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ApiResponses(value = {
@ApiResponse(code = 404, message = "Not Found"),
@ApiResponse(code = 400, message = "No Name Provided"),
})
publicApiResult getUserInfoByApp(@Validated UserAppAuthority userAppAuthority){
return memberService.getUserInfoByApp(userAppAuthority);
}
请求体:
/**
* @program: maobc-small_routine
* @description: app微信授权
* @author: z.hw
**/
@Data
public class UserAppAuthority {
//新旧app 用来备份 自定义数据
@NotNull(message = "类型不能为空")
@NotEmpty(message = "类型不能为空")
private String type;
//重点需要 app调起微信获取到的code
@NotEmpty(message = "code不能为空")
@NotNull(message = "code不能为空")
private String code;
}
ApiResult:
@Data
@ToString(callSuper = true)
@EqualsAndHashCode
public class ApiResult implements Serializable {
/**
* 状态码
* 0表示成功
*/
private String code = "0000";
/**
* 状态信息
*/
private String msg = "调用成功";
private Object result;
。。。。。。。。。。。。。
}
业务层:
/**
* @Description: 用户app微信登陆
* @Param:
* @return:
* @Author: z.hw
*/
public ApiResult getUserInfoByApp(UserAppAuthority userAppAuthority) {
try {
Member member = new Member();
BeanUtils.copyProperties(userAppAuthority, member);
//调用微信授权
WeixinLoginUtils weixinLoginUtils = new WeixinLoginUtils();
StringBuilder stringBuilder = weixinLoginUtils.getAccessTokenBycode(commConfig.userAppID, commConfig.userAppSecret, userAppAuthority.getCode());
if (stringBuilder != null) {
if (stringBuilder.toString().trim().contains("errcode")) {
return ApiResult.Fail();
}
JSONObject object = JSONObject.parseObject(stringBuilder.toString().trim());
String accessToken = object.getString("access_token"); //接口调用凭证
String openID = object.getString("openid"); //授权用户唯一标识
//获取微信用户基本信息
WeiXinParam appWeiXinUserInfo = weixinLoginUtils.getAppWeiXinUserInfo(accessToken, openID, WeiXinParam.class);
// TODO 业务逻辑
。。。。。。。。。。。。
return ApiResult.build(member);
}
} catch (Exception e) {
e.printStackTrace();
}
return MaobcApiResult.Fail();
}
Member:
import lombok.*;
import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author
*/
@Entity
@Table(name = "cat_member")
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Member implements Serializable {
private static final long serialVersionUID = -27353316177184L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private String id;
/**
* 账号状态:0 正常;1 冻结
*/
@Column(name = "status")
private String status;
/**
* del_flag
*/
@Column(name = "del_flag")
private String delFlag;
/**
* 手机
*/
@Column(name = "mobile_phone")
private String mobilePhone;
/**
* 用户名
*/
@Column(name = "user_name")
private String userName;
/**
* 邮箱
*/
@Column(name = "email")
private String email;
/**
* 用户密码
*/
@Column(name = "password")
private String password;
/**
* 昵称
*/
@Column(name = "nickname")
private String nickname;
/**
* 性别
*/
@Column(name = "sex")
private String sex;
/**
* 头像url
*/
@Column(name = "headimgurl")
private String headimgurl;
/**
* 城市
*/
@Column(name = "city")
private String city;
/**
* 国家
*/
@Column(name = "country")
private String country;
/**
* 省份
*/
@Column(name = "province")
private String province;
/**
* 语言
*/
@Column(name = "language")
private String language;
/**
* 备注
*/
@Column(name = "remark")
private String remark;
/**
* 生日
*/
@Column(name = "birthday")
private String birthday;
/**
* 地址
*/
@Column(name = "address")
private String address;
/**
* 会员积分
*/
@Column(name = "accumulate_points")
private BigDecimal accumulatePoints;
/**
* 平台会员等级
*/
@Column(name = "level")
private Integer level;
/**
* cat_membership会员类型表的ID
*/
@Column(name = "membership_id")
private String membershipId;
/**
* 创建人
*/
@Column(name = "create_by")
private String createBy;
/**
* 创建时间
*/
@Column(name = "create_date")
private Date createDate;
/**
* 修改人
*/
@Column(name = "update_by")
private String updateBy;
/**
* 修改时间
*/
@Column(name = "update_date")
private Date updateDate;
/**
* 会员是否同意条款标识(0-未同意 1-同意)
*/
@Column(name = "item_status")
private Integer itemStatus;
private String openid;
private String unionId;
private String type;
//是否绑定手机号码
private String isBindedPhone;
private String oauthId;
private String memberId;
private String outhId;
}
WeiXinParam :
import lombok.Data;
@Data
public class WeiXinParam {
String openid = "";
String unionId = "";
String sex = "1";
String nickname = "";
String city = "";
String province = "";
String country = "";
String avatarUrl = "";
String headimgurl="";
}
参考资料:
微信开发平台:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&lang=zh_CN