使用策略模式实现微信扫码登录

Posted 陈远波

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用策略模式实现微信扫码登录相关的知识,希望对你有一定的参考价值。

本人该篇博客是按照完全小白的角度进行编写,从哪里登录查找开发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/

以上是关于使用策略模式实现微信扫码登录的主要内容,如果未能解决你的问题,请参考以下文章

Java实现微信扫码登录

java实现微信扫码登录功能 精讲

微信扫码登录实现

用企业微信的扫码功能,识别用户,登录自己的网站么

[小小造梦]个人应用实现微信扫码登录

微信扫码登录实现原理