目录

一、微信登录流程

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)
这里,我们主要介绍一下授权码模式
        授权码模式是功能最完整,流程最严密的授权模式。它的特点是通过客户端的后台服务器,与“服务提供商”的认证服务器进行互动:

        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、前端点击微信登录向后端发起请求

 <li><a href="http://localhost:80/wechat/toLogin"><i class="am-icon-weixin am-icon-sm"></i><span>微信登录</span> </a></li>

        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、通过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<String, Object> gotoBinderOrLogin(@RequestBody Map<String,String> param) throws UnsupportedEncodingException {
        //获取授权码
        String code = param.get("code");
        //通过授权码获取token
         return wechatService.gotoBinderOrLogin(code);
    }

               gotoBinderOrLogin Service层:

    /**
     * 跳转到绑定界面,或者直接的登录
     * 通过code获取token
     * @param code  授权码
     */
    @Override
    public Map<String,Object> 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是否有值,如果有值,直接登录,如果没有跳转到绑定界面,绑定登录用户

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

 细节:

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

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

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

        常量封装:

    //通过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<String, Object> 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<String,Object> 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:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rk.pethome.user.mapper.WechatUserMapper">

    <!--通过openid查询微信用户 -->
    <select id="loadByOpenid" resultMap="wechUserResultMap">
        select w.*,u.id uid,u.username uusername from t_wxuser w
        left join t_user u on w.user_id=u.id
        where openid=#{openid}
    </select>

    <resultMap id="wechUserResultMap" type="wechatUser">
        <id column="id" property="id"/>
        <result column="openid" property="openid"/>
        <result column="nickname" property="nickname"/>
        <result column="headimgurl" property="headimgurl"/>
        <association property="user" javaType="User">
            <id column="uid" property="id"/>
            <result column="uusername" property="username"/>
        </association>
    </resultMap>

    <!--保存微信用户-->
    <insert id="saveUser" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        insert into t_wxuser(openid, nickname,headimgurl,user_id)
         values (#{openid},#{nickname},#{headimgurl},#{user.id})
    </insert>

</mapper>

        最后将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、用户绑定界面

       发起绑定请求:

 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<String,String> 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<String, String> 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<String,Object> 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 id="updateByOpenid">
        update t_wxuser set user_id=#{u.id} where openid=#{openid}
    </update>

三、总结

  

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐