整个流程,
1.前端调用授权url 接口(创建一个重定向的请求方法,访问自动回调方法wechat.mp.callbackUrl的地址)。
2.微信自动回调方法里判断该用户是需要注册还是直接登录(如果直接登录就返回token)
是注册还是登录返回到配置文件中的 wechat.mp.url前端中转地址后面拼接上参数用来标识 是注册还是登录,比如是注册就在地址后拼接 action=register 如果是直接登录就返回action=jump后面在拼接上&token=值 &openId=值 &accountId=值,用重定向的方式
微信公众号后台配置地址:微信公众平台 (微信扫码就能登录进来)
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 后面拼接的参数判断是注册还是登录,继续往下处理