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.

Programmer DD
Programmer DD
Programmer DD
Integrate WeChat OAuth2 with Spring Security for Secure Web Apps

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_redirect

Spring 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/userinfo

With these steps, WeChat web authorization is fully integrated into Spring Security, enabling secure login and user data retrieval for your application.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaSpring BootOAuth2Web Securityspring-securityWeChat OAuth2
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.