1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 微信公众号开发——实现用户微信网页授权流程

微信公众号开发——实现用户微信网页授权流程

时间:2023-08-26 02:55:12

相关推荐

微信公众号开发——实现用户微信网页授权流程

😊 @ 作者: 一恍过去💖 @ 主页: /zhuocailing3390🎊 @ 社区: Java技术栈交流🎉 @ 主题: 微信公众号开发——实现用户微信网页授权流程⏱️ @ 创作时间: 12月16日

目录

准备工作授权说明yaml配置接口常量定义定义工具类用户授权获取Code通过Code 换取授权access_token及用户信息获取用户信息流程测试

准备工作

1、在本地进行联调时,为让微信端能够访问到本地服务,需要进行内网穿透,参考《本地服务器内网穿透实现(NATAPP)》

2、配置网页授权获取用户基本信息,用于告诉微信发起授权的后端服务器地址

正式公众号:在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”进行配置操作;测试沙箱环境:在 测试环境 中,进行配置网页授权

授权说明

微信授权时,分为snsapi_basesnsapi_userinfo两种授权方式

snsapi_base:用来获取进入页面的用户的 openid 的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面,不会有对于的认为操作);snsapi_userinfo:是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。

网页授权流程最主要分三步:

1、引导用户进入授权页面同意授权,获取code

2、通过 code 换取网页授权access_token(与基础支持中的access_token不同)和openid

3、通过网页授权access_token和 openid 获取用户基本信息(支持 UnionID 机制)

后端接口流程说明:

当前Demo,后端只会对前端暴漏一个接口,微信授权回调地址直接重定向到后端地址,由后端进行后续授权以及获取用户信息操作;

暴漏给前端的接口只需要传入两个参数,分别为:socpe(授权类型)、baseUrl(授权成功后重定向到前端的页面地址)

比如:

1、前端调用`wechant/code`接口,传入`socpe=snsapi_userinfo、baseUrl=/h5`2、微信回调的授权地址,直接为后端地址3、获取code、通过code获取access_token的流程由后端完成4、后端获取到openid信息后,重定向到`/h5`,比如`return "redirect:" + baseUrl + "?openid=" + openid;`

yaml配置

wx:# 来源于测试平台appid: wx79ec4331f29311b9secret: 1c79a199560f94096f26b8caa2a73a08apiUrl: https://api./openApiUrl: https://open./authRedirectUri: /wechat/auth

接口常量定义

InterfaceConstant:

public interface InterfaceConstant {/*** 用户同意授权,获取code*/String OAUTH2_AUTHORIZE = "connect/oauth2/authorize";/*** 通过 code 换取网页授权access_token*/String OAUTH2_ACCESS_TOKEN = "sns/oauth2/access_token";/*** 获取用户信息*/String OAUTH2_USERINFO = "sns/userinfo";}

定义工具类

MapUtils:

public class MapUtils {/*** Map转换为 Entity** @param params 包含参数的Map* @param t需要赋值的实体* @param <T> 类型*/public static <T> T mapToEntity(Map<String, Object> params, T t) {if (null == params) {return t;}Class<?> clazz = t.getClass();Field[] declaredFields = clazz.getDeclaredFields();try {for (Field declaredField : declaredFields) {declaredField.setAccessible(true);String name = declaredField.getName();if (null != params.get(name)) {declaredField.set(t, params.get(name));}}} catch (Exception e) {throw new RuntimeException("属性设置失败!");}return t;}/*** 将对象转换为HashMap** @param t 转换为Map的对象* @param <T> 转换为Map的类* @return Map*/public static <T> Map<String, Object> entityToMap(T t) {Class<?> clazz = t.getClass();List<Field> allField = getAllField(clazz);Map<String, Object> hashMap = new LinkedHashMap<>(allField.size());try {for (Field declaredField : allField) {declaredField.setAccessible(true);Object o = declaredField.get(t);if (null != o) {hashMap.put(declaredField.getName(), o);}}} catch (Exception e) {throw new RuntimeException("属性获取失败!");}return hashMap;}/*** 获取所有属性** @param clazz class* @param <T> 泛型* @return List<Field>*/public static <T> List<Field> getAllField(Class<T> clazz) {List<Field> fields = new ArrayList<>();Class<?> superClazz = clazz;while (null != superClazz) {fields.addAll(Arrays.asList(superClazz.getDeclaredFields()));superClazz = superClazz.getSuperclass();}return fields;}/*** 将Map参数转换为字符串** @param map* @return*/public static String mapToString(Map<String, Object> map) {StringBuffer sb = new StringBuffer();map.forEach((key, value) -> {sb.append(key).append("=").append(value.toString()).append("&");});String str = sb.toString();str = str.substring(0, str.length() - 1);return str;}/*** 将Bean对象转换Url请求的字符串** @param t* @param <T>* @return*/public static <T> String getUrlByBean(T t) {String pre = "?";Map<String, Object> map = entityToMap(t);return pre + mapToString(map);}}

用户授权获取Code

接口说明:

由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问;链接属性如下:https://open./connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

重定向说明:

通过微信接口connect/oauth2/authorize获取授权code后,微信将进行一次重定向回调,回调地址就是请求设置的redirect_uri值;

需要注意的是,微信重定向回调地址,一定要在微信公众号平台进行配置

定义请求实体类 Oauth2AuthorizeRep:

@Datapublic class Oauth2AuthorizeRep {/*** 公众号的唯一标识*/private String appid;/*** 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理*/private String redirect_uri;/*** 返回类型,请填写code*/private String response_type = "code";/*** 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)* snsapi_userinfo (弹出授权页面,可通过 openid 拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )*/private String scope;/*** 重定向后会带上 state 参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节*/private String state;}

Controller层示例:

@Slf4j@Api(tags = "公众号/订阅号开发")@Controllerpublic class WeChantController {/*** 由后端来进行授权操作(需要在微信页面打开)** @param baseUrl 前端页面地址 用于授权完成后,后端重定向到前端页面* @param scope 应用授权作用域,此处为了模拟两种情况,进行传值:*snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)*snsapi_userinfo (弹出授权页面,可通过 openid 拿到昵称、性别、所在地。 即使在未关注的情况下,只要用户授权,也能获取其信息 )* @return*/@GetMapping(value = "/code")@ApiOperation(value = "用户请求进行授权及获取信息", notes = "用户请求进行授权及获取信息")public String code(@RequestParam("baseUrl") String baseUrl, String scope) throws UnsupportedEncodingException {log.info("------ 用户请求进行授权及获取信息 ------");//通过code获取用户其信息String url = weChantService.getAuthCode(baseUrl, scope);return "redirect:" + url;}}

Service层示例:

@Service@Slf4jpublic class WeChantService {/*** 获取用户授权码** @param baseUrl* @param scope* @return*/public String getAuthCode(String baseUrl, String scope) throws UnsupportedEncodingException {String appId = wxBean.getAppid();// 设置回调地址 /wechat/auth,该地址为后端地址String redirectUri = wxBean.getAuthRedirectUri();// urlEncode处理redirectUri = URLEncoder.encode(redirectUri, "utf-8");// 组装url,在url中让state属性存方baseUrl的值String url = wxBean.getOpenApiUrl() + InterfaceConstant.OAUTH2_AUTHORIZE;// 封装url请求参数Oauth2AuthorizeRep rep = new Oauth2AuthorizeRep();rep.setAppid(appId);rep.setRedirect_uri(redirectUri);rep.setScope(scope);// 设置回调参数,需要进行urlEncode处理Map<String, String> stateMap = new HashMap<>(4);stateMap.put("baseUrl", baseUrl);stateMap.put("scope", scope);String stateMapStr = JSON.toJSONString(stateMap);stateMapStr = new String(Base64.getEncoder().encode(stateMapStr.getBytes(StandardCharsets.UTF_8)));rep.setState(stateMapStr);// 参数的顺序必须是:appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirecturl = url + MapUtils.getUrlByBean(rep) + "#wechat_redirect";// 重定向url,微信会自动访问redirectUri,进行回调return url;}}

请求示例:

需要在微信中打开以下链接(实际情况是前端在微信环境中进行调用),这里进行Demo演示,手动在微信中进行访问:/wechat/code?baseUrl=&scope=snsapi_base

其中为通过NatApp配置的内网穿透域名,保证微信功能访问到本地项目;

通过Code 换取授权access_token及用户信息

接口说明:

由于公众号的 secret 和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。

该接口就是用户授权获取Code中定义的redirectUri值,微信重定向时会进行调用

需要注意的是,微信重定向回调地址,一定要在微信公众号平台进行配置

定义请求实体类 Oauth2AccessTokenRep:

@Datapublic class Oauth2AccessTokenRep {/*** 公众号的唯一标识*/private String appid;/*** 公众号的appsecret*/private String secret;/*** 填写第一步获取的 code 参数*/private String code;/*** 填写为authorization_code*/private String grant_type = "authorization_code";}

定义响应实体类 Oauth2AccessTokenRes:

@Datapublic class Oauth2AccessTokenRes {/*** 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同*/private String access_token;/*** access_token接口调用凭证超时时间,单位(秒)*/private Integer expires_in;/*** 用户刷新access_token*/private String refresh_token;/*** 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID*/private String openid;/*** 用户授权的作用域,使用逗号(,)分隔*/private String scope;/*** 是否为快照页模式虚拟账号,只有当用户是快照页模式虚拟账号时返回,值为1*/private String is_snapshotuser;/*** 用户统一标识(针对一个微信开放平台帐号下的应用,同一用户的 unionid 是唯一的),只有当 scope 为"snsapi_userinfo"时返回* 并且公众号与微信开放平台进行了绑定才会返回*/private String unionid;}

Controller层示例:

该接口接收微信的授权回调,获取code及state值,并且在完成授权后,重定向到指定的前端页面

@Slf4j@Controllerpublic class WeChantController {/*** @param code* @param state 存放的前端页面地址,授权后回调用* @return*/@GetMapping(value = "/auth")@ApiOperation(value = "前端根据code获取信息", notes = "前端根据code获取信息")public String auth(@RequestParam(value = "code", required = false) String code, @RequestParam(value = "state", required = false) String state) throws UnsupportedEncodingException {log.info("------ 回显Code:{} ------", code);// 解析回传的 state值state = new String(Base64.getDecoder().decode(state.getBytes(StandardCharsets.UTF_8)));Map map = JSON.parseObject(state, Map.class);String baseUrl = map.get("baseUrl").toString();String scope = map.get("scope").toString();// 通过code获取用户openidString openid = weChantService.getUserAuth(code, scope);// 直接跳转到前端地址return "redirect:" + baseUrl + "?openid=" + openid;}}

Service层示例:

@Service@Slf4jpublic class WeChantService {/*** 用户授权,并且返回openid返回给前端** @param code* @param scope* @return*/public String getUserAuth(String code, String scope) {String appId = wxBean.getAppid();String secret = wxBean.getSecret();String url = wxBean.getApiUrl() + InterfaceConstant.OAUTH2_ACCESS_TOKEN;// 封装url请求参数Oauth2AccessTokenRep rep = new Oauth2AccessTokenRep();rep.setAppid(appId);rep.setSecret(secret);rep.setCode(code);url = url + MapUtils.getUrlByBean(rep);Map map = restHttpRequest.doHttp(url, HttpMethod.GET, null);if (map == null) {throw new RuntimeException("授权失败!");}Oauth2AccessTokenRes res = new Oauth2AccessTokenRes();MapUtils.mapToEntity(map, res);log.info("Oauth2AccessTokenRes:" + JSON.toJSONString(res));// 获取access_token过期时间long expiresToken = res.getExpires_in() - 100;String access_token = res.getAccess_token();String openid = res.getOpenid();log.info("------ access_token:{} ------", access_token);log.info("------ openid:{} ------", openid);// 根据openid和access_token获取用户信息,如果"snsapi_userinfo"授权方式,再调用接口获取用户信息if (scope.equals(SNS_API_USERINFO)) {getAndInsertUserInfo(openid, access_token);}return openid;}}

获取用户信息

定义请求实体类 Oauth2UserInfoRep:

@Datapublic class Oauth2UserInfoRep {/*** 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同*/private String access_token;/*** 用户的唯一标识*/private String openid;/*** 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语*/private String lang = "zh_CN";}

定义响应实体类 Oauth2UserInfoRes:

@Datapublic class Oauth2UserInfoRes {/*** 用户昵称*/private String nickname;/*** 用户的唯一标识*/private String openid;/*** 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知*/private Integer sex;/*** 用户个人资料填写的省份*/private String province;/*** 普通用户个人资料填写的城市*/private String city;/*** 国家,如中国为CN*/private String country;/*** 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),* 用户没有头像时该项为空。若用户更换头像,原有头像 URL 将失效。*/private String headimgurl;/*** 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)*/private List<String> privilege;/*** 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。*/private String unionid;}

Service层示例:

该接口在后端通过code获取accessTokenopenid后,直接调用该方法;

@Service@Slf4jpublic class WeChantService {private void getAndInsertUserInfo(String openid, String accessToken) {// 获取用户信息String url = wxBean.getApiUrl() + InterfaceConstant.OAUTH2_USERINFO;Oauth2UserInfoRep rep = new Oauth2UserInfoRep();rep.setAccess_token(accessToken);rep.setOpenid(openid);url = url + MapUtils.getUrlByBean(rep);Map userMap = restHttpRequest.doHttp(url, HttpMethod.GET, null);Oauth2UserInfoRes res = new Oauth2UserInfoRes();MapUtils.mapToEntity(userMap, res);// 打印信息log.info("UserInfo:" + JSON.toJSONString(res));}}

流程测试

注意:由于使用了NatApp进行免费的内网穿透,可以会出现域名变更情况,如果域名发生了变更需要重新在公众号平台配置网页授权域名。

1、微信中访问后端接口,地址:/wechat/code?baseUrl=&scope=snsapi_userinfo

2、弹出授权页面如下

3、同意授权后,重定向到指定的页面(测试时使用了百度页面)

4、查看百度页面的链接信息,发现url后面带上了openid,就表示授权及重定向成功,比如:/?openid=oTnaY6332ssfv4WiQBU0dES-WxJg

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。