微信第三方登录、小程序登录

一、统一登录时序图参考

二、微信一键登录实现

1、前端(ios/androd等)获取微信授权code、具体操作请参考微信开放文档

iOS 平台应用授权登录接入代码示例(请参考 iOS 接入指南):


-(void)sendAuthRequest
{
	//构造 SendAuthReq 结构体
	SendAuthReq* req =[[[SendAuthReq alloc]init]autorelease];
	req.scope = @"snsapi_userinfo";
	req.state = @"123";
	//第三方向微信终端发送一个 SendAuthReq 消息结构
	[WXApi sendReq:req];
}

Android 平台应用授权登录接入代码示例(请参考 Android 接入指南):

{
	// send oauth request
	Final SendAuth.Req req = new SendAuth.Req();
	req.scope = "snsapi_userinfo";
	req.state = "wechat_sdk_demo_test";
	api.sendReq(req);
}

2、后台一键登录、绑定后台账号登录接口实现(accessTokenUrl参考地址:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.ruoyi.common.result.CommonResult;
import com.ruoyi.growth.api.domain.wechat.WechatAccessTokenDO;
import com.ruoyi.growth.api.domain.wechat.WechatAccessTokenVO;
import com.ruoyi.growth.api.domain.wechat.WechatBangdingPhoneNODO;
import com.ruoyi.growth.core.entity.UserCommonEntity;
import com.ruoyi.growth.core.mapper.user.UserMapper;
import com.ruoyi.system.domain.vo.LoginVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * app调用微信一键登录 前端控制器
 */
@RestController
@Slf4j
@RequestMapping("/v1/wechat")
public class WechatAPPLoginController {
    @Resource
    WechatService wechatService;
    @Resource
    private UserMapper userMapper;
    @Value("${wechat.accessTokenUrl}")
    private String accessTokenUrl;
    @Value("${wechat.appid}")
    private String appid;
    @Value("${wechat.secret}")
    private String secret;

    /**
     * 微信一键登录
     */
    @PostMapping(value = "/oneKeyLogin")
    public CommonResult<WechatAccessTokenVO> oneKeyLogin(@RequestBody WechatAccessTokenDO wechatAccessTokenDO) {
        WechatAccessTokenVO accessToken = null;
        String requestUrl = accessTokenUrl.replace("APPID", appid)
                .replace("SECRET", secret)
                .replace("CODE", wechatAccessTokenDO.getCode());
        String jsonString = HttpUtil.get(requestUrl);
        if (StringUtils.isNotBlank(jsonString)) {
            try {
                accessToken = com.alibaba.fastjson.JSONObject.parseObject(jsonString, WechatAccessTokenVO.class);
            } catch (JSONException e) {
                // 获取token失败
                log.error("获取token失败, jsonString:{}", jsonString);
                return CommonResult.failed(jsonString);
            }
        }
        //根据Unionid查询是否存在
        LambdaQueryWrapper<UserCommonEntity> lambdaQueryWrapper = new LambdaQueryWrapper<UserCommonEntity>();
        lambdaQueryWrapper.eq(UserCommonEntity::getUnionid, accessToken.getUnionid());
        UserCommonEntity userCommonEntity = userMapper.selectOne(lambdaQueryWrapper);
        //存在且有手机号做登录
        if (!ObjectUtils.isEmpty(userCommonEntity) && !ObjectUtils.isEmpty(userCommonEntity.getPhonenumber())) {
            CommonResult<LoginVO> loginVOCommonResult = wechatService.userLogin(userCommonEntity);
            if (loginVOCommonResult.getCode() != 200) {
                return CommonResult.failed("微信登录失败");
            }
            accessToken.setLoginVO(loginVOCommonResult.getData());
        }
        //不存在按微信返回参数返回
        return CommonResult.success(accessToken);
    }

    /**
     * 手机号码注册或绑定登录
     */
    @PostMapping(value = "/phoneBangdingOrReg")
    public CommonResult<LoginVO> phoneBangdingOrReg(@RequestBody WechatBangdingPhoneNODO wechatBangdingPhoneNODO) {
        //根据Unionid查询是否存在
        LambdaQueryWrapper<UserCommonEntity> lambdaQueryWrapper = new LambdaQueryWrapper<UserCommonEntity>();
        lambdaQueryWrapper.eq(UserCommonEntity::getUnionid, wechatBangdingPhoneNODO.getUnionid());
        UserCommonEntity userCommonEntity = userMapper.selectOne(lambdaQueryWrapper);
        //存在则返回错误
        if (ObjectUtils.isNotEmpty(userCommonEntity)) {
            return CommonResult.failed("用户已存在不能重复绑定");
        }
        //不存在就查询绑定号码是否存在存在则直接绑定,不存在就注册
        CommonResult<UserCommonEntity> userCommonEntityCommonResult = wechatService.userRegOrBangDing(wechatBangdingPhoneNODO);
        if (ObjectUtils.isNotEmpty(userCommonEntityCommonResult) && userCommonEntityCommonResult.getCode() == 200) {
            //登录
            return wechatService.userLogin(userCommonEntityCommonResult.getData());
        }
        return CommonResult.failed(userCommonEntityCommonResult.getMessage());
    }

}

二、小程序登录(和第三方微信登录一样先获取code、然后发起登录、登录为注册就注册后登录)

1、微信授权code获取

//res是请求成功返回的数据里面包括code码,通过凭证进而换取用户登录态信息
wx.login({
  success(res) {
    console.log(res);
})

2、后台一键登录、绑定后台账号登录接口实现(accessTokenUrl参考地址:https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code&appid=APPID&secret=SECRET&js_code=CODE

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.google.gson.Gson;
import com.ruoyi.common.result.CommonResult;
import com.ruoyi.growth.api.domain.wechat.WechatAccessTokenDO;
import com.ruoyi.growth.api.domain.wechat.WechatAccessTokenVO;
import com.ruoyi.growth.api.domain.wechat.WechatBangdingPhoneNODO;
import com.ruoyi.growth.core.entity.UserCommonEntity;
import com.ruoyi.growth.core.mapper.user.UserMapper;
import com.ruoyi.system.domain.vo.LoginVO;
import com.ruoyi.system.domain.vo.WeChatPhoneVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信小程序 控制 前端控制器
 */
@RestController
@Slf4j
@RequestMapping("/v1/wechat/applet")
public class WechatAppletController {
    @Resource
    WechatService wechatService;
    @Resource
    private UserMapper userMapper;
    @Value("${applet.accessTokenUrl}")
    private String accessTokenUrl;
    @Value("${applet.getUserPhoneNoAssessTokenUrl}")
    private String getUserPhoneNoAssessTokenUrl;
    @Value("${applet.getUserPhoneNoUrl}")
    private String getUserPhoneNoUrl;
    @Value("${applet.appid}")
    private String appid;
    @Value("${applet.secret}")
    private String secret;
    @Resource
    RestTemplate restTemplate;

    private String appletAccessToken = null;

    /**
     * 小程序一键登录
     */
    @PostMapping(value = "/oneKeyLogin")
    public CommonResult<WechatAccessTokenVO> oneKeyLogin(@RequestBody WechatAccessTokenDO wechatAccessTokenDO) {
        WechatAccessTokenVO accessToken = null;
        if (ObjectUtils.isEmpty(appletAccessToken)) {
            String requestUrl = accessTokenUrl.replace("APPID", appid).replace("SECRET", secret).replace("CODE", wechatAccessTokenDO.getCode());
            String jsonString = HttpUtil.get(requestUrl);
            if (StringUtils.isNotBlank(jsonString)) {
                try {
                    accessToken = JSONObject.parseObject(jsonString, WechatAccessTokenVO.class);
                } catch (JSONException e) {
                    // 获取token失败
                    log.error("获取token失败, jsonString:{}", jsonString);
                    return CommonResult.failed(jsonString);
                }
            }
            if (accessToken != null) {
                appletAccessToken = accessToken.getAccessToken();
            }
        }
        //根据Unionid查询是否存在
        LambdaQueryWrapper<UserCommonEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserCommonEntity::getUnionid, accessToken.getUnionid());
        UserCommonEntity userCommonEntity = userMapper.selectOne(lambdaQueryWrapper);
        //存在且有手机号做登录
        if (!ObjectUtils.isEmpty(userCommonEntity) && !ObjectUtils.isEmpty(userCommonEntity.getPhonenumber())) {
            CommonResult<LoginVO> loginVOCommonResult = wechatService.userLogin(userCommonEntity);
            if (loginVOCommonResult.getCode() != 200) {
                return CommonResult.failed("微信登录失败");
            }
            accessToken.setLoginVO(loginVOCommonResult.getData());
        }
        //不存在按微信返回参数返回
        return CommonResult.success(accessToken);
    }

    /**
     * 手机号码注册或绑定登录
     */
    @PostMapping(value = "/phoneBangdingOrReg")
    public CommonResult<LoginVO> phoneBangdingOrReg(@RequestBody WechatBangdingPhoneNODO wechatBangdingPhoneNODO) {
        //根据Unionid查询是否存在
        LambdaQueryWrapper<UserCommonEntity> lambdaQueryWrapper = new LambdaQueryWrapper<UserCommonEntity>();
        lambdaQueryWrapper.eq(UserCommonEntity::getUnionid, wechatBangdingPhoneNODO.getUnionid());
        UserCommonEntity userCommonEntity = userMapper.selectOne(lambdaQueryWrapper);
        //存在则返回错误
        if (ObjectUtils.isNotEmpty(userCommonEntity)) {
            return CommonResult.failed("用户已存在不能重复绑定");
        }
        //不存在就查询绑定号码是否存在存在则直接绑定,不存在就注册
        CommonResult<UserCommonEntity> userCommonEntityCommonResult = wechatService.userRegOrBangDing(wechatBangdingPhoneNODO);
        if (ObjectUtils.isNotEmpty(userCommonEntityCommonResult) && userCommonEntityCommonResult.getCode() == 200) {
            //登录
            return wechatService.userLogin(userCommonEntityCommonResult.getData());
        }
        return CommonResult.failed(userCommonEntityCommonResult.getMessage());
    }

    /**
     * 获取用户手机号
     */
    @PostMapping(value = "/getUserPhoneNo")
    public CommonResult<WeChatPhoneVo> getUserPhoneNo(@RequestBody WechatAccessTokenDO wechatAccessTokenDO) {
        // 获取token
        if (ObjectUtils.isEmpty(appletAccessToken)) {
            getUserPhoneNoAssessTokenUrl = getUserPhoneNoAssessTokenUrl.replace("APPID", appid).replace("SECRET", secret);
            WechatAccessTokenVO accessToken = JSONObject.parseObject(HttpUtil.get(getUserPhoneNoAssessTokenUrl), WechatAccessTokenVO.class);
            appletAccessToken = accessToken.getAccessToken();
        }
        // ACCESS_TOKEN要放在url参数链接上
        getUserPhoneNoUrl = getUserPhoneNoUrl.replace("ACCESS_TOKEN", appletAccessToken);
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("code", wechatAccessTokenDO.getCode());
        HttpHeaders headers = new HttpHeaders();
        HttpEntity<Map<String, String>> httpEntity = new HttpEntity<>(paramMap, headers);
        ResponseEntity<Object> response = restTemplate.postForEntity(getUserPhoneNoUrl, httpEntity, Object.class);
        Gson gson = new Gson();
        String s = gson.toJson(response.getBody());
        WeChatPhoneVo weChatPhoneVo = gson.fromJson(s, WeChatPhoneVo.class);
        return CommonResult.success(weChatPhoneVo);
    }

    /**
     * 1个小时刷新一次
     */
    @Scheduled(cron = "0 0 */1  * * ?")
    private void flushAsstocken() {
        // 获取token
        if (ObjectUtils.isEmpty(appletAccessToken)) {
            getUserPhoneNoAssessTokenUrl = getUserPhoneNoAssessTokenUrl.replace("APPID", appid).replace("SECRET", secret);
            WechatAccessTokenVO accessToken = JSONObject.parseObject(HttpUtil.get(getUserPhoneNoAssessTokenUrl), WechatAccessTokenVO.class);
            appletAccessToken = accessToken.getAccessToken();
        }
    }
}

三、备注:因为平台要做微信收取登录、小程序登录、公众号等登录,所有用户的微信唯一标识只能用Unionid,一个微信用户对应的微信各平台(小程序、公众号等)的openID是不一样的。还有就是各平台的appid、secret不通用,比如不能用小程序的appid去调用微信授权登录的accessTokenUrl(appid、secret、accessTokenUrl都不能夸平台互换),其他实体类参考:

import com.alibaba.fastjson.annotation.JSONField;
import com.ruoyi.system.domain.vo.LoginVO;
import lombok.Data;

@Data
public class WechatAccessTokenVO {
    /**
     * 获取微信信息所需的token
     */
    @JSONField(name = "access_token")
    private String accessToken;
    /**
     * 微信获取token的返回状态
     */
    @JSONField(name = "expires_in")
    private Integer expiresIn;
    /**
     * 授权用户唯一标识
     */
    private String unionid;
    private String openid;
    /**
     * 用户授权的作用域,使用逗号(,)分隔
     */
    private String scope;
    /**
     * 微信一键登录成功后快乐成长平台给的合法token,为空就要走注册绑定流程
     */
    private LoginVO loginVO;
}
import lombok.Data;

@Data
public class WeChatPhoneVo {
    // 用户绑定的手机号(国外手机号会有区号)
    private String phoneNumber;
    // 没有区号的手机号
    private String purePhoneNumber;
    // 区号
    private String countryCode;
    // 数据水印
    private String watermark;
}
# 微信APP一键登录
wechat:
  accessTokenUrl: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
  appid: wx************53
  secret: 4************************e
# 微信小程序
applet:
  appid: wx*************a
  secret: a************************0
  # 获取基本信息的assess_token
  accessTokenUrl: https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code&appid=APPID&secret=SECRET&js_code=CODE
  # 获取用户手机号的assess_token
  getUserPhoneNoAssessTokenUrl: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET
  # 获取用户手机号
  getUserPhoneNoUrl: https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN

猜你喜欢

转载自blog.csdn.net/qq_29653373/article/details/126345562
今日推荐