第三方的登录----微信登录实现流程

目录

一、微信登录流程

1、客户端授权 - 授权码模式流程

二、微信登录实现

1、准备工作

        1、完成微信开放平台的注册,获取参数

        2、配置本地虚拟路径

2、拉取微信二维码

        1、前端点击微信登录向后端发起请求

        2、后端toLogin接口

2、通过code获取access_token

        1、扫码完成后,会访问回调地址

        2、通过gotoBinderOrLogin接口获取token值

3、通过access_token获取用户资源

4、绑定用户

        1、用户绑定界面

        2、后端进行绑定

三、总结


一、微信登录流程

1、客户端授权 - 授权码模式流程

客户端必须得到用户的授权,才能获得令牌,OAuth2.0定义了四种授权方式:
        1、授权码模式(authorization code)
        2、简化模式(implicit)
        3、密码模式(resource owner password credentials)
        4、客户端模式(client credentials)
这里,我们主要介绍一下授权码模式
        授权码模式是功能最完整,流程最严密的授权模式。它的特点是通过客户端的后台服务器,与“服务提供商”的认证服务器进行互动:

第三方的登录----微信登录实现流程_第1张图片

        A、用户访问客户端选择微信登录,向授权服务器发起一个请求,拉取一个授权码(登录二维码)

        B、用户扫码,选择是否给予客户端授权(是否登录)

        C、假设用户给予授权(确认登录),认证服务器将用户导向客户端事先指定的"重定向URI",同时附上一个授权码。

        D、客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后 台的服务器上完成的,对用户不可见。

        E、认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和 更新令牌(refresh token)。

二、微信登录实现

1、准备工作

        1、完成微信开放平台的注册,获取参数

                在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。

        2、配置本地虚拟路径

        打开C:\WINDOWS\System32\drivers\etc目录下的host文件配置虚拟路径

自定义一个url指向本机 ,后端需要一个扫码完成点击确认的回调url地址。

          前端项目使用端口8082后端项目使用端口80

2、拉取微信二维码

        1、前端点击微信登录向后端发起请求

 
  • 微信登录
  •         2、后端toLogin接口

            将要经常用到的参数封装成常量

        //获取授权码的url地址
        public static final String CODEURL="https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect";
        //APPID
        public static final String APPID="微信发放平台完成开发者认证后给的参数";
        //REDIRECT_URI  扫码点击确认成功后回调的url地址    此处的bugtracker.itsource.cn就是本机
        public static final String REDIRECT_URI="http://bugtracker.itsource.cn/wechat/callback";
    

            后端toLogin接口:

    /**
     * 微信相关的controller
     */
    @Controller
    @RequestMapping("/wechat")
    public class WechatController {
    
        private IWechatService wechatService;
    
        /**
         * 跳转到登录界面(拉取二维码)
         * @return
         */
        @RequestMapping("/toLogin")
        public String toLogin(){
            //获取授权码的url地址(拉取二维码)
            String codeUrl = WechatConstant.CODEURL.
                    replace("APPID", WechatConstant.APPID)
                    .replace("REDIRECT_URI", WechatConstant.REDIRECT_URI);
            return "redirect:"+codeUrl;
        }
    }

            点击微信登录,拉取到二维码,如下图:

     第三方的登录----微信登录实现流程_第2张图片

    2、通过code获取access_token

            1、扫码完成后,会访问回调地址

             编写回调接口callback:

        /**
         * 扫码成功之后,自动调用这个接口
         * @param code
         * @return
         */
        @RequestMapping("/callback")
        public String callback(String code){
            System.out.println(code);
            return "redirect:http://localhost:8082/callback.html?"+code;
        }
    

            前端common.js中添加工具函数:

    /**
     * 动态获取url地址?后端的参数,它会把参数封装成一个json对象
     * @returns {Object}
     */
    function getParam() {
        var url = location.search; //获取url中"?"符后的字串
        var theRequest = new Object();
        if (url.indexOf("?") != -1) {
            var str = url.substr(1);
            strs = str.split("&");
            for(var i = 0; i < strs.length; i ++) {
                theRequest[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]); }
        }
        return theRequest;
    }
    

                  在callback.html中:调用getAccessToken接口

         mounted() {
                //获取?后面的参数
                let param=getParam();
                //获取授权码
                let code=param.code;
                //通过授权码获取token值
                this.$http.post("/wechat/gotoBinderOrLogin",param).then((res)=>{
                });
            }

            2、通过gotoBinderOrLogin接口获取token值

            获取token的连接封装成常量:

        //SECRET
        public static final String SECRET="微信开放平台注册完成后给的参数";
    
        //获取token的url地址
        public static final String TOKENURL="https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
    

            HttpClientUtils工具类: 

    package com.rk.pethome.basic.util;
    import org.apache.commons.httpclient.HttpClient;
    import org.apache.commons.httpclient.methods.GetMethod;
    import java.io.IOException;
    
    
    /**
     * 使用httpclient组件发送http请求
     *   get:现在只用到get
     *   post
     */
    public class HttpClientUtils {
        /**
         * 发送get请求
         * @param url 请求地址
         * @return 返回内容 json
         */
        public static String httpGet(String url){
    
            // 1 创建发起请求客户端
            try {
                HttpClient client = new HttpClient();
                // 2 创建要发起请求-tet
                GetMethod getMethod = new GetMethod(url);
                // 3 通过客户端传入请求就可以发起请求,获取响应对象
                client.executeMethod(getMethod);
                // 4 提取响应json字符串返回
                String result = new String(getMethod.getResponseBodyAsString().getBytes("utf8"));
                return result;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

            gotoBinderOrLogin接口:

        /**
         * 跳转到绑定界面,或者直接的登录
         * 通过code获取token
         * @param
         */
        @RequestMapping("/gotoBinderOrLogin")
        @ResponseBody
        public Map gotoBinderOrLogin(@RequestBody Map param) throws UnsupportedEncodingException {
            //获取授权码
            String code = param.get("code");
            //通过授权码获取token
             return wechatService.gotoBinderOrLogin(code);
        }

                   gotoBinderOrLogin Service层:

        /**
         * 跳转到绑定界面,或者直接的登录
         * 通过code获取token
         * @param code  授权码
         */
        @Override
        public Map gotoBinderOrLogin(String code) {
            System.out.println("获取的code:"+code);
            //获取token的url地址
            String tokenUrl = WechatConstant.TOKENURL
                    .replace("APPID", WechatConstant.APPID)
                    .replace("SECRET",WechatConstant.SECRET)
                    .replace("CODE",code);
            //使用HttpClientUtils工具类发送请求,返回Json字符串
            String tokenJsonStr = HttpClientUtils.httpGet(tokenUrl);
            //把Json字符串转换为json对象
            JSONObject jsonObject= JSONObject.parseObject(tokenJsonStr);
            //获取token
            String access_token = jsonObject.getString("access_token");
        }

                   此时已经获取到了token,接下来通过token获取用户资源

    3、通过access_token获取用户资源

            通过openid(请求tokenUrl返回的json里包含openid)在t_wxuser中查找是否有对应的数据

            如果没有,就通过token获取用户的资源数据,并且把资源数据保存到t_wxuser中==>跳转到绑定界面,绑定登录用户:2021年10月以后微信开放平台将不再提供用户的性别和地址

            如果有:查看user_id是否有值,如果有值,直接登录,如果没有跳转到绑定界面,绑定登录用户

    第三方的登录----微信登录实现流程_第3张图片

    只能被迫将原来的t_wxuser表修改为如下结构:

    第三方的登录----微信登录实现流程_第4张图片

     细节:

            跳转到绑定登录界面:输入用户名和密码  性别

                    如果用户名不存在:注册用户===>并且绑定到微信===>直接登录

                    如果用户名存在,密码也是正确的===>绑定微信====>直接登录

            常量封装:

        //通过access_token获取用户信息的接口
        public static final String USERINFOURL="https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
    

            gotoBinderOrLoginService层:通过token获取用户接口信息

        /**
         * 跳转到绑定界面,或者直接的登录
         * 通过code获取token
         * @param code  授权码
         * @return
         */
        @Override
        public Map gotoBinderOrLogin(String code) throws UnsupportedEncodingException {
            System.out.println("获取的code:"+code);
            //获取token的url地址
            String tokenUrl = WechatConstant.TOKENURL
                    .replace("APPID", WechatConstant.APPID)
                    .replace("SECRET",WechatConstant.SECRET)
                    .replace("CODE",code);
            //使用HttpClientUtils工具类发送请求,返回Json字符串
            String tokenJsonStr = HttpClientUtils.httpGet(tokenUrl);
            //把Json字符串转换为json对象
            JSONObject jsonObject= JSONObject.parseObject(tokenJsonStr);
            //获取token
            String access_token = jsonObject.getString("access_token");
            System.out.println("access_token:  "+access_token);
            //获取openid
            String openid = jsonObject.getString("openid");
    
            //通过oppenid查询微信用户
            WechatUser wechatUser = wechatUserMapper.loadByOpenid(openid);
            System.out.println("wechatUser:v  "+wechatUser);
            //创建一个map,该map要返回到前端去(前端根据返回的结果值,就可以判定我是否跳转到绑定界面)
            Map map=new HashMap<>();
    
            //如果微信用户为空,就通过token获取资源信息村入t_wxuser
            if(wechatUser==null){
                //获取用户信息的Url地址
                String userinfourl = WechatConstant.USERINFOURL
                        .replace("ACCESS_TOKEN", access_token)
                        .replace("OPENID", openid);
                //通过HttpClientUtils工具类发送请求获取用户信息,json字符串
                String userinfoJsonStrl = HttpClientUtils.httpGet(userinfourl);
                //解决乱码问题
                String userinfoJsonStr = new String(userinfoJsonStrl.getBytes("ISO-8859-1"), "UTF-8");
    
                System.out.println("微信用户信息"+userinfoJsonStr);
                //将json字符串转换为json对象
                JSONObject userinfojson = JSONObject.parseObject(userinfoJsonStr);
                //获取用户信息,将用户信息添加到t_wxuser表中
                WechatUser wxuser=new WechatUser();
                //设置地址      2021年10月以后微信开放平台将不再提供用户的性别和地址
                /*wxuser.setAddress(userinfojson.getString("country")
                        +userinfojson.getString("province")
                        +userinfojson.getString("city"));*/
                //设置性别
                //wxuser.setSex(userinfojson.getIntValue("sex"));
    
                //设置头像
                wxuser.setHeadimgurl(userinfojson.getString("headimgurl"));
                //设置昵称
                wxuser.setNickname(userinfojson.getString("nickname"));
                //设置openid
                wxuser.setOpenid(openid);
    
                //保存wxuser对象
                wechatUserMapper.saveUser(wxuser);
    
                //如果map保存了openid,就意味着要跳转到绑定界面
                //绑定界面输入用户名和密码查出来user  通过openid查出来wxuser然后进行绑定
                map.put("openid",openid);
                return map;
    
            }
    
    
            //获取绑定的user对象
            User user = wechatUser.getUser();
    
            //如果user为空,就跳转到绑定界面  就是该用户未绑定
            if(user==null){
                //跳转到绑定界面
                map.put("openid",openid);
                return map;
            }
    
            //该用户已绑定,直接进行登录操作
    
             //redis的key值
            String token = UUID.randomUUID().toString();
            //将loginuser转化为json字符串 存入redis
            RedisUtils.INSTANCE.set(token,JSONObject.toJSONString(user),30*60);
    
            //将redis中存储的token保存到map中返回到前端页面
            map.put("token",token);
            System.out.println("微信登录的token值:"+token);
            map.put("loginUser",user);
    
            //user不为空,说明该微信用户已经存储到t_wxuser表中,并且已进行绑定,可以直接登录
            return map;
        }
    

            WechatUserMapper.xml:

    
    
    
    
        
        
    
        
            
            
            
            
            
                
                
            
        
    
        
        
            insert into t_wxuser(openid, nickname,headimgurl,user_id)
             values (#{openid},#{nickname},#{headimgurl},#{user.id})
        
    
    

            最后将map要返回到前端去:前端根据返回的结果值,就可以判定我是否跳转到绑定界面

            前端callback.html:如果返回了openid就跳转到绑定用户界面,如果没有直接登录

       this.$http.post("/wechat/gotoBinderOrLogin",param).then((res)=>{
                    //如果后台响应了openid就意味着必须跳转到绑定界面
                    console.debug(res.data);
                    let {openid,token,loginUser}=res.data;
                    if(openid){//如果openid有值跳转到绑定界面
                        location.href="binder.html?openid="+openid;
                    }else{
    
                        //设置到浏览器
                        localStorage.setItem("token",token);
                        localStorage.setItem("loginUser",JSON.stringify(loginUser));
                        //跳转到首页
                        location.href="index.html";
                    }
    
                });

    4、绑定用户

           微信扫码登录后,将用户存入t_wxuser表中后进行用户绑定

            1、用户绑定界面

    第三方的登录----微信登录实现流程_第5张图片

           发起绑定请求:

     binder(){
                    //获取openid
                    let param=getParam();
                    let para={"username":this.name,"password":this.password,"openid":param.openid};
                    this.$http.post("/wechat/binder",para).then((res)=>{
                        let {success,msg,result}=res.data;
                        if(!success){//绑定失败
                            this.errorMsg=msg;
                        }else {//绑定成功
                            this.errorMsg="";
                            //获取登录用户
                            let loginUser=result.loginUser;
                            let  token=result.token;
                            console.debug("result:"+result);
                            //设置到浏览器
                            localStorage.setItem("token",token);
                            localStorage.setItem("loginUser",JSON.stringify(loginUser));
                            location.href="index.html";
                        }
                    });
                }

            2、后端进行绑定

            Controller层:

     /**
         * 绑定用户
         * @param
         * @return
         */
        @PostMapping("/binder")
        @ResponseBody
        public AjaxResult binder(@RequestBody HashMap param){
            try {
                return wechatService.binder(param);
            } catch (Exception e) {
                e.printStackTrace();
                return new AjaxResult(false,e.getMessage());
            }
        }
    

            service层:

      /**
         * 绑定用户
         *
         * @param param
         * @return
         */
        @Override
        public AjaxResult binder(HashMap param) {
            String username = param.get("username");
            //通过用户名查询到用户
            User user = userMapper.loadByUsername(username);
            //校验用户
            if(user==null)
                return new AjaxResult(false,"用户名错误!!");
            if(user.getState()==0)
                return new AjaxResult(false,"该用户未激活,请先激活!");
            String password = param.get("password");
            if((MD5Utils.encrypByMd5(password)+user.getSalt()).equals(user.getPassword()))
                return new AjaxResult(false,"密码错误!!");
    
            //进行绑定
            String openid = param.get("openid");
            wechatUserMapper.updateByOpenid(user,openid);
    
            //redis的key值
            String token = UUID.randomUUID().toString();
            //将loginuser转化为json字符串 存入redis
            RedisUtils.INSTANCE.set(token,JSONObject.toJSONString(user),30*60);
    
            //将redis中存储的token保存到map中返回到前端页面
            Map map=new HashMap<>();
            map.put("token",token);
            System.out.println("微信登录的token值:"+token);
            map.put("loginUser",user);
            return new AjaxResult().setResult(map);
        }

             Mapper层:

    /**
         * 绑定微信号
         * @param u 用户对象
         * @param openid    微信的唯一标志
         */
        void updateByOpenid(@Param("u")User u,@Param("openid") String openid);
    

            mapper.xml:

        
            update t_wxuser set user_id=#{u.id} where openid=#{openid}
        
    

    三、总结

    第三方的登录----微信登录实现流程_第6张图片

      

    你可能感兴趣的:(Springboot,Vue,springboot)