Java实现微信扫码登录

article/2025/9/12 23:47:46

微信扫码登录

    • 1. 授权流程说明
        • 第一步:请求 code
        • 第二步:通过 code 获取 access_token
        • 第三步:通过 access_token 调用接口
    • 2. 授权流程代码
    • 3. 用户登录和登出
    • 4. Spring AOP 校验用户有没有登录
    • 5. 拦截登录校验不通过抛出的异常

1. 授权流程说明

微信 OAuth2.0授权登录让用户使用微信身份安全登录第三方应用或网站,在微信用户授权登录已接入微信 OAuth2.0的第三方应用后,第三方可以获取到用户的接口调用凭证(access_token),通过 access_token 可以进行微信开放平台授权关系接口调用,从而可实现获取微信用户基本开放信息和帮助用户实现基础开放开发功能等。

官方文档:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html

微信OAuth2.0授权登录目前支持authorization_code模式,适用于拥有server端的应用授权。该模式整体流程为:

  1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据 code 参数;
  2. 通过 code 参数加上 AppID 和 AppSecret 等,通过 API 换取 access_token;
  3. 通过 access_token 进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。
    在这里插入图片描述

第一步:请求 code

第三方使用网站应用授权登录前,需要注意已获取相应网页授权作用域(scope = snsapi_login),则可以通过在 PC端打开以下链接:记得替换成自己的参数
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

返回说明

用户允许授权后,将会重定向到 redirect_uri 的网址上,并带上 code 和 state 参数

redirect_uri?code=CODE&state=STATE

若用户禁止授权,则重定向后不会带上 code 参数,仅会带上 state 参数

redirect_uri?state=STATE

例如:
登录一号店网站应用:https://passport.yhd.com/wechat/login.do
打开后,一号店会生成 state 参数,跳转到 :
https://open.weixin.qq.com/connect/qrconnect?appid=wxbdc5610cc59c1631&redirect_uri=https%3A%2F%2Fpassport.yhd.com%2Fwechat%2Fcallback.do&response_type=code&scope=snsapi_login&state=3d6be0a4035d839573b04816624a415e#wechat_redirect
微信用户使用微信扫描二维码并且确认登录后,PC端会跳转到:https://passport.yhd.com/wechat/callback.do?code=CODE&state=3d6be0a4035d839573b04816624a415e

第二步:通过 code 获取 access_token

通过 code 获取 access_token

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

返回说明

正确的返回:

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

错误的返回样例:

{"errcode":40029,"errmsg":"invalid code"}
  • AppSecret 是应用接口使用密钥,泄露后将可能导致应用数据泄露、应用的用户数据泄露等高风险后果;存储在客户端,极有可能被恶意窃取(如反编译获取 AppSecret );
  • access_token 为用户授权第三方应用发起接口调用的凭证(相当于用户登录态),存储在客户端,可能出现恶意获取 access_token 后导致的用户数据泄露、用户微信相关接口功能被恶意发起等行为;
  • refresh_token 为用户授权第三方应用的长效凭证,仅用于刷新 access_token,但泄露后相当于 access_token 泄露,风险同上。

建议将 Secret、用户数据(如 access_token)放在App云端服务器,由云端中转接口调用请求。

第三步:通过 access_token 调用接口

获取access_token 后,进行接口调用,有以下前提:

1.access_token 有效且未超时;
2.微信用户已授权给第三方应用账号相应接口作用域(scope)

对于接口作用域(scope),能调用的接口有以下:
在这里插入图片描述

2. 授权流程代码

因为微信开放平台和微信公众平台的 AppID、AppSecret 都是不同的,因此需要配置以下:

# 开放平台
wechat.open-app-id = wx6ad144e54af67d87
wechat.open-app-secret = 91a2ff6d38a2bbccfb7e9f9079108e2e
@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WechatAccountConfig {//公众号appidprivate String mpAppId;//公众号appSecretprivate String mpAppSecret;//商户号private String mchId;//商户秘钥private String mchKey;//商户证书路径private String keyPath;//微信支付异步通知private String notifyUrl;//开放平台idprivate String openAppId;//开放平台秘钥private String openAppSecret;
}
@Configuration
public class WechatOpenConfig {@Autowiredprivate WechatAccountConfig accountConfig;@Beanpublic WxMpService wxOpenService() {WxMpService wxOpenService = new WxMpServiceImpl();wxOpenService.setWxMpConfigStorage(wxOpenConfigStorage());return wxOpenService;}@Beanpublic WxMpConfigStorage wxOpenConfigStorage() {WxMpInMemoryConfigStorage wxMpInMemoryConfigStorage = new WxMpInMemoryConfigStorage();wxMpInMemoryConfigStorage.setAppId(accountConfig.getOpenAppId());wxMpInMemoryConfigStorage.setSecret(accountConfig.getOpenAppSecret());return wxMpInMemoryConfigStorage;}
}
@Controller
@RequestMapping("/wechat")
@Slf4j
public class WeChatController {@Autowiredprivate WxMpService wxMpService;@Autowiredprivate WxMpService wxOpenService;@GetMapping("/qrAuthorize")public String qrAuthorize() {//returnUrl就是用户授权同意后回调的地址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;}//用户授权同意后回调的地址,从请求参数中获取code@GetMapping("/qrUserInfo")public String qrUserInfo(@RequestParam("code") String code) {WxMpOAuth2AccessToken wxMpOAuth2AccessToken = new WxMpOAuth2AccessToken();try {//通过code获取access_tokenwxMpOAuth2AccessToken = wxOpenService.oauth2getAccessToken(code);} catch (WxErrorException e) {log.error("【微信网页授权】{}", e);throw new SellException(ResultEnum.WECHAT_MP_ERROR.getCode(), e.getError().getErrorMsg());}//从token中获取openidString openId = wxMpOAuth2AccessToken.getOpenId();//这个地址可有可无,反正只是为了拿到openid,但是如果没有会报404错误,为了好看随便返回一个百度的地址String  returnUrl = "http://www.baidu.com";log.info("openid={}", openId);return "redirect:" + returnUrl + "?openid="+openId;}
}

请求路径:在浏览器打开
https://open.weixin.qq.com/connect/qrconnect?appid=wx6ad144e54af67d87&redirect_uri=http%3A%2F%2Fsell.springboot.cn%2Fsell%2Fqr%2FoTgZpwenC6lwO2eTDDf_-UYyFtqI&response_type=code&scope=snsapi_login&state=http%3a%2f%2fheng.nat300.top%2fsell%2fwechat%2fqrUserInfo

获取了openid:openid = o9AREv7Xr22ZUk6BtVqw82bb6AFk
在这里插入图片描述

3. 用户登录和登出

@Controller
@RequestMapping("/seller")
public class SellerUserController {@Autowiredprivate SellerService sellerService;@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate ProjectUrlConfig projectUrlConfig;@GetMapping("/login")public ModelAndView login(@RequestParam("openid") String openid,                               HttpServletResponse response,                               Map<String, Object> map) {//1. openid去和数据库里的数据匹配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");}//2. 设置token至redisString token = UUID.randomUUID().toString();//设置token的过期时间Integer expire = RedisConstant.EXPIRE;redisTemplate.opsForValue().set(String.format(RedisConstant.TOKEN_PREFIX, token), openid, expire, TimeUnit.SECONDS);//3. 设置token至cookieCookieUtil.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) {//1. 从cookie里查询Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN);if (cookie != null) {//2. 清除redisredisTemplate.opsForValue().getOperations().delete(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue()));//3. 清除cookieCookieUtil.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);}
}
  • 将上面获取到的 openID 存入数据库
    在这里插入图片描述
  • 将授权后跳转的地址改为登录地址:
	//用户授权同意后回调的地址,从请求参数中获取code@GetMapping("/qrUserInfo")public String qrUserInfo(@RequestParam("code") String code) {WxMpOAuth2AccessToken wxMpOAuth2AccessToken = new WxMpOAuth2AccessToken();try {//通过code获取access_tokenwxMpOAuth2AccessToken = wxOpenService.oauth2getAccessToken(code);} catch (WxErrorException e) {log.error("【微信网页授权】{}", e);throw new SellException(ResultEnum.WECHAT_MP_ERROR.getCode(), e.getError().getErrorMsg());}//从token中获取openidString openId = wxMpOAuth2AccessToken.getOpenId();//授权成功后跳转到卖家系统的登录地址String  returnUrl = "http://heng.nat300.top/sell/seller/login";log.info("openid={}", openId);return "redirect:" + returnUrl + "?openid="+openId;}
  • 在浏览器请求这个链接:
    https://open.weixin.qq.com/connect/qrconnect?appid=wx6ad144e54af67d87&redirect_uri=http%3A%2F%2Fsell.springboot.cn%2Fsell%2Fqr%2FoTgZpwenC6lwO2eTDDf_-UYyFtqI&response_type=code&scope=snsapi_login&state=http%3a%2f%2fheng.nat300.top%2fsell%2fwechat%2fqrUserInfo
    第三方应用请求使用微信扫码登录,而不是使用本网站的密码:
    在这里插入图片描述
    用户同意授权后登入第三方应用的后台管理系统中:
    在这里插入图片描述
    在这里插入图片描述

4. Spring AOP 校验用户有没有登录

@Aspect
@Component
@Slf4j
public class SellerAuthorizeAspect {@Autowiredprivate StringRedisTemplate redisTemplate;@Pointcut("execution(public * com.hh.controller.Seller*.*(..))" +"&& !execution(public * com.hh.controller.SellerUserController.*(..))")public void verify() {}@Before("verify()")public void doVerify() {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();//查询cookieCookie cookie = CookieUtil.get(request, CookieConstant.TOKEN);//如果cookie中没有token说明已经登出或者根本没有登录if (cookie == null) {log.warn("【登录校验】Cookie中查不到token");//校验不通过,抛出异常throw new SellerAuthorizeException();}//去redis里查询String tokenValue = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue()));//如果redis中没有对应的openid,同样表示登出或者根本没有登录if (StringUtils.isEmpty(tokenValue)) {log.warn("【登录校验】Redis中查不到token");throw new SellerAuthorizeException();}}
}

5. 拦截登录校验不通过抛出的异常

拦截及登录校验不通过的异常,让其跳转的登录页面,扫码登录

@ControllerAdvice
public class SellExceptionHandler {//拦截登录异常@ExceptionHandler(value = SellerAuthorizeException.class)     public ModelAndView handlerAuthorizeException() {//拦截异常后,跳转到登录界面return new ModelAndView("redirect:".concat("https://open.weixin.qq.com/connect/qrconnect?" +"appid=wx6ad144e54af67d87" +"&redirect_uri=http%3A%2F%2Fsell.springboot.cn%2Fsell%2Fqr%2F" +"oTgZpwenC6lwO2eTDDf_-UYyFtqI" +"&response_type=code&scope=snsapi_login" +"&state=http%3a%2f%2fheng.nat300.top%2fsell%2fwechat%2fqrUserInfo"));}@ExceptionHandler(value = SellException.class)     @ResponseBody     public ResultVO handlerSellerException(SellException e) {return ResultVOUtil.error(e.getCode(), e.getMessage());}@ExceptionHandler(value = ResponseBankException.class)     @ResponseStatus(HttpStatus.FORBIDDEN)     public void handleResponseBankException() {}
}

好事定律:每件事最后都会是好事,如果不是好事,说明还没到最后。


http://chatgpt.dhexx.cn/article/nUfu7dsv.shtml

相关文章

vue 使用企业微信扫一扫

vue 使用企业微信扫一扫 vue 使用企业微信扫一扫 第一次调用企业微信功能&#xff0c;有点坑&#xff0c;折腾了好几天&#xff0c;终于好了&#xff0c;记录一下操作过程。 了解功能所需权限&#xff08;config和agentConfig&#xff09; 首先要确定使用的功能需要获取的权…

VUE实现微信扫码登录

获取access_token时序图&#xff1a; public中index.html引入 <script src"https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script> 微信登录操作 new WxLogin({// 以下操作把请求到的二维码嵌入到id为"weixin"的标签中i…

微信扫码登录原理解析

&#xff08;尊重劳动成果&#xff0c;转载请注明出处&#xff1a;http://blog.csdn.net/qq_25827845/article/details/78823861冷血之心的博客&#xff09; 最近针对扫码登录机制做了一个调研&#xff0c;以下以微信网页扫码登录为例进行一个总结。 1、微信扫码登录过程&…

web微信扫码登录

微信web扫码登录的大致流程&#xff0c;最后有源码基本是够用了&#xff0c;后续登录这一块会继续完善&#xff0c;会加上shiro、redis&#xff0c;前端准备用react来做&#xff0c;搞个全套的 开始之前我们先来看几个问题&#xff0c;有兴趣的可以了解下欢迎发表评论提出意见…

使用码上登录实现微信扫一扫登录

微信扫一扫登录测试 码上登录开发和使用登录的时序图准备工作后台开发前端显示 码上登录 码上登录是一个小程序&#xff0c;对个体开发者提供了免费的微信扫一扫登录入口&#xff0c;因为微信开发者需要企业认证&#xff0c;没办法在个人网站上做测试。码上登录相当于一个桥接…

微信扫码登录的一种开发思路

微信扫码授权登录流程&#xff1a; 用户在显示二维码的页面用手机扫码授权页面跳转到指定地址&#xff0c;URL上带有参数code前端通过code向服务端请求用于权限认证的token前端后续请求在请求头带上token作为身份标识 需要解决的问题 按照上述的流程&#xff0c;前端最简单的…

java集成微信扫码登录

具体流程可以看微信官网的扫码登录文档 地址&#xff1a;https://open.weixin.qq.com/cgi-bin/showdocument?actiondir_list&tresource/res_list&verify1&idopen1419316505&token&langzh_CN 一、 前期准备 1、注册 微信开放平台&#xff1a;https://open…

企业微信扫码登录

企业微信扫码登录步骤&#xff1a; 1.首先在要放置二维码的页面提供一个盒子用于防止生成的二维码 2.在当前页面将企业微信提供的js进行引入 3.调用提供的方法实例&#xff0c; 4.要获得扫码成功之后的code和state值&#xff0c;调用服务&#xff0c;就能查到当前用户的token&…

扫描微信二维码实现快速登录

一、什么是二维码 二维码又称二维条码&#xff0c;常见的二维码为QR Code&#xff0c;QR全称Quick Response&#xff0c;是一个近几年来移动设备上超流行的一种编码方式&#xff0c;它比传统的Bar Code条形码能存更多的信息&#xff0c;也能表示更多的数据类型。二维条码/二维码…

个人网站如何使用微信扫一扫登录---SpringBoot项目

文章目录 前言一、码上登录是什么&#xff1f;二、使用步骤1.登录前准备&#xff0c;获取SecretKey2.前端发起登录请求3.后端调用“码上登录”服务器3.1. 配置文件3.2. 发起请求3.3.返回的参数 4.前端显示二维码5、扫一扫登录6 、登录成功后接收用户信息7、通知前端登录成功并返…

微信扫码登录是如何实现的?

网页版微信刚推出时&#xff0c;无数人被它的登录方式惊艳了一下&#xff0c;不需要输入用户名密码&#xff0c;打开手机微信扫一扫&#xff0c;便自动登录。从原理上讲&#xff0c;二维码只能是一段文本的编码&#xff0c;如何用它实现快捷登录的呢&#xff1f; 打开网页版微…

Python 最强 IDE 详细使用指南!

本文经机器之心&#xff08;微信公众号&#xff1a;almosthuman2014&#xff09;授权转载&#xff0c;禁止二次转载 选自RealPython&#xff0c;作者&#xff1a;Jahongir Rahmonov 机器之心编译&#xff0c;参与&#xff1a;魔王 PyCharm 是一种 Python IDE&#xff0c;可以帮…

用 Python 给全球女神颜值排个序

点击上方“码农突围”&#xff0c;马上关注&#xff0c;每天上午8:50准时推送 这里是码农充电第一站&#xff0c;回复“666”&#xff0c;获取一份专属大礼包 真爱&#xff0c;请设置“星标”或点个“在看” 作者 | 数据森麟 来源 | 数据森麟&#xff08;ID: shujusenlin&#…

官宣:Python 3.8正式发布!来看看有哪些新功能

点击上方“码农突围”&#xff0c;马上关注&#xff0c;每天上午8:50准时推送 这里是码农充电第一站&#xff0c;回复“666”&#xff0c;获取一份专属大礼包 真爱&#xff0c;请设置“星标”或点个“在看” 来源&#xff1a;.python.org 编辑&#xff1a;肖琴&#xff0c;转自…

人工智能可以产生自主意识吗?

作者&#xff1a;人民邮电出版社 链接&#xff1a;https://www.zhihu.com/question/372639666/answer/1343242547 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 「既然人类对自己存在的认知来源于“感知”和“记忆…

CET6 历年真题原词复现 核心词汇 做题技巧 听力拿分技巧汇总(这篇文章是英语四六级考试最后的版本,谢谢大家一路陪伴)

话不多说直接进入 这次是最后一次 也是最用心的一次关于CET6的整理&#xff0c;CET4很简单&#xff0c;我认为没有必要去整理或者帮助各位同学&#xff0c;cet4只是cet6的前置任务罢了&#xff0c;但要是你说你考完cet4就收手了&#xff0c;不考了&#xff0c;不考6级了。我只…

这一款神器,助你秒级定位线上问题!

点击上方“Github中文社区”&#xff0c;关注 触达Github项目来源&#xff1a;https://my.oschina.net/leejun2005/blog/1524687 背景 经常做后端服务开发的同学&#xff0c;或多或少都遇到过 CPU 负载特别高的问题。 尤其是在周末或大半夜&#xff0c;突然群里有人反馈线上机器…

前端工程师高手说说CSS学习中的瓶颈

一、何为瓶颈&#xff1f; “瓶颈”指瓶子的颈部&#xff0c;相对狭窄。这是很传神的一个词&#xff0c;因为狭窄&#xff0c;因此难以突破&#xff1b;但是&#xff0c;一旦突破了&#xff0c;就是广阔天空&#xff08;偌大瓶身&#xff09;&#xff01; 小弟不才&#xff0…

线上服务 CPU 100%?一键定位 so easy!

转自&#xff1a;大数据之路&#xff0c; 链接&#xff1a;my.oschina.net/leejun2005/blog/1524687 背景 经常做后端服务开发的同学&#xff0c;或多或少都遇到过 CPU 负载特别高的问题。 尤其是在周末或大半夜&#xff0c;突然群里有人反馈线上机器负载特别高&#xff0c;不熟…

线上服务 CPU 100% ?一键定位 so easy!

来源&#xff1a;my.oschina.net/leejun2005/blog/1524687 背景 经常做后端服务开发的同学&#xff0c;或多或少都遇到过 CPU 负载特别高的问题。 尤其是在周末或大半夜&#xff0c;突然群里有人反馈线上机器负载特别高&#xff0c;不熟悉定位流程和思路的同学可能登上服务器一…