目前实现微信授权登陆分以下几个部分:
1️⃣:调取微信wx.login()接口获取code
关于code:
2️⃣将code通过微信wx.request()接口传到后台,然后将code、appid、secret拼接之后传给微信服务器,从而换取上述openid及本次会话秘钥session_key等。
上述接口中:
1.url为后台接口地址。
2.data为本次会话要传递的数据,例如将code传入后台
3.header为请求头部行,关于这个,在微信的官方文档中即图片中’application/json’已经不是最新的,目前微信已经更新这个请求头部行,最新的头部行为
header: {
'Content-Type': 'application/x-www-form-urlencoded' // 默认值
}
如果上述请求头部行不正确,在发起网络请求时会报400错误(Bad Request)错误请求!
上述代码中也要加上请求方式为POST请求
完整代码如下:
wx.login({
success (res) {
console.log("code"+"==="+res.code)
wx.request({
url: 'http://localhost:1314/wx/login', //并非真实的接口地址,此处采用本机作为服务器示例!!!
method: 'POST',
data: {
code:res.code
},
header: {
'Content-Type': 'application/x-www-form-urlencoded' // 默认值
},
success (res) {
console.log(res.data)
}
})
}
})
前端目前只需要这些内容~
3️⃣后端接收前端传来的code并请求微信服务器换取openid等。【springboot】
tips:如果不会spring的请看我的一篇springboot入门案例,授权登陆牵扯到的springboot并不深入,可以简要了解
此篇文章采取两种方式给大家讲解(主要区别在于发起请求的方式!没必要的话可以只用一种!!!):
(一):
包结构如下:
第一种方法请忽略图中config包,采用utils工具类封装好的http请求工具类
无法导入文件,在此我将两个文件的代码写在下面(太长了,请忽略直接复制粘贴!):
HttpClientUtil:
package com.java1234.util;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.util.PublicSuffixMatcher;
import org.apache.http.conn.util.PublicSuffixMatcherLoader;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* httpClient 工具类
* @company Java知识分享网
* @create 2019-02-10 下午 2:49
*/
@Component
public class HttpClientUtil {
/**
* 默认参数设置
* setConnectTimeout:设置连接超时时间,单位毫秒。
* setConnectionRequestTimeout:设置从connect Manager获取Connection 超时时间,单位毫秒。
* setSocketTimeout:请求获取数据的超时时间,单位毫秒。访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。 暂时定义15分钟
*/
private RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(600000).setConnectTimeout(600000).setConnectionRequestTimeout(600000).build();
/**
* 静态内部类---作用:单例产生类的实例
* @author Administrator
*
*/
private static class LazyHolder {
private static final HttpClientUtil INSTANCE = new HttpClientUtil();
}
private HttpClientUtil(){}
public static HttpClientUtil getInstance(){
return LazyHolder.INSTANCE;
}
/**
* 发送 post请求
* @param httpUrl 地址
*/
public String sendHttpPost(String httpUrl) {
HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
return sendHttpPost(httpPost);
}
/**
* 发送 post请求
* @param httpUrl 地址
* @param params 参数(格式:key1=value1&key2=value2)
*/
public String sendHttpPost(String httpUrl, String params) {
HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
try {
//设置参数
StringEntity stringEntity = new StringEntity(params, "UTF-8");
stringEntity.setContentType("application/x-www-form-urlencoded");
httpPost.setEntity(stringEntity);
} catch (Exception e) {
e.printStackTrace();
}
return sendHttpPost(httpPost);
}
/**
* 发送 post请求
* @param httpUrl 地址
* @param maps 参数
*/
public String sendHttpPost(String httpUrl, Map<String, String> maps) {
HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost
// 创建参数队列
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
for (String key : maps.keySet()) {
nameValuePairs.add(new BasicNameValuePair(key, maps.get(key)));
}
try {
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
return sendHttpPost(httpPost);
}
/**
* 发送Post请求
* @param httpPost
* @return
*/
private String sendHttpPost(HttpPost httpPost) {
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
HttpEntity entity = null;
String responseContent = null;
try {
// 创建默认的httpClient实例
httpClient = HttpClients.createDefault();
httpPost.setConfig(requestConfig);
// 执行请求
long execStart = System.currentTimeMillis();
response = httpClient.execute(httpPost);
long execEnd = System.currentTimeMillis();
System.out.println("=================执行post请求耗时:"+(execEnd-execStart)+"ms");
long getStart = System.currentTimeMillis();
entity = response.getEntity();
responseContent = EntityUtils.toString(entity, "UTF-8");
long getEnd = System.currentTimeMillis();
System.out.println("=================获取响应结果耗时:"+(getEnd-getStart)+"ms");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 关闭连接,释放资源
if (response != null) {
response.close();
}
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseContent;
}
/**
* 发送 get请求
* @param httpUrl
*/
public String sendHttpGet(String httpUrl) {
HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求
return sendHttpGet(httpGet);
}
/**
* 发送 get请求Https
* @param httpUrl
*/
public String sendHttpsGet(String httpUrl) {
HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求
return sendHttpsGet(httpGet);
}
/**
* 发送Get请求
* @param httpGet
* @return
*/
private String sendHttpGet(HttpGet httpGet) {
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
HttpEntity entity = null;
String responseContent = null;
try {
// 创建默认的httpClient实例.
httpClient = HttpClients.createDefault();
httpGet.setConfig(requestConfig);
// 执行请求
response = httpClient.execute(httpGet);
entity = response.getEntity();
responseContent = EntityUtils.toString(entity, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 关闭连接,释放资源
if (response != null) {
response.close();
}
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseContent;
}
/**
* 发送Get请求Https
* @param httpGet
* @return
*/
private String sendHttpsGet(HttpGet httpGet) {
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
HttpEntity entity = null;
String responseContent = null;
try {
// 创建默认的httpClient实例.
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(new URL(httpGet.getURI().toString()));
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
httpClient = HttpClients.custom().setSSLHostnameVerifier(hostnameVerifier).build();
httpGet.setConfig(requestConfig);
// 执行请求
response = httpClient.execute(httpGet);
entity = response.getEntity();
responseContent = EntityUtils.toString(entity, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 关闭连接,释放资源
if (response != null) {
response.close();
}
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseContent;
}
/**
* 发送xml数据
* @param url
* @param xmlData
* @return
* @throws ClientProtocolException
* @throws IOException
*/
public static HttpResponse sendXMLDataByPost(String url, String xmlData)
throws ClientProtocolException, IOException {
HttpClient httpClient = HttpClients.createDefault();
HttpPost httppost = new HttpPost(url);
StringEntity entity = new StringEntity(xmlData);
httppost.setEntity(entity);
httppost.setHeader("Content-Type", "text/xml;charset=UTF-8");
HttpResponse response = httpClient.execute(httppost);
return response;
}
/**
* 获得响应HTTP实体内容
*
* @param response
* @return
* @throws IOException
* @throws UnsupportedEncodingException
*/
public static String getHttpEntityContent(HttpResponse response) throws IOException, UnsupportedEncodingException {
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream is = entity.getContent();
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String line = br.readLine();
StringBuilder sb = new StringBuilder();
while (line != null) {
sb.append(line + "\n");
line = br.readLine();
}
return sb.toString();
}
return "";
}
}
StringUtil(为字符串处理文件,和授权登陆也没有关系,忘记删除了,分享给大家叭):
package com.mjc.boot.utils;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 字符串工具类
* @author
*
*/
public class StringUtil {
/**
* 判断是否是空
* @param str
* @return
*/
public static boolean isEmpty(String str){
if(str==null||"".equals(str.trim())){
return true;
}else{
return false;
}
}
/**
* 判断是否不是空
* @param str
* @return
*/
public static boolean isNotEmpty(String str){
if((str!=null)&&!"".equals(str.trim())){
return true;
}else{
return false;
}
}
/**
* 格式化模糊查询
* @param str
* @return
*/
public static String formatLike(String str){
if(isNotEmpty(str)){
return "%"+str+"%";
}else{
return null;
}
}
/**
* 过滤掉集合里的空格
* @param list
* @return
*/
public static List<String> filterWhite(List<String> list){
List<String> resultList=new ArrayList<String>();
for(String l:list){
if(isNotEmpty(l)){
resultList.add(l);
}
}
return resultList;
}
/**
* 去除html标签
*/
public static String stripHtml(String content) {
// <p>段落替换为换行
content = content.replaceAll("<p .*?>", "\r\n");
// <br><br/>替换为换行
content = content.replaceAll("<br\\s*/?>", "\r\n");
// 去掉其它的<>之间的东西
content = content.replaceAll("\\<.*?>", "");
// 去掉空格
content = content.replaceAll(" ", "");
return content;
}
/**
* 生成六位随机数
* @return
*/
public static String genSixRandomNum(){
Random random = new Random();
String result="";
for (int i=0;i<6;i++)
{
result+=random.nextInt(10);
}
return result;
}
/**
* 生成由[A-Z,0-9]生成的随机字符串
* @param length 欲生成的字符串长度
* @return
*/
public static String getRandomString(int length){
Random random = new Random();
StringBuffer sb = new StringBuffer();
for(int i = 0; i < length; ++i){
int number = random.nextInt(2);
long result = 0;
switch(number){
case 0:
result = Math.round(Math.random() * 25 + 65);
sb.append(String.valueOf((char)result));
break;
case 1:
sb.append(String.valueOf(new Random().nextInt(10)));
break;
}
}
return sb.toString();
}
}
同时pom.xml文件里要导入
<!-- 添加Httpclient支持 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.40</version>
</dependency>
以此来支持网络请求
至此准备工作结束:开始编写Controller
@RestController
@RequestMapping("/wx")
public class getopenid{
@Autowired
private HttpClientUtil httpClientUtil;
@PostMapping("/login")
public String wxLogin(@RequestParam("code")String code){
//用自己的小程序信息
String appid="你的小小程序appid";
String secret="你的小程序secret";
//拼接sql
String loginUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid +
"&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code";
String result = httpClientUtil.sendHttpGet(loginUrl);
System.out.println(result);
JSONObject jsonObject = JSON.parseObject(result);//将json字符串转化为jsonobject
String openid = jsonObject.get("openid").toString(); // 获取openId
System.out.println(openid);
return "hello";
}
}
对上述代码的解释:
关于几个注解,需要知道的可以去学一下springboot及注解,在这不做赘述
我们刚才写的HttpClientUtil已经被丢到容器中,然后注入一下,对应下面这步
然后就是编写方法,通过得到code,再加上已知的appid和secret,如果secret忘记可以去微信小程序官网重置!
然后拼接成sql,对应下面这步
然后向微信服务器发起网络请求
至此,请求成功之后微信会返回给我们一个结果,就是result
我们打印一下result来看
可见包含session_key和openid,且为json格式
我们通过对格式进行解析分别获得openid和session_key,采用JSONObject,不懂的话可以自行百度~
之后打印就会分别打印出来,我们将结果进行存储,可以采取map集合存储最后返回给前台map集合即可。由于我并没有做这些步骤,不在演示。
由于要返回map集合,上述代码有些部分要更改:
public Map<String, Object> wxLogin(@RequestParam("code")String code){
String appid="你的小程序appid";
String secret="你的小程序secret";
//拼接sql
String loginUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid +
"&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code";
String result = httpClientUtil.sendHttpGet(loginUrl);
System.out.println(result);
JSONObject jsonObject = JSON.parseObject(result);//将json字符串转化为jsonobject
String openid = jsonObject.get("openid").toString(); // 获取openId
String session_key = jsonObject.get("session_key").toString();
System.out.println(openid);
System.out.println(session_key);
Map<String,Object> resultMap=new HashMap<>();
resultMap.put("openid",openid);
resultMap.put("session_key",session_key);
return resultMap;
}
至此,我们演示一下
后台:
可以看到,解析出来openid和session_key并且打印出来前台:
后台已经将集合返回给前台,前台也获取到了
这就是方法(一)实现(二):
方法二和方法一的区别在于实现网络请求的部分不同,如果不必要!可以不用看!
springboot提供了一个模板:RestTemplate
还记得我们忽略的config包吗
package com.mjc.boot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class LoginConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
代码中:
将该类作为springboot的配置类,采用注解@Configuration
@Bean将方法丢到容器中
然后我们在刚才的Controller中
注入这个模板
并且采用这个对象里的方法进行网络请求就可以了!!
其余部分和方法一一样!
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
至此调用微信接口实现授权登录主体内容就是上面所述,至于登录态控制和将用户信息存入数据库本例未涉及,可以自行编写!
本例也是自己和朋友钻研的结果,之前很是头疼,可以说手把手,nt式教学,不懂的宝贝们评论区见~~
再见再见再见~