微信扫码登录

打印 上一主题 下一主题

主题 866|帖子 866|积分 2600


  • 微信登录之前还需要了解OAuth2知识
  • 前期准备

    • 注册微信开放平台
    • 邮箱激活
    • 完善开发者资料(暂不支持个体用户)
    • 开发者资质认证:营业执照、1-2个工作如审批、300元
    • 网站应用:最好是已经部署到服务器上的项目,7个工作日审批

  • 审核通过之后会有AppID和AppSecret两个值

    • AppID: 申请的ID
    • AppSecret: 申请的Secret

  • 说明:微信登录二维码是以弹出层的形式打开,所以前端需要做一些引入

    • 在页面中引入js文件:https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
    • 在需要使用微信登录的地方实例js对象

  1. var obj = new WxLogin(
  2.         {
  3.           self_redirect: true,
  4.           id: "放置二维码的容器id",
  5.           appid: "",
  6.           scope: "",
  7.           redirect_uri: "",
  8.           state: "",
  9.           style: "",
  10.           href: ""
  11.         }
  12. );
复制代码

  • 整个过程

    • 通过调用后端的接口,将上面js对象所需要的参数返回给前端页面
    • 前端页面获取到所需要的参数即可生成二维码
    • 用户扫描二维码重定向(回调)到当前后端返回的redirect_uri
    • 回调成功则微信登录成功

  • 从这个过程中发现微信登录过程中至少需要两个接口
  • SpringBoot实现

    • application.properties添加微信登录的一些配置,其中redirect_uri应该是在申请微信登录的时候会存在一个映射关系(这里博主也不是很懂),
      目前微信登录支持localhost,端口应该是尚硅谷老师设定了(有可能是博主的映射关系(狗头)),后面的映射路径就是回调接口的映射路径

  1. wx.open.app_id=上面提供的AppID
  2. wx.open.app_secret=上面提供的AppSecret
  3. wx.open.redirect_uri=http://localhost:8160/api/user/wx/callback
  4. web.base_url=http://localhost:3000
复制代码

  • 新建常量类,读取配置文件中微信的配置信息
  1. import org.springframework.beans.factory.InitializingBean;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class WxConstantProperties implements InitializingBean {
  6.     @Value("${wx.open.app_id}")
  7.     private String appId;
  8.     @Value("${wx.open.app_secret}")
  9.     private String appSecret;
  10.     @Value("${wx.open.redirect_uri}")
  11.     private String redirectUrl;
  12.     @Value("${web.base_url}")
  13.     private String yyghBaseUrl;
  14.     public static String WX_OPEN_APP_ID;
  15.     public static String WX_OPEN_APP_SECRET;
  16.     public static String WX_OPEN_REDIRECT_URL;
  17.     public static String WEB_BASE_URL;
  18.     @Override
  19.     public void afterPropertiesSet() throws Exception {
  20.         WX_OPEN_APP_ID = appId;
  21.         WX_OPEN_APP_SECRET = appSecret;
  22.         WX_OPEN_REDIRECT_URL = redirectUrl;
  23.         WEB_BASE_URL = yyghBaseUrl;
  24.     }
  25. }
复制代码

  • 第一个接口:返回前端生成二维码的参数
  1. import java.util.HashMap;
  2. @Controller
  3. @RequestMapping("/api/user/wx")
  4. public class WxApiController {
  5.     @GetMapper("/getWxParam")
  6.     @ResponseBody
  7.     public Result<Map<String, Object>> getParam() {
  8.         try {
  9.             Map<String, Object> map = new HashMap<>();
  10.             map.put("appid", WxConstantProperties.WX_OPEN_APP_ID);
  11.             // 固定值
  12.             map.put("scope", "snsapi_login");
  13.             String wxOpenRedirectUrl = WxConstantProperties.WX_OPEN_REDIRECT_URL;
  14.             // 应微信开放平台要求,需要使用URLEncoder规范url
  15.             wxOpenRedirectUrl = URLEncoder.encode(wxOpenRedirectUrl, "utf-8");
  16.             map.put("redirect_uri", wxOpenRedirectUrl);
  17.             // 随意值
  18.             map.put("state", System.currentTimeMillis() + "");
  19.             return Result.ok(map);
  20.         } catch (Exception e) {
  21.             e.printStackTrace();
  22.             return Result.fail();
  23.         }
  24.     }
  25. }
复制代码

  • 前端创建两个文件

    • 请求后端接口的js文件

  1. import request from '@/utils/request'
  2. export default {
  3.     getWxParam() {
  4.         return request({
  5.             url: `/api/user/wx/getWxParam`,
  6.             method: 'get'
  7.         })
  8.     }
  9. }
复制代码

  • 当前登录页面的vue组件
  1. [/code]
  2. [list]
  3. [*]在编写第二个接口前,需要了解一个服务端向另一个服务端发送请求,往往是需要一个工具类
  4. [/list][code]import org.apache.commons.io.IOUtils;
  5. import org.apache.commons.lang.StringUtils;
  6. import org.apache.http.Consts;
  7. import org.apache.http.HttpEntity;
  8. import org.apache.http.HttpResponse;
  9. import org.apache.http.NameValuePair;
  10. import org.apache.http.client.HttpClient;
  11. import org.apache.http.client.config.RequestConfig;
  12. import org.apache.http.client.config.RequestConfig.Builder;
  13. import org.apache.http.client.entity.UrlEncodedFormEntity;
  14. import org.apache.http.client.methods.HttpGet;
  15. import org.apache.http.client.methods.HttpPost;
  16. import org.apache.http.client.methods.HttpRequestBase;
  17. import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  18. import org.apache.http.conn.ssl.X509HostnameVerifier;
  19. import org.apache.http.entity.ContentType;
  20. import org.apache.http.entity.StringEntity;
  21. import org.apache.http.impl.client.CloseableHttpClient;
  22. import org.apache.http.impl.client.HttpClients;
  23. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
  24. import org.apache.http.message.BasicNameValuePair;
  25. import org.apache.http.ssl.SSLContextBuilder;
  26. import org.apache.http.ssl.TrustStrategy;
  27. import javax.net.ssl.SSLContext;
  28. import javax.net.ssl.SSLException;
  29. import javax.net.ssl.SSLSession;
  30. import javax.net.ssl.SSLSocket;
  31. import java.io.IOException;
  32. import java.nio.charset.StandardCharsets;
  33. import java.security.GeneralSecurityException;
  34. import java.security.cert.CertificateException;
  35. import java.security.cert.X509Certificate;
  36. import java.util.ArrayList;
  37. import java.util.List;
  38. import java.util.Map;
  39. import java.util.Map.Entry;
  40. import java.util.Set;
  41. public class HttpClientUtils {
  42.     public static final int CONN_TIMEOUT = 10000;
  43.     public static final int READ_TIMEOUT = 10000;
  44.     public static final String CHARSET = "UTF-8";
  45.     public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
  46.     private static HttpClient client;
  47.     static {
  48.         PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
  49.         cm.setMaxTotal(128);
  50.         cm.setDefaultMaxPerRoute(128);
  51.         client = HttpClients.custom().setConnectionManager(cm).build();
  52.     }
  53.     public static String postParameters(String url, String params) throws Exception {
  54.         return post(url, params, CONTENT_TYPE, CHARSET, CONN_TIMEOUT, READ_TIMEOUT);
  55.     }
  56.     public static String postParameters(String url, String params, String charset, Integer connTimeout, Integer readTimeout) throws Exception {
  57.         return post(url, params, CONTENT_TYPE, charset, connTimeout, readTimeout);
  58.     }
  59.     public static String postParameters(String url, Map<String, String> params) throws Exception {
  60.         return postForm(url, params, null, CONN_TIMEOUT, READ_TIMEOUT);
  61.     }
  62.     public static String postParameters(String url, Map<String, String> params, Integer connTimeout, Integer readTimeout) throws Exception {
  63.         return postForm(url, params, null, connTimeout, readTimeout);
  64.     }
  65.     public static String get(String url) throws Exception {
  66.         return get(url, CHARSET, null, null);
  67.     }
  68.     public static String get(String url, String charset) throws Exception {
  69.         return get(url, charset, CONN_TIMEOUT, READ_TIMEOUT);
  70.     }
  71.     /**
  72.      * 发送一个get请求
  73.      * @param url 请求地址
  74.      * @param charset 字符集编码
  75.      * @param connTimeout 连接超时时间
  76.      * @param readTimeout 响应超时时间
  77.      * @return String
  78.      * @throws Exception ConnectTimeoutException、SocketTimeoutException等其他异常
  79.      */
  80.     public static String get(String url, String charset, Integer connTimeout, Integer readTimeout) throws Exception {
  81.         HttpClient client = null;
  82.         HttpGet get = new HttpGet(url);
  83.         String result = "";
  84.         try {
  85.             // 设置参数
  86.             getHttpRequestBase(get, connTimeout, readTimeout);
  87.             // 获取响应信息
  88.             HttpResponse res = getHttpResponse(url, get, client);
  89.             result = IOUtils.toString(res.getEntity().getContent(), charset);
  90.         }finally {
  91.             get.releaseConnection();
  92.             if (url.startsWith("https") && client instanceof CloseableHttpClient) {
  93.                 ((CloseableHttpClient) client).close();
  94.             }
  95.         }
  96.         return result;
  97.     }
  98.     /**
  99.      * 发送一个post请求
  100.      * @param url 请求地址
  101.      * @param body 请求体
  102.      * @param mimeType 类似于Content-Type
  103.      * @param charset 字符集编码
  104.      * @param connTimeout 连接超时时间
  105.      * @param readTimeout 响应超时时间
  106.      * @return String
  107.      * @throws Exception ConnectTimeoutException、SocketTimeoutException等其他异常
  108.      */
  109.     public static String post(String url, String body, String mimeType, String charset, Integer connTimeout, Integer readTimeout) throws Exception {
  110.         HttpClient client = null;
  111.         HttpPost post = new HttpPost(url);
  112.         String result = "";
  113.         try {
  114.             if(!StringUtils.isEmpty(body)) {
  115.                 HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
  116.                 post.setEntity(entity);
  117.             }
  118.             // 设置参数
  119.             getHttpRequestBase(post, connTimeout, readTimeout);
  120.             // 获取响应信息
  121.             HttpResponse res = getHttpResponse(url, post, client);
  122.             result = IOUtils.toString(res.getEntity().getContent(), charset);
  123.         } finally {
  124.             post.releaseConnection();
  125.             if (url.startsWith("https") && client instanceof CloseableHttpClient) {
  126.                 ((CloseableHttpClient) client).close();
  127.             }
  128.         }
  129.         return result;
  130.     }
  131.     /**
  132.      * form表单提交方式请求
  133.      * @param url 请求地址
  134.      * @param params 表单参数
  135.      * @param headers 请求头
  136.      * @param connTimeout 连接超时时间
  137.      * @param readTimeout 响应超时时间
  138.      * @return String
  139.      * @throws Exception ConnectTimeoutException、SocketTimeoutException等其他异常
  140.      */
  141.     public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout, Integer readTimeout) throws Exception {
  142.         HttpClient client = null;
  143.         HttpPost post = new HttpPost(url);
  144.         try {
  145.             if (params != null && !params.isEmpty()) {
  146.                 List<NameValuePair> formParams = new ArrayList<>();
  147.                 Set<Entry<String, String>> entrySet = params.entrySet();
  148.                 for (Entry<String, String> entry : entrySet) {
  149.                     formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
  150.                 }
  151.                 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
  152.                 post.setEntity(entity);
  153.             }
  154.             if (headers != null && !headers.isEmpty()) {
  155.                 for (Entry<String, String> entry : headers.entrySet()) {
  156.                     post.addHeader(entry.getKey(), entry.getValue());
  157.                 }
  158.             }
  159.             // 设置参数
  160.             getHttpRequestBase(post, connTimeout, readTimeout);
  161.             // 获取响应信息
  162.             HttpResponse res = getHttpResponse(url, post, client);
  163.             return IOUtils.toString(res.getEntity().getContent(), String.valueOf(StandardCharsets.UTF_8));
  164.         }finally {
  165.             post.releaseConnection();
  166.             if (url.startsWith("https") && client instanceof CloseableHttpClient) {
  167.                 ((CloseableHttpClient) client).close();
  168.             }
  169.         }
  170.     }
  171.     /**
  172.      * 设置参数
  173.      * @param base 协议请求对象
  174.      * @param connTimeout 连接超时时间
  175.      * @param readTimeout 响应超时时间
  176.      */
  177.     private static void getHttpRequestBase(HttpRequestBase base, Integer connTimeout, Integer readTimeout) {
  178.         Builder customReqConf = RequestConfig.custom();
  179.         if(connTimeout != null) customReqConf.setConnectTimeout(connTimeout);
  180.         if(readTimeout != null) customReqConf.setSocketTimeout(readTimeout);
  181.         base.setConfig(customReqConf.build());
  182.     }
  183.     /**
  184.      * 获取响应信息
  185.      * @param url 请求地址
  186.      * @param base 协议请求对象
  187.      * @return HttpResponse
  188.      * @throws Exception 异常信息
  189.      */
  190.     private static HttpResponse getHttpResponse(String url, HttpRequestBase base, HttpClient client) throws Exception{
  191.         if (url.startsWith("https")) {
  192.             // 执行 Https 请求.
  193.             client = createSSLInsecureClient();
  194.         } else {
  195.             // 执行 Http 请求.
  196.             client = HttpClientUtils.client;
  197.         }
  198.         return client.execute(base);
  199.     }
  200.     /**
  201.      * 创建SSL连接供https协议请求
  202.      * @return CloseableHttpClient
  203.      * @throws GeneralSecurityException 安全异常
  204.      */
  205.     private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
  206.         try {
  207.             SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
  208.                 @Override
  209.                 public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
  210.                     return true;
  211.                 }
  212.             }).build();
  213.             SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
  214.                 @Override
  215.                 public void verify(String s, SSLSocket sslSocket) throws IOException {}
  216.                 @Override
  217.                 public void verify(String s, X509Certificate x509Certificate) throws SSLException {}
  218.                 @Override
  219.                 public void verify(String s, String[] strings, String[] strings1) throws SSLException {}
  220.                 @Override
  221.                 public boolean verify(String s, SSLSession sslSession) {
  222.                     return true;
  223.                 }
  224.             });
  225.             return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
  226.         } catch (GeneralSecurityException e) {
  227.             throw e;
  228.         }
  229.     }
  230.     /**
  231.      * 从 response 里获取 charset
  232.      * @param response  响应信息
  233.      */
  234.     @SuppressWarnings("unused")
  235.     private static String getCharsetFromResponse(HttpResponse response) {
  236.         // Content-Type:text/html; charset=GBK
  237.         if (response.getEntity() != null  && response.getEntity().getContentType() != null && response.getEntity().getContentType().getValue() != null) {
  238.             String contentType = response.getEntity().getContentType().getValue();
  239.             if (contentType.contains("charset=")) {
  240.                 return contentType.substring(contentType.indexOf("charset=") + 8);
  241.             }
  242.         }
  243.         return null;
  244.     }
  245. }
复制代码

  • 第二个接口:处理扫码之后的接口(一般是获取当前扫码用户的信息)
  1. @Controller
  2. @RequestMapping("/api/user/wx")
  3. public class WxApiController {
  4.     @GetMapping("/callback")
  5.     public String callback(String code, String state) {
  6.         // 根据临时票据code和微信id及密钥请求微信固定地址,获取两个值(access_token和openid)
  7.         StringBuffer baseAccessTokenUrl = new StringBuffer()
  8.                 .append("https://api.weixin.qq.com/sns/oauth2/access_token")
  9.                 .append("?appid=%s").append("&secret=%s")
  10.                 .append("&code=%s").append("&grant_type=authorization_code");
  11.         String accessTokenUrl = String.format(
  12.                 baseAccessTokenUrl.toString(), ConstantWxPropertiesUtils.WX_OPEN_APP_ID,
  13.                 ConstantWxPropertiesUtils.WX_OPEN_APP_SECRET, code);
  14.         
  15.         try {
  16.             // 使用HttpClientUtils工具类请求地址
  17.             String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
  18.             JSONObject jsonObject = JSONObject.parseObject(accessTokenInfo);
  19.             String accessToken = jsonObject.getString("access_token");
  20.             String openid = jsonObject.getString("openid");
  21.             // 如果数据库表中存在openid值,
  22.             UserInfo userInfo = userInfoService.selectWxInfoOpenId(openid);
  23.             if(userInfo == null) {
  24.                 // 根据access_token和openid获取扫码人信息
  25.                 String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
  26.                         "?access_token=%s" + "&openid=%s";
  27.                 String userInfoUrl = String.format(baseUserInfoUrl, accessToken, openid);
  28.                 String userInfoResult = HttpClientUtils.get(userInfoUrl);
  29.                 JSONObject userObject = JSONObject.parseObject(userInfoResult);
  30.                 String nickname = userObject.getString("nickname");
  31.                 String headImageUrl = userObject.getString("headimgurl");
  32.                
  33.                 // 信息保存到数据库中(省略)
  34.             }
  35.             // 最后重定向到前端某一个页面,期间可以随带一些重要的信息
  36.             return "redirect:" + WxConstantProperties.WEB_BASE_URL + "/weixin/callback?token=" +
  37.                     token + "&openid=" + openid + "&name=" + URLEncoder.encode(name, "utf-8");
  38.         } catch (Exception e) {
  39.             e.printStackTrace();
  40.             return null;
  41.         }
  42.     }
  43. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

拉不拉稀肚拉稀

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表