微信公众号集成第三方开发保姆教程

750 阅读3分钟

目的1:有项目开发需要,亲自踩坑无数,网上难有合适全套的,故产出回报社会

目的2:几年了,老是白嫖不合适,出来混的总得还

目的3:处女作,凡事总会有第一次

1.流程图

微信截图_20210525195819.png

2.菜单配置

菜单可直接配置为:微信鉴权路径(固定写法)+转发后的地址(转发的地址一般为前端页面)

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx77777777&redirect_uri=http%3A%2F%2F7d.natapp.cc&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

JAVA代码如下:

/**
 * 获取微信授权
 * 此请求需在公众号菜单中配置,做为开始
 */
@ApiOperation("跳转至微信鉴权")
@RequestMapping("/wxLoginInit")
public void loginInt(HttpServletResponse response) throws IOException {
    String APP_ID = "MP_APPID";
    String BACK_URL = "BACK_URL";
    //拼接地址
    String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="
            +APP_ID
            + "&redirect_uri="
            + URLEncoder.encode(BACK_URL,"UTF-8")
            + "&response_type=code"
            + "&scope=snsapi_userinfo"
            + "&state=STATE#wechat_redirect";
    //重定向到重定向地址
    log.info("【微信网页授权】获取code,redirectURL={}", url);
    response.sendRedirect(url);
}

注意:

1.转发后的地址必须在可信域中,必须必须必须加http://,必须URLEncoder

2.微信公众号在网页授权域名中,要求将以下文件上传至填写域名或路径指向的web服务器(或虚拟主机)的目录

此文件必须放在前端前端前端根目录下,可拿nginx做代理,放在nginx/html目标下通过域名校验

3.获取code

前端页面拿到请求,从请求中截取拿到code

//从请求中截取code
let code = window.location.href.match(/code=(\S*)&/) ? window.location.href.match(/code=(\S*)&/)[1] : '';

4.拿code换取所需信息

前端拿到code后再发一个请求去后台请求,便可拿到所需信息,包括openId等

WxMpService wxMpService = WxMpConfig.getWxMpService();
//根据code获取access_toke
WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);
String lang = "zh_CN";
//根据access_token获取用户基本信息
WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(accessToken, lang);
String openId = wxUserInfo.getOpenId();

5.根据openId获取系统用户

根据拿到的openId去系统中查找是否有相对应的用户,如果有,可以直接登录生成token,如果没有,可以返回错误信息,让前端引导到登录注册页面

//根据openId去系统中查找用户
User byAccount = userService.getByAccount(openId);
if(ToolUtil.isNotEmpty(byAccount)){
    Map returnData = new HashMap<>();
    String token = authService.login(openId);
    returnData.put("token", token);
    returnData.put("wxUserInfo", wxUserInfo);
    return ResponseData.success(returnData);
}else {
    return ResponseData.error("用户未注册!");
}

6.前端存储token

本项目为uniapp框架,其他前端类似,在拿到token后,存储到LocalStorage

uni.request({
    url: 'https://www.example.com/wechat/getUserInfo', //仅为示例,并非真实接口地址。
    data: {
        'token': token
    },
    header: {
        'custom-header': 'hello' //自定义请求头信息
        'X-Requested-With': 'XMLHttpRequest',
        "Accept": "application/json",
        "Content-Type": "application/json; charset=UTF-8"
    },
    success: (res) => {
        console.log(res.data.data.token);
        this.text = 'request success';
    }
});

7.前端请求数据

所有前端请求时都从LocalStorage中取到token附加到请求头里去请求后台数据

8.完整代码

8.1 pom.xml
<!--微信公众号开发包-->
<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-mp</artifactId>
    <version>3.8.0</version>
</dependency>
8.2 后台
@Controller
@RequestMapping("/wechat")
@Slf4j
public class WechatController {

    @Autowired
    private UserService userService;

    @Autowired
    private AuthService authService;

    /**
     * 获取微信授权
     * 此请求需在公众号菜单中配置,做为开始
     */
    @ApiOperation("跳转至微信鉴权")
    @RequestMapping("/wxLoginInit")
    public void loginInt(HttpServletResponse response) throws IOException {
        String APP_ID = (String) ConstantsContext.getConstntsMap().get("MP_APPID");
        String BACK_URL = (String) ConstantsContext.getConstntsMap().get("BACK_URL");
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="
                +APP_ID
                + "&redirect_uri="
                + URLEncoder.encode(BACK_URL,"UTF-8")
                + "&response_type=code"
                + "&scope=snsapi_userinfo"
                + "&state=STATE#wechat_redirect";
        //重定向到重定向地址
        log.info("【微信网页授权】获取code,redirectURL={}", url);
        response.sendRedirect(url);
    }

    /**
     * 根据code获取个人基本信息
     * @param code
     * @return
     * @throws Exception
     */
    @PostMapping("/getUserInfo")
    @ResponseBody
    public ResponseData getUserInfo(String code,HttpServletResponse response) throws Exception {
        WxMpService wxMpService = WxMpConfig.getWxMpService();
        //根据code获取access_toke
        WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);
        String lang = "zh_CN";
        //根据access_token获取用户基本信息
        WxMpUser wxUserInfo = wxMpService.oauth2getUserInfo(accessToken, lang);
        String openId = wxUserInfo.getOpenId();
        //根据openId去系统中查找用户
        User byAccount = userService.getByAccount(openId);
        if(ToolUtil.isNotEmpty(byAccount)){
            Map returnData = new HashMap<>();
            String token = authService.login(openId);
            returnData.put("token", token);
            returnData.put("wxUserInfo", wxUserInfo);
            return ResponseData.success(returnData);
        }else {
            return ResponseData.error("用户未注册!");
        }
    }
}

8.3前端(uniapp)

封装请求api.js

const baseUrl = 'localhost:17000';

/**
 * 普通请求
 */
const httpRequest = (opts, data) => {
	let httpDefaultOpts = {
		url: baseUrl + opts.url,
		data: data,
		method: opts.method,
		header: opts.method == 'get' ? {
			'X-Requested-With': 'XMLHttpRequest',
			"Accept": "application/json",
			"Content-Type": "application/json; charset=UTF-8"
		} : {
			'X-Requested-With': 'XMLHttpRequest',
			'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
		},
		dataType: 'json',
	}
	let promise = new Promise(function(resolve, reject) {
		uni.request(httpDefaultOpts).then(
			(res) => {
				resolve(res[1])
			}
		).catch(
			(response) => {
				reject(response)
			}
		)
	})
	return promise
};

/**
 * 带Token请求
 */
const httpTokenRequest = (opts, data) => {
	let token = "";
	uni.getStorage({
		key: 'token',
		success: function(ress) {
			token = ress.data
		}
	});
	//此token是登录成功后后台返回保存在storage中的
	let httpDefaultOpts = {
		url: baseUrl + opts.url,
		data: data,
		method: opts.method,
		header: opts.method == 'get' ? {
			'Authorization': token,
			'X-Requested-With': 'XMLHttpRequest',
			"Accept": "application/json",
			"Content-Type": "application/json; charset=UTF-8"
		} : {
			'Authorization': token,
			'X-Requested-With': 'XMLHttpRequest',
			'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
		},
		dataType: 'json',
	}
	let promise = new Promise(function(resolve, reject) {
		uni.request(httpDefaultOpts).then(
			(res) => {
				resolve(res[1])
			}
		).catch(
			(response) => {
				reject(response)
			}
		)
	})
	return promise
};

export default {
	baseUrl,
	httpRequest,
	httpTokenRequest
}

App.vue

<script>
	export default {
		onLaunch: function() {
			const wxUserInfo = uni.getStorageSync('wxUserInfo');
			if (!wxUserInfo) {
				console.log('请求个人信息!');
				//从请求中截取code
				let code = window.location.href.match(/code=(\S*)&/) ? window.location.href.match(/code=(\S*)&/)[1] :
					'';
				if (code) {
					let opts = {
						url: '/wechat/getUserInfo',
						method: 'POST'
					};
					let param = {
						"code": code
					};
					this.$myRequest.httpTokenRequest(opts, param).then(
						res => {
							let token = res.data.data.token;
							let wxUserInfo = res.data.data.wxUserInfo;
							uni.setStorageSync('wxUserInfo', wxUserInfo);
							uni.setStorageSync('token', token);
						},
						error => {
							uni.navigateTo({
								url: "pages/register/register"
							})
						}
					)
				} else {
					console.error("code获取失败!!!");
					uni.navigateTo({
						url: "pages/register/register"
					})
				}
			} else {
				console.log("正常在线");
			}
		}
	}
</script>

<style>
	/*每个页面公共css */
</style>

main.js

import Vue from 'vue'
import App from './App'

Vue.config.productionTip = false

App.mpType = 'app'

import myRequest from './pages/base/api.js';
Vue.prototype.$myRequest = myRequest;

const app = new Vue({
	...App
})
app.$mount()

最后感谢掘金这个平台,真心不错