Integrate WeChat OAuth2 with Spring Security for Secure Web Apps
This guide walks through setting up WeChat web authorization, customizing Spring Security's OAuth2 flow, handling token exchange, and retrieving user info, providing a complete backend solution for secure WeChat-enabled applications.
WeChat offers promotional tools like payments, coupons, and H5 red packets, which require the user's specific WeChat identifier (openid) and possibly user information. By leveraging WeChat's web authorization mechanism, a third‑party application can obtain these details after the user authorizes the public account.
Environment Preparation
Before starting, ensure you have a WeChat Service Account (only service accounts provide the needed capabilities) and set up a sandbox test account for easier development.
WeChat Service Accounts are only available to enterprises and government agencies.
Intranet Penetration
Because WeChat servers need to call a callback URL, use a tunneling tool to expose a public virtual domain and configure it in the test account's callback settings.
When configuring the callback domain, omit the protocol (e.g., use felord.cn/wechat/callback instead of https://felord.cn/wechat/callback).
OAuth2.0 Client Integration
Based on Spring Security 5.x
WeChat web authorization uses OAuth2.0. After the user authorizes the public account, an access_token is obtained, which can be used to call protected APIs such as fetching user info.
Add the following dependencies to your project:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>Since we need user information, use OAuth2.0 Login ; if you don't need user info, OAuth2.0 Client suffices.
WeChat Web Authorization Flow
WeChat uses the OAuth2.0 authorization‑code grant. The authorization request URL differs from the standard OAuth2.0 spec: client_id is replaced by appid and the URL must end with #wechat_redirect.
Obtain Authorization Code
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirectSpring Security provides a template URL {baseUrl}/oauth2/authorization/{registrationId}. To adapt it for WeChat, customize OAuth2AuthorizationRequestRedirectFilter and its OAuth2AuthorizationRequestResolver.
Custom URL Builder
/**
* Compatibility for WeChat OAuth2 endpoint.
*/
public class WechatOAuth2AuthRequestBuilderCustomizer {
private static final String WECHAT_ID = "wechat";
public static void customize(OAuth2AuthorizationRequest.Builder builder) {
String regId = (String) builder.build().getAttributes().get(OAuth2ParameterNames.REGISTRATION_ID);
if (WECHAT_ID.equals(regId)) {
builder.authorizationRequestUri(WechatOAuth2RequestUriBuilderCustomizer::customize);
}
}
private static class WechatOAuth2RequestUriBuilderCustomizer {
public static URI customize(UriBuilder builder) {
String reqUri = builder.build().toString()
.replaceAll("client_id=", "appid=")
.concat("#wechat_redirect");
return URI.create(reqUri);
}
}
}Configure Resolver
private OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository) {
DefaultOAuth2AuthorizationRequestResolver resolver = new DefaultOAuth2AuthorizationRequestResolver(
clientRegistrationRepository, OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);
resolver.setAuthorizationRequestCustomizer(WechatOAuth2AuthRequestBuilderCustomizer::customize);
return resolver;
}Configure in Spring Security
httpSecurity.oauth2Login()
.authorizationEndpoint()
.authorizationRequestResolver(oAuth2AuthorizationRequestResolver);Exchange Code for Access Token
WeChat's token endpoint URL is https://api.weixin.qq.com/sns/oauth2/access_token. The request must include appid, secret, code, and optionally redirect_uri. Customize the request entity converter to build these parameters.
private MultiValueMap<String, String> buildWechatQueryParameters(OAuth2AuthorizationCodeGrantRequest request) {
ClientRegistration client = request.getClientRegistration();
OAuth2AuthorizationExchange exchange = request.getAuthorizationExchange();
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add(OAuth2ParameterNames.GRANT_TYPE, request.getGrantType().getValue());
params.add(OAuth2ParameterNames.CODE, exchange.getAuthorizationResponse().getCode());
String redirectUri = exchange.getAuthorizationRequest().getRedirectUri();
if (redirectUri != null) {
params.add(OAuth2ParameterNames.REDIRECT_URI, redirectUri);
}
params.add("appid", client.getClientId());
params.add("secret", client.getClientSecret());
return params;
} @Override
public RequestEntity<?> convert(OAuth2AuthorizationCodeGrantRequest request) {
ClientRegistration client = request.getClientRegistration();
HttpHeaders headers = getTokenRequestHeaders(client);
String tokenUri = client.getProviderDetails().getTokenUri();
if (WECHAT_ID.equals(client.getRegistrationId())) {
MultiValueMap<String, String> query = buildWechatQueryParameters(request);
URI uri = UriComponentsBuilder.fromUriString(tokenUri).queryParams(query).build().toUri();
return RequestEntity.get(uri).headers(headers).build();
}
// fallback for other providers
MultiValueMap<String, String> form = buildFormParameters(request);
URI uri = UriComponentsBuilder.fromUriString(tokenUri).build().toUri();
return new RequestEntity<>(form, headers, HttpMethod.POST, uri);
}Because WeChat returns text/plain instead of application/json, extend the token response converter to handle this media type and set a default token type of Bearer.
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient();
client.setRequestEntityConverter(new WechatOAuth2AuthorizationCodeGrantRequestEntityConverter());
OAuth2AccessTokenResponseHttpMessageConverter converter = new OAuth2AccessTokenResponseHttpMessageConverter();
converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN,
new MediaType("application", "*+json")));
converter.setTokenResponseConverter(new WechatMapOAuth2AccessTokenResponseConverter());
RestTemplate restTemplate = new RestTemplate(Arrays.asList(new FormHttpMessageConverter(), converter));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
client.setRestOperations(restTemplate);
return client;
}Fetch User Information
Define a custom OAuth2UserService that builds a request to https://api.weixin.qq.com/sns/userinfo with the access token and openid obtained from the token response.
LinkedMultiValueMap<String, String> query = new LinkedMultiValueMap<>();
query.add(OAuth2ParameterNames.ACCESS_TOKEN, userRequest.getAccessToken().getTokenValue());
query.add("openid", String.valueOf(userRequest.getAdditionalParameters().get("openid")));
query.add("lang", "zh_CN");
URI userInfoUri = UriComponentsBuilder.fromUriString(userInfoEndpoint).queryParams(query).build().toUri();
return restOperations.exchange(userInfoUri, HttpMethod.GET, null, OAUTH2_USER_OBJECT);Implement a WechatOAuth2User class that stores fields such as openid, nickname, sex, etc., and returns openid as the principal name.
@Data
public class WechatOAuth2User implements OAuth2User {
private String openid;
private String nickname;
private Integer sex;
private String province;
private String city;
private String country;
private String headimgurl;
private List<String> privilege;
private String unionid;
@Override
public Map<String, Object> getAttributes() { return Collections.emptyMap(); }
@Override
public Collection<? extends GrantedAuthority> getAuthorities() { return null; }
@Override
public String getName() { return openid; }
}Configure User Service
httpSecurity.oauth2Login()
.userInfoEndpoint()
.userService(oAuth2UserService);Success Redirect
httpSecurity.oauth2Login().defaultSuccessUrl("/weixin/h5/redirect");In the redirect controller you can obtain the authorized client and the WechatOAuth2User via @RegisteredOAuth2AuthorizedClient("wechat") and @AuthenticationPrincipal, then perform post‑login business logic.
Configuration Example (application.yaml)
spring:
security:
oauth2:
client:
registration:
wechat:
client-id: wxdf9033184b2xxx38e7f
client-secret: bf1306baaa0dxxxxxxb15eb02d68df5
redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
authorization-grant-type: authorization_code
scope: snsapi_userinfo
provider:
wechat:
authorization-uri: https://open.weixin.qq.com/connect/oauth2/authorize
token-uri: https://api.weixin.qq.com/sns/oauth2/access_token
user-info-uri: https://api.weixin.qq.com/sns/userinfoWith these steps, WeChat web authorization is fully integrated into Spring Security, enabling secure login and user data retrieval for your application.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
