WeChat third-party login, mini program login

1. Unified login sequence diagram reference

 

2. WeChat one-click login implementation

1. The front-end (ios/androd, etc.) obtains the WeChat authorization code. Please refer to the WeChat open document for specific operations.

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. Implementation of backend one-click login and binding backend account login interface (accessTokenUrl reference address: 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());
    }

}

2. Mini program login (just like third-party WeChat login, first obtain the code, then initiate login, if the login is registered, register and then log in)

1. Obtain WeChat authorization code

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

2. Implementation of backend one-click login and binding backend account login interface (accessTokenUrl reference address: 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();
        }
    }
}

3. Remarks: Because the platform needs to accept WeChat login, mini program login, official account login, etc., the unique WeChat identifier of all users can only be Unionid, and the openID of each WeChat platform (mini program, official account, etc.) corresponding to a WeChat user are different. Also, the appid and secret of each platform are not universal. For example, you cannot use the appid of a mini program to call the accessTokenUrl authorized to log in with WeChat (appid, secret, and accessTokenUrl cannot be interchanged between platforms). References for other entity classes:

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

Guess you like

Origin blog.csdn.net/qq_29653373/article/details/126345562