- 微信登录之前还需要了解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对象
- var obj = new WxLogin(
- {
- self_redirect: true,
- id: "放置二维码的容器id",
- appid: "",
- scope: "",
- redirect_uri: "",
- state: "",
- style: "",
- href: ""
- }
- );
复制代码
- 整个过程
- 通过调用后端的接口,将上面js对象所需要的参数返回给前端页面
- 前端页面获取到所需要的参数即可生成二维码
- 用户扫描二维码重定向(回调)到当前后端返回的redirect_uri
- 回调成功则微信登录成功
- 从这个过程中发现微信登录过程中至少需要两个接口
- SpringBoot实现
- application.properties添加微信登录的一些配置,其中redirect_uri应该是在申请微信登录的时候会存在一个映射关系(这里博主也不是很懂),
目前微信登录支持localhost,端口应该是尚硅谷老师设定了(有可能是博主的映射关系(狗头)),后面的映射路径就是回调接口的映射路径
- wx.open.app_id=上面提供的AppID
- wx.open.app_secret=上面提供的AppSecret
- wx.open.redirect_uri=http://localhost:8160/api/user/wx/callback
- web.base_url=http://localhost:3000
复制代码- import org.springframework.beans.factory.InitializingBean;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
- @Component
- public class WxConstantProperties implements InitializingBean {
- @Value("${wx.open.app_id}")
- private String appId;
- @Value("${wx.open.app_secret}")
- private String appSecret;
- @Value("${wx.open.redirect_uri}")
- private String redirectUrl;
- @Value("${web.base_url}")
- private String yyghBaseUrl;
- public static String WX_OPEN_APP_ID;
- public static String WX_OPEN_APP_SECRET;
- public static String WX_OPEN_REDIRECT_URL;
- public static String WEB_BASE_URL;
- @Override
- public void afterPropertiesSet() throws Exception {
- WX_OPEN_APP_ID = appId;
- WX_OPEN_APP_SECRET = appSecret;
- WX_OPEN_REDIRECT_URL = redirectUrl;
- WEB_BASE_URL = yyghBaseUrl;
- }
- }
复制代码- import java.util.HashMap;
- @Controller
- @RequestMapping("/api/user/wx")
- public class WxApiController {
- @GetMapper("/getWxParam")
- @ResponseBody
- public Result<Map<String, Object>> getParam() {
- try {
- Map<String, Object> map = new HashMap<>();
- map.put("appid", WxConstantProperties.WX_OPEN_APP_ID);
- // 固定值
- map.put("scope", "snsapi_login");
- String wxOpenRedirectUrl = WxConstantProperties.WX_OPEN_REDIRECT_URL;
- // 应微信开放平台要求,需要使用URLEncoder规范url
- wxOpenRedirectUrl = URLEncoder.encode(wxOpenRedirectUrl, "utf-8");
- map.put("redirect_uri", wxOpenRedirectUrl);
- // 随意值
- map.put("state", System.currentTimeMillis() + "");
- return Result.ok(map);
- } catch (Exception e) {
- e.printStackTrace();
- return Result.fail();
- }
- }
- }
复制代码- import request from '@/utils/request'
- export default {
- getWxParam() {
- return request({
- url: `/api/user/wx/getWxParam`,
- method: 'get'
- })
- }
- }
复制代码- [/code]
- [list]
- [*]在编写第二个接口前,需要了解一个服务端向另一个服务端发送请求,往往是需要一个工具类
- [/list][code]import org.apache.commons.io.IOUtils;
- import org.apache.commons.lang.StringUtils;
- import org.apache.http.Consts;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpResponse;
- import org.apache.http.NameValuePair;
- import org.apache.http.client.HttpClient;
- import org.apache.http.client.config.RequestConfig;
- import org.apache.http.client.config.RequestConfig.Builder;
- import org.apache.http.client.entity.UrlEncodedFormEntity;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.client.methods.HttpRequestBase;
- import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
- import org.apache.http.conn.ssl.X509HostnameVerifier;
- import org.apache.http.entity.ContentType;
- import org.apache.http.entity.StringEntity;
- import org.apache.http.impl.client.CloseableHttpClient;
- import org.apache.http.impl.client.HttpClients;
- import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
- import org.apache.http.message.BasicNameValuePair;
- import org.apache.http.ssl.SSLContextBuilder;
- import org.apache.http.ssl.TrustStrategy;
- import javax.net.ssl.SSLContext;
- import javax.net.ssl.SSLException;
- import javax.net.ssl.SSLSession;
- import javax.net.ssl.SSLSocket;
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
- import java.security.GeneralSecurityException;
- import java.security.cert.CertificateException;
- import java.security.cert.X509Certificate;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- public class HttpClientUtils {
- public static final int CONN_TIMEOUT = 10000;
- public static final int READ_TIMEOUT = 10000;
- public static final String CHARSET = "UTF-8";
- public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
- private static HttpClient client;
- static {
- PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
- cm.setMaxTotal(128);
- cm.setDefaultMaxPerRoute(128);
- client = HttpClients.custom().setConnectionManager(cm).build();
- }
- public static String postParameters(String url, String params) throws Exception {
- return post(url, params, CONTENT_TYPE, CHARSET, CONN_TIMEOUT, READ_TIMEOUT);
- }
- public static String postParameters(String url, String params, String charset, Integer connTimeout, Integer readTimeout) throws Exception {
- return post(url, params, CONTENT_TYPE, charset, connTimeout, readTimeout);
- }
- public static String postParameters(String url, Map<String, String> params) throws Exception {
- return postForm(url, params, null, CONN_TIMEOUT, READ_TIMEOUT);
- }
- public static String postParameters(String url, Map<String, String> params, Integer connTimeout, Integer readTimeout) throws 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, CONN_TIMEOUT, READ_TIMEOUT);
- }
- /**
- * 发送一个get请求
- * @param url 请求地址
- * @param charset 字符集编码
- * @param connTimeout 连接超时时间
- * @param readTimeout 响应超时时间
- * @return String
- * @throws Exception ConnectTimeoutException、SocketTimeoutException等其他异常
- */
- public static String get(String url, String charset, Integer connTimeout, Integer readTimeout) throws Exception {
- HttpClient client = null;
- HttpGet get = new HttpGet(url);
- String result = "";
- try {
- // 设置参数
- getHttpRequestBase(get, connTimeout, readTimeout);
- // 获取响应信息
- HttpResponse res = getHttpResponse(url, get, client);
- result = IOUtils.toString(res.getEntity().getContent(), charset);
- }finally {
- get.releaseConnection();
- if (url.startsWith("https") && client instanceof CloseableHttpClient) {
- ((CloseableHttpClient) client).close();
- }
- }
- return result;
- }
- /**
- * 发送一个post请求
- * @param url 请求地址
- * @param body 请求体
- * @param mimeType 类似于Content-Type
- * @param charset 字符集编码
- * @param connTimeout 连接超时时间
- * @param readTimeout 响应超时时间
- * @return String
- * @throws Exception ConnectTimeoutException、SocketTimeoutException等其他异常
- */
- public static String post(String url, String body, String mimeType, String charset, Integer connTimeout, Integer readTimeout) throws Exception {
- HttpClient client = null;
- HttpPost post = new HttpPost(url);
- String result = "";
- try {
- if(!StringUtils.isEmpty(body)) {
- HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
- post.setEntity(entity);
- }
- // 设置参数
- getHttpRequestBase(post, connTimeout, readTimeout);
- // 获取响应信息
- HttpResponse res = getHttpResponse(url, post, client);
- result = IOUtils.toString(res.getEntity().getContent(), charset);
- } finally {
- post.releaseConnection();
- if (url.startsWith("https") && client instanceof CloseableHttpClient) {
- ((CloseableHttpClient) client).close();
- }
- }
- return result;
- }
- /**
- * form表单提交方式请求
- * @param url 请求地址
- * @param params 表单参数
- * @param headers 请求头
- * @param connTimeout 连接超时时间
- * @param readTimeout 响应超时时间
- * @return String
- * @throws Exception ConnectTimeoutException、SocketTimeoutException等其他异常
- */
- public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout, Integer readTimeout) throws Exception {
- HttpClient client = null;
- HttpPost post = new HttpPost(url);
- try {
- if (params != null && !params.isEmpty()) {
- List<NameValuePair> formParams = new ArrayList<>();
- 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());
- }
- }
- // 设置参数
- getHttpRequestBase(post, connTimeout, readTimeout);
- // 获取响应信息
- HttpResponse res = getHttpResponse(url, post, client);
- return IOUtils.toString(res.getEntity().getContent(), String.valueOf(StandardCharsets.UTF_8));
- }finally {
- post.releaseConnection();
- if (url.startsWith("https") && client instanceof CloseableHttpClient) {
- ((CloseableHttpClient) client).close();
- }
- }
- }
- /**
- * 设置参数
- * @param base 协议请求对象
- * @param connTimeout 连接超时时间
- * @param readTimeout 响应超时时间
- */
- private static void getHttpRequestBase(HttpRequestBase base, Integer connTimeout, Integer readTimeout) {
- Builder customReqConf = RequestConfig.custom();
- if(connTimeout != null) customReqConf.setConnectTimeout(connTimeout);
- if(readTimeout != null) customReqConf.setSocketTimeout(readTimeout);
- base.setConfig(customReqConf.build());
- }
- /**
- * 获取响应信息
- * @param url 请求地址
- * @param base 协议请求对象
- * @return HttpResponse
- * @throws Exception 异常信息
- */
- private static HttpResponse getHttpResponse(String url, HttpRequestBase base, HttpClient client) throws Exception{
- if (url.startsWith("https")) {
- // 执行 Https 请求.
- client = createSSLInsecureClient();
- } else {
- // 执行 Http 请求.
- client = HttpClientUtils.client;
- }
- return client.execute(base);
- }
- /**
- * 创建SSL连接供https协议请求
- * @return CloseableHttpClient
- * @throws GeneralSecurityException 安全异常
- */
- private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
- try {
- SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
- @Override
- public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
- return true;
- }
- }).build();
- SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
- @Override
- public void verify(String s, SSLSocket sslSocket) throws IOException {}
- @Override
- public void verify(String s, X509Certificate x509Certificate) throws SSLException {}
- @Override
- public void verify(String s, String[] strings, String[] strings1) throws SSLException {}
- @Override
- public boolean verify(String s, SSLSession sslSession) {
- return true;
- }
- });
- return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
- } catch (GeneralSecurityException e) {
- throw e;
- }
- }
- /**
- * 从 response 里获取 charset
- * @param response 响应信息
- */
- @SuppressWarnings("unused")
- private static String getCharsetFromResponse(HttpResponse response) {
- // Content-Type:text/html; charset=GBK
- if (response.getEntity() != null && response.getEntity().getContentType() != null && response.getEntity().getContentType().getValue() != null) {
- String contentType = response.getEntity().getContentType().getValue();
- if (contentType.contains("charset=")) {
- return contentType.substring(contentType.indexOf("charset=") + 8);
- }
- }
- return null;
- }
- }
复制代码
- 第二个接口:处理扫码之后的接口(一般是获取当前扫码用户的信息)
- @Controller
- @RequestMapping("/api/user/wx")
- public class WxApiController {
- @GetMapping("/callback")
- public String callback(String code, String state) {
- // 根据临时票据code和微信id及密钥请求微信固定地址,获取两个值(access_token和openid)
- StringBuffer baseAccessTokenUrl = new StringBuffer()
- .append("https://api.weixin.qq.com/sns/oauth2/access_token")
- .append("?appid=%s").append("&secret=%s")
- .append("&code=%s").append("&grant_type=authorization_code");
- String accessTokenUrl = String.format(
- baseAccessTokenUrl.toString(), ConstantWxPropertiesUtils.WX_OPEN_APP_ID,
- ConstantWxPropertiesUtils.WX_OPEN_APP_SECRET, code);
-
- try {
- // 使用HttpClientUtils工具类请求地址
- String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
- JSONObject jsonObject = JSONObject.parseObject(accessTokenInfo);
- String accessToken = jsonObject.getString("access_token");
- String openid = jsonObject.getString("openid");
- // 如果数据库表中存在openid值,
- UserInfo userInfo = userInfoService.selectWxInfoOpenId(openid);
- if(userInfo == null) {
- // 根据access_token和openid获取扫码人信息
- String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
- "?access_token=%s" + "&openid=%s";
- String userInfoUrl = String.format(baseUserInfoUrl, accessToken, openid);
- String userInfoResult = HttpClientUtils.get(userInfoUrl);
- JSONObject userObject = JSONObject.parseObject(userInfoResult);
- String nickname = userObject.getString("nickname");
- String headImageUrl = userObject.getString("headimgurl");
-
- // 信息保存到数据库中(省略)
- }
- // 最后重定向到前端某一个页面,期间可以随带一些重要的信息
- return "redirect:" + WxConstantProperties.WEB_BASE_URL + "/weixin/callback?token=" +
- token + "&openid=" + openid + "&name=" + URLEncoder.encode(name, "utf-8");
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |