请选择 进入手机版 | 继续访问电脑版

实现微信扫描二维码登陆网站

发表于 2016-05-23 15:46 显示全部楼层 31 3537

前提条件:

1,该网站需要进行微信OAuth2.0网页授权。


2,微信用户已绑定网站账号。


即为那种经过一次绑定后,直接点击公众号菜单就能进入到个人页面,无需再次登陆,这种绑定一般是利用openID实现,OpenID是微信唯一对应用户身份的标识,网站或应用可将此ID进行存储,便于用户下次登录时辨识其身份,或将其与用户在网站上或应用中的原有账号进行绑定。


流程:

PC端—>打开登陆页面—>请求获取uuid及二维码图片—>服务器相应结果返回二维码相关信息—>页面轮询检查二维码是否被扫描—>服务器返回检查结果,若已扫描成功则跳转页面,否则继续轮询检查。


微信端—>扫一扫二维码—>微信浏览器自动打开二维码中的链接,并带上code参数,通过该参数可获取openID—>服务器检查uuid有效性,并查询该openID是否已经绑定账号,返回登陆结果。


注:如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。 其流程为:当微信浏览器请求了授权页面,会带上code参数,通过该参数我们可以获取微信用户的openID,具体流程可查看微信开发官方文档:


http://mp.weixin.qq.com/wiki/9/01f711493b5a02f24b04365ac5d8fd95.html


文章内使用简单的Servlet容器,使用QRGen生成二维码,这是一款轻量级的QRCode二维码生成工具。


QRGen依赖包:

<dependency>
        <groupId>net.glxn.qrgen</groupId>
        <artifactId>javase</artifactId>
        <version>2.0</version>
</dependency>

代码部分:

二维码生成工具:

public class QrGenUtil {
    public static ByteArrayOutputStream createQrGen(String url) throws IOException {
        //如果有中文,可使用withCharset("UTF-8")方法
        //设置二维码url链接,图片宽度250*250,JPG类型
        return QRCode.from(url).withSize(250, 250).to(ImageType.JPG).stream();
    }
}

domain:

public class User {
/**
 * 账号名
 */
private String name;
/**
 * 密码
 */
private String password;
/**
 * 微信账号唯一标识
 */
private String openID;
//省略get,set方法
}

页面:

807144-24158e3adf9921ec.jpg


页面重点代码:

获取到图片后PC端页面轮询检测二维码是否被扫描

<img id="QrGen" src="" />
<script type="text/javascript">  
 $(document).ready(function() {
    var uuid;
    $.get("/qrgen?cmd=showQrGen", function(data, status) {
        var obj = eval("(" + data + ")");
        //设置该uuid值
        uuid = obj.uuid;
        //设置二维码图片地址
        $("#QrGen").attr("src", obj.img);
        //检查验证登录
        checkScan();
    });
    function checkScan() {
        setInterval(function() {
            $.get("/qrgenLogin/qrgen?cmd=checkScan&uuid=" + uuid,
                    function(data, status) {
                        if (data == "ok") {
                            //验证成功并重定向到welcome页面
                            window.location = "welcome.jsp";
                        }
                    });
        },4000)
    }
});
</script>

服务器需要生成一个二维码,并且使用UUID来保持唯一性,避免登陆信息混乱。

/**
 * 获取uuid及二维码图片地址
 * @param req
 * @param resp
 * @throws ServletException
 * @throws IOException
 */
protected void showQrGen(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    //生成UUID随机数
    UUID randomUUID = UUID.randomUUID();
    //通过应用获取共享的uuid集合
    Map<String,User> map = (Map) req.getServletContext().getAttribute("UUID_MAP");
    if (map == null) {
        map = new HashMap<String,User>();
        req.getServletContext().setAttribute("UUID_MAP", map);
    }
    //把uuid放入map中
    map.put(randomUUID.toString(), null);
    //二维码图片扫描后的链接
    String url = "http://192.168.1.104:8080/login?cmd=loginByQrGen&uuid="+ randomUUID;
    //生成二维码图片
    ByteArrayOutputStream qrOut = QrGenUtil.createQrGen(url);
    String fileName = randomUUID+ ".jpg";
    OutputStream os = new FileOutputStream(new File(req.getServletContext().getRealPath("/temp"),fileName));
    os.write(qrOut.toByteArray());
    os.flush();
    os.close();
    //返回页面json字符串,uuid用于轮询检查时所带的参数, img用于页面图片显示
    String jsonStr = "{\"uuid\":\"" + randomUUID + "\",\"img\":\"" + "/temp/"+fileName + "\"}";
    OutputStream outStream = resp.getOutputStream();
    outStream.write(jsonStr.getBytes());
    outStream.flush();    
    outStream.close();
  }
}

二维码中的请求链接:

当用户使用与网站账号绑定后的微信号扫描二维码的时候,会将uuid和唯一标识openID的用户进行绑定,这个时候,浏览器通过轮询查询到uid扫描记录,得到相关响应信息,客户端由此也进入一个新的页面。


/**
 * 手机端扫描二维码执行的方法
 * 
 * @param req
 * @param resp
 * @throws ServletException
 * @throws IOException
 */
protected void loginByQrGen(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    // 获取二维码链接中的uuid
    String uuid = req.getParameter("uuid");
    // 通过应用获取共享的uuid集合
    Map uuidMap = (Map) req.getServletContext().getAttribute("UUID_MAP");
    // 如果集合内没有这个uuid,则响应结果
    if (uuidMap == null || !uuidMap.containsKey(uuid)) {
        resp.getOutputStream().write("二维码不存在或已失效!".getBytes());
        return;
    }
    // 根据微信传来的code来获取用户的openID
    String code = req.getParameter("code");
    try {
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=YOUR_APPID"
                + "&secret=YOUR_SECRTC"
                + "&grant_type=authorization_code" + "&code=" + code;
        Gson gson = new Gson();
        Map map = gson.fromJson(HttpUtil.get(url, "utf-8"),
                new TypeToken<Map>() {}.getType());
        Object openID = map.get("openid");
        if (openID != null && !"".equals(openID)) {
            // 通过openID获取user对象
            User user = dao.getUserByOpenId(openID.toString());
            if (user != null) {
                // 如果查询到某个user拥有该openID,则设置到map集合内
                uuidMap.put(uuid, user);
                // 并返回手机端扫描结果
                resp.getOutputStream().write("登陆成功!".getBytes());
                return;
            }
        }
        // 如果没有openID参数,或查询不到openID对应的user对象,则移除该uuid,并响应结果
        uuidMap.remove(uuid);
        resp.getOutputStream().write("你还未绑定,请关注微信号并绑定账号!并使用微信客户端扫描!".getBytes());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

PC端进行轮询检查:

/**
 * PC端不断进行轮询检查的方法
 * @param req
 * @param resp
 * @throws ServletException
 * @throws IOException
 */
protected void checkScan(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    //获取页面的uuid参数
    String uuid = req.getParameter("uuid");
    //通过应用获取共享的uuid集合
    Map map = (Map) req.getServletContext().getAttribute("UUID_MAP");
    if (map != null) {
        //查询该uuid是否存在,且二维码已经被扫描并匹配到账号
        if(map.containsKey(uuid)){
            User user = (User) map.get(uuid);
            if (user != null) {
                //从集合中移除
                map.remove(uuid);
                //设置登录信息
                req.getSession().setAttribute("USER_IN_SESSION", user);
                resp.getOutputStream().write("ok".getBytes());
            }else{
                resp.getOutputStream().write("native".getBytes());
            }
        }
    }
}

无论是通过微信扫一扫还是网站自身的客户端扫码功能,都是需要一方登陆或绑定账号,在扫码的时候才可以获取到用户身份的唯一标识,并且所使用的二维码需要有唯一性,失效时并不是二维码图片失效,而是二维码对应的链接或uuid参数失效,二维码只是文字和超链接的图形化而已。只是在超时、网络断开、过期等情况,此前获得的uuid应失效,对检测过程形成有效的防护。


回复 使用道具
举报
青埂峰峰主

发表于 2017-10-06 09:39 显示全部楼层

惠惠.....厉害哟
回复 支持 反对 使用道具
举报
艇仔饭

发表于 2017-02-08 17:17 显示全部楼层

无需回复的,我们保持沉默,如需回复的,就回21个字!

回复 支持 反对 使用道具
举报
愚人花园

发表于 2017-02-08 16:23 显示全部楼层

回复 支持 反对 使用道具
举报
loverol

发表于 2017-02-08 16:21 显示全部楼层

路过,支持一下

回复 支持 反对 使用道具
举报
begeekmyfriend

发表于 2017-02-08 16:09 显示全部楼层

观后纵想法再多,也不如一句回复实在!

回复 支持 反对 使用道具
举报
凌大胖纸

发表于 2017-02-08 14:58 显示全部楼层

以诚感人者,人亦诚而应。一点回复,敬请笑纳!

回复 支持 反对 使用道具
举报
活泼的方小闹

发表于 2017-02-08 14:49 显示全部楼层

必须给个赞!

回复 支持 反对 使用道具
举报
PPAP!

发表于 2017-02-08 14:41 显示全部楼层

为了学币每天必水一下,望见谅

回复 支持 反对 使用道具
举报
东风玖哥

发表于 2017-02-08 14:06 显示全部楼层

每一个成功者都有一个开始。成功的路,始于回复。

回复 支持 反对 使用道具
举报
1234下一页

发表新文章
陈惠

小码哥名师团队

0

学分

243

学币

252

积分

小码哥名师团队

Rank: 5Rank: 5

积分
252
Ta的主页 发消息
精华帖排行榜

精彩推荐

  • 关注小码哥教育