使用策略模式实现微信扫码登录
本人该篇博客是按照完全小白的角度进行编写,从哪里登录查找开发API文档、开发原理及开发代码进行讲解,希望对有需要的博友有所帮助。
一、准备工作
(一)进入微信公众号测试平台
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login&token=579271486&lang=zh_CN
(二)修改oauth2.0网页授权回调页面域名
(三)微信网页授权API地址
登录进入微信公众平台测试账号后,点击“网页授权获取用户基本信息”链接则进入对应API文档
二、网页授权开发原理
该原理API文档已说明,并每个步骤的参数都已明确说明
三、代码编写
(一)数据库代码
向数据库中插入时要把APPID及授权码替换成你自己的。
INSERT INTO `cyb_union_login` VALUES ('2', '腾讯微信联合登陆', 'cyb_weixin', 'weiXinUnionLoginStrategy', 'wx5c43fde3c9733d9e', 'b8b217126c33a5fb7074927d5e72a81a', 'http://www.cyb.com:7070/login/oauth/callback?unionPublicId=cyb_weixin', 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx5c43fde3c9733d9e&redirect_uri=http%3A%2F%2Fwww.cyb.com%3A7070%2Flogin%2Foauth%2Fcallback%3FunionPublicId%3Dmayikt_weixin&response_type=code&scope=snsapi_userinfo&state=111#wechat_redirect ', '1');
(二)联合登录API接口
import com.alibaba.fastjson.JSONObject; import com.cyb.base.BaseResponse; import io.swagger.annotations.Api; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @Api(tags = "联合登陆接口") public interface MemberUnionLoginService { /** * 根据不同的联合登陆id * * @param unionPublicId * @return */ @GetMapping("/unionLogin") BaseResponse<String> unionLogin(@RequestParam("unionPublicId") String unionPublicId); /** * 联合登陆回调接口 * * @return */ @GetMapping("/login/oauth/callback") BaseResponse<JSONObject> unionLoginCallback(@RequestParam("unionPublicId") String unionPublicId); }
(三)联合登录接口实现类
import com.alibaba.fastjson.JSONObject; import com.cyb.base.BaseApiService; import com.cyb.base.BaseResponse; import com.cyb.member.api.service.MemberUnionLoginService; import com.cyb.member.impl.entitydo.UnionLoginDo; import com.cyb.member.impl.mapper.UnionLoginMapper; import com.cyb.member.impl.strategy.UnionLoginStrategy; import com.cyb.utils.SpringContextUtils; import com.cyb.utils.TokenUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; @RestController public class MemberUnionLoginServiceImpl extends BaseApiService implements MemberUnionLoginService { @Autowired private UnionLoginMapper unionLoginMapper; @Autowired private TokenUtils tokenUtils; @Override public BaseResponse<String> unionLogin(String unionPublicId) { if (StringUtils.isEmpty(unionPublicId)) { return setResultError("unionPublicId不能为空"); } // 根据渠道id查询 联合基本信息 UnionLoginDo unionLoginDo = unionLoginMapper.selectByUnionLoginId(unionPublicId); if (unionLoginDo == null) { return setResultError("该渠道可能已经关闭或者不存在"); } String state = tokenUtils.createToken("member.unionLogin", ""); String requestAddres = unionLoginDo.getRequestAddress() + "&state=" + state; JSONObject dataObjects = new JSONObject(); dataObjects.put("requestAddres", requestAddres); return setResultSuccess(dataObjects); } @Override public BaseResponse<JSONObject> unionLoginCallback(String unionPublicId) { if (StringUtils.isEmpty(unionPublicId)) { return setResultError("unionPublicId不能为空"); } // 根据渠道id查询 联合基本信息 UnionLoginDo unionLoginDo = unionLoginMapper.selectByUnionLoginId(unionPublicId); if (unionLoginDo == null) { return setResultError("该渠道可能已经关闭或者不存在"); } String unionBeanId = unionLoginDo.getUnionBeanId(); if (StringUtils.isEmpty(unionBeanId)) { return setResultError("系统参数错误"); } // 从Spring容器中根据beanid 查找到我们的策略类 UnionLoginStrategy unionLoginStrategy = SpringContextUtils.getBean(unionBeanId, UnionLoginStrategy.class); // 根据当前线程获取request对象 HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); String openId = unionLoginStrategy.unionLoginCallback(request, unionLoginDo); if (StringUtils.isEmpty(openId)) { return setResultError("系统错误"); } JSONObject jsonObject = new JSONObject(); jsonObject.put("openId", openId); jsonObject.put("unionPublicId", unionPublicId); String openToken = tokenUtils.createToken("cyb.unionLogin.", jsonObject.toJSONString()); JSONObject dataToken = new JSONObject(); dataToken.put("openToken", openToken); return setResultSuccess(dataToken); } }
(四)联合登录策略接口
/* * 联合登录策略接口 * @Author 陈远波 * @Date 2020-04-11 */ public interface UnionLoginStrategy { String unionLoginCallback(HttpServletRequest request, UnionLoginDo unionLoginDo); UserDo getDbOpenId(String openId); }
(五)微信策略模式对应类
import com.alibaba.fastjson.JSONObject; import com.cyb.http.HttpClientUtils; import com.cyb.member.impl.entitydo.UnionLoginDo; import com.cyb.member.impl.entitydo.UserDo; import com.cyb.member.impl.mapper.UserMapper; import com.cyb.member.impl.strategy.UnionLoginStrategy; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; /* * * @Author 陈远波 * @Date 2020-04-11 */ @Component public class WeiXinUnionLoginStrategy implements UnionLoginStrategy { @Value("${cyb.login.wx.accesstoken}") private String weixinAccessTokenAddres; @Autowired private UserMapper userMapper; @Override public String unionLoginCallback(HttpServletRequest request, UnionLoginDo unionLoginDo) { String code = request.getParameter("code"); if (StringUtils.isEmpty(code)) { return null; } // 1.根据授权码获取accessToken 和openid // 获取微信newWeixinAccessTokenAddres String newWeixinAccessTokenAddres = weixinAccessTokenAddres.replace("APPID", unionLoginDo.getAppId() + "").replace("SECRET", unionLoginDo.getAppKey()).replace("CODE", code); JSONObject accessTokenResult = HttpClientUtils.httpGet(newWeixinAccessTokenAddres); if (accessTokenResult == null) { return null; } boolean errcode = accessTokenResult.containsKey("errcode"); if (errcode) { return null; } // 获取openid String openid = accessTokenResult.getString("openid"); if (StringUtils.isEmpty(openid)) { return null; } return openid; } @Override public UserDo getDbOpenId(String openId) { return userMapper.selectByOpenId(openId); } }
(六)联合登录mapper
import com.cyb.member.impl.entitydo.UnionLoginDo; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; /* * 联合登录mapper * @Author 陈远波 * @Date 2020-04-11 */ public interface UnionLoginMapper { @Select("SELECT ID AS ID ,union_name AS unionname ,\n" + "union_public_id AS unionpublicid, union_bean_Id as unionBeanId, app_id AS appid,\n" + "app_key AS appkey,redirect_uri as redirecturi,\n" + "request_address as requestaddress,is_availability as isavailability\n" + " FROM meite_union_login where union_public_id=#{unionPublicId} and is_availability='1'") UnionLoginDo selectByUnionLoginId(@Param("unionPublicId") String unionPublicId); }
(七)配置文件
cyb: login: token: prefix: memberlogin channel: pc,android,ios qq: accesstoken: https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id={client_id}&client_secret={client_secret}&code={code}&redirect_uri={redirect_uri} openid: https://graph.qq.com/oauth2.0/me?access_token= wx: accesstoken: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
四、注意事项
(一)请使用客户端进行测试
在浏览器上使用后端生成的链接在浏览器上运行时会报“请使用客户端进行测试”
报以下错误
解决办法:
进入https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
下载stable Build 安装,将生成的requestAddres的链接在该模拟器中运行
如果授权过一次,第二次则不会显示右侧弹框,如果想显示则在工具右上角清除缓存即可
(二)Oauth2.0回调地址一致性
Oauth2.0回调地址的域名要与数据库中设置的回调地址域名一致,不然会出现以下错误
“当前账号未在公众平台绑定,无法调试此授权登录链接”
如果对以上内容有疑问的可以私聊本人,转载请说明出处,本人博客地址为:https://www.cnblogs.com/chenyuanbo/