网站实现微信扫码登录

1 准备工作

1.1 申请网站应用

在接入微信扫码登录之前,在微信开放平台注册开发者账号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。
在这里插入图片描述
在这里插入图片描述

找到回调授权域名这一项,修改成要回调的域名,例如:www.baidu.com 不要加http://或https://。
在这里插入图片描述

2 快速开始

2.1 微信扫码登录流程说明

在这里插入图片描述

1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;
2. 通过code参数加上AppID和AppSecret等,通过API换取access_token;
3. 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。

简单来说,我们用手机微信扫描网站应用上的微信登录二维码,会弹出是否允许拒绝按钮,当我们点击允许按钮时,微信会重定向到授权回调域地址上。若用户禁止授权,则不会发生重定向。授权回调域映射的是我们网站应用后端接口,来处理相关登录逻辑。

2.2 生成微信二维码的两种方式

2.2.1 微信提供的二维码生成URL

https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect

参数说明:

参数是否必须说明
appid应用唯一标识
redirect_uri请使用urlEncode对链接进行处理
response_type填code
scope应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login
state用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验
lang界面语言,支持cn(中文简体)与en(英文),默认为cn

正确填写微信提供的二维码生成URL参数后,在浏览器上访问会生成二维码。例如:
在这里插入图片描述

2.2.2 将微信登录二维码内嵌到自己页面

步骤1:在页面中先引入如下JS文件(支持https)

http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js

步骤2:在需要使用微信登录的地方实例以下JS对象:

 var obj = new WxLogin({
 self_redirect:true,
 id:"login_container", 
 appid: "", 
 scope: "", 
 redirect_uri: "",
  state: "",
 style: "",
 href: ""
 });

参数说明:

参数是否必须说明
self_redirecttrue:手机点击确认登录后可以在 iframe 内跳转到 redirect_uri,false:手机点击确认登录后可以在 top window 跳转到 redirect_uri。默认为 false
id第三方页面显示二维码的容器id
appid应用唯一标识,在微信开放平台提交应用审核通过后获得
scope应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可
redirect_uri重定向地址,需要进行UrlEncode
state用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验
style提供"black"、"white"可选,默认为黑色文字描述。
href自定义样式链接,第三方可根据实际需求覆盖默认样式。

2.3 系统微信扫码登录示例

2.3.1 流程说明

① 对于我们系统来说,微信扫码登录的本质就是将自己系统用户与微信用户进行绑定,我这里是通过微信openid进行绑定。
② 用户扫描微信二维码允许登录,微信会重定向到授权回调域,并且带上codestate参数,授权回调域与/user/callback接口进行映射。
/user/callback接口处理时,通过appIdappSecretcode参数去调用 https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code接口,调用接口的凭证,有了它可以获取里面的openid等信息。
④ 通过openid去自己系统数据库查询用户,如果查到用户,就允许微信登录成功,查不到用户,就微信登录失败。
⑤ 关于扫码登录成功跳转系统首页,可以在/user/callback接口中直接发起页面重定向。这种重定向适合简单业务的微信扫码登录,复杂的就不适用。

2.3.2 核心代码

2.3.2.1 前端代码
<div *ngIf="loginWayFlag" class="login-WX">
  <div id="login_container"></div>
</div>
initWxLogin () {
  new WxLogin({
    self_redirect: true,
    id: 'login_container',
    appid: 'xxx',
    scope: 'snsapi_login',
    redirect_uri: encodeURIComponent('https://xxx.com/'),
    state: '@platformId@:@' + this.user.platformId + '@,@systemId@:@' + this.user.systemId + '@,@scanType@:@login@,@wxRandomId@:@' + this.user.wxRandomId + '@',
    style: 'black',
    href: 'data:text/css;base64,IC5pbXBvd2VyQm94IC5xcmNvZGUge3dpZHRoOiAxNTBweDt9Ci5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9Ci5zdGF0dXNfaWNvbiB7ZGlzcGxheTogbm9uZX0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30KLmltcG93ZXJCb3ggLmljb24zOF9tc2cuc3VjY3t3aWR0aDogMzBweDtoZWlnaHQ6IDMwcHg7ZGlzcGxheTogYmxvY2s7bWFyZ2luLWxlZnQ6IDExMXB4O30KLmltcG93ZXJCb3ggLmljb24zOF9tc2cud2Fybnt3aWR0aDogMzBweDtoZWlnaHQ6IDMwcHg7ZGlzcGxheTogYmxvY2s7bWFyZ2luLWxlZnQ6IDExMXB4O30KaDEsIGgyLCBoMywgaDQsIGg1LCBoNiwgcHtmb250LXNpemU6IDE0cHg7fQ==',
  });
}
2.3.2.2 后端代码

微信常量类

/**
 * 微信常量类
 */
public class WxConstant {
    /**
     * APPID
     */
    public static final String APP_ID = "xxx";

    /**
     * APP密钥
     */
    public static final String APP_SECRET = "xxx";

    /**
     * 授权回调域
     */
    public static final String CALLBACK_SERVER = "http://xxx.com";

    /**
     * 获取二维码接口URL
     */
    public static final String QRCODE_URL = "https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect";

    /**
     * 获取access_token和openid接口URL
     */
    public static final String OPENID_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
}

微信工具类

/**
 * 微信工具类
 */
@Log4j2
public class WxUtil {
    /**
     * 用于将扫描二维码后重定向的资源url进行编码
     */
    public static String urlEncode(String source){
        String result = source;
        try {
            result = java.net.URLEncoder.encode(source,"utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 根据code、appid和secret获取access_token(也就是调用接口的凭证,有了它可以获取里面的openid等信息)
     */
    public static WxTokenDTO getTokenWithOpenid(String appId, String appSecret, String code) throws Exception {
        String baseUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        String requestUrl = baseUrl.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);
        // 发起GET请求获取凭证
        String result = GetUtil.doGet(requestUrl);
        JSONObject jsonObject = JSON.parseObject(result);
        WxTokenDTO token = new WxTokenDTO();;
        token.setOpenId(jsonObject.getString("openid"));
        token.setAccessToken(jsonObject.getString("access_token"));
        token.setExpiresIn(jsonObject.getIntValue("expires_in"));
        return token;
    }

    /**
     * 根据access_token和openid获取微信用户信息
     */
    public static WxUserDTO getWeChatUserInfo(String access_token, String openid) throws Exception {
        String baseUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
        String requestUrl = baseUrl.replace("ACCESS_TOKEN", access_token).replace("OPENID", openid);
        String result = GetUtil.doGet(requestUrl);
        JSONObject jsonObject = JSON.parseObject(result);
        WxUserDTO userDTO = new WxUserDTO();
        userDTO.setNickname(jsonObject.getString("nickname"));
        userDTO.setHeadImgUrl(jsonObject.getString("headimgurl"));
        userDTO.setUnionId(jsonObject.getString("unionid"));
        return userDTO;
    }
}

用户控制器类
授权回调域与/user/callback接口进行映射,code和state参数是微信传递过来的,state参数为自定义参数,我们可以通过它进行传递想要的参数。

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/callback")
    public RequestResult callback(String code, String state) throws Exception {
        return userService.callback(code, state);
    }
}

用户服务类

   @Override
    public RequestResult callback(String code, String state) throws Exception {
        JSONObject jsonObject = new JSONObject();
        JSONObject params = JSON.parseObject(String.format("{%s}", state.replaceAll("@", "\"")));
        WxTokenDTO tokenDTO = WxUtil.getTokenWithOpenid(WxConstant.APP_ID, WxConstant.APP_SECRET, code);
        log.info("[获取微信openId信息]:" + tokenDTO);

        if ("login".equals(params.getString("scanType"))) {
            wxLogin(tokenDTO.getOpenId(), params.getString("wxRandomId"), params.getLong("systemId"), jsonObject);
        }

        if ("bindUserOpenId".equals(params.getString("scanType"))) {
            bindUserOpenId(params.getLong("userId"), tokenDTO.getOpenId(), params.getLong("systemId"), jsonObject);
        }
        return RequestResult.success(jsonObject);
    }

    /**
     * 微信扫码登录
     */
    private void wxLogin(String openId, String wxRandomId, Long systemId, JSONObject resultObject) {
        UserDTO userDTO = baseMapper.getByOpenId(openId, systemId);
        if (ObjectUtil.isEmpty(userDTO)) {
            resultObject.put("loginResult", getLoginFailHtml());
            return;
        }

        int result = baseMapper.updateUserLoginWxRandomId(userDTO.getId(), wxRandomId);
        if (result > 0) {
            resultObject.put("loginResult", getLoginSuccessHtml());
        } else {
            resultObject.put("loginResult", getLoginFailHtml());
        }
    }

    /**
     * 添加用户信息后,绑定微信openId
     */
    private void bindUserOpenId(Long userId, String openId, Long systemId, JSONObject jsonObject) {
        UserDTO findUserDTO = baseMapper.getByOpenId(openId, systemId);
        if (ObjectUtil.isNotEmpty(findUserDTO)) {
            jsonObject.put("bindUserOpenIdResult", getAlreadyBindOpenIdHtml());
            return;
        }

        User user = new User();
        user.setId(userId);
        user.setOpenId(openId);
        if (baseMapper.updateById(user) > 0) {
            jsonObject.put("bindUserOpenIdResult", getBindOpenIdSuccessHtml());
        } else {
            jsonObject.put("bindUserOpenIdResult", getBindOpenIdFailHtml());
        }
    }

3 参考博客

1.微信官方文档
2.微信扫码登录第三方网站 - 爱码网
3.微信第三方扫码登录(亲测可用)

要PC网站实现微信扫码登录功能,可以按照以下步骤进行: 1. 注册微信开放平台账号:访问微信开放平台(https://open.weixin.qq.com/),使用微信账号登录并注册一个开放平台账号。 2. 创建应用并获取应用ID和密钥:在微信开放平台上创建一个应用,并获取对应的应用ID(AppID)和密钥(AppSecret)。 3. 引入微信登录SDK:下载并引入微信登录的Java SDK,可以使用官方提供的SDK或第三方库,例如weixin-java-tools。 4. 配置回调URL:在微信开放平台上配置回调URL,该URL用于接收微信授权回调的code。 5. 实现扫码登录页面:在PC网站上创建一个扫码登录页面,可以使用HTML和CSS进行布局和样式设计。 6. 发起微信授权请求:在扫码登录页面中,通过调用微信登录SDK提供的接口,生成带有应用ID和回调URL的二维码图片,并显示在页面上供用户扫码。 7. 处理微信授权回调:在设置的回调URL对应的接口中,获取微信授权回调的code,并使用该code调用微信登录SDK提供的接口,获取access_token和openid等用户信息。 8. 实现登录逻辑:根据获取到的用户信息,可以选择将用户信息保存到数据库中,或者进行其他逻辑处理。 需要注意的是,微信授权登录涉及到用户隐私和安全,建议在实施过程中加强安全验证和保护用户信息。 以上是一个大致的步骤,具体实现过程可以参考微信开放平台的文档和示例代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值