文章目录
一、前期准备
对于微信登录来说,我们需要先取注册开发者账号
登录 https://open.weixin.qq.com/,并在管理中心—移动应用中创建我们的应用。然后点击查看就可以看到属于我们的一个AppID和AppSecret。
二、数据库设计
为了整合第三方的登录所以我们需要新建一张表,此时我们本身肯定就有一张用户表其中包括user_id、password等字段。这时我们需要再建一张用户认证信息表来存放认证信息。大致包括以下几个字段
字段名 | 描述 |
---|---|
id | 自增 |
user_id | 关联用户表中的user_id |
third_type | 第三方平台类型 wx、qq等 |
third_key | 第三方平台的用户唯一标识,微信可以使用openid或unionid |
create_time | 创建时间 |
三、实现思路
先说一个通用的思路再对微信的操作进行详细展开。
- 首先app端调取用户授权,用户同意授权后返回一个用户在第三方平台的唯一标识
- 将取到的唯一标识放到用户认证信息表user_auth中去查询,如果没有查到就说明用户是第一次登录,那么就在user表中创建一个新用户并获取到user_id。将user_id和唯一标识存到user_auth表中。如果用户认证信息表中已经查询到了结果,那么我们就利用查询结果中的user_id,再去user表中进行查询获取用户的相关信息。
四、微信实现
4.1 实现流程
- app端调取用户授权页面,获取一个code并传给后端
- 后端利用code、appid、appsecret调取微信的接口获取access_token以及openid
- 利用access_token和openid就可以获取到微信用户的相关信息
如下所示为微信官方给出的时序图:
4.2 微信接口说明
4.2.1 通过 code 获取 access_token
//请求说明
GET https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
{
"access_token": "ACCESS_TOKEN",
"expires_in": 7200,
"refresh_token": "REFRESH_TOKEN",
"openid": "OPENID",
"scope": "SCOPE"
}
4.2.2 获取用户个人信息(UnionID 机制)
此接口用于获取用户个人信息。开发者可通过 OpenID 来获取用户基本信息。特别需要注意的是,如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的 unionid 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号,用户的 unionid 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid 是相同的。请注意,在用户修改微信头像后,旧的微信头像 URL 将会失效,因此开发者应该自己在获取用户信息后,将头像图片保存下来,避免微信头像 URL 失效后的异常情况。
//请求说明
GET https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
{
"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"
}
五、代码实现
接口实现类代码如下
@Override public UserInfo weChatLogin(String code) {
try {
//根据code获取 access_token信息
JSONObject jsonObject = AuthUtil.getAccessToken(code);
if(jsonObject.containsKey("access_token")){
String accessToken = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
UserInfo userInfo = new UserInfo();
// 判断用户是否注册过
UserAuths userAuths = userAuthsMapper.selectByThirdKey("wx",openid);
if (userAuths == null){
// 开始用户注册
// 根据token和openid获取用户的信息
JSONObject userInfoJsonObject = AuthUtil.getUserInfo(accessToken,openid);
if(userInfoJsonObject.containsKey("unionid")){
WXUserInfoVO wxUserInfo = userInfoJsonObject.toJavaObject(WXUserInfoVO.class);
userInfo.setNickname(wxUserInfo.getNickname());
userInfo.setName(wxUserInfo.getNickname());
userInfo.setGender(wxUserInfo.getSex());
userInfo.setAvatar(wxUserInfo.getHeadimgurl());
userInfoMapper.insertUserInfoSelective(userInfo);
Long userId = userInfo.getUserId();
if (userId == null) {
throw new ServiceException("用户基本信息注册失败");
}
// 添加用户认证信息
userAuths = new UserAuths();
userAuths.setUserId(userId);
userAuths.setThirdKey(openid);
userAuths.setThirdType("wx");
userAuthsMapper.insertSelective(userAuths);
}else {
logger.error("查询微信用户信息接口调用失败");
}
}else {
// 根据用户id查询用户的信息
List<UserInfo> userInfoList = userInfoMapper.selectUserInfoByUserId(userAuths.getUserId());
if(userInfoList.size() > 0){
userInfo = userInfoList.get(0);
}
}
}else {
logger.error("查询accessToken接口调用失败");
}
} catch (Exception e) {
throw new ServiceException("用户登录失败");
}
return userInfo;
}
工具类代码如下
package com.youngmaker.userservice.utils;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
import org.springframework.http.*;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URL;
@Slf4j
public class AuthUtil {
public static final String APPID = "xxxxxxxxxxxx";
public static final String APPSECRET = "xxxxxxxxxxxxxxxxxxxx";
/**
* @Title: getAccessToken
* @Description: 获取接口调用凭证
* @param: @return
* @return: String
*/
public static JSONObject getAccessToken(String code) {
RestTemplate restTemplate = new RestTemplate();
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + AuthUtil.APPID + "&secret=" + AuthUtil.APPSECRET + "&code=" + code
+ "&grant_type=authorization_code";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
String body = responseEntity.getBody();
// 返回结果转换为json对象
JSONObject jObject = JSONObject.parseObject(body);
return jObject;
}
public static JSONObject getUserInfo(String accessToken, String openid) {
RestTemplate restTemplate = new RestTemplate();
String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
HttpEntity<String> entity = new HttpEntity<String>(headers);
String body = restTemplate.exchange(url, HttpMethod.GET, entity, String.class).getBody();
// 返回结果转换为json对象
JSONObject jObject = JSONObject.parseObject(body);
return jObject;
}
微信用户信息实体类
package com.youngmaker.userservice.VO.request.user;
import java.util.List;
public class WXUserInfoVO {
private String openid;
private String nickname;
private int sex;
private String province;
private String city;
private String country;
private String headimgurl;
private String unionid;
private List<String> privilege;
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getHeadimgurl() {
return headimgurl;
}
public void setHeadimgurl(String headimgurl) {
this.headimgurl = headimgurl;
}
public String getUnionid() {
return unionid;
}
public void setUnionid(String unionid) {
this.unionid = unionid;
}
public List<String> getPrivilege() {
return privilege;
}
public void setPrivilege(List<String> privilege) {
this.privilege = privilege;
}
}
用户信息实体类就根据自己的具体情况写了,这里就不放具体的代码了。