SpringBoot快速实现微信授权登录
文章目录
- 1.引入weixin-java-mp
- 2.配置文件配置公众号信息
- 3.加载配置信息
- 3.1加载配置属性
- 3.2初始化配置
- 3.3Controller实现
- 4.设置微信公众平台
- 5.前台请求及地址跳转
SpringBoot中快速实现微信授权回调获取用户信息,支持配置多个appId
1.引入weixin-java-mp
pom.xml
文件中引入
<!-- https://mvnrepository.com/artifact/com.github.binarywang/weixin-java-mp -->
<dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-mp</artifactId><version>4.0.0</version>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
2.配置文件配置公众号信息
application.yml
中配置
wx:mp:configs:- appId: wxdafc984af070114c # 第一个公众号的appidsecret: c5b427fdf9d925816cdc4f5420c47278 # 公众号的appsecrettoken: token # 接口配置里的Token值aesKey: # 接口配置里的EncodingAESKey值- appId: 2222 # 第二个公众号的appid,以下同上secret: 1111token: 111aesKey: 111
3.加载配置信息
3.1加载配置属性
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;/*** wechat mp properties*/
@Data
@ConfigurationProperties(prefix = "wx.mp")
public class WxMpProperties {/*** 多个公众号配置信息*/private List<MpConfig> configs;@Datapublic static class MpConfig {/*** 设置微信公众号的appid*/private String appId;/*** 设置微信公众号的app secret*/private String secret;/*** 设置微信公众号的token*/private String token;/*** 设置微信公众号的EncodingAESKey*/private String aesKey;}}
3.2初始化配置
import com.google.common.collect.Maps;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@Configuration
@EnableConfigurationProperties(WxMpProperties.class)
public class WxMpConfiguration {private WxMpProperties properties;private static Map<String, WxMpService> mpServices = Maps.newHashMap();@Autowiredpublic WxMpConfiguration(WxMpProperties properties) {this.properties = properties;}public static Map<String, WxMpService> getMpServices() {return mpServices;}@Beanpublic Object services() {final List<WxMpProperties.MpConfig> configs = this.properties.getConfigs();if (configs == null) {throw new RuntimeException("无相关配置");}mpServices = configs.stream().map(a -> {WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();configStorage.setAppId(a.getAppId());configStorage.setSecret(a.getSecret());configStorage.setToken(a.getToken());configStorage.setAesKey(a.getAesKey());WxMpService service = new WxMpServiceImpl();service.setWxMpConfigStorage(configStorage);return service;}).collect(Collectors.toMap(s -> s.getWxMpConfigStorage().getAppId(), a -> a, (o, n) -> o));return Boolean.TRUE;}}
3.3Controller实现
import com.alibaba.fastjson.JSON;
import com.cztv.news.common.util.ToUrlParamsUtils;
import com.cztv.news.portal.config.WxMpConfiguration;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;@Slf4j
@RestController
@RequestMapping("/api/wx")
public class WxOauthController {@RequestMapping("/callback")public ModelAndView wxCallback(@RequestParam String appId,@RequestParam String state,@RequestParam String code) {final WxMpService wxService = WxMpConfiguration.getMpServices().get(appId);if (!wxService.switchover(appId)) {throw new IllegalArgumentException(String.format("未找到对应appId=[%s]的配置,请核实!", appId));}try {WxOAuth2AccessToken accessToken = wxService.getOAuth2Service().getAccessToken(code);WxOAuth2UserInfo user = wxService.getOAuth2Service().getUserInfo(accessToken, null);String url = ToUrlParamsUtils.addParamToUrl(state, "wxUserInfo", JSON.toJSONString(user));return new ModelAndView("redirect:" + url);} catch (WxErrorException e) {log.error("微信appId:{}授权获取用户信息异常", appId, e);}String url = ToUrlParamsUtils.addParamToUrl(state, "wxUserInfo", null);return new ModelAndView("redirect:" + url);}}
防止URL中可能有同名参数以及?#
的拼接处理
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;/*** @Description 实体类对象转换为url参数*/
@Slf4j
public class ToUrlParamsUtils {/*** url后追加参数* 如果url包含当前参数,则输出原始url* @param url* @param param* @return*/public static String addParamToUrl(String url, String param, String paramValue) {if (StringUtils.isBlank(url)) {return StringUtils.EMPTY;}if (url.contains("?")) {if (isContainParam(url, param)) {return url;}return url.contains("#") && url.indexOf("#") > url.indexOf("?") ? url.replaceFirst("#", "&" + param + "=" + paramValue + "#") : url + "&" + param + "=" + paramValue;} else {return url.contains("#") ? url.replaceFirst("#", "?" + param + "=" + paramValue + "#") : url + "?" + param + "=" + paramValue;}}/*** 解析出url请求的路径,包括页面* @param strURL url地址* @return url路径*/public static String urlPage(String strURL) {String strPage = null;String[] arrSplit;strURL = strURL.trim().toLowerCase();arrSplit = strURL.split("[?]");if (strURL.length() > 0) {if (arrSplit.length > 1) {if (arrSplit[0] != null) {strPage = arrSplit[0];}}}return strPage;}/*** 去掉url中的路径,留下请求参数部分* @param strURL url地址* @return url请求参数部分*/private static String truncateUrlPage(String strURL) {String strAllParam = null;String[] arrSplit;strURL = strURL.trim().toLowerCase();arrSplit = strURL.split("[?]");if (strURL.length() > 1) {if (arrSplit.length > 1) {if (arrSplit[1] != null) {strAllParam = arrSplit[1];}}}return strAllParam;}/*** 解析出url参数中的键值对* 如 "index.jsp?Action=del&id=123",解析出Action:del,id:123存入map中* @param url url地址* @return url请求参数部分*/public static Map<String, String> urlRequest(String url) {Map<String, String> mapRequest = new HashMap<>();String[] arrSplit;String strUrlParam = truncateUrlPage(url);if (strUrlParam == null) {return mapRequest;}// 每个键值为一组arrSplit = strUrlParam.split("[&]");return putKVInMap(mapRequest, arrSplit);}public static Map<String, String> strToMap(String splitStr, String delimiter) {Map<String, String> map = new HashMap<>();if (StringUtils.isBlank(splitStr)) {return map;}// 以指定分隔符切分String[] arrSplit = splitStr.split("[" + delimiter + "]");return putKVInMap(map, arrSplit);}private static Map<String, String> putKVInMap(Map<String, String> map, String[] arrSplit) {for (String strSplit : arrSplit) {String[] arrSplitEqual = strSplit.split("[=]");//解析出键值if (arrSplitEqual.length > 1) {//正确解析map.put(arrSplitEqual[0], arrSplitEqual[1]);} else {if (!arrSplitEqual[0].equals("")) {//只有参数没有值,不加入map.put(arrSplitEqual[0], "");}}}return map;}/*** 请求参数中是否包含参数* @param url* @param param* @return*/public static boolean isContainParam(String url, String param) {return urlRequest(url).containsKey(param);}/*** @param clazz 参数实体类* @return String* @Description: 将实体类clazz的属性转换为url参数*/public static String getParams(Object clazz) {// 遍历属性类、属性值Field[] fields = clazz.getClass().getDeclaredFields();StringBuilder requestURL = new StringBuilder();try {boolean flag = true;String property, value;for (int i = 0; i < fields.length; i++) {Field field = fields[i];// 允许访问私有变量field.setAccessible(true);// 属性名property = field.getName();// 属性值value = Optional.ofNullable(String.valueOf(field.get(clazz))).orElse("");String params = property + "=" + value;if (flag) {requestURL.append(params);flag = false;} else {requestURL.append("&" + params);}}} catch (Exception e) {log.error("转换URL参数异常:", e);}return requestURL.toString();}}
4.设置微信公众平台
5.前台请求及地址跳转
前台访问地址
https://open.weixin.qq.com/connect/oauth2/authorize?
appid=wxdafc984af070114c&redirect_uri=http://www.xxx.com/solang/api/wx/callback?appId=wxdafc984af070114c
&response_type=code&scope=snsapi_userinfo&state=http://www.baidu.com?id=123#wechat_redirect
http://www.xxx.com/solang/api/wx/callback?appId=wxdafc984af070114c
改为服务器端回调的地址,state
微信会原样返回,此处修改为要跳转的链接地址。
用户授权后跳转会在地址上带上用户信息json参数
https://www.baidu.com/?id=123&wxUserInfo={%22openid%22:%22oCK1JuNf_CIsSBvHAnmRZMa5SMOw%22,%22nickname%22:%22suo?%22,%22sex%22:1,%22city%22:%22??%22,%22province%22:%22??%22,%22country%22:%22??%22,%22headimgurl%22:%22https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLRAEjMy5cspdSHPL91ILukRVxN1dc7qOyrRrOk4FPicFfJVFSeQpd9mNXaaEx2UGbFT3IcBicne86w/132%22,%22privilege%22:[]}
这里面跳转实现可以根据业务处理,用户信息可以存储下然后单独出个接口供前台调用。由于考虑到多个appId处理的通用性,本人这边获取到用户信息后直接拼接到前台要跳转的地址上,由前台处理。一开始考虑redirect_uri
地址使用http://www.xxx.com/solang/api/wx/callback?appId=wxdafc984af070114c&redirect=http://www.baidu.com?id=123
,然后Controller层接参数,@RequestParam String appId, @RequestParam String redirect, @RequestParam String code
,但是微信回调时&redirect=http://www.baidu.com?id=123
未带入到回调地址上,故使用state参数处理页面跳转。注意state参数微信会原样回调,所以和前台约定好形式就好。
本文参考:
https://github.com/binarywang/weixin-java-mp-demo
网页授权
SpringBoot快速实现微信授权登录相关推荐
- springboot微信授权登录
水平有限!实现方法直接找的网上的以为大神所编写的api来实现,这里主要是记录一下自己实现的过程.具体方法请参考网址:https://github.com/liyiorg/weixin-popular, ...
- uniappH5+springboot微信授权登录获取用户数据(非静默授权)
uniappH5+springboot微信授权登录获取用户数据(非静默授权) 微信网页授权开发文档 准备工作 微信公众号appid和appSecret及配置相关的ip白名单 配置网页授权域名,具体操作 ...
- code换取微信openid_微信授权登录开发的两种方式
本文主要针对微信公众号(公众平台的开发) 首先理解一个概念:OAuth: OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表), ...
- 五行代码搞定微信授权登录
Authing 通过 SDK 为开发者提供了一种快速在微信网页中获取用户信息并完成登录的方法.如果用户在微信客户端中访问第三方网页.公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑 ...
- springboot快速开发微信支付
springboot快速开发微信支付 1. POM文件添加依赖 <properties><wechat.sdk.version>3.3.7.B</wechat.sdk.v ...
- 从前后端交互逻辑出发、基于企业开发标准,Web微信授权登录系统开发项目分享
背景 首先,在网页中开发第三方登录系统的案例越来越多,用户的操作习惯也逐渐被引导改变,更加简洁有效的登录交互系统将会成为网页开发中必要的一环.从项目的用户使用背景.微信公众号的开发模式.企业代码的标准 ...
- 慕课网_《微信授权登录》学习总结
时间:2017年08月12日星期六 说明:本文部分内容均来自慕课网.@慕课网:http://www.imooc.com 教学源码:无 学习源码:https://github.com/zccodere/ ...
- html5+ mui框架 微信授权登录后跳回app无任何回调事件
2019独角兽企业重金招聘Python工程师标准>>> 微信授权登录可以调起微信,但是在微信上点击确认登陆后跳回app,但是之后无任何回掉事件. 问题原因: 1 因为我在集成Face ...
- thinkphp 微信授权登录 以及微信实现分享
<?php namespace app\wechat\controller; use think\Controller; use think\Request; /** * 微信授权登录类 * U ...
最新文章
- NeurIPS 2019 | 一种对噪音标注鲁棒的基于信息论的损失函数
- 服务器监控工具_系统管理员不可错过的 6 款服务器监控工具
- 进击的UI------------------UISegmentedControlUISlide
- Unity子线程编程无法报错
- Zemax 全新 22.1 版本产品现已发布
- HDU - 6184 Counting Stars (无向图找三元环)
- java的框架gwt介绍_GWT Portlets
- linux内核支持浮点吗,浅谈linux kernel对于浮点运算的支持
- 构建 Web 应用之 Service Worker 初探
- 研发人员欠缺的“不要脸”文化
- 《一个投资家的20年》读书笔记
- 大虾救命啊~~~~~~~~~~
- 美国大学生数学建模matlab,2019MCM美国大学生数学建模竞赛A题赛后总结
- 远程网络教学系统功能
- Docker之镜像底层原理
- Word2003表格内容无法居中的几种解决方案
- cloudera环境搭建
- 如何让APP不在settings/Notifications/Recently sent中显示(MTK6757 Android9.0)
- revit2018注册表删除_Revit软件的彻底卸载方法 注册表卸载 定稿.docx
- 魔兽世界编程宝典(6)