使用策略模式实现微信扫码登录
本人该篇博客是按照完全小白的角度进行编写,从哪里登录查找开发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/
浙公网安备 33010602011771号