核心概念与基础
在开始编码之前,你必须理解微信网页开发的几个核心概念和限制。

微信 JS-SDK
这是整个微信网页开发的核心,它是一组由微信提供的、基于微信内的网页开发工具包,通过使用 JS-SDK,网页开发者可以借助微信的能力,方便地使用微信原生的一些功能,如:
- 分享:将网页分享给好友或朋友圈。
- 扫一扫:调用微信扫一扫功能。
- 地理位置:获取用户地理位置。
- 图像/音频/视频:处理媒体文件。
- 界面操作:如关闭当前网页、打开小程序等。
关键限制: JS-SDK 的使用有严格的域名白名单限制,你只能在微信后台配置好的、且经过认证的域名下才能调用这些接口。
OAuth2.0 网页授权
这是微信网页开发中最关键、也最容易让人困惑的一步,由于微信环境是封闭的,网页无法直接获取用户的 openid(用户在公众号下的唯一标识)和 access_token。
网页授权流程分为两种模式:

-
snsapi_base (静默授权)
- 特点:用户无感知,静默完成授权。
- 获取信息:只能获取用户的
openid。 - 应用场景:不需要获取用户基本信息,只需要知道是谁在访问即可,个性化推荐、记录用户行为。
-
snsapi_userinfo (手动授权)
- 特点:会弹出一个授权页面,用户点击“同意”后才能获取信息。
- 获取信息:可以获取
openid、nickname(昵称)、headimgurl(头像)、sex(性别)等用户基本信息。 - 应用场景:需要展示用户头像、昵称,或进行与用户身份强相关的操作。
授权流程图解(以 snsapi_userinfo 为例):
用户访问你的网页
|
v
你的服务器重定向到微信授权URL (带上你的appid和回调地址)
|
v
用户在微信内点击“同意授权”
|
v
微信重定向到你配置的回调地址 (带上 code 参数)
|
v
你的服务器拿着 code 去向微信服务器请求 access_token 和 openid
|
v
微信服务器返回 access_token 和 openid
|
v
(如果是snsapi_userinfo) 用 access_token 去请求用户信息
|
v
获取到用户信息,你的服务器可以创建或更新用户账户,然后重定向到原始网页或执行其他操作
微信支付 (H5 支付)
如果你的网页需要集成微信支付,流程比网页授权更复杂:

- 用户在网页点击支付。
- 你的后台服务器生成一个支付订单,调用微信的统一下单接口 (
wxpay.unifiedorder)。 - 微信返回一个
prepay_id(预支付交易会话标识)。 - 你的后台服务器将
appId,timeStamp,nonceStr,package,signType等参数签名后,返回给前端。 - 前端调用微信 JS-SDK 的
chooseWXPay方法,弹出微信支付收银台。
Java Web 开发环境搭建
你需要一个标准的 Java Web 开发环境。
- JDK: 1.8 或更高版本。
- 构建工具: Maven 或 Gradle (推荐 Maven,社区支持更广)。
- Web 容器: Tomcat, Jetty 等。
- IDE: IntelliJ IDEA 或 Eclipse。
- Spring Boot: 强烈推荐使用 Spring Boot,它能极大地简化配置,让你专注于业务逻辑,而不是 XML 配置文件。
开发步骤详解 (以 Spring Boot 为例)
我们将以一个最常见的场景为例:用户访问网页 -> 静默授权获取 openid -> 在页面上显示欢迎信息。
第 1 步:微信公众平台配置
- 登录微信公众平台:https://mp.weixin.qq.com/
- 获取 AppID 和 AppSecret:在“开发” -> “基本配置”中找到。
- 配置网页授权域名:
- 进入“设置” -> “公众号设置” -> “功能设置”。
- 在“网页授权域名”一栏,填写你的域名(
www.yourdomain.com),注意,这里不需要加http://或https://,也不需要加路径。 - 域名需要先进行备案,并且可以通过该域名访问一个验证文件。
第 2 步:后端项目搭建 (Spring Boot)
- 创建一个新的 Spring Boot 项目。
- 添加必要的依赖 (
pom.xml):
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok (简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- HttpClient (用于调用微信API) -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<!-- FastJSON (处理JSON) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
</dependencies>
第 3 步:配置文件 (application.yml)
将你的 AppID 和 AppSecret 配置到文件中。
server: port: 8080 wechat: app-id: "你的AppID" app-secret: "你的AppSecret" # 回调地址,必须和公众号配置的域名一致 redirect-uri: "http://www.yourdomain.com/wechat/callback" # snsapi_base 或 snsapi_userinfo scope: "snsapi_base"
第 4 步:编写后端 Controller
这是核心逻辑所在,处理授权流程。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@Controller
public class WeChatController {
@Value("${wechat.app-id}")
private String appId;
@Value("${wechat.app-secret}")
private String appSecret;
@Value("${wechat.redirect-uri}")
private String redirectUri;
@Value("${wechat.scope}")
private String scope;
@Autowired
private WeChatService weChatService; // 我们将业务逻辑抽离到Service层
/**
* 1. 用户访问的入口
* 重定向到微信授权URL
*/
@GetMapping("/wechat/login")
public String wechatLogin(HttpSession session) throws UnsupportedEncodingException {
// 对回调地址进行URL编码
String encodedRedirectUri = URLEncoder.encode(redirectUri, "UTF-8");
// 构造微信授权URL
String authUrl = String.format(
"https://open.weixin.qq.com/connect/oauth2/authorize?" +
"appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=%s" +
"&state=STATE#wechat_redirect",
appId, encodedRedirectUri, scope
);
// 重定向到微信
return "redirect:" + authUrl;
}
/**
* 2. 微信授权后的回调地址
* 微信会带着 code 参数访问这个地址
*/
@GetMapping("/wechat/callback")
public String callback(@RequestParam("code") String code, HttpSession session) {
// 使用 code 去换取 access_token 和 openid
JSONObject userInfo = weChatService.getOpenidAndUserInfo(code, scope);
if (userInfo != null) {
String openid = userInfo.getString("openid");
// 将 openid 存入 session,后续请求可以用来识别用户
session.setAttribute("openid", openid);
// 如果是snsapi_userinfo,这里已经拿到了用户信息
String nickname = userInfo.getString("nickname");
System.out.println("用户昵称: " + nickname);
}
// 获取 openid 后,可以重定向到你的业务主页
return "redirect:/index";
}
/**
* 3. 业务主页
* 从 session 中获取 openid,并展示给用户
*/
@GetMapping("/index")
@ResponseBody
public String index(HttpSession session) {
String openid = (String) session.getAttribute("openid");
if (openid != null) {
return "欢迎回来!您的 openid 是: " + openid;
} else {
return "未授权,请先<a href='/wechat/login'>点击登录</a>";
}
}
}
第 5 步:编写 Service 层
将调用微信 API 的逻辑封装起来。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Service;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
@Service
public class WeChatService {
public JSONObject getOpenidAndUserInfo(String code, String scope) {
// 1. 通过 code 获取 access_token 和 openid
String tokenUrl = String.format(
"https://api.weixin.qq.com/sns/oauth2/access_token?" +
"appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code",
"你的AppID", "你的AppSecret", code
);
JSONObject tokenInfo = sendGetRequest(tokenUrl);
if (tokenInfo == null || !tokenInfo.containsKey("access_token")) {
return null; // 获取失败
}
String accessToken = tokenInfo.getString("access_token");
String openid = tokenInfo.getString("openid");
// 2. 如果是 snsapi_userinfo,则进一步获取用户信息
if ("snsapi_userinfo".equals(scope)) {
String userInfoUrl = String.format(
"https://api.weixin.qq.com/sns/userinfo?" +
"access_token=%s" +
"&openid=%s",
accessToken, openid
);
return sendGetRequest(userInfoUrl);
}
// 如果是 snsapi_base,只返回包含 openid 的对象
JSONObject result = new JSONObject();
result.put("openid", openid);
return result;
}
private JSONObject sendGetRequest(String url) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet httpGet = new HttpGet(url);
HttpResponse response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
if (entity != null) {
String result = EntityUtils.toString(entity, "UTF-8");
return JSON.parseObject(result);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
第 6 步:前端页面
在你的 src/main/resources/static 目录下创建 index.html。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">微信网页开发示例</title>
</head>
<body>
<h1>欢迎来到我的网站</h1>
<p id="user-info">正在加载...</p>
<script>
// 页面加载时,从后端获取用户信息
window.onload = function() {
fetch('/index')
.then(response => response.text())
.then(data => {
document.getElementById('user-info').innerText = data;
})
.catch(error => {
console.error('Error:', error);
document.getElementById('user-info').innerText = '加载失败,请刷新页面重试。';
});
};
</script>
</body>
</html>
第 7 步:部署与测试
- 将你的 Spring Boot 应用打包成 JAR 或 WAR 文件。
- 部署到服务器上,确保可以通过
http://www.yourdomain.com:8080访问。 - 注意:微信只允许
http或https协议,且端口必须是 80 或 443,如果你在本地开发(端口8080),可以使用 内网穿透工具(如 frp, ngrok)将本地端口映射到公网域名。 - 在手机微信中访问
http://www.yourdomain.com/wechat/login,测试整个流程。
JS-SDK 集成示例(分享功能)
假设你已经通过 OAuth2.0 获取了用户的 access_token 和 openid,现在要实现网页分享功能。
后端生成签名
JS-SDK 的调用需要签名,签名规则比较复杂,后端需要生成 signature, timestamp, noncestr。
// 在你的 Controller 或 Service 中添加一个方法
@GetMapping("/jsapi/signature")
@ResponseBody
public JSONObject getJsapiSignature() {
// 1. 获取 access_token (通过你的公众号的AppID和Secret,不是网页授权的)
String accessToken = weChatService.getOfficialAccountAccessToken();
if (accessToken == null) {
return null;
}
// 2. 获取 jsapi_ticket
String ticket = weChatService.getJsapiTicket(accessToken);
if (ticket == null) {
return null;
}
// 3. 生成签名
String url = "http://www.yourdomain.com/current-page-url"; // 当前页面的完整URL
String nonceStr = UUID.randomUUID().toString().replace("-", "");
long timestamp = System.currentTimeMillis() / 1000;
String string1 = "jsapi_ticket=" + ticket +
"&noncestr=" + nonceStr +
"×tamp=" + timestamp +
"&url=" + url;
String signature = SHA1Util.sha1(string1);
// 4. 返回给前端
JSONObject result = new JSONObject();
result.put("appId", "你的AppID");
result.put("timestamp", timestamp);
result.put("nonceStr", nonceStr);
result.put("signature", signature);
return result;
}
(你需要自己实现 getOfficialAccountAccessToken 和 getJsapiTicket 方法,并缓存它们,因为它们有有效期)
前端调用 JS-SDK
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">分享示例</title>
<!-- 引入微信JS-SDK -->
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>
<body>
<h1>这是一个可以分享的页面</h1>
<script>
// 页面加载时,从后端获取签名配置
window.onload = function() {
fetch('/jsapi/signature')
.then(response => response.json())
.then(config => {
if (config) {
// 通过config接口注入权限验证配置
wx.config({
beta: true,
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: config.appId,
timestamp: config.timestamp,
nonceStr: config.nonceStr,
signature: config.signature,
jsApiList: [
'updateAppMessageShareData', // 分享给朋友
'updateTimelineShareData' // 分享到朋友圈
] // 必填,需要使用的JS接口列表
});
wx.ready(function () {
// 分享给朋友
wx.updateAppMessageShareData({
title: '分享给朋友的标题', // 分享标题
desc: '分享给朋友的描述', // 分享描述
link: 'http://www.yourdomain.com/share-page', // 分享链接,该链接域名或路径必须在当前公众号JSAPI安全域名范围内
imgUrl: 'http://www.yourdomain.com/images/share-icon.png', // 分享图标
success: function () {
// 设置成功
}
});
// 分享到朋友圈
wx.updateTimelineShareData({
title: '分享到朋友圈的标题', // 分享标题
link: 'http://www.yourdomain.com/share-page', // 分享链接,该链接域名或路径必须在当前公众号JSAPI安全域名范围内
imgUrl: 'http://www.yourdomain.com/images/share-icon.png', // 分享图标
success: function () {
// 设置成功
}
});
});
wx.error(function (res) {
alert('配置失败: ' + res.errMsg);
});
}
});
};
</script>
</body>
</html>
常见问题与最佳实践
- 域名与端口:务必使用微信配置的域名,并且端口是 80 或 443,本地开发务必使用内网穿透。
- 缓存:
access_token和jsapi_ticket都有有效期(7200秒),一定要缓存起来,避免频繁请求微信API,否则可能会被限制。 - 用户状态管理:
openid是识别用户的关键,通常在用户首次授权时,根据openid在你的数据库中创建或查找用户记录,并生成你自己的user_id,后续请求通过session或JWT来保持登录状态。 - 安全性:注意保护你的
AppSecret,不要泄露在前端代码中,所有与微信服务器的敏感交互都应该在后端完成。 - 错误处理:微信API调用可能会失败,要有完善的错误处理和日志记录机制。
- 测试:多在真机上测试,微信在 iOS 和 Android 上的表现可能略有不同。
希望这份详细的指南能帮助你顺利开启 Java Web 微信网页开发之旅!
