How to Implement WeChat QR Code Login with Spring Boot and OAuth2

This guide explains the complete WeChat OAuth2.0 QR code login flow, from requesting the authorization code and exchanging it for an access token to configuring Spring Boot beans, handling callbacks, storing tokens securely, and enforcing login checks with Spring AOP.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
How to Implement WeChat QR Code Login with Spring Boot and OAuth2

WeChat OAuth2.0 Authorization Login Overview

WeChat OAuth2.0 allows users to log in to third‑party applications using their WeChat identity. After user consent, the third‑party can obtain an access_token, which can be used to call WeChat Open Platform APIs to retrieve basic user information.

Authorization Flow

Third‑party initiates login request; after user authorizes, WeChat redirects with a temporary code.

Exchange the code, AppID and AppSecret for an access_token via API.

Use the access_token to call APIs and obtain user data.

Step 1 – Request CODE

Ensure the scope snsapi_login is set, then open the following URL (replace APPID, REDIRECT_URI, etc.):

https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

WeChat redirects to redirect_uri?code=CODE&state=STATE on success, or redirect_uri?state=STATE if the user denies.

Step 2 – Exchange CODE for access_token

Call:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

Successful response example:

{
  "access_token":"ACCESS_TOKEN",
  "expires_in":7200,
  "refresh_token":"REFRESH_TOKEN",
  "openid":"OPENID",
  "scope":"SCOPE",
  "unionid":"o6_bmasdasdsad6_2sgVt7hMZOPfL"
}

Error example: {"errcode":40029,"errmsg":"invalid code"} Store AppSecret and tokens on the server side to avoid leakage.

Step 3 – Call APIs with access_token

Before calling, ensure the token is valid and the user has granted the required scope.

Spring Boot Configuration

Configure Open Platform credentials in application.properties:

# Open Platform
wechat.open-app-id=wx6ad144e54af67d87
wechat.open-app-secret=91a2ff6d38a2bbccfb7e9f9079108e2e

Define a configuration class to bind these properties:

@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WechatAccountConfig {
    private String mpAppId;
    private String mpAppSecret;
    private String mchId;
    private String mchKey;
    private String keyPath;
    private String notifyUrl;
    private String openAppId;
    private String openAppSecret;
}

Create beans for WxMpService:

@Configuration
public class WechatOpenConfig {
    @Autowired
    private WechatAccountConfig accountConfig;

    @Bean
    public WxMpService wxOpenService() {
        WxMpService wxOpenService = new WxMpServiceImpl();
        wxOpenService.setWxMpConfigStorage(wxOpenConfigStorage());
        return wxOpenService;
    }

    @Bean
    public WxMpConfigStorage wxOpenConfigStorage() {
        WxMpInMemoryConfigStorage storage = new WxMpInMemoryConfigStorage();
        storage.setAppId(accountConfig.getOpenAppId());
        storage.setSecret(accountConfig.getOpenAppSecret());
        return storage;
    }
}

Controller to start QR authorization and handle the callback:

@Controller
@RequestMapping("/wechat")
@Slf4j
public class WeChatController {
    @Autowired
    private WxMpService wxOpenService;

    @GetMapping("/qrAuthorize")
    public String qrAuthorize() {
        String returnUrl = "http://heng.nat300.top/sell/wechat/qrUserInfo";
        String url = wxOpenService.buildQrConnectUrl(returnUrl,
                WxConsts.QRCONNECT_SCOPE_SNSAPI_LOGIN,
                URLEncoder.encode(returnUrl));
        return "redirect:" + url;
    }

    @GetMapping("/qrUserInfo")
    public String qrUserInfo(@RequestParam("code") String code) {
        WxMpOAuth2AccessToken token = wxOpenService.oauth2getAccessToken(code);
        String openId = token.getOpenId();
        log.info("openid={}", openId);
        return "redirect:http://www.baidu.com?openid=" + openId;
    }
}

Login controller uses the obtained openid to create a token stored in Redis and set a cookie:

@Controller
@RequestMapping("/seller")
public class SellerUserController {
    @Autowired
    private SellerService sellerService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @GetMapping("/login")
    public ModelAndView login(@RequestParam("openid") String openid,
                              HttpServletResponse response, Map<String,Object> map) {
        SellerInfo sellerInfo = sellerService.findSellerInfoByOpenid(openid);
        if (sellerInfo == null) {
            map.put("msg", ResultEnum.LOGIN_FAIL.getMessage());
            map.put("url", "/sell/seller/order/list");
            return new ModelAndView("common/error");
        }
        String token = UUID.randomUUID().toString();
        Integer expire = RedisConstant.EXPIRE;
        redisTemplate.opsForValue().set(String.format(RedisConstant.TOKEN_PREFIX, token),
                openid, expire, TimeUnit.SECONDS);
        CookieUtil.set(response, CookieConstant.TOKEN, token, expire);
        return new ModelAndView("redirect:http://heng.nat300.top/sell/seller/order/list");
    }

    @GetMapping("/logout")
    public ModelAndView logout(HttpServletRequest request,
                               HttpServletResponse response, Map<String,Object> map) {
        Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN);
        if (cookie != null) {
            redisTemplate.delete(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue()));
            CookieUtil.set(response, CookieConstant.TOKEN, null, 0);
        }
        map.put("msg", ResultEnum.LOGOUT_SUCCESS.getMessage());
        map.put("url", "/sell/seller/order/list");
        return new ModelAndView("common/success", map);
    }
}

Spring AOP aspect verifies login status by checking the token cookie and Redis entry, throwing SellerAuthorizeException when invalid:

@Aspect
@Component
@Slf4j
public class SellerAuthorizeAspect {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Pointcut("execution(public * com.hh.controller.Seller*.*(..)) && !execution(public * com.hh.controller.SellerUserController.*(..))")
    public void verify() {}

    @Before("verify()")
    public void doVerify() {
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attrs.getRequest();
        Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN);
        if (cookie == null) {
            log.warn("【登录校验】Cookie中查不到token");
            throw new SellerAuthorizeException();
        }
        String tokenValue = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue()));
        if (StringUtils.isEmpty(tokenValue)) {
            log.warn("【登录校验】Redis中查不到token");
            throw new SellerAuthorizeException();
        }
    }
}

Exception handler redirects unauthenticated users back to the WeChat QR login URL:

@ControllerAdvice
public class SellExceptionHandler {
    @ExceptionHandler(SellerAuthorizeException.class)
    public ModelAndView handlerAuthorizeException() {
        return new ModelAndView("redirect:" +
                "https://open.weixin.qq.com/connect/qrconnect?" +
                "appid=wx6ad144e54af67d87&redirect_uri=...&response_type=code&scope=snsapi_login&state=...");
    }
    // other handlers omitted for brevity
}
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.

JavaBackend IntegrationSpring BootOAuth2WeChatQR login
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

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.