4
2
0
2

Springboot实现微信登录

参考: https://www.cnblogs.com/dw3306/p/13577488.html

一、登录流程

微信小程序登录流程涉及到三个角色:小程序、开发者服务器、微信服务器

三者交互步骤如下:

第一步:小程序通过wx.login()获取code。
第二步:小程序通过wx.request()发送code到开发者服务器。
第三步:开发者服务器接收小程序发送的code,并携带appid、appsecret(这两个需要到微信小程序后台查看)、code发送到微信服务器
第四步:微信服务器接收开发者服务器发送的appid、appsecret、code进行校验。校验通过后向开发者服务器发送session_key、openid。
第五步:开发者服务器自己生成一个skey(自定义登录状态)与openid、session_key进行关联,并存到数据库中(mysql、redis等)。
第六步:开发者服务器返回生成skey(自定义登录状态)到小程序。
第七步:小程序存储skey(自定义登录状态)到本地。
第八步:小程序通过wx.request()发起业务请求到开发者服务器,同时携带skey(自定义登录状态)。
第九步:开发者服务器接收小程序发送的skey(自定义登录状态),查询skey在数据库中是否有对应的openid、session_key。
第十步:开发者服务器返回业务数据到小程序

image

二、搭建Springboot工程

三、整合微信登录

controller接口

import com.system.common.core.domain.AjaxResult;
import com.system.common.core.domain.entity.SysUser;
import com.system.common.core.domain.model.LoginUser;
import com.system.framework.web.service.TokenService;
import com.system.system.domain.wx.RemoteWxResult;
import com.system.system.domain.wx.WeChatUserInfo;
import com.system.system.domain.wx.WxRowData;
import com.system.system.service.ISysUserService;
import com.system.web.core.config.WeChatUtil;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
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;
import java.util.Date;


@RestController
@Api(tags = "微信用户登录")
@RequestMapping("wx")
public class WeChatUserLoginController {

    @Resource
    private ISysUserService iSysUserService;

    @Autowired
    private WeChatUtil weChatUtil;

    /**
     * 微信用户登录详情
     */
    @ApiImplicitParams({
            @ApiImplicitParam(value = "微信用户信息", name = "weChatUserInfo", required = true, paramType = "body", dataType = "WeChatUserInfo", dataTypeClass = WeChatUserInfo.class)
    })
    @ApiOperation(value = "微信用户登录详情", notes = "微信用户登录详情", httpMethod = "POST")
    @PostMapping("/login")
    public AjaxResult user_login(@RequestBody WeChatUserInfo weChatUserInfo) throws Base64DecodingException {
        // 2.开发者服务器 登录凭证校验接口 appId + appSecret + 接收小程序发送的code
        RemoteWxResult SessionKeyOpenId = weChatUtil.getSessionKeyOrOpenId(weChatUserInfo.getCode());
        // 3.接收微信接口服务 获取返回的参数
        String openid = SessionKeyOpenId.getOpenid();
        String sessionKey = SessionKeyOpenId.getSession_key();
        // 用户非敏感信息:rawData
        // 签名:signature
        WxRowData rawDataJson = weChatUserInfo.getRawData();
        // 4.校验签名 小程序发送的签名signature与服务器端生成的签名signature2 = sha1(rawData + sessionKey)
     //   String signature2 = DigestUtils.sha1Hex(weChatUserInfo.getRawData() + sessionKey);
       // if (!weChatUserInfo.getSignature().equals(signature2)) {
         //   return ResultInfo.error( "签名校验失败");
        //}
        //encrypteData比rowData多了appid和openid
        //JSONObject userInfo = WeChatUtil.getUserInfo(weChatUserInfo.getEncrypteData(),
        //        sessionKey, weChatUserInfo.getIv());
        // 5.根据返回的User实体类,判断用户是否是新用户,是的话,将用户信息存到数据库;不是的话,更新最新登录时间
        SysUser sysUser = iSysUserService.selectUserByUserName(openid);
        if (sysUser == null) {
            // 用户信息入库
           // 新增用户到数据库
           //略

            iSysUserService.insertUser(sysUser1);
        } else {
            // 已存在,更新用户登录时间
            
        }
        // uuid生成唯一key,用于维护微信小程序用户与服务端的会话(或者生成Token)
       //略
        //6. 把新的skey返回给小程序
        return AjaxResult.success();
    }


}

远程调用返回实体

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @Author codertl
 * @Date 2022/4/7
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RemoteWxResult implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 用户唯一标识
     */
    private String openid;

    /**
     * 会话密钥
     */
    private String session_key;

}

工具类

import com.alibaba.fastjson.JSON;
import com.system.system.domain.wx.RemoteWxResult;
import com.system.system.feign.WxRemoteClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WeChatUtil {

    @Value("${wx.miniapp.configs.appid}")
    private String appId;

    @Value("${wx.miniapp.configs.secret}")
    private String secret;

    @Autowired
    private WxRemoteClient wxRemoteClient;

    public RemoteWxResult getSessionKeyOrOpenId(String code) {
        //发送post请求读取调用微信接口获取openid用户唯一标识
        String remoteAuthCode2Session = wxRemoteClient.remoteAuthCode2Session(appId, secret, code, "authorization_code");
        return JSON.parseObject(remoteAuthCode2Session, RemoteWxResult.class);
    }
}

前端传入实体类

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@ApiModel(value = "WeChatUserInfo", description = "微信用户信息")
@Data
public class WeChatUserInfo {
    /**
     * 微信返回的code
     */
    @ApiModelProperty(value = "微信返回的code", required = true)
    private String code;
    /**
     * 非敏感的用户信息
     */
    @ApiModelProperty(value = "非敏感的用户信息", required = true)
    private WxRowData rawData;
    /**
     * 签名信息
     */
    @ApiModelProperty(value = "签名信息", required = true)
    private String signature;
    /**
     * 加密的数据
     */
    @ApiModelProperty(value = "加密的数据", required = true)
    private String encrypteData;
    /**
     * 加密密钥
     */
    @ApiModelProperty(value = "加密密钥", required = true)
    private String iv;

}

通过openFeign调用微信auth.code2Session 官网地址: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html

pom.xml

<!-- SpringBoot的依赖配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.5.9</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>3.0.4</version>
</dependency>

wxClient

package com.system.system.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Map;

/**
 * @Author codertl
 * @Date 2022/04/07
 */
@FeignClient(url = "https://api.weixin.qq.com/sns", name = "wxLogin")
public interface WxRemoteClient {

    /**
     * 远程调用登录接口
     * @param appid
     * @param secret
     * @return
     */
    @GetMapping(value = "/jscode2session")
    String remoteAuthCode2Session(
            @RequestParam("appid") String appid,
            @RequestParam("secret") String secret,
            @RequestParam("js_code") String jsCode,
            @RequestParam("grant_type") String grantType
    );
}

application.yml

# 微信登录相关
wx:
  miniapp:
    configs:
      appid: # 添加自己的appid
      secret: # 添加自己的secret

前端测试方法 地址:https://uniapp.dcloud.io/api/plugins/login.html

<template>
	<view>
		<view class="index-bg">
			<image src="../../static/wxlogin.png" mode="widthFix"></image>
			<view class="index-content">
			<view><button class="index-btn" @click="login()">微信一键登录/注册</button></view>
			</view>
		</view>
		

</template>

<script>
	export default {
		data() {
			return {
			} 
		},
		watch: {
				
		},
		methods:{
			login() {
				uni.login({
				  provider: 'weixin',
				  success: function (loginRes) {
				    // 获取用户信息
				    uni.getUserInfo({
				      provider: 'weixin',
				      success: function (infoRes) {
						 uni.request({
						 	url: "http://localhost:8010/wx/login",
							method: "POST",
							header: {
								'content-type': 'application/json'
							},
							data:{
							  code: loginRes.code, //临时登录凭证
							  rawData: infoRes.rawData, //用户非敏感信息
							  signature: infoRes.signature, //签名
							  encrypteData: infoRes.encryptedData, //用户敏感信息
							  iv: infoRes.iv //解密算法的向量
							},
							success:function(res){
								console.log(res)
								if(res.data.code == 200) {
									console.log("登录成功", res.data.msg)
								} else{
									console.log("登录失败")
								}
							},
							 fail: function(error) {
							  //调用服务端登录接口失败
							  console.log(error);
							}
						 })
				      }
				    });
				  }
				});
			}
		},
		onLoad() {
			
		},
	}
</script>
<style scoped lang="less">
.index-bg {
	width: 100%;
	padding-top: 20%;
}
.index-bg image {
	width: 100%;
	height: 100%;
}
.index-content {
	width: 500rpx;
	margin: 40% auto;
}
.index-btn {
	background-color: #2b90fa;
	color: #fff;
	font-size: 32rpx;
	width: 500rpx;
}
</style>


posted @ 2022-04-07 21:03  CoderTL  阅读(212)  评论(0编辑  收藏  举报