如何实现微信扫码登录--OAuth2

第三方登录

用户不想去注册你的网站,觉得输入这些信息麻烦。更愿意像直接扫码进行登录这样,简单。

扫码人的信息自动注册,加入到你的数据库中。

微信登录是一个固定的流程,是腾讯规定的固定流程去做。

微信登录讲解之前,先讲解OAuth2

OAuth2是什么?

OAuth2是针对特定问题的一种解决方案。

主要可以解决两个问题:

   1. 开放系统间授权   2.分布式访问问题

开放系统间授权:

如何实现微信扫码登录--OAuth2_第1张图片

lucy照了很多照片,她把它们都存到了自己的百度网盘上去了。但是百度网盘没法打印照片啊,某公司自己研发了一款系统,专门用于这种云存储的打印服务。

但是,默认情况下,这款系统默认是不能操作lucy存到她百度网盘上的东西的。没有权限。

但是百度网盘提供了授权服务的相关方法。

这个时候,只要lucy授权给这款打印系统,那么就可以实现打印服务。

具体怎么做到的,这就是OAuth2要做的事情。也是百度网盘要规定的流程。

分布式访问

如何实现微信扫码登录--OAuth2_第2张图片

我们只需要把token相关的代码抽取出来,生成一个公共的类。

这样,即使是不同的模块之间,他们的token创建和解析的加密方式也都是一样的,也就能解析到cookie中传过来的token信息,进行登录。

当然了,我们可以使用redis存储token以及对应用户信息,这样不同模块就都能拿到用户信息。否则的话,我们只知道是当前用户,当前用户的用户信息,也只是token解析出来的信息。比如用户id。当然,因为用户id具有唯一性嘛,所以通过用户id关联自己模块的业务也是可以的。比如:某人登录了视频服务,在视频服务会存储一个视频记录的数据表。这个时候,只要数据表关联用户id就可以了。对于视频服务而言,是没有问题的。

OAuth2解决方案:按照一定规则生成字符串,字符串包含用户信息。

OAuth2只是一种解决方案,这种方案具体怎么做由方案提供者自己定。

下面我们开始进行微信扫码登录:

准备工作

1. 首先你想要使用腾讯公司微信做登录这样的相关操作,必须在腾讯的开发者平台注册,获得资质!微信开放平台

(1) 支持企业类型(以前只支持企业注册)

(2) 注册之后,会给你提供微信id和微信密钥

ps:注册需要准备营业执照、1-2个工作日审批、300元认证费

2. 申请网站应用名称。

就是说你扫描二维码,它会显示当前网站的应用名称,比如:我的谷粒。这个一般在7个工作日内审批。

3. 需要域名地址。

地址作用:微信扫完二维码之后,找你的域名做跳转。

熟悉微信登录流程:

下面我们快速看一下

参考文档:微信开放平台

如何实现微信扫码登录--OAuth2_第3张图片

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

后端开发(重点)

1、添加配置

在你的用户模块中,application.properties添加相关配置信息

# 微信开放平台 appid
wx.open.app_id=你的appid
# 微信开放平台 appsecret
wx.open.app_secret=你的appsecret
# 微信开放平台 重定向url
wx.open.redirect_url=http://你的服务器名称/api/ucenter/wx/callback

2、创建常量类

创建util包,创建ConstantWxUtils.java常量类

package com.atguigu.educenter.utils;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ConstantWxUtils implements InitializingBean {

    @Value("${wx.open.app_id}")
    private String appId;

    @Value("${wx.open.app_secret}")
    private String appSecret;

    @Value("${wx.open.redirect_url}")
    private String redirectUrl;

    public static String WX_OPEN_APP_ID;
    public static String WX_OPEN_APP_SECRET;
    public static String WX_OPEN_REDIRECT_URL;

    @Override
    public void afterPropertiesSet() throws Exception {
        WX_OPEN_APP_ID = appId;
        WX_OPEN_APP_SECRET = appSecret;
        WX_OPEN_REDIRECT_URL = redirectUrl;
    }
}

3、生成微信扫描的二维码

直接请求微信提供的固定的地址,向地址的后面拼接参数即可。

即重定向到微信的二维码生成地址去。

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

参数说明:

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

在当前educenter模块中创建api包

api包中创建WxApiController

代码的写法

package com.atguigu.educenter.controller;

import com.atguigu.educenter.utils.ConstantWxUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.net.URLEncoder;

@CrossOrigin
@Controller  //只是请求地址,不需要返回数据,这个时候不能用@RestController
@RequestMapping("/api/ucenter/wx")
public class WxApiController {


    //1 生成微信扫描二维码
    @GetMapping("login")
    public String getWxCode() {
        //固定地址,后面拼接参数
        //写法一(这么做效率很低,多了还容易写错):
//        String url = "https://open.weixin.qq.com/connect/qrconnect" +
//                "?appid="+
//                ConstantWxUtils.WX_OPEN_APP_ID +
//                "&redirect_uri=" +
//                ConstantWxUtils.WX_OPEN_REDIRECT_URL +
//                "&response_type=code" +
//                "&scope=snsapi_login" +
//                "state=atguigu" +
//                "#wechat_redirect";
        //当然,上面这个redirect_uri地址要先编码再进行传,不能那样直接传。

        //写法二(推荐)
        // 微信开放平台授权baseUrl  %s 相当于?标识占位符
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";

        //对redirect_url进行URLEncoder编码
        String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
        try {
            redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
        }catch(Exception e) {
        }

        //设置%s里面值
        String url = String.format(
                    baseUrl,
                    ConstantWxUtils.WX_OPEN_APP_ID,
                    redirectUrl,
                    "atguigu"
                 );

        //重定向到请求微信地址里面
        return "redirect:" + url;
    }
}

此时启动改服务,然后请求当前接口:localhost:8160/api/ucenter/wx/login

它会根据你接口中写的代码,重定向到微信的二维码生成地址,拼接的参数就是你传的参数

比如:

https://open.weixin.qq.com/connect/qrconnect?appid=wxed9954c01bb80b37&redirect_uri=http%3A%2F%2Flocalhost%3A8160%2Fapi%2Fucenter%2Fwx%2Fcallback&response_type=code&scope=snsapi_login&state=atguigu#wechat_redirect

如何实现微信扫码登录--OAuth2_第4张图片

 4.扫描后获取用户信息的过程

扫描成功确认之后,微信会获取到用户的一个code值。然后微信会把code值给到我们,跳转到如下我们的回调地址,地址后拼接了code参数和state参数:

localhost:8160/api/ucenter/wx/callback?code=021Grg0w34r37Z2c9S2w3xmv180Grg0o&state=atguigu

api/ucenter/wx/callback肯定不是我们的地址,我们还没有写这个方法。

ps:这个回调地址就是我们之前在配置文件中配置的redirect_url重定向地址,它是当初注册资质时填写的域名地址。在获取验证码时就传递给了微信,这个时候微信在将地址返回给我们。

wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback

这个回调方法的作用就是用于我们获取用户信息。微信说让我们用这个接口去请求用户信息。

下面我们就需要自己来写这个回调方法,在方法里面,通过获取到微信传回来的code值,带着这个值去请求微信另外的一个固定地址获取asses_token,openid

通过httpclint直接请求微信,然后获取到返回的accsess_token 和 openid信息。

接着我们可以把它当前用户信息加入到自己的数据库。(你也可以选择不加入)

首先判断当前用户的openid是否在自己的数据库已经存在,如果存在不用添加了。

如果不存在,那么就通过这个accsess_token 和 openid再去请求微信的又一个固定地址,获取用户信息。

还是通过HttpClient发送请求,然后接收返回值。

一样也需要通过json转换工具转换成字符串。否则是json格式的数据无法操作。

拿到用户信息后我们就可以加入到自己的数据库里面了。

我们看看代码怎么写:

package com.atguigu.educenter.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.atguigu.commonutils.JwtUtils;
import com.atguigu.educenter.bean.UcenterMember;
import com.atguigu.educenter.service.UcenterMemberService;
import com.atguigu.educenter.utils.ConstantWxUtils;
import com.atguigu.educenter.utils.HttpClientUtils;
import com.atguigu.servicebase.exceptionHandler.GuliException;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

@CrossOrigin
@Controller  //只是请求地址,不需要返回数据。这个时候不能用@RestController
@RequestMapping("/api/ucenter/wx")
public class WxApiController {

    @Autowired
    private UcenterMemberService memberService;

    //2 获取扫描人信息,添加数据
    @GetMapping("callback")
    public String callback(String code, String state) {
        try {
            //1 获取code值,临时票据,类似于验证码
            //2 拿着code请求 微信固定的地址,得到两个值 accsess_token 和 openid
            String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
                    "?appid=%s" +
                    "&secret=%s" +
                    "&code=%s" +
                    "&grant_type=authorization_code";
            //拼接三个参数 :id  秘钥 和 code值
            String accessTokenUrl = String.format(
                    baseAccessTokenUrl,
                    ConstantWxUtils.WX_OPEN_APP_ID,
                    ConstantWxUtils.WX_OPEN_APP_SECRET,
                    code
            );
            //请求这个拼接好的地址,得到返回两个值 accsess_token 和 openid
            //使用httpclient发送请求,得到返回结果
            String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);

            //从accessTokenInfo字符串获取出来两个值 accsess_token 和 openid
            //把accessTokenInfo字符串转换map集合,根据map里面key获取对应值
            //使用json转换工具 Gson
            Gson gson = new Gson();
            HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
            String access_token = (String)mapAccessToken.get("access_token");
            String openid = (String)mapAccessToken.get("openid");


            //把扫描人信息添加数据库里面
            //判断数据表里面是否存在相同微信信息,根据openid判断
            UcenterMember member = memberService.getOpenIdMember(openid);
            if(member == null) {//memeber是空,表没有相同微信数据,进行添加

                //3 拿着得到accsess_token 和 openid,再去请求微信提供固定的地址,获取到扫描人信息
                //访问微信的资源服务器,获取用户信息
                String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                        "?access_token=%s" +
                        "&openid=%s";
                //拼接两个参数
                String userInfoUrl = String.format(
                        baseUserInfoUrl,
                        access_token,
                        openid
                );
                //发送请求
                String userInfo = HttpClientUtils.get(userInfoUrl);
                //获取返回userinfo字符串扫描人信息
                HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
                String nickname = (String)userInfoMap.get("nickname");//昵称
                String headimgurl = (String)userInfoMap.get("headimgurl");//头像

                member = new UcenterMember();
                member.setOpenid(openid);
                member.setNickname(nickname);
                member.setAvatar(headimgurl);
                memberService.save(member);
            }

            //使用jwt根据member对象生成token字符串
            String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
            //最后:返回首页面,通过路径传递token字符串
            return "redirect:http://localhost:3000?token="+jwtToken;
        }catch(Exception e) {
            throw new GuliException(20001,"登录失败");
        }
    }

    //1 生成微信扫描二维码
    @GetMapping("login")
    public String getWxCode() {
        //固定地址,后面拼接参数
        //写法一(这么做效率很低,多了还容易写错):
//        String url = "https://open.weixin.qq.com/connect/qrconnect" +
//                "?appid="+
//                ConstantWxUtils.WX_OPEN_APP_ID +
//                "&redirect_uri=" +
//                ConstantWxUtils.WX_OPEN_REDIRECT_URL +
//                "&response_type=code" +
//                "&scope=snsapi_login" +
//                "state=atguigu" +
//                "#wechat_redirect";
        //当然,上面这个redirect_uri地址要先编码再进行传,不能那样直接传。

        //写法二(推荐)
        // 微信开放平台授权baseUrl  %s 相当于?标识占位符
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";

        //对redirect_url进行URLEncoder编码
        String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
        try {
            redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
        }catch(Exception e) {
        }

        //设置%s里面值
        String url = String.format(
                    baseUrl,
                    ConstantWxUtils.WX_OPEN_APP_ID,
                    redirectUrl,
                    "atguigu"
                 );

        //重定向到请求微信地址里面
        return "redirect:" + url;
    }
}

当然json转换工具你自己选择,fastjson也可以。比如:

    //对比Gson和FastJson    
    //Gson
    Gson gson = new Gson();
    HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
    String access_token = (String)mapAccessToken.get("access_token");
    String openid = (String)mapAccessToken.get("openid");

    //FastJson
    Map parse = (Map) JSON.parse(accessTokenInfo);
    String access_token1 = (String) parse.get("access_token");
    System.out.println(access_token1 +"这是fastjson解析出来的");

如何实现微信扫码登录--OAuth2_第5张图片

 我们这里怎么写都对,因为都是localhost,但是实际项目中,我们不建议这么做。

 然后在首页页面中显示用户信息如何实现微信扫码登录--OAuth2_第6张图片

你可能感兴趣的:(java,spring,boot,微信)