zoukankan      html  css  js  c++  java
  • SpringBoot 实现App第三方微信登录

    1.准备工作

    移动应用微信登录是基于OAuth2.0协议标准 构建的微信OAuth2.0授权登录系统。

    在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的移动应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。
    2.授权流程说明

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

    3. 获取access_token时序图:


    4. maven依赖

    <dependency>
          <groupId>commons-httpclient</groupId>
          <artifactId>commons-httpclient</artifactId>
          <version>3.0.1</version>
       </dependency>

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-devtools</artifactId>
       </dependency>
       <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
    </dependency>

     <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
      </dependency>

     <dependency>
          <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.3.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.38</version>
        </dependency>

    5.在application.yml文件中配置你的

    #第三方微信登录(用你自己的)
    #appID App的ID
    #appSecret
    weixinconfig:
    weixinappID: wxf7865421a3c4d5f
    weixinappSecret: 6cdbe6d4ce6sbcf0593c913d8a0ce12
    创建配置类

    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;

    @Component
    @ConfigurationProperties(prefix="weixinconfig")
    public class WeixinLoginProperties {
        
        private String weixinappID; // 商户appid
        
        private String weixinappSecret; // 私钥 pkcs8格式的

        public String getWeixinappID() {
            return weixinappID;
        }

        public void setWeixinappID(String weixinappID) {
            this.weixinappID = weixinappID;
        }

        public String getWeixinappSecret() {
            return weixinappSecret;
        }

        public void setWeixinappSecret(String weixinappSecret) {
            this.weixinappSecret = weixinappSecret;
        }

    }


    6.第一步:请求CODE

    这一步客户端会把code传过来 ,不用你操心
    7.第二步:通过code获取access_token

    package io.renren.api.controller;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.configurationprocessor.json.JSONException;
    import org.springframework.boot.configurationprocessor.json.JSONObject;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import io.renren.api.dao.TpAccesstokenMapper;
    import io.renren.api.dao.TpUsersMapper;
    import io.renren.api.entity.TpAccesstoken;
    import io.renren.api.entity.TpAccesstokenExample;
    import io.renren.api.entity.TpAccumulativeAward;
    import io.renren.api.entity.TpUsers;
    import io.renren.api.entity.TpUsersExample;
    import io.renren.api.properties.WeixinLoginProperties;
    import io.renren.api.service.TpAccesstokenService;
    import io.renren.api.service.TpAccumulativeAwardService;
    import io.renren.api.service.TpUsersService;
    import io.renren.common.utils.R;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.math.BigDecimal;
    import java.net.URI;
    import java.util.List;
    import javax.annotation.Resource;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.DefaultHttpClient;

    /**
     * 第三方微信登录
     * @author Administrator
     *
     */
    @SuppressWarnings("deprecation")
    @Controller
    @RequestMapping("/api")
    public class WeXinController {
        
        
        //微信公众平台申请
        //应用唯一标识,在微信开放平台提交应用审核通过后获得 appID
        //应用密钥AppSecret,在微信开放平台提交应用审核通过后获得 appSecret
        //TpAccesstoken 用来保存微信返回的用户信息oppid等
        
        
        @Resource
        private WeixinLoginProperties weixinLoginProperties;
        @Autowired
        TpUsersService tpUsersService;    
        @Autowired
        TpUsersMapper tpUsersMapper;
        @Autowired
        TpAccesstokenService tpAccesstokenService;    
        @Autowired
        TpAccesstokenMapper tpAccesstokenMapper;
        @Autowired
        TpAccumulativeAwardService tpAccumulativeAwardService;

        /**
         * 获取accessToken,该步骤返回的accessToken期限为一个月
         *
         * @param code
         * @return
         * @throws Exception
         */
        @SuppressWarnings("all")
        @RequestMapping("weixincallback")
        @ResponseBody
        public R getAccessToken(String code) throws Exception {
            
            String appID = weixinLoginProperties.getWeixinappID();
            String appSecret = weixinLoginProperties.getWeixinappSecret();
            String accesstoken;
            String openid = null;
            String refreshtoken;
            int expiresIn;
            String unionid;//可通过获取用户基本信息中的unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号,
            //用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。
            if (code != null) {
                System.out.println(code);
            }
            String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appID+"&secret="+appSecret+"&code="+code+"&grant_type=authorization_code";
            URI uri = URI.create(url);
            org.apache.http.client.HttpClient client = new DefaultHttpClient();
            HttpGet get = new HttpGet(uri);
            HttpResponse response;
            try {
                response = client.execute(get);
                if (response.getStatusLine().getStatusCode() == 200) {
                    HttpEntity entity = response.getEntity();

                    BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
                    StringBuilder sb = new StringBuilder();

                    for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
                        sb.append(temp);
                    }
                    JSONObject object = new JSONObject(sb.toString().trim());
                    System.out.println("object:"+object);
                    accesstoken = object.getString("access_token");
                    System.out.println("accesstoken:"+accesstoken);
                    openid = object.getString("openid");
                    System.out.println("openid:"+openid);
                    refreshtoken = object.getString("refresh_token");
                    System.out.println("refreshtoken:"+refreshtoken);
                    expiresIn = (int) object.getLong("expires_in");
                    unionid = object.getString("unionid");
                    // 将用户信息保存到数据库
                    //1.先查询用户是否是第一次第三方登录如果是第一次那么是将用户信息添加到数据库 如果不是那么是更新到数据库
                    TpUsers userInfo = getUserInfo(accesstoken,openid);
                    Integer userId = userInfo.getUserId();
                    
                    TpAccesstokenExample example = new TpAccesstokenExample();
                    example.createCriteria().andOpenidEqualTo(openid);
                    List<TpAccesstoken> list = tpAccesstokenMapper.selectByExample(example);
                    if(list!=null&&list.size()>0) {
                        //那么该用户不是第一次 执行更新操作
                        TpAccesstoken tpAccesstoken = list.get(0);
                        tpAccesstoken.setAccesstoken(accesstoken);
                        tpAccesstoken.setUserId(userId);
                        tpAccesstoken.setExpiresIn(expiresIn);
                        tpAccesstoken.setOpenid(openid);
                        tpAccesstoken.setRefreshtoken(refreshtoken);
                        tpAccesstokenService.save(tpAccesstoken);
                    }else {
                        TpAccesstoken tpAccesstoken=new TpAccesstoken();
                        tpAccesstoken.setUserId(userId);
                        tpAccesstoken.setAccesstoken(accesstoken);
                        tpAccesstoken.setExpiresIn(expiresIn);
                        tpAccesstoken.setOpenid(openid);
                        tpAccesstoken.setRefreshtoken(refreshtoken);
                        tpAccesstokenService.save(tpAccesstoken);
                        //tpAccesstokenService.insertAccesstoken(userId,openid, accesstoken, expiresIn, refreshtoken);
                    }
                    //refreshAccessToken(openid);
                    System.out.println("Openid"+userInfo.getOpenid());
                    return R.ok().put("userInfo", userInfo).put("openid", openid);
                }
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }

            return R.ok().put("openid", openid);
        }
            /*
             *
             * 1 { 2 "access_token":"ACCESS_TOKEN", 3 "expires_in":7200, 4
             * "refresh_token":"REFRESH_TOKEN", 5 "openid":"OPENID", 6 "scope":"SCOPE", 7
             * "unionid":"o6_bmasdasdsad6_2sgVt7hMZOPfL" 8 } 复制代码 复制代码 参数 说明 access_token
             * 接口调用凭证 expires_in access_token 接口调用凭证超时时间,单位(秒) refresh_token
             * 用户刷新access_token openid 授权用户唯一标识 scope 用户授权的作用域,使用逗号(,)分隔 unionid
             * 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
             *
             */

        
        /**
         * 刷新token
         *
         * @param openID
         * @return
         */
        @SuppressWarnings({ "unused", "resource" })
        private void refreshAccessToken(String openid) {
            String refreshtoken=null;
            TpAccesstoken tpAccesstoken=new TpAccesstoken();
            String appID = weixinLoginProperties.getWeixinappID();
            String appSecret = weixinLoginProperties.getWeixinappSecret();
            TpAccesstokenExample example = new TpAccesstokenExample();
            example.createCriteria().andOpenidEqualTo(openid);
            List<TpAccesstoken> list = tpAccesstokenMapper.selectByExample(example);
            if(list!=null&&list.size()>0) {
                 tpAccesstoken = list.get(0);
                 refreshtoken = tpAccesstoken.getRefreshtoken();
            }
            
    String uri = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="+appID+"&grant_type=refresh_token&refresh_token="+refreshtoken;
            org.apache.http.client.HttpClient client = new DefaultHttpClient();
            HttpGet get = new HttpGet(URI.create(uri));
            try {
                HttpResponse response = client.execute(get);
                if (response.getStatusLine().getStatusCode() == 200) {
                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
                    StringBuilder builder = new StringBuilder();
                    for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
                        builder.append(temp);
                    }
                    JSONObject object = new JSONObject(builder.toString().trim());
                    String    accessToken = object.getString("access_token");
                    String    refreshToken = object.getString("refresh_token");
                    openid = object.getString("openid");
                    int   expires_in = (int) object.getLong("expires_in");
                    tpAccesstoken.setAccesstoken(accessToken);
                    tpAccesstoken.setExpiresIn(expires_in);
                    tpAccesstoken.setOpenid(openid);
                    tpAccesstoken.setRefreshtoken(refreshToken);
                    tpAccesstokenService.save(tpAccesstoken);
                }
            } catch (ClientProtocolException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
        }

        /**
         * 根据accessToken获取用户信息
         *
         * @param accessToken
         * @param openID
         * @return
         * @throws Exception
         */
        @SuppressWarnings({ "unused", "resource" })
        public TpUsers getUserInfo(String accessToken, String openID) throws Exception {
            String appID = weixinLoginProperties.getWeixinappID();
            String appSecret = weixinLoginProperties.getWeixinappSecret();
            String uri = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openID;
            org.apache.http.client.HttpClient client = new DefaultHttpClient();
            HttpGet get = new HttpGet(URI.create(uri));
            try {
                HttpResponse response = client.execute(get);
                if (response.getStatusLine().getStatusCode() == 200) {
                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
                    StringBuilder builder = new StringBuilder();
                    for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
                        builder.append(temp);
                    }
                    JSONObject object = new JSONObject(builder.toString().trim());
                    
                    String country = object.getString("country");
                    String nikeName = object.getString("nickname");
                    String unionid = object.getString("unionid");
                    String province = object.getString("province");
                    String city = object.getString("city");
                    String openid = object.getString("openid");
                    String sex = object.getString("sex");
                    String headimgurl = object.getString("headimgurl");
                    String language = object.getString("language");
                    BigDecimal bigDecimal=new BigDecimal(0.0);
                    TpUsersExample example=new TpUsersExample();
                    example.createCriteria().andOpenidEqualTo(openid);
                    List<TpUsers> list = tpUsersMapper.selectByExample(example);
                    if(list!=null&&list.size()>0) {
                        TpUsers tpUsers = list.get(0);
                        System.out.println("---------");
        
                        return tpUsers;
                        
                    }else {
                        TpUsers tpUsers=new TpUsers();
                        tpUsers.setOauth("wx");
                        tpUsers.setOpenid(openid);
                        tpUsers.setUnionid(unionid);
                        tpUsers.setUserName(nikeName);
                        tpUsers.setUserMoney(bigDecimal);
                        tpUsersService.save(tpUsers);
                        System.out.println("+++++++");
                        return tpUsers;
                    }
                }
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }

            return null;
        }
        
        @RequestMapping("/isaccesstoken")
        @SuppressWarnings({ "resource" })
        private boolean isAccessTokenIsInvalid(String accessToken,String openID) {
            String url = "https://api.weixin.qq.com/sns/auth?access_token=" + accessToken + "&openid=" + openID;
            URI uri = URI.create(url);
            org.apache.http.client.HttpClient client = new DefaultHttpClient();
            HttpGet get = new HttpGet(uri);
            HttpResponse response;
            try {
                response = client.execute(get);
                if (response.getStatusLine().getStatusCode() == 200) {
                    HttpEntity entity = response.getEntity();

                    BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
                    StringBuilder sb = new StringBuilder();

                    for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
                        sb.append(temp);
                    }
                    JSONObject object = new JSONObject(sb.toString().trim());
                      /* {
                        "errcode":0,"errmsg":"ok"
                        }
                        错误的Json返回示例:
                        {
                        "errcode":40003,"errmsg":"invalid openid"
                        }*/
                    int errorCode = object.getInt("errcode");
                    if (errorCode == 0) {
                        return true;
                    }
                }
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return false;
            
        }


    }

    获取第一步的code后,请求以下链接进行refresh_token:

    https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN


      参数说明
        
        参数    是否必须    说明
        appid    是    应用唯一标识
        grant_type    是    填refresh_token
        refresh_token    是    填写通过access_token获取到的refresh_token参数
        返回说明
        
        正确的返回:
        
        {
        "access_token":"ACCESS_TOKEN",
        "expires_in":7200,
        "refresh_token":"REFRESH_TOKEN",
        "openid":"OPENID",
        "scope":"SCOPE"
        }
        参数    说明
        access_token    接口调用凭证
        expires_in    access_token接口调用凭证超时时间,单位(秒)
        refresh_token    用户刷新access_token
        openid    授权用户唯一标识
        scope    用户授权的作用域,使用逗号(,)分隔
        错误返回样例:
        
        {"errcode":40030,"errmsg":"invalid refresh_token"}

    刷新或续期access_token使用

    接口说明

    access_token是调用授权关系接口的调用凭证,由于access_token有效期(目前为2个小时)较短,当access_token超时后,可以使用refresh_token进行刷新,access_token刷新结果有两种:

    1.若access_token已超时,那么进行refresh_token会获取一个新的access_token,新的超时时间;

    2.若access_token未超时,那么进行refresh_token不会改变access_token,但超时时间会刷新,相当于续期access_token。

    refresh_token拥有较长的有效期(30天)且无法续期,当refresh_token失效的后,需要用户重新授权后才可以继续获取用户头像昵称。

    请求方法

    使用/sns/oauth2/access_token接口获取到的refresh_token进行以下接口调用:

    http请求方式: GET
    https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
    参数说明

    参数    是否必须    说明
    appid    是    应用唯一标识
    grant_type    是    填refresh_token
    refresh_token    是    填写通过access_token获取到的refresh_token参数
    返回说明

    正确的返回:

    {
    "access_token":"ACCESS_TOKEN",
    "expires_in":7200,
    "refresh_token":"REFRESH_TOKEN",
    "openid":"OPENID",
    "scope":"SCOPE"
    }
    参数    说明
    access_token    接口调用凭证
    expires_in    access_token接口调用凭证超时时间,单位(秒)
    refresh_token    用户刷新access_token
    openid    授权用户唯一标识
    scope    用户授权的作用域,使用逗号(,)分隔
    错误返回样例:

    {
    "errcode":40030,"errmsg":"invalid refresh_token"
    }

    获取用户个人信息(UnionID机制)

    接口说明

    此接口用于获取用户个人信息。开发者可通过OpenID来获取用户基本信息。特别需要注意的是,如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号,用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。请注意,在用户修改微信头像后,旧的微信头像URL将会失效,因此开发者应该自己在获取用户信息后,将头像图片保存下来,避免微信头像URL失效后的异常情况。

    请求说明

    http请求方式: GET
    https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
    参数说明

    参数    是否必须    说明
    access_token    是    调用凭证
    openid    是    普通用户的标识,对当前开发者帐号唯一
    lang    否    国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语,默认为zh-CN
    返回说明

    正确的Json返回结果:

    {
    "openid":"OPENID",
    "nickname":"NICKNAME",
    "sex":1,
    "province":"PROVINCE",
    "city":"CITY",
    "country":"COUNTRY",
    "headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
    "privilege":[
    "PRIVILEGE1",
    "PRIVILEGE2"
    ],
    "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
    }
    参数    说明
    openid    普通用户的标识,对当前开发者帐号唯一
    nickname    普通用户昵称
    sex    普通用户性别,1为男性,2为女性
    province    普通用户个人资料填写的省份
    city    普通用户个人资料填写的城市
    country    国家,如中国为CN
    headimgurl    用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
    privilege    用户特权信息,json数组,如微信沃卡用户为(chinaunicom)
    unionid    用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。
    建议:

    开发者最好保存unionID信息,以便以后在不同应用之间进行用户信息互通。

    错误的Json返回示例:

    {
    "errcode":40003,"errmsg":"invalid openid"
    }

    工具类

    package io.renren.common.utils;

    import java.util.HashMap;
    import java.util.Map;

    /**
     * 返回数据
     *
     * @author chenshun
     * @email sunlightcs@gmail.com
     * @date 2016年10月27日 下午9:59:27
     */
    public class R extends HashMap<String, Object> {
        private static final long serialVersionUID = 1L;
        
        public R() {
            put("code", 0);
            put("msg", "success");
        }
        
        public static R error() {
            return error(500, "未知异常,请联系管理员");
        }
        
        public static R error(String msg) {
            return error(500, msg);
        }
        
        public static R error(int code, String msg) {
            R r = new R();
            r.put("code", code);
            r.put("msg", msg);
            return r;
        }

        public static R ok(String msg) {
            R r = new R();
            r.put("msg", msg);
            return r;
        }
        
        public static R ok(Map<String, Object> map) {
            R r = new R();
            r.putAll(map);
            return r;
        }
        
        public static R ok() {
            return new R();
        }

        @Override
        public R put(String key, Object value) {
            super.put(key, value);
            return this;
        }
    }
    ————————————————
    原文链接:https://blog.csdn.net/weixin_42694286/java/article/details/84344786

  • 相关阅读:
    【Python-Django模型迁移】用户数据库模型的迁移(对其他数据库迁移同样适用)!!!
    【Python-Django定义用户模型类】Python-Django定义用户模型类详解!!!
    【OpenCV-ANN神经网络自动驾驶】树莓派OpenCV神经网络自动驾驶小车【源码+实物】
    【Python-Django】Jinja2模板引擎配置教程详解!!!!
    js实现事件委托
    前端里面如何进行搜索引擎优化(SEO)
    ajax的优缺点
    css中px,em,rem,rpx的区别
    margin与padding的bug
    css3动画:transition和animation
  • 原文地址:https://www.cnblogs.com/uzxin/p/13385264.html
Copyright © 2011-2022 走看看