admin管理员组

文章数量:1559110


文章目录

  • springboot整合微信登录
    • 1.准备工作
      • 1.1 获取微信登录凭证
      • 1.2 配置文件
      • 1.3 添加依赖
      • 1.4 创建读取公共常量的工具类
      • 1.5 httpclient工具类
    • 2.实现微信登录
      • 2.1 具体流程
      • 2.2 生成微信扫描的二维码(请求code)
      • 2.3 回调

springboot整合微信登录

1.准备工作

1.1 获取微信登录凭证

前往九游会真人第一品牌官网微信开放平台 (qq),完成以下步骤:

  1. 注册
  2. 邮箱激活
  3. 完善开发者资料
  4. 开发者资质认证
  5. 创建网站应用

1.2 配置文件

在配置文件application.properties添加相关配置信息:

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

1.3 添加依赖


<dependency>
    <groupid>org.apache.httpcomponentsgroupid>
    <artifactid>httpclientartifactid>
dependency>
<dependency>
    <groupid>commons-iogroupid>
    <artifactid>commons-ioartifactid>
dependency>
<dependency>
    <groupid>com.google.code.gsongroupid>
    <artifactid>gsonartifactid>
dependency>

1.4 创建读取公共常量的工具类

创建读取公共常量的工具类constantwxutils

/**
 * @author xppll
 * @date 2021/12/11 14:39
 */
@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;
    }
}

1.5 httpclient工具类

/**
 *  依赖的jar包有:commons-lang-2.6.jar、httpclient-4.3.2.jar、httpcore-4.3.1.jar、commons-io-2.4.jar
 * @author zhaoyb
 *
 */
public class httpclientutils {
   public static final int conntimeout=10000;
   public static final int readtimeout=10000;
   public static final string charset="utf-8";
   private static httpclient client = null;
   static {
      poolinghttpclientconnectionmanager cm = new poolinghttpclientconnectionmanager();
      cm.setmaxtotal(128);
      cm.setdefaultmaxperroute(128);
      client = httpclients.custom().setconnectionmanager(cm).build();
   }
   public static string postparameters(string url, string parameterstr) throws connecttimeoutexception, sockettimeoutexception, exception{
      return post(url,parameterstr,"application/x-www-form-urlencoded",charset,conntimeout,readtimeout);
   }
   public static string postparameters(string url, string parameterstr,string charset, integer conntimeout, integer readtimeout) throws connecttimeoutexception, sockettimeoutexception, exception{
      return post(url,parameterstr,"application/x-www-form-urlencoded",charset,conntimeout,readtimeout);
   }
   public static string postparameters(string url, map<string, string> params) throws connecttimeoutexception,
         sockettimeoutexception, exception {
      return postform(url, params, null, conntimeout, readtimeout);
   }
   public static string postparameters(string url, map<string, string> params, integer conntimeout,integer readtimeout) throws connecttimeoutexception,
         sockettimeoutexception, exception {
      return postform(url, params, null, conntimeout, readtimeout);
   }
   public static string get(string url) throws exception {
      return get(url, charset, null, null);
   }
   public static string get(string url, string charset) throws exception {
      return get(url, charset, conntimeout, readtimeout);
   }
   /**
    * 发送一个 post 请求, 使用指定的字符集编码.
    *
    * @param url
    * @param body requestbody
    * @param mimetype 例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3
    * @param charset 编码
    * @param conntimeout 建立链接超时时间,毫秒.
    * @param readtimeout 响应超时时间,毫秒.
    * @return responsebody, 使用指定的字符集编码.
    * @throws connecttimeoutexception 建立链接超时异常
    * @throws sockettimeoutexception  响应超时
    * @throws exception
    */
   public static string post(string url, string body, string mimetype,string charset, integer conntimeout, integer readtimeout)
         throws connecttimeoutexception, sockettimeoutexception, exception {
      httpclient client = null;
      httppost post = new httppost(url);
      string result = "";
      try {
         if (stringutils.isnotblank(body)) {
            httpentity entity = new stringentity(body, contenttype.create(mimetype, charset));
            post.setentity(entity);
         }
         // 设置参数
         builder customreqconf = requestconfig.custom();
         if (conntimeout != null) {
            customreqconf.setconnecttimeout(conntimeout);
         }
         if (readtimeout != null) {
            customreqconf.setsockettimeout(readtimeout);
         }
         post.setconfig(customreqconf.build());
         httpresponse res;
         if (url.startswith("https")) {
            // 执行 https 请求.
            client = createsslinsecureclient();
            res = client.execute(post);
         } else {
            // 执行 http 请求.
            client = httpclientutils.client;
            res = client.execute(post);
         }
         result = ioutils.tostring(res.getentity().getcontent(), charset);
      } finally {
         post.releaseconnection();
         if (url.startswith("https") && client != null&& client instanceof closeablehttpclient) {
            ((closeablehttpclient) client).close();
         }
      }
      return result;
   }
   /**
    * 提交form表单
    *
    * @param url
    * @param params
    * @param conntimeout
    * @param readtimeout
    * @return
    * @throws connecttimeoutexception
    * @throws sockettimeoutexception
    * @throws exception
    */
   public static string postform(string url, map<string, string> params, map<string, string> headers, integer conntimeout,integer readtimeout) throws connecttimeoutexception,
         sockettimeoutexception, exception {
      httpclient client = null;
      httppost post = new httppost(url);
      try {
         if (params != null && !params.isempty()) {
            list<namevaluepair> formparams = new arraylist<namevaluepair>();
            set<entry<string, string>> entryset = params.entryset();
            for (entry<string, string> entry : entryset) {
               formparams.add(new basicnamevaluepair(entry.getkey(), entry.getvalue()));
            }
            urlencodedformentity entity = new urlencodedformentity(formparams, consts.utf_8);
            post.setentity(entity);
         }
         if (headers != null && !headers.isempty()) {
            for (entry<string, string> entry : headers.entryset()) {
               post.addheader(entry.getkey(), entry.getvalue());
            }
         }
         // 设置参数
         builder customreqconf = requestconfig.custom();
         if (conntimeout != null) {
            customreqconf.setconnecttimeout(conntimeout);
         }
         if (readtimeout != null) {
            customreqconf.setsockettimeout(readtimeout);
         }
         post.setconfig(customreqconf.build());
         httpresponse res = null;
         if (url.startswith("https")) {
            // 执行 https 请求.
            client = createsslinsecureclient();
            res = client.execute(post);
         } else {
            // 执行 http 请求.
            client = httpclientutils.client;
            res = client.execute(post);
         }
         return ioutils.tostring(res.getentity().getcontent(), "utf-8");
      } finally {
         post.releaseconnection();
         if (url.startswith("https") && client != null
               && client instanceof closeablehttpclient) {
            ((closeablehttpclient) client).close();
         }
      }
   }

   /**
    * 发送一个 get 请求
    *
    * @param url
    * @param charset
    * @param conntimeout  建立链接超时时间,毫秒.
    * @param readtimeout  响应超时时间,毫秒.
    * @return
    * @throws connecttimeoutexception   建立链接超时
    * @throws sockettimeoutexception   响应超时
    * @throws exception
    */
   public static string get(string url, string charset, integer conntimeout,integer readtimeout)
         throws connecttimeoutexception,sockettimeoutexception, exception {
      httpclient client = null;
      httpget get = new httpget(url);
      string result = "";
      try {
         // 设置参数
         builder customreqconf = requestconfig.custom();
         if (conntimeout != null) {
            customreqconf.setconnecttimeout(conntimeout);
         }
         if (readtimeout != null) {
            customreqconf.setsockettimeout(readtimeout);
         }
         get.setconfig(customreqconf.build());
         httpresponse res = null;
         if (url.startswith("https")) {
            // 执行 https 请求.
            client = createsslinsecureclient();
            res = client.execute(get);
         } else {
            // 执行 http 请求.
            client = httpclientutils.client;
            res = client.execute(get);
         }
         result = ioutils.tostring(res.getentity().getcontent(), charset);
      } finally {
         get.releaseconnection();
         if (url.startswith("https") && client != null && client instanceof closeablehttpclient) {
            ((closeablehttpclient) client).close();
         }
      }
      return result;
   }
   /**
    * 从 response 里获取 charset
    *
    * @param ressponse
    * @return
    */
   @suppresswarnings("unused")
   private static string getcharsetfromresponse(httpresponse ressponse) {
      // content-type:text/html; charset=gbk
      if (ressponse.getentity() != null  && ressponse.getentity().getcontenttype() != null && ressponse.getentity().getcontenttype().getvalue() != null) {
         string contenttype = ressponse.getentity().getcontenttype().getvalue();
         if (contenttype.contains("charset=")) {
            return contenttype.substring(contenttype.indexof("charset=")  8);
         }
      }
      return null;
   }
   /**
    * 创建 ssl连接
    * @return
    * @throws generalsecurityexception
    */
   private static closeablehttpclient createsslinsecureclient() throws generalsecurityexception {
      try {
         sslcontext sslcontext = new sslcontextbuilder().loadtrustmaterial(null, new truststrategy() {
            public boolean istrusted(x509certificate[] chain,string authtype) throws certificateexception {
               return true;
            }
         }).build();
         sslconnectionsocketfactory sslsf = new sslconnectionsocketfactory(sslcontext, new x509hostnameverifier() {
            @override
            public boolean verify(string arg0, sslsession arg1) {
               return true;
            }
            @override
            public void verify(string host, sslsocket ssl)
                  throws ioexception {
            }
            @override
            public void verify(string host, x509certificate cert)
                  throws sslexception {
            }
            @override
            public void verify(string host, string[] cns,
                           string[] subjectalts) throws sslexception {
            }
         });
         return httpclients.custom().setsslsocketfactory(sslsf).build();
      } catch (generalsecurityexception e) {
         throw e;
      }
   }
   public static void main(string[] args) {
      try {
         string str= post("https://localhost:443/ssl/test.shtml","name=12&page=34","application/x-www-form-urlencoded", "utf-8", 10000, 10000);
         //string str= get("https://localhost:443/ssl/test.shtml?name=12&page=34","gbk");
            /*map map = new hashmap();
            map.put("name", "111");
            map.put("page", "222");
            string str= postform("https://localhost:443/ssl/test.shtml",map,null, 10000, 10000);*/
         system.out.println(str);
      } catch (connecttimeoutexception e) {
         // todo auto-generated catch block
         e.printstacktrace();
      } catch (sockettimeoutexception e) {
         // todo auto-generated catch block
         e.printstacktrace();
      } catch (exception e) {
         // todo auto-generated catch block
         e.printstacktrace();
      }
   }
}

2.实现微信登录

可以参考官方文档:[网站应用微信登录开发指南](准备工作 | 微信开放文档 (qq))

2.1 具体流程

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

获取access_token时序图:

2.2 生成微信扫描的二维码(请求code)

controller层:

/**
 * @author xppll
 * @date 2021/12/11 14:48
 */
@crossorigin
@controller
@requestmapping("/api/ucenter/wx")
public class wxapicontroller {
    @autowired
    private ucentermemberservice memberservice;
    /**
     * 生成微信扫描二维码
     * @return 定向到请求微信地址
     */
    @getmapping("login")
    public string getwxcode() {
        //微信开放平台授权baseurl
        string baseurl = "https://open.weixin.qq/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 (unsupportedencodingexception e) {
            throw new guliexception(20001, e.getmessage());
        }
        //设置%s的值
        string url = string.format(
            baseurl,
            constantwxutils.wx_open_app_id,
            redirecturl,
            "atguigu"
        );
        //重定向到请求微信地址
        return "redirect:"  url;
    }
}

访问:http://localhost:8160/api/ucenter/wx/login

访问授权url后会得到一个微信登录二维码:

用户扫描二维码会看到确认登录的页面:

用户点击“确认登录”后,微信服务器会向谷粒学院的业务服务器发起回调,因此接下来我们需要开发回调controller

2.3 回调

具体分几步:

  1. 通过code获取access_token

    https://api.weixin.qq.com/sns/oauth2/access_token?appid=appid&secret=secret&code=code&grant_type=authorization_code
    
    参数是否必须说明
    appid应用唯一标识,在微信开放平台提交应用审核通过后获得
    secret应用密钥appsecret,在微信开放平台提交应用审核通过后获得
    code填写第一步获取的code参数
    grant_type填authorization_code
  2. 从返回结果获取两个值access_tokenopenid

  3. 通过openid查询数据库判断该用户是不是第一次登录

  4. 如果是第一次登录,根据access_tokenopenid再去访问微信的资源服务器,获取用户信息,存入数据库

  5. 使用jwt根据member对象生成token字符串,最后返回j9九游会老哥俱乐部交流区首页面,通过路径传递token字符串

/**
 * 获取扫描人信息,添加数据
 * @param code 类似于手机验证码,随机唯一的值
 * @param state 用于保持请求和回调的状态,授权请求后原样带回给第三方
 * @return
 */
@getmapping("callback")
public string callback(string code, string state) {
    try {
        //获取code值,临时票据类似于验证码
        //拿着code请求微信固定的地址,得到两个值
        //1.向认证服务器发送请求换取access_token
        string baseaccesstokenurl =
                "https://api.weixin.qq/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
        );
        //2.请求拼接好的地址,得到返回的两个值access_token和openid
        //使用httpclient发送请求,得到返回结果(json形式的字符串)
        string accesstokeninfo = httpclientutils.get(accesstokenurl);
        //从accesstokeninfo字符串获取两个值access_token、openid
        //把accesstokeninfo字符串转换为map集合,根据map里面的key获取值
        //这里使用json转换工具gson
        gson gson = new gson();
        hashmap accesstokenmap = gson.fromjson(accesstokeninfo, hashmap.class);
        string access_token = (string) accesstokenmap.get("access_token");
        string openid = (string) accesstokenmap.get("openid");
        //3.判断该用户是不是第一次扫码登录
        //通过openid判断
        ucentermember member = memberservice.getopenidmember(openid);
        //4.只有第一次登录才获取信息
        if (member == null) {
            //根据access_token和openid再去访问微信的资源服务器,获取用户信息
            string baseuserinfourl = "https://api.weixin.qq/sns/userinfo" 
                    "?access_token=%s" 
                    "&openid=%s";
            string userinfourl = string.format(
                    baseuserinfourl,
                    access_token,
                    openid
            );
            //发送请求,得到用户信息
            string userinfo = httpclientutils.get(userinfourl);
            system.out.println(userinfo);
            //将用户信息存入数据库
            //把json转换为map
            hashmap userinfomap = gson.fromjson(userinfo, hashmap.class);
            //得到nickname
            string nickname = (string) userinfomap.get("nickname");
            //得到微信头像avatar
            string headimgurl = (string) userinfomap.get("headimgurl");
            member = new ucentermember();
            member.setopenid(openid);
            member.setnickname(nickname);
            member.setavatar(headimgurl);
            memberservice.save(member);
        }
        //5.使用jwt根据member对象生成token字符串
        string jwttoken = jwtutils.getjwttoken(member.getid(), member.getnickname());
        //最后返回j9九游会老哥俱乐部交流区首页面,通过路径传递token字符串
        return "redirect:http://localhost:3000?token="  jwttoken;
    } catch (exception e) {
        throw new guliexception(20001, "微信登录失败");
    }
}

判断该用户是不是第一次扫码登录:

/**
 * 根据openid查询是否存在用户
 * @param openid 授权用户唯一标识
 * @return ucentermember
 */
@override
public ucentermember getopenidmember(string openid) {
    lambdaquerywrapper<ucentermember> querywrapper=new lambdaquerywrapper<>();
    querywrapper.eq(ucentermember::getopenid,openid);
    ucentermember member = basemapper.selectone(querywrapper);
    return member;
}


最后喜欢的小伙伴,记得三联哦!😏🍭😘

本文标签: springboot