微信测试账号 (2)-消息验证sha1签名

本文深入解析微信消息认证过程,介绍如何使用SHA1算法生成签名,确保消息来源于微信官方,防止黑客伪造请求。同时,探讨了token的重要性,nonce和timestamp在防止重放攻击中的角色,以及如何利用commons-codec库实现SHA1签名。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第1篇中实现了收发微信消息,但是没有做验证,本篇将介绍微信如何使用sha签名,对消息进行认证。其中安全相关的概念,如sha1散列值、签名等,可参考web安全(1)

验证参数

	@GetMapping("/handler")
	public String handler(@RequestParam Map<String,String> map)  {

回顾 第1篇的get验证方法,在map中会收到微信发过来的4个参数,如下:

signature:签名,是微信用timestamp、nonce、你的token三个参数进行sha1算法得出的,用于验证消息来源。

echostr:随机字符串,第1篇演示过,如果signature签名验证成功,返回echostr给微信即可确认。

timestamp:时间戳(秒),可用于阻止重放攻击,下面会讲。

nonce:随机数,增加签名的不可预测性。

验证方法 

1)将token、timestamp、nonce三个参数进行字典序排序

2)将三个参数字符串拼接成一个字符串进行sha1加密

3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

token的作用

以上3条是微信官网原文,注意:我在web安全(1)中讲过,sha散列值与signature签名是两个不同的东西。消息参数中nonce和timestamp谁都可以生成,如果只对这样的内容进行sha1加密,实际作用只能是消息摘要,无法真正起到签名的作用,黑客可以自己生成nonce和timestamp及其sha1摘要,冒名微信请求我们的服务器。

而token是我们在微信网站中配置的,加入token以后的sha1摘要就真正起到了签名的作用,因为黑客不知道token,要伪造包含你token的假sha1摘要是相当困难的。除非你把token设的很短或很简单。

重放攻击

即使黑客无法自己生成签名,他只要知道了一次正常请求的get参数及签名,就可以反复使用这次传参伪装为真实用户,但他不能擅改时间时间戳,这会导致签名不一致。所以我们可以检查时间戳与当前时间的差值,超过一定时间,就判定是非法请求。

实现代码

添加commons-codec依赖,是apache开源的用于一些加解密之类的算法,下面会用它来实现sha1签名。

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
</dependency>

	@GetMapping("/handler")
	public String handler(@RequestParam Map<String,String> map)  {
		//1.将三个参数存入数组
		String[] array = {token,map.get("timestamp"),map.get("nonce")};
		//调用数组的排序方法,进行排序
		Arrays.sort(array);
		//2.调用spring的转换方法,将数组中已排序过的三个参数拼接成一个字符串
		String s = StringUtils.arrayToDelimitedString(array, "");
		//调用commons-codec的sha1加密方法生成签名
		String signature = DigestUtils.sha1Hex(s);
		//3.与signature对比,确认消息是否源自微信
		if(!map.get("signature").equals(signature)){
                        //验证不成功,随便回一个无关的内容
			return "fail";
		}
                //验证成功
		return map.get("echostr");

回复消息和success

以上get验证通过后,每次用post请求业务消息都要先进行以上验证,保证消息真实源自微信,再去做业务处理。但是以后不再有echostr这个参数,而且可以根据业务场景回复特定内容,如第1篇回复hello world。

如果业务处理后不需要任何回复,可回复“success”,通知微信处理成功,否则微信会提示用户错误信息。

消息加密

以上消息都是明文发送的,并没有加密,这是微信默认的设置。假如要对消息进行加密,需要登录账号进行配置,而测试账号并没有相关配置,以后讲正式账号时再详细介绍。

<think>好的,我现在需要帮助用户解决如何在Spring Boot中实现微信服务号扫码登录功能的问题。用户已经提供了之前的对话历史,其中包含了详细的实现步骤和一些参考引用。现在用户希望得到具体的Spring Boot教程和示例代码,特别是参考了一个名为wxlogin-spring-boot-starter的库。 首先,我需要确认用户的需求。他们想要使用Spring Boot来实现微信服务号的扫码登录,并且可能希望利用现有的Starter库来简化流程。在之前的回答中,已经提到了使用该Starter的依赖,用户现在希望看到更详细的步骤和代码示例。 接下来,我需要回顾之前的回答,确保不重复信息,同时补充更多Spring Boot相关的细节。之前的回答分为几个步骤:准备阶段、生成二维码、前端轮询、处理回调、登录确认等。现在需要将这些步骤转换为Spring Boot的具体实现,特别是结合提到的Starter库。 用户提供的引用[1]中提到了wxlogin-spring-boot-starter,这个库可能已经封装了微信登录的许多细节,比如自动配置、WebSocket支持等。因此,我需要考虑如何利用这个库来简化开发流程,而不是从头开始实现所有步骤。 首先,需要指导用户添加依赖,这在引用中已经提到。然后配置必要的参数,如appId、secret、token等。这些配置可能在application.properties或application.yml中完成。 接下来,生成二维码的部分,可能需要调用Starter提供的接口,或者自动处理。用户可能需要编写控制器来生成二维码的URL,并返回给前端。 前端轮询部分,可能需要使用WebSocket,如引用中提到的,这样比传统的轮询更高效。需要指导用户如何集成WebSocket,并处理前端连接。 处理微信回调方面,Starter可能已经封装了事件处理,用户只需定义回调方法即可。需要检查是否需要实现特定的接口或注解来处理SCAN事件,并存储openid等信息。 登录状态确认部分,可能需要结合Redis来存储临时凭证,并设置有效期。Starter可能已经集成了Redis,或者需要用户自行配置。 同时,需要注意安全性,比如校验签名,防止中间人攻击。用户可能需要在配置中设置Token,并在回调时验证签名。 此外,异常处理和用户体验优化也是重点,比如二维码过期处理、自动刷新、错误提示等。需要确保这些方面在代码示例中有所体现。 最后,生成相关问题,帮助用户进一步理解可能遇到的挑战,如安全措施、账号绑定等。 在组织回答时,需要结构清晰,分步骤说明,并提供代码示例。同时,引用之前提到的Starter库的优势,如自动配置、WebSocket支持等,让用户了解使用该库的好处。 可能还需要验证示例代码的正确性,确保依赖、配置和控制器方法的正确性。例如,在Spring Boot中,使用@RestController处理请求,@Autowired注入服务等。 总结一下,回答的结构应该包括:依赖添加、配置参数、生成二维码接口、WebSocket集成、回调处理、登录状态确认、安全措施,以及相关问题的提出。每个步骤都需要结合Spring Boot的特性和Starter库的功能,提供具体的代码示例和配置说明。</think>#### Spring Boot实现微信服务号扫码登录教程(整合`wxlogin-spring-boot-starter`) --- ### 一、快速集成步骤 #### 1. 添加依赖 ```xml <dependency> <groupId>com.tofries</groupId> <artifactId>wxlogin-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency> ``` #### 2. 配置参数(application.yml) ```yaml wx: login: app-id: your_appid # 微信服务号AppID secret: your_secret # 服务号AppSecret token: your_token # 服务器Token(需与微信后台一致) redirect-uri: /login/callback # 扫码回调路径 expire-seconds: 300 # 二维码有效期(秒) ``` --- ### 二、核心代码实现 #### 1. 生成二维码接口 ```java @RestController @RequestMapping("/login") public class WxLoginController { @Autowired private WxLoginService wxLoginService; // 生成扫码登录二维码 @GetMapping("/qrcode") public ResponseEntity<String> generateQrCode() { String sceneId = UUID.randomUUID().toString(); String qrCodeUrl = wxLoginService.generateQrCode(sceneId); return ResponseEntity.ok(qrCodeUrl); } } ``` #### 2. WebSocket状态推送(自动配置) ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private WxLoginWebSocketHandler webSocketHandler; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(webSocketHandler, "/ws/login") .setAllowedOrigins("*"); } } ``` #### 3. 微信回调处理(自动完成) ```java // Starter已自动处理微信服务器验证及扫码事件 // 开发者只需关注业务回调 @Slf4j @Component public class WxLoginCallbackHandler { @Autowired private RedisTemplate<String, String> redisTemplate; // 扫码成功回调(自动触发) @WxEvent(type = EventType.SCAN) public void handleScanEvent(ScanEvent event) { String sceneId = event.getSceneId(); String openId = event.getFromUserName(); redisTemplate.opsForValue().set(sceneId, openId, 5, TimeUnit.MINUTES); } } ``` #### 4. 登录状态查询 ```java @RestController public class AuthController { @Autowired private WxUserService userService; @GetMapping("/check-login") public ResponseEntity<?> checkLogin(@RequestParam String sceneId) { String openId = redisTemplate.opsForValue().get(sceneId); if (openId != null) { User user = userService.findByOpenId(openId); return ResponseEntity.ok(user); } return ResponseEntity.status(HttpStatus.PROCESSING).build(); } } ``` --- ### 三、前端交互示例 #### 1. 获取二维码 ```javascript // 调用后端接口获取二维码 fetch('/login/qrcode') .then(res => res.text()) .then(url => { document.getElementById('qrcode').src = url; initWebSocket(); // 建立WebSocket连接 }); ``` #### 2. WebSocket监听 ```javascript function initWebSocket() { const socket = new WebSocket('ws://yourdomain/ws/login'); socket.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'LOGIN_SUCCESS') { window.location.href = '/dashboard'; } }; } ``` --- ### 四、关键安全措施 1. **签名验证** Starter自动完成微信消息签名验证: ```java // 自动校验逻辑(源码片段) if (!SHA1.gen(timestamp, nonce, token).equals(signature)) { throw new InvalidSignatureException(); } ``` 2. **防重放攻击** 通过Redis记录已处理的微信消息ID: ```java if (redisTemplate.hasKey(msgId)) { throw new DuplicateMessageException(); } redisTemplate.opsForValue().set(msgId, "", 60, TimeUnit.SECONDS); ``` --- ### 五、高级配置项 #### 自定义登录成功处理 ```java @Configuration public class WxLoginConfig implements WxLoginCallback { @Override public void onLoginSuccess(String openId, HttpServletResponse response) { User user = userService.findOrCreate(openId); String token = JwtUtils.generateToken(user); response.addHeader("Authorization", token); } } ``` ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值