Implementing WeChat QR Code Login with Spring Boot and Thymeleaf
This tutorial explains how to build a WeChat QR‑code login system using Spring‑Boot, Thymeleaf, HttpClient and JSON, covering preparation steps, project configuration, OAuth2 flow, code examples for generating QR pages, handling callbacks, and customizing the login experience.
WeChat QR‑code login is popular because of high traffic on the WeChat platform; this article walks through the complete implementation using Spring‑Boot and Thymeleaf.
Demo Effect
Preparation
Obtain a WeChat Open Platform account and create a website application to get appId and appSecret .
Start ngrok locally to expose a public URL for OAuth callback.
Configure the ngrok address as the authorized callback domain in the WeChat application settings.
Technologies Used
Spring‑Boot
Thymeleaf
HttpClient
JSON (org.json)
Learning Goals
Generate a full‑screen QR code page and a customizable QR code page.
Analyze the integration flow and implement the required functionality.
List the technologies involved.
Implementation
Create Spring‑Boot Project and Add Dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>Configuration File (application.properties)
spring.thymeleaf.prefix: classpath:/templates/
spring.thymeleaf.suffix: .html
# WeChat app secret
appsecret=
# WeChat app id
appid=
scope=snsapi_login
# Authorized callback domain
domain=http://test.xcx.cxylt.cn/
server.port=8083Authorization Flow Explanation
WeChat OAuth2.0 login lets users authenticate with their WeChat identity. The flow (authorization_code mode) is:
User clicks the QR‑code login link; after authorizing, WeChat redirects back with a temporary code parameter.
The server exchanges code for an access_token using the appId and appSecret.
Using the access_token (and openid ) the server retrieves the user's basic profile.
In short, the QR code leads to a callback URL that receives code , which is then exchanged for an accessToken to obtain the user's information.
First QR Code Page (Backend Controller)
@RequestMapping("/")
public String index(Model model) throws UnsupportedEncodingException {
String oauthUrl = "https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
String redirect_uri = URLEncoder.encode(callBack, "utf-8");
oauthUrl = oauthUrl.replace("APPID", appid)
.replace("REDIRECT_URI", redirect_uri)
.replace("SCOPE", scope);
model.addAttribute("name", "liuzp");
model.addAttribute("oauthUrl", oauthUrl);
return "index";
}Corresponding Thymeleaf view (index.html):
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>hello! <label th:text="${name}"></label></h1>
<a th:href="${oauthUrl}">点击扫码登录</a>
</body>
</html>Callback Handling
@RequestMapping("/callBack")
public String callBack(String code, String state, Model model) throws Exception {
logger.info("进入授权回调,code:{},state:{}", code, state);
// 1. Exchange code for access_token
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
url = url.replace("APPID", appid).replace("SECRET", appsecret).replace("CODE", code);
String tokenInfoStr = HttpRequestUtils.httpGet(url, null, null);
JSONObject tokenInfoObject = new JSONObject(tokenInfoStr);
// 2. Use access_token and openid to get user info
String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
userInfoUrl = userInfoUrl.replace("ACCESS_TOKEN", tokenInfoObject.getString("access_token"))
.replace("OPENID", tokenInfoObject.getString("openid"));
String userInfoStr = HttpRequestUtils.httpGet(userInfoUrl, null, null);
model.addAttribute("tokenInfoObject", tokenInfoObject);
model.addAttribute("userInfoObject", userInfoStr);
return "result";
}Result page (result.html) displays the token and user information using Thymeleaf expressions.
Custom QR Code Page
Backend route for custom page:
@RequestMapping("/1")
public String index1(Model model) throws UnsupportedEncodingException {
String redirect_uri = URLEncoder.encode(callBack, "utf-8");
model.addAttribute("name", "liuzp");
model.addAttribute("appid", appid);
model.addAttribute("scope", scope);
model.addAttribute("redirect_uri", redirect_uri);
return "index1";
}Corresponding view (index1.html) embeds the WeChat JS SDK to render a QR code inside a div with id login_container :
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>内嵌(自定义二维码)</title>
<script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
</head>
<body>
<h1>hello! <label th:text="${name}"></label></h1>
<div id="login_container"></div>
<script th:inline="javascript">
var obj = new WxLogin({
self_redirect: true,
id: "login_container",
appid: [[${appid}]],
scope: [[${scope}]],
redirect_uri: [[${redirect_uri}]],
state: "",
style: "",
href: ""
});
</script>
</body>
</html>Source Code
All source files are available at https://github.com/pengziliu/spring-boot-2.0-leaning .
If you find this tutorial helpful, feel free to like and share.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.