整体流程
微信官方文档:网页授权 | 微信开放文档
前期准备
在这里,我们不需要申请真的公众号,微信团队为广大的开发者提供了一个测试账号。该帐号无需公众账号,快速申请接口测试,直接体验和测试公众平台所有高级接口。地址:微信公众平台 ,扫描测试号二维码, 并关注公众号。我们将得到appID和appsecret(后续开发中将会用到)。
因为我们要与微信服务器进行交互,而我们开发的程序访问网址为127.0.0.1:8080,因此我们需要先将自己的访问地址暴露在公网上,这里我使用的是NATAPP。(注册需要实名验证)
官方网址:NATAPP-内网穿透 基于ngrok的国内高速内网映射工具。
内网穿透步骤
第一步:登录成功后在左侧 我的隧道 中选择 购买隧道,下图以免费隧道为例。
第二步:购买成功后在左侧 我的隧道 中选择 我的隧道,如下图所示。
第三步:显示成功后,下载NATAPP客户端。
第四步:双击 natapp.exe,在命令行输入 natapp -authtoken=xxx (其中,xxx为网页中我的隧道的authtoken),成功界面如下图所示。
红框内马赛克的地方就是你的公网上的地址。 (因为是免费隧道,该地址不固定,隔一段时间将会改变)。
第五步:将该地址去掉 http:// 填入 微信公众平台->网页服务->网页账号中。
微信公众平台测试号接口配置
URL中填写 微信服务器 访问开发程序的 接口地址,马赛克处为前面配置的公网地址;Token需要与代码中一致,这边我设置为 wxtoken 。
代码编写
项目结构
WeixinOauthController
@Controller
@RequestMapping("weixin")
public class WeixinOauthCotroller {
@RequestMapping("oauth")
public void oauth(HttpServletResponse response) throws IOException {
String path = MenuManager.REAL_URL + "weixin/invoke";
try {
path = URLEncoder.encode(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
"appid=" + MenuManager.APP_ID +
"&redirect_uri=" + path +
"&response_type=code" +
"&scope=snsapi_base" +
"&state=STATE" +
"#wechat_redirect";
response.sendRedirect(url);
}
@ResponseBody
@RequestMapping("invoke")
public JSONObject oauthInvoke(HttpServletRequest request) throws JSONException {
//获得code
String code = request.getParameter("code");
String state = request.getParameter("state");
//通过code换取网页授权access_token
//认证服务器
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
"appid=" + MenuManager.APP_ID +
"&secret=" + MenuManager.APP_SECRET +
"&code=" + code +
"&grant_type=authorization_code";
//认证服务器 带着code发送请求 得到 access_token
JSONObject jsonObjectPost = new JSONObject(Send.sendPost(url));
System.out.println(jsonObjectPost);
String accessToken = jsonObjectPost.getString("access_token");
String openid = jsonObjectPost.getString("openid");
//带着openid和access_token 获取资源信息
String urlInfo = "https://api.weixin.qq.com/sns/userinfo?" +
"access_token=" + accessToken +
"&openid=" + openid +
"&lang=zh_CN";
JSONObject jsonObjectGet = new JSONObject(Send.sendGet(urlInfo));
System.out.println(jsonObjectGet);
System.out.println(jsonObjectGet.getString("nickname"));
return jsonObjectGet;
}
}
WxSignatureCheckController
@RestController
public class WxSignatureCheckController {
@Autowired
private WxSignatureCheckService wxSignatureCheckService;
@RequestMapping("/wxCheck")
public String wxSignatureCheck(
@RequestParam(value = "signature") String signature,
@RequestParam(value = "timestamp") String timestamp,
@RequestParam(value = "nonce") String nonce,
@RequestParam(value = "echostr") String echostr
){
return wxSignatureCheckService.wxSignatureCheck(signature, timestamp, nonce, echostr);
}
}
WxSignatureCheckService(token需要与微信公众平台上一致)
@Service
public class WxSignatureCheckService {
//token值必须和微信公众号中配置的完全一致!!!
private final String token = "wxtoken";
public String wxSignatureCheck(String signature, String timestamp, String nonce, String echostr) {
ArrayList<String> array = new ArrayList<String>();
array.add(signature);
array.add(timestamp);
array.add(nonce);
//排序
String sortString = sort(token, timestamp, nonce);
//加密
String mytoken = Decript.SHA1(sortString);
//校验签名
if (mytoken != null && mytoken != "" && mytoken.equals(signature)) {
System.out.println("签名校验通过。");
return echostr; //如果检验成功输出echostr,微信服务器接收到此输出,才会确认检验完成。
} else {
System.out.println("签名校验失败。");
return null;
}
}
/**
* 排序方法
* @param token
* @param timestamp
* @param nonce
* @return
*/
public static String sort(String token, String timestamp, String nonce) {
String[] strArray = { token, timestamp, nonce };
Arrays.sort(strArray);
StringBuilder sbuilder = new StringBuilder();
for (String str : strArray) {
sbuilder.append(str);
}
return sbuilder.toString();
}
}
Decript
/**
* 加密方法
*/
public class Decript {
public static String SHA1(String decript) {
try {
MessageDigest digest = MessageDigest
.getInstance("SHA-1");
digest.update(decript.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
}
MenuManager(需要修改为自己的信息)
public class MenuManager {
public static final String APP_ID = 微信公众平台上的appID;
public static final String APP_SECRET = 微信公众平台上的appsecret;
public static final String REAL_URL = 公网地址(包含http://);
}
Send
public class Send {
/**
* 向指定 URL 发送POST方法的请求
* @param url 发送请求的 URL
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!" + e);
e.printStackTrace();
}
//使用finally块来关闭输出流、输入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
/**
* 向指定URL发送GET方法的请求
* @param url 发送请求的URL
* @return URL 所代表远程资源的响应结果
*/
public static String sendGet(String url) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url;
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立实际的连接
connection.connect();
// 定义 BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
}
结果
未制作前端界面,仅为控制台能输出用户信息。