一、准备工作
1、 https://open.weixin.qq.com 注册
2、熟悉OAuth 2.0(授权机制)本次采用授权码模式
3、熟悉获取access_token时序图:
二、后端开发
1、application.yml配置
wechat:
APPID: 你的APPID
APPSECRET: 你的APPSECRET
REDIRECTURL: 你的REDIRECTURL
enabled: false #密码校验是否开启
2、微信二维码生成
/**
* 生成微信扫描二维码
* @return
*/
@GetMapping("/wxUrl")
@Functional(visible = Visible.FREEDOM)
@ApiOperation(value = "生成微信扫描二维码")
public ResultInfo<String> wxUrl() {
String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=%s" +
"#wechat_redirect";
//对redirect_url进行URLEncoder编码
String redirect_url = REDIRECTURL;
try {
redirect_url = URLEncoder.encode(redirect_url, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String state = UUID.randomUUID().toString();
//设置%s里面的值
String url = String.format(baseUrl,APPID,redirect_url, state);
//重定向到请求微信地址里面
return new ResultInfo<>("redirect:"+url);
}
/**
* 生成微信二维码
* @return
*/
@GetMapping("/wechatUrl")
@Functional(visible = Visible.FREEDOM)
@ApiOperation(value = "生成微信二维码")
public ResultInfo getWxQRCodeParam() {
HashMap<String, String> map = new HashMap<>();
try {
// 第一步:发送请求获取access_token
String getAccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +
"&appid=" + APPID +
"&secret=" + APPSECRET;
String accessTokenRes = HttpClientUtils.doGet(getAccessTokenUrl);
String accessToken = (String) JSON.parseObject(accessTokenRes).get("access_token"); // 获取到access_token
//生成uuid
String state = UUID.randomUUID().toString();
// 第二步:通过access_token和一些参数发送post请求获取二维码Ticket
String getTicketUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;
// 封装参数
Map<String, Object> ticketInfo = new HashMap<>();
ticketInfo.put("expire_seconds", 604800); // 二维码超时时间
ticketInfo.put("action_name", "QR_STR_SCENE");
// 放入uuid
ticketInfo.put("action_info", new HashMap<String, HashMap>() {{
put("scene", new HashMap<String, String>() {{
put("scene_str", state);
}}
);
}}
);
String ticketJsonInfo = JSON.toJSON(ticketInfo).toString();
String ticketRes = HttpClientUtils.doPostJson(getTicketUrl, ticketJsonInfo);
String ticket = (String) JSON.parseObject(ticketRes).get("ticket");
// 第三步:通过ticket获取二维码url
String encodeTicket = URLEncoder.encode(ticket, "utf-8"); // 编码ticket
String getQRUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + encodeTicket;
String QRUrl = getQRUrl; // 二维码url
//存入map
map.put("QRUrl",QRUrl);
map.put("uuid",state);
} catch (Exception e) {
e.printStackTrace();
}
return new ResultInfo<>(map);
}
2.2、HttpClientUtils
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.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.CloseableHttpResponse;
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.client.utils.URIBuilder;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
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.HttpClientBuilder;
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.SSLContexts;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
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 connTimeout=10000;
public static final int readTimeout=10000;
public static final String charset="UTF-8";
private static HttpClient client = null;
private static final CloseableHttpClient httpClient;
// 采用静态代码块,初始化超时时间配置,再根据配置生成默认httpClient对象
static {
RequestConfig config = RequestConfig.custom().setConnectTimeout(30000).setSocketTimeout(15000).build();
httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();
}
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;
}
/**
* 发送 HTTP GET请求,不带请求参数和请求头
* @param url 请求地址
* @return
* @throws Exception
*/
public static String doGet(String url) throws Exception {
HttpGet httpGet = new HttpGet(url);
return doHttp(httpGet);
}
/**
* 发送 HTTP GET,请求带参数,不带请求头
* @param url 请求地址
* @param params 请求参数
* @return
* @throws Exception
*/
public static String doGet(String url, Map<String, Object> params) throws Exception {
// 转换请求参数
List<NameValuePair> pairs = covertParamsToList(params);
// 装载请求地址和参数
URIBuilder ub = new URIBuilder();
ub.setPath(url);
ub.setParameters(pairs);
HttpGet httpGet = new HttpGet(ub.build());
return doHttp(httpGet);
}
/**
* 发送 HTTP GET请求,带请求参数和请求头
* @param url 请求地址
* @param headers 请求头
* @param params 请求参数
* @return
* @throws Exception
*/
public static String doGet(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
// 转换请求参数
List<NameValuePair> pairs = covertParamsToList(params);
// 装载请求地址和参数
URIBuilder ub = new URIBuilder();
ub.setPath(url);
ub.setParameters(pairs);
HttpGet httpGet = new HttpGet(ub.build());
// 设置请求头
for (Map.Entry<String, Object> param : headers.entrySet()) {
httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
}
return doHttp(httpGet);
}
/**
* 发送 HTTP POST请求,不带请求参数和请求头
*
* @param url 请求地址
* @return
* @throws Exception
*/
public static String doPost(String url) throws Exception {
HttpPost httpPost = new HttpPost(url);
return doHttp(httpPost);
}
/**
* 发送 HTTP POST请求,带请求参数,不带请求头
*
* @param url 请求地址
* @param params 请求参数
* @return
* @throws Exception
*/
public static String doPost(String url, Map<String, Object> params) throws Exception {
// 转换请求参数
List<NameValuePair> pairs = covertParamsToList(params);
HttpPost httpPost = new HttpPost(url);
// 设置请求参数
httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name()));
return doHttp(httpPost);
}
/**
* 发送 HTTP POST请求,带请求参数和请求头
*
* @param url 地址
* @param headers 请求头
* @param params 参数
* @return
* @throws Exception
*/
public static String doPost(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
// 转换请求参数
List<NameValuePair> pairs = covertParamsToList(params);
HttpPost httpPost = new HttpPost(url);
// 设置请求参数
httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name()));
// 设置请求头
for (Map.Entry<String, Object> param : headers.entrySet()) {
httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
}
return doHttp(httpPost);
}
/**
* 发送 HTTP POST请求,请求参数是JSON格式,数据编码是UTF-8
*
* @param url 请求地址
* @param param 请求参数
* @return
* @throws Exception
*/
public static String doPostJson(String url, String param) throws Exception {
HttpPost httpPost = new HttpPost(url);
// 设置请求头
httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
// 设置请求参数
httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));
return doHttp(httpPost);
}
/**
* 发送 HTTP POST请求,请求参数是XML格式,数据编码是UTF-8
*
* @param url 请求地址
* @param param 请求参数
* @return
* @throws Exception
*/
public static String doPostXml(String url, String param) throws Exception {
HttpPost httpPost = new HttpPost(url);
// 设置请求头
httpPost.addHeader("Content-Type", "application/xml; charset=UTF-8");
// 设置请求参数
httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));
return doHttp(httpPost);
}
/**
* 发送 HTTPS POST请求,使用指定的证书文件及密码,不带请求头信息<
*
* @param url 请求地址
* @param param 请求参数
* @param path 证书全路径
* @param password 证书密码
* @return
* @throws Exception
* @throws Exception
*/
public static String doHttpsPost(String url, String param, String path, String password) throws Exception {
HttpPost httpPost = new HttpPost(url);
// 设置请求参数
httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));
return doHttps(httpPost, path, password);
}
/**
* 发送 HTTPS POST请求,使用指定的证书文件及密码,请求头为“application/xml;charset=UTF-8”
*
* @param url 请求地址
* @param param 请求参数
* @param path 证书全路径
* @param password 证书密码
* @return
* @throws Exception
* @throws Exception
*/
public static String doHttpsPostXml(String url, String param, String path, String password) throws Exception {
HttpPost httpPost = new HttpPost(url);
// 设置请求头
httpPost.addHeader("Content-Type", "application/xml; charset=UTF-8");
// 设置请求参数
httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));
return doHttps(httpPost, path, password);
}
/**
* 发送 HTTPS 请求,使用指定的证书文件及密码
*
* @param request
* @param path 证书全路径
* @param password 证书密码
* @return
* @throws Exception
* @throws Exception
*/
private static String doHttps(HttpRequestBase request, String path, String password) throws Exception {
// 获取HTTPS SSL证书
SSLConnectionSocketFactory csf = getHttpsFactory(path, password);
// 通过连接池获取连接对象
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
return doRequest(httpClient, request);
}
/**
* 获取HTTPS SSL连接工厂,使用指定的证书文件及密码
*
* @param path 证书全路径
* @param password 证书密码
* @return
* @throws Exception
* @throws Exception
*/
private static SSLConnectionSocketFactory getHttpsFactory(String path, String password) throws Exception {
// 初始化证书,指定证书类型为“PKCS12”
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// 读取指定路径的证书
FileInputStream input = new FileInputStream(new File(path));
try {
// 装载读取到的证书,并指定证书密码
keyStore.load(input, password.toCharArray());
} finally {
input.close();
}
// 获取HTTPS SSL证书连接上下文
SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray()).build();
// 获取HTTPS连接工厂,指定TSL版本
SSLConnectionSocketFactory sslCsf = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
return sslCsf;
}
/**
* 发送 HTTP 请求
*
* @param request
* @return
* @throws Exception
*/
private static String doHttp(HttpRequestBase request) throws Exception {
// 通过连接池获取连接对象
return doRequest(httpClient, request);
}
/**
* 处理Http/Https请求,并返回请求结果,默认请求编码方式:UTF-8
* @param httpClient
* @param request
* @return
*/
private static String doRequest(CloseableHttpClient httpClient, HttpRequestBase request) throws Exception {
String result = null;
try (CloseableHttpResponse response = httpClient.execute(request)) {
// 获取请求结果
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
request.abort();
throw new RuntimeException("HttpClient error status code: " + statusCode);
}
// 解析请求结果
HttpEntity entity = response.getEntity();
// 转换结果
result = EntityUtils.toString(entity, StandardCharsets.UTF_8.name());
// 关闭IO流
EntityUtils.consume(entity);
}
return result;
}
/**
* 转换请求参数,将Map键值对拼接成QueryString字符串
*
* @param params
* @return
*/
public static String covertMapToQueryStr(Map<String, Object> params) {
List<NameValuePair> pairs = covertParamsToList(params);
return URLEncodedUtils.format(pairs, StandardCharsets.UTF_8.name());
}
/**
* 转换请求参数
*
* @param params
* @return
*/
public static List<NameValuePair> covertParamsToList(Map<String, Object> params) {
List<NameValuePair> pairs = new ArrayList<>();
for (Map.Entry<String, Object> param : params.entrySet()) {
pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
}
return pairs;
}
/**
* 创建 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);
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();
}
}
}
3、接入微信接口callback:
/**
* 接入微信接口
* @return
*/
@GetMapping("/wechat/callback")
@Functional(visible = Visible.FREEDOM)
@ApiOperation(value = "接入微信接口")
public String checkSign (HttpServletRequest request){
// 获取微信请求参数
String signature = request.getParameter ("signature");
String timestamp = request.getParameter ("timestamp");
String nonce = request.getParameter ("nonce");
String echostr = request.getParameter ("echostr");
if (CheckWXTokenUtils.checkSignature(signature, timestamp, nonce)) {
return echostr;
}
return " ";
}
3.2、CheckWXTokenUtils
import lombok.extern.log4j.Log4j2;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
* 和微信建立链接参数校验
*/
@Log4j2
public class CheckWXTokenUtils {
private static final String TOKEN = "你定义的token"; // 自定义的token
/**
* 校验微信服务器Token签名
*
* @param signature 微信加密签名
* @param timestamp 时间戳
* @param nonce 随机数
* @return boolean
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
//将token、timestamp、nonce三个参数进行字典序排序
String[] arr = {TOKEN, timestamp, nonce};
Arrays.sort(arr);
//将三个参数字符串拼接成一个字符串进行sha1加密
StringBuilder stringBuilder = new StringBuilder();
for (String param : arr) {
stringBuilder.append(param);
}
String hexString = SHA1(stringBuilder.toString());
//获得加密后的字符串可与signature对比,标识该请求来源于微信
return signature.equals(hexString);
}
private static String SHA1(String str) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(str.getBytes());
return toHexString(digest);
} catch (NoSuchAlgorithmException e) {
log.info("校验令牌Token出现错误:{}", e.getMessage());
}
return "";
}
/**
* 字节数组转化为十六进制
*
* @param digest 字节数组
* @return String
*/
private static String toHexString(byte[] digest) {
StringBuilder hexString = new StringBuilder();
for (byte b : digest) {
String shaHex = Integer.toHexString(b & 0xff);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
}
}
4、微信扫码登录
4.1、controller
/**
* 微信扫码登录
* @return
**/
@GetMapping("/wechat/wechatLogin/{code}")
@Functional(visible = Visible.FREEDOM)
@ApiOperation(value = "微信扫码登录")
public ResultInfo wechatLogin(@PathVariable("code") String code,String state){
ResultInfo wxlogin = userService.wechatLogin(code,state);
return new ResultInfo<>(wxlogin);
}
4.2、service
@Override
public ResultInfo wechatLogin(String code, String state) {
try {
//获取到code值,拿着code去请求微信固定的地址,得到两个值 access_token和openid
String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";
//拼接好的地址,得到两个值 access_token和openid
String accessTokenUrl = String.format(baseAccessTokenUrl,APPID,APPSECRET,code);
//使用httpclient发送请求,得到返回结果
String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
//从accessTokenInfo字符串里面获取access_token和openid两个值
//把accessTokenInfo字符串转换成map集合,根据Map里面的key获取对应值
//使用json转换工具 Gson
Gson gson=new Gson();
HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
String access_token = (String) mapAccessToken.get("access_token");
String openid = (String) mapAccessToken.get("openid");
//拿着得到的access_token和openid,再去请求微信提供的固定地址,获取扫码人的信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
String userInfoUrl= String.format(baseUserInfoUrl,access_token,openid);
//访问微信的资源服务器,获取用户信息
String userInfo = HttpClientUtils.get(userInfoUrl);
//获取返回userInfo中的用户信息(扫码人信息)
HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
String nickname = (String) userInfoMap.get("nickname"); //昵称
String headimgurl = (String) userInfoMap.get("headimgurl"); //头像
//判断数据库里面是否存在相同的微信信息,根据openid判断
MemberInfo openId = memberInfoMapper.selectByOpenid(openid);
//openId是null,表示表里面没有相同的微信数据
//第一种可能 首次扫码
if(openId == null){
//把扫码人信息添加到数据库里面
MemberInfo memberInfo = new MemberInfo();
memberInfo.setOpenId(openid);
memberInfo.setMemberName(nickname);
MemberDetail memberDetail = new MemberDetail();
memberDetail.setUserName(nickname);
memberDetail.setPortrait(headimgurl);
//添加
memberInfoMapper.insert(memberInfo);
memberDetailMapper.insert(memberDetail);
return new ResultInfo<>("500","手机号为空",openid);
}
//第二种可能 二次扫码 首次未手机号注册
if(openId.getMemberId() == null){
return new ResultInfo<>("500","手机号为空",openId.getOpenId());
}
//第三种可能 二次及以上扫码 微信用户绑定了系统用户
MemberInfo memberInfo = memberInfoMapper.selectById(openId.getMemberId());
LoginDto loginDto = new LoginDto();
loginDto.setUserName(memberInfo.getMemberName());
//根据用户名去查询数据库
MemberInfo memberInfoName = memberInfoMapper.selectByName(loginDto.getUserName());
if(memberInfoName == null){
// 没查到 抛异常
throw ErrCodeMsgEnum.ACCOUNT_PWD_NOT_COORECT.err();
}
if(enabled){
//true 开启密码校验 false 关闭密码校验
String password = loginDto.getPassword();
String salt = memberInfo.getSalt();
String saltPassword = SecureUtil.md5(salt + password);
if(!memberInfo.getMemberPwd().equals(saltPassword)){
throw ErrCodeMsgEnum.ACCOUNT_PWD_NOT_COORECT.err();
}
}
// 生成token
AuthParam authParam = new AuthParam();
String token = jwtHelper.general(authParam);
// 保存redis
redisTemplate.opsForValue().set(token , memberInfo.toString() ,30, TimeUnit.MINUTES);
// 返回 token 和 用户信息
Map<String,Object> map = new HashMap<>(2);
map.put("token",token);
//清空密码返回 保证安全
memberInfo.setMemberPwd("");
memberInfo.setSalt("");
map.put("wxUser",memberInfo);
return new ResultInfo<>(map);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResultInfo<>("500","内部错误");
}
}