如上提问,web端扫码通过微信登录,然后后端获取了用户信息,但怎么准确的传回前端呢?
首先,web端手机扫码,实际交互的是手机微信和腾讯服务器,最后获取到信息的终端其实是手机。当然服务器也可以获取到。但是怎么样把这个信息准确的发到前端呢?
比如有ABCDEFG个前端,后端怎么准确的把信息发到对应的前端(终端机)呢?
如上提问,web端扫码通过微信登录,然后后端获取了用户信息,但怎么准确的传回前端呢?
首先,web端手机扫码,实际交互的是手机微信和腾讯服务器,最后获取到信息的终端其实是手机。当然服务器也可以获取到。但是怎么样把这个信息准确的发到前端呢?
比如有ABCDEFG个前端,后端怎么准确的把信息发到对应的前端(终端机)呢?
轮询:
function checkLoginStatus(uuid) {
fetch(`/api/check-login-status?uuid=${uuid}`)
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
console.log('用户信息:', data.userInfo);
window.location.href = '/dashboard';
} else if (data.status === 'waiting') {
setTimeout(() => checkLoginStatus(uuid), 2000);
}
});
}
fetch('/api/generate-qrcode')
.then(response => response.json())
.then(data => {
const uuid = data.uuid;
document.getElementById('qrcode').src = data.qrcodeUrl;
checkLoginStatus(uuid);
});
WebSocket :
const socket = new WebSocket('ws://your-domain.com/ws');
let uuid = '';
socket.onopen = () => {
socket.send(JSON.stringify({
type: 'generate_qrcode'
}));
};
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'qrcode') {
uuid = data.uuid;
document.getElementById('qrcode').src = data.qrcodeUrl;
} else if (data.type === 'login_success') {
console.log('用户信息:', data.userInfo);
window.location.href = '/dashboard';
}
};
@RestController
@RequestMapping("/api/wechat")
public class WechatLoginController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@GetMapping("/generate-qrcode")
public Map<String, Object> generateQrcode() {
String uuid = UUID.randomUUID().toString();
String appId = "你的AppID";
String redirectUri = URLEncoder.encode("你的回调地址", StandardCharsets.UTF_8);
String scope = "snsapi_login";
String qrcodeUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=" + appId +
"&redirect_uri=" + redirectUri +
"&response_type=code" +
"&scope=" + scope +
"&state=" + uuid;
Map<String, Object> result = new HashMap<>();
result.put("uuid", uuid);
result.put("qrcodeUrl", qrcodeUrl);
return result;
}
// 微信回调接口
@GetMapping("/callback")
public String wechatCallback(@RequestParam String code, @RequestParam String state) {
// state就是之前生成的uuid
try {
// 获取微信用户信息
WechatUserInfo userInfo = getWechatUserInfo(code);
// 将用户信息存入Redis,设置过期时间5分钟
redisTemplate.opsForValue().set(
"wechat:login:" + state,
new ObjectMapper().writeValueAsString(userInfo),
5, TimeUnit.MINUTES
);
return "登录成功,请返回网页";
} catch (Exception e) {
return "登录失败:" + e.getMessage();
}
}
// 前端轮询接口
@GetMapping("/check-login-status")
public Map<String, Object> checkLoginStatus(@RequestParam String uuid) {
Map<String, Object> result = new HashMap<>();
String userInfoJson = redisTemplate.opsForValue().get("wechat:login:" + uuid);
if (userInfoJson != null) {
try {
// 找到用户信息,登录成功
WechatUserInfo userInfo = new ObjectMapper().readValue(userInfoJson, WechatUserInfo.class);
// 删除Redis中的数据
redisTemplate.delete("wechat:login:" + uuid);
result.put("status", "success");
result.put("userInfo", userInfo);
} catch (Exception e) {
result.put("status", "error");
result.put("message", e.getMessage());
}
} else {
// 未找到用户信息,继续等待
result.put("status", "waiting");
}
return result;
}
// 获取微信用户信息
private WechatUserInfo getWechatUserInfo(String code) {
// 微信开放平台参数
String appId = "你的AppID";
String appSecret = "你的AppSecret";
// 1. 通过code获取access_token
String tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=" + appId +
"&secret=" + appSecret +
"&code=" + code +
"&grant_type=authorization_code";
RestTemplate restTemplate = new RestTemplate();
String tokenResponse = restTemplate.getForObject(tokenUrl, String.class);
// 解析响应获取access_token和openid
// 这里简化处理,实际应该使用JSON解析
JsonNode tokenJson = new ObjectMapper().readTree(tokenResponse);
String accessToken = tokenJson.get("access_token").asText();
String openid = tokenJson.get("openid").asText();
// 2. 通过access_token和openid获取用户信息
String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=" + accessToken +
"&openid=" + openid;
String userInfoResponse = restTemplate.getForObject(userInfoUrl, String.class);
// 解析用户信息
return new ObjectMapper().readValue(userInfoResponse, WechatUserInfo.class);
}
}
WebSocket:
@ServerEndpoint("/ws/wechat-login/{uuid}")
@Component
public class WechatLoginWebSocket {
private static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session, @PathParam("uuid") String uuid) {
sessionMap.put(uuid, session);
}
@OnClose
public void onClose(@PathParam("uuid") String uuid) {
sessionMap.remove(uuid);
}
public static void sendUserInfo(String uuid, WechatUserInfo userInfo) {
Session session = sessionMap.get(uuid);
if (session != null) {
try {
Map<String, Object> message = new HashMap<>();
message.put("type", "login_success");
message.put("userInfo", userInfo);
session.getBasicRemote().sendText(new ObjectMapper().writeValueAsString(message));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
10 回答11.6k 阅读
2 回答3.1k 阅读✓ 已解决
2 回答4.1k 阅读✓ 已解决
3 回答2.7k 阅读✓ 已解决
3 回答1.8k 阅读✓ 已解决
2 回答1.7k 阅读✓ 已解决
4 回答2.5k 阅读✓ 已解决
就是标准的 OAuth 2.0 流程。
这里的 code 就具有短期唯一性,并且是微信那边保证的,为了防跨站攻击,还提供的有 state 参数。