微信公众号登录

news/2024/11/8 6:35:40/

整个流程,
1.前端调用授权url 接口(创建一个重定向的请求方法,访问自动回调方法wechat.mp.callbackUrl的地址)。
2.微信自动回调方法里判断该用户是需要注册还是直接登录(如果直接登录就返回token)
是注册还是登录返回到配置文件中的 wechat.mp.url前端中转地址后面拼接上参数用来标识 是注册还是登录,比如是注册就在地址后拼接 action=register 如果是直接登录就返回action=jump后面在拼接上&token=值 &openId=值 &accountId=值,用重定向的方式

微信公众号后台配置地址:微信公众平台 (微信扫码就能登录进来)

image.png

 

1.配置文件
1.1 properties文件
 

# 微信公众平台授权
wechat.mp.clientId=wxb90a4c***f2de80
wechat.mp.secret=8e4380916bac1***d11a30e0cb7af68
//该回调地址需要在公众号后台配置
wechat.mp.callbackUrl=https://gw-dev.公司域名部分.com.cn/training/h5/social/callback
//这个是前端 中转页, (验证是否注册还是登录重定向到该页面,前端根据参数判断)
wechat.mp.url=https://gw-dev.公司域名部分.com.cn/training/doc.html#/home

1.2 java代码配置文件
1.2.1 WeChatMpProperty.class(自动注入配置)
 

package com.公司域名部分.training.config;import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.List;/*** @author * @description* @date 2022/12/1*/
@Data
@ConfigurationProperties(prefix = "wechat.mp")
public class WeChatMpProperty {private String clientId;private String secret;private String callbackUrl;private String url;private String token;private String aesKey;@Value("${useRedis:true}")private Boolean useRedis;private List<Msg> msgs;@Datapublic static class Msg {private String title;private String templateId;}
}

1.2.2 WxMpConfiguration.class(公众号配置类)
 

package com.公司简写.training.config;import com.公司简写.training.common.constants.RedisConstant;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;/*** @author * @description* @date 2023/2/28*/
@AllArgsConstructor
@Configuration
@EnableConfigurationProperties(WeChatMpProperty.class)
public class WxMpConfiguration {private final WeChatMpProperty weChatMpProperty;private final StringRedisTemplate redisTemplate;@Beanpublic WxMpService wxMpService() {WxMpService service = new WxMpServiceImpl();WxMpDefaultConfigImpl configStorage;if(weChatMpProperty.getUseRedis()) {configStorage = new WxMpRedisConfigImpl(new RedisTemplateWxRedisOps(redisTemplate), RedisConstant.REDIS_MP);}else {configStorage = new WxMpDefaultConfigImpl();}configStorage.setAppId(weChatMpProperty.getClientId());configStorage.setSecret(weChatMpProperty.getSecret());service.setWxMpConfigStorage(configStorage);return service;}}

2. 生成授权url及微信回调方法
2.1 controller代码
 

package com.公司简写.training.controller.wechat.auth;import com.公司简写.boot.controller.BaseController;
import com.公司简写.training.service.SocialUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import me.zhyd.oauth.model.AuthCallback;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;/*** 公众号* @author * @date 2022/12/1*/
@Api(tags = "h5-公众号")
@RestController("h5SocialController")
@RequiredArgsConstructor
@RequestMapping("/h5/social")
public class SocialController extends BaseController {private final SocialUserService socialUserService;@GetMapping("/render")@ApiOperation(value = "生成授权url", notes = "生成授权url")@ApiImplicitParams({@ApiImplicitParam(name = "part", value = "前端跳转到页面的标识", required = false, dataType = "String"),@ApiImplicitParam(name = "type", value = "1.老师 2.家长", required = true, dataType = "Integer")})public void renderAuth(@RequestParam(required = false) final String part,@RequestParam final Integer type,HttpServletResponse response) {socialUserService.renderAuth(part,type, response);}/**
* 该方法是微信自动回调的方法
*/**@RequestMapping("/callback")@ApiOperation(value = "第三方授权登录", notes = "第三方授权登录")public void socialCallback(@RequestParam(required = false) final String part,@RequestParam final Integer type,@SpringQueryMap AuthCallback callback,HttpServletResponse response) {socialUserService.socialCallback(part,type, callback, response);}
}

serviceImpl方法

package com.公司简写.training.service.impl;import com.公司简写.boot.service.impl.BaseServiceImpl;
import com.公司简写.cloud.authen.api.api.AuthTokenApi;
import com.公司简写.cloud.authen.api.dto.AuthTokenDTO;
import com.公司简写.cloud.authen.api.vo.AuthTokenVO;
import com.公司简写.cloud.usercenter.api.api.AuthenticationApi;
import com.公司简写.cloud.usercenter.api.dto.AuthSmsCodeDTO;
import com.公司简写.cloud.usercenter.api.enums.ExceptionEnum;
import com.公司简写.cloud.usercenter.api.vo.AuthSmsCodeVO;
import com.公司简写.framework.exception.InternalOutException;
import com.公司简写.framework.utils.base.ExceptionUtils;
import com.公司简写.training.api.dto.wechat.RegisterValidCodeDTO;
import com.公司简写.training.common.constants.AppCode;
import com.公司简写.training.common.enums.TerminalEnum;
import com.公司简写.training.common.model.TrainParent;
import com.公司简写.training.common.model.TrainTeacher;
import com.公司简写.training.common.vo.SocialVO;
import com.公司简写.training.config.WeChatMpProperty;
import com.公司简写.training.service.SocialUserService;
import com.公司简写.training.service.TrainParentService;
import com.公司简写.training.service.TrainTeacherService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.request.AuthWeChatMpRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.Objects;/*** * @description: 公众号登录* * @author: * * @create: 2023/6/26 14:50*/
@Service
@Slf4j
@RequiredArgsConstructor
public class SocialUserServiceImpl extends BaseServiceImpl implements SocialUserService {private final TrainParentService parentService;private final TrainTeacherService teacherService;private final AuthTokenApi authTokenApi;private final AuthenticationApi authenticationApi;private final WeChatMpProperty weChatMpProperty;@Value("${sms_template_id}")private String sms_template_id;//part=login.html  type=1
//authorizeUrl:https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxb90*****4f2de80&redirect_uri=https%3A%2F%2Fhy-test.cunwedu.com.cn%2Fh5%2Fsocial%2Fcallback%3Fpart%3Dlogin.html%26type%3D1&response_type=code&scope=snsapi_userinfo&
//  state=56d5a46bb89e8cb1e02bdc0ff2b8ffd4#wechat_redirect@Overridepublic void renderAuth(String part, Integer type, HttpServletResponse response) {AuthRequest authRequest = getAuthRequest(part, type, response);String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());if (log.isDebugEnabled()) {log.debug("微信网页授权获取用户基本信息链接:{}", authorizeUrl);}redirect(response, authorizeUrl);}//type是标识是老师还是家长授权@Overridepublic void socialCallback(String part, Integer type, AuthCallback callback, HttpServletResponse response) {log.debug("第三方回调入参: part=[{}] ,type=[{}]", part, type);String accountId = null, account = null;try {AuthRequest authRequest = getAuthRequest(part, type, response);AuthResponse<AuthUser> authResponse = authRequest.login(callback);boolean isRegister = true;if (authResponse.ok()) {log.debug("第三方回调:第三方获取信息通过");AuthUser authUser = authResponse.getData();AuthToken authToken = authUser.getToken();String openId;String unionId = "";if (Objects.nonNull(authToken)) {openId = authToken.getOpenId();unionId = authToken.getUnionId();} else {openId = authUser.getUuid();}String finalOpenId = openId;if (type.equals(TerminalEnum.WECHAT_APPLET_TEACHER.getCode())) {//老师TrainTeacher teacher = teacherService.queryList(q -> q.eq(TrainTeacher::getOpenId, finalOpenId)).stream().findFirst().orElse(null);if (Objects.nonNull(teacher)) {isRegister = false;//把帐号赋值 用于创建tokenaccountId = teacher.getUserId();account = teacher.getUserAccount();}} else {//家长端TrainParent parent = parentService.queryList(q -> q.eq(TrainParent::getOpenId, finalOpenId)).stream().findFirst().orElse(null);if (Objects.nonNull(parent)) {isRegister = false;//把帐号赋值 用于创建tokenaccountId = parent.getUserId();account = parent.getUserAccount();}}if (isRegister) {redirectRegister(response, openId, unionId, part, type);return;} else {AuthTokenDTO dto = new AuthTokenDTO();dto.setAppCode(AppCode.CODE);dto.setUserId(accountId);dto.setUserAccount(account);AuthTokenVO token = validData(authTokenApi.create(dto));SocialVO socialVO = new SocialVO();socialVO.setAccessToken(token.getAccessToken());socialVO.setAccountId(accountId);socialVO.setOpenId(finalOpenId);socialVO.setUnionId(unionId);redirectLogin(response, socialVO, part, type);return;}} else {log.info("登录失败: 第三方授权失败Code {}, Msg {}", authResponse.getCode(), authResponse.getMsg());}} catch (Exception e) {log.error("第三方登录异常:{}", ExceptionUtils.stackTraceText(e));}errorResponse(response);}/*** 重定向到登录页** @param response* @param socialVO* @param part*/private void redirectLogin(HttpServletResponse response, SocialVO socialVO, String part, Integer type) {String url = weChatMpProperty.getUrl();url += "?action=jump&token=" + socialVO.getAccessToken()+ "&openId=" + socialVO.getOpenId()+ "&accountId=" + socialVO.getAccountId();String unionId = socialVO.getUnionId();if (StringUtils.isNotEmpty(unionId)) {url += "&unionId=" + unionId;}url = dealPart(response, part, url, "&part=", type);log.debug("登录响应:跳转URL[{}]", url);redirect(response, url);}/*** 注册 response 处理** @param response* @param openId* @param unionId* @param part*/private void redirectRegister(HttpServletResponse response, String openId, String unionId, String part, Integer type) {String url = weChatMpProperty.getUrl();url += "?action=register&openId=" + openId;if (StringUtils.isNotEmpty(unionId)) {url += "&unionId=" + unionId;}url = dealPart(response, part, url, "&part=", type);log.debug("注册响应:跳转URL[{}]", url);redirect(response, url);}/*** 重定向** @param response* @param url*/private void redirect(HttpServletResponse response, String url) {try {response.sendRedirect(url);} catch (Exception e) {throw new InternalOutException(ExceptionEnum.OFFICIAL_ERROR);}}/*** 创建 request** @param part     前端跳转到页面的标识* @param type     1.老师 2.家长* @param response* @return*/public AuthRequest getAuthRequest(String part, Integer type, HttpServletResponse response) {String callbackUrl = weChatMpProperty.getCallbackUrl();callbackUrl = dealPart(response, part, callbackUrl, "?part=", type);return new AuthWeChatMpRequest(AuthConfig.builder().clientId(weChatMpProperty.getClientId()).clientSecret(weChatMpProperty.getSecret()).redirectUri(callbackUrl).build());}/*** 处理 part** @param response* @param part* @param url* @param s* @return*/private String dealPart(HttpServletResponse response, String part, String url, String s, Integer type) {if (StringUtils.isNoneBlank(part)) {try {part = URLEncoder.encode(part, "UTF-8");} catch (Exception e) {log.info("创建request异常:{}", ExceptionUtils.stackTraceText(e));errorResponse(response);}url += s + part + "&type=" + type;}return url;}/*** 异常错误 response 处理** @param response*/private void errorResponse(HttpServletResponse response) {String url = weChatMpProperty.getUrl();url += "?action=error";log.debug("异常响应:跳转URL[{}]", url);redirect(response, url);}}

3. 微信回调方法处理完就会重定向到前端,前端根据url 后面拼接的参数判断是注册还是登录,继续往下处理


http://www.ppmy.cn/news/917813.html

相关文章

微型计算机 bios电池,16进制BIOS超频方法

近&#xff0c;在一些AMD主板BIOS中采用了16进制计数法来表示处理器、北桥倍频、电压(将主板 BIOS中的Custom P-States设置为Enable后&#xff0c;该选项即可出现)的BIOS。那么在这类主板上进行超频时应该如何设置呢&#xff1f; 采用16进制计数的BIOS 其实并不可怕&#xff0c…

power bi PP页面(power bi Desktop)

建立模型关系 1、设置多张表之间的关联关系 关联方向&#xff1a;1&#xff1a;1 &#xff08;一对一&#xff09;主表&#xff1a;主表 两张表中每条记录一一对应 1&#xff1a;* &#xff08;一对多&#xff09;主表&#xff1a;子表 主表中每一条记录不重复&#xff0c;子表…

v-model 和 .sync 深度解读

v-model 和 .sync 都是 Vue.js 中用于实现数据双向绑定的指令和修饰符。 语法糖 vs 修饰符&#xff1a; v-model 是 Vue.js 提供的一个语法糖&#xff0c;用于简化表单元素的双向绑定&#xff0c;它在内部实际上是使用了 :value 和 input 这两个指令的结合形式。.sync 是一个…

吐血整理,接口测试到接口自动化集成总结,你不知道的都在这...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 后端接口都测试什…

React jsx 中写更优雅、直观的条件运算符

为什么80%的码农都做不了架构师&#xff1f;>>> 在这篇文字中我学到了很多知识&#xff0c;同时结合工作中的一些经验也在思考一些东西。比如条件运算符 Conditional Operator condition ? expr_if_true : expr_if_false 在jsx中书写条件语句我们经常都会使用到的…

nodejs抓取https://themeforest.net网页

为什么80%的码农都做不了架构师&#xff1f;>>> 使用nodejs抓取 https://themeforest.net 网页模版预览放入mongoose。 以后找模版就不用那么费劲了&#xff0c;我是太懒了。。。 执行图片&#xff1a; 代码&#xff1a; var request require(request); var cheer…

js版的oschian分页类

为什么80%的码农都做不了架构师&#xff1f;>>> 代码如下&#xff1a; page.js代码如下 /** * 分页类 * pageCount // 分页总数 * currentPage // 当前页 * link // 连接地址 */ function Page(opts) {this.opts {// 基本配置pageCount: …

Objective-C中的一些特殊的数据类及NSLog的输出格式

Objective-C中的一些特殊的数据类及NSLog的输出格式 在Objective-C中&#xff0c;有一些我们之前并不熟悉但是经常见到的数据类型&#xff0c;比如id、nil、Nil、SEL等等。在很多文章里&#xff0c;我们都见过这些数据类型的介绍&#xff0c;但是都没有说的太清楚。 这篇文章从…