前言:本文主要内容是微信后端逻辑。
前言:本项目前端用vue,微信后端用ssm,后台用ssm。后端主要处理与微信的交互,转发入参给后台,接收后台给的出参,转发出参给前端。后台是真正涉及业务的。
前言:本文依据的项目,前端后端在本地启动,后台部署在linux服务器上。后端通过拼接接口路径访问后台。部署配置,详情见第一章。
1、本地启动前后端后,在微信开发者工具上,输入超链接local.test.com:8989。
若没有跳到授权链接,报redirect_uri参数错误,返回码是10003,应该是回调地址写错了,参考第一章的配置的授权回调页面域名。
微信授权机制,详情请参考官方文档https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html。
3、点击同意,获取code,进入公众号首页
前端请求头Request Headers会封装进后端入参HttpServletRequest request。客户端浏览器发出的请求被封装成为一个HttpServletRequest对象。对象包含了客户端请求信息包括请求的地址,请求的参数,提交的数据,上传的文件客户端的ip甚至客户端操作系统都包含在其内。
前端请求参数body,对应后端入参UserBean bean,由@RequestBody自动装配。
1、获取前端传来的code。请求https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code获取access_token和openid。
2、根据access_token和openid,请求https://api.weixin.qq.com/sns/userinfo?access_token=TOKEN&openid=OPENID&lang=zh_CN拉取用户信息。
3、根据用户信息,判断用户是否关注了公众号。没有关注,则返回error并提示用户关注。若关注了进行第4步。
4、从后台拿到tokenid,tokenid其实就是后台redis的key。
5、根据tokenid,在后端生成redis的key和value,使后端的value和tokenid相同。这样后端就能方便地调用后台接口。
/**
* 通过code获取用户信息
* @param request
* @return 返回用户信息
* @throws Exception
*/
@RequestMapping(value = "/getUserInfo", method = RequestMethod.POST)
@ResponseBody
public Result getUserInfo(@RequestBody UserBean bean, HttpServletRequest request)
throws Exception {}
public class UserBean {
private String tokenid;// 权限验证码,对应后台redis的key
private String channelcode;// 渠道
private String accountName;// 用户名
private String accountPwd;// 密码
private String openid;// 微信openid
private String code;// 微信网页授权中获取的code
}
javax.servlet.http.HttpServletRequest
//网页授权授权地址
private final String oauthApiURL = "https://api.weixin.qq.com/sns";
/**
* 方法:根据code,获取access_token和openid
* @param code
* @return result
* result包括access_token、expires_in、efresh_token、openid和scope
* appid、appSecret 是测试号信息,详见第一章
*/
public JSONObject getUserInfoAuth(String code) {
String apiUrl = oauthApiURL+ "/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
apiUrl = apiUrl.replace("APPID", appid).replace("SECRET", appSecret).replace("CODE", code);
JSONObject result = httpRequest(apiUrl);
if (!result.isNullObject() && result.containsKey("openid")) {
this.access_token = result.getString("access_token");
return result;
} else {
log.info("获取openid失败:code{}/errmsg:{}");
return null;
}
}
//方法:httpRequest()。发起https请求并获取结果。太长了,文章末尾贴出来。
/*
* 方法:根据access_token和openid,拉取用户信息
*/
public JSONObject getUserInfoByOpenid(String accessToken, String openid) {
String apiUrl = oauthApiURL + "/userinfo?access_token=TOKEN&openid=OPENID&lang=zh_CN";
apiUrl = apiUrl.replace("TOKEN", accessToken).replace("OPENID", openid);
JSONObject res = httpRequest(apiUrl);
if (!res.isNullObject() && res.containsKey("openid")) {
return res;
} else {
return null;
}
}
从后台拿到tokenid,tokenid其实就是后台redis的key。
然后,根据tokenid,在后端生成redis的key和value
/**
* 方法:从后台拿到tokenid,tokenid其实就是后台redis的key。然后,根据tokenid,在后端生成redis的key 和value。
* 备注:入参sessionId,设为key的前缀
*/
public Result login(UserBean bean, String sessionId) {
String url = "http://balabala/bala/bala/bala/checkLogin";//后台登录接口路径
JSONObject jsonData = new JSONObject();
jsonData.put("channelcode", bean.getChannelcode());
jsonData.put("username", bean.getAccountName());
jsonData.put("password", bean.getAccountPwd());
String result = (String) getWebClient(url, jsonData, String.class);//调后台接口
JSONObject jsonResult = Common.strToJSONObject(result);
if (null == jsonResult) {
return error("接口请求错误");
}
String statusCode = jsonResult.getString("statusCode");
if (statusCode.equals("200")) {
try {
//在后端生成redis的key和value。value设成跟tokenid一样
JedisUtil.setValue(sessionId + "_tokenid", jsonResult.getString("data"));
} catch (Exception e) {
e.printStackTrace();
}
}
return result(statusCode, jsonResult.getString("message"), jsonResult.get("data"));
}
/*
* 方法:调后台接口
* 备注:ClientConfig、Client、WebResource、ClientResponse,都是包com.sun.jersey.api.client里的
*/
public static Object getWebClient(String url, JSONObject json, Class<?> resultClass) {
ClientConfig cc = new DefaultClientConfig();
Client client = Client.create(cc);
WebResource webResource = client.resource(url);
//javax.ws.rs.core.MediaType
ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON_TYPE)
.type(MediaType.APPLICATION_JSON_TYPE).post(ClientResponse.class, json.toString());
client.destroy();
return response.getEntity(resultClass);
}
/**
* 方法:redis,设置key和value
* 备注:JedisPool、Jedis都是包redis.clients.jedis里的
*/
public static void setValue(String key, String value, Integer outTime)
throws Exception {
JedisPool publicJedisPool = null;
Jedis publicJedis = null;
try {
publicJedisPool = DataBase.getJedisPublicPool();
publicJedis = publicJedisPool.getResource();
publicJedis.setex(key, 60 * outTime, value);
} catch (Exception e) {
if (publicJedisPool != null) {
publicJedisPool.returnBrokenResource(publicJedis);
}
throw new Exception("设置redis失败: key:" + key + ",value:" + value, e);
} finally {
if (publicJedis != null) {
publicJedisPool.returnResource(publicJedis);
}
}
}
//方法:根据code,获取access_token和openid。里用的。
/*
* 用到javax.net.ssl.HttpsURLConnection、 javax.net.ssl.SSLSocketFactory、*java.io.OutputStream、java.io.InputStream
*/
/**
* 发起https请求并获取结果
*
* @param requestUrl
* 请求地址
* @param requestMethod
* 请求方式(GET、POST)
* @param outputStr
* 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = new JSONObject();
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
jsonObject = JSONObject.fromObject(buffer.toString());
} catch (ConnectException ce) {
System.out.println("微信服务连接超时");
} catch (Exception e) {
System.out.println("https请求错误:" + e);
}
return jsonObject;
}