WebSocket实现app扫描二维码登录

后台框架采用SpringMVC,不同的框架可根据逻辑更改即可:

【思路】- PC端生成二维码,二维码包含uuid(全局唯一标识符),且打通websocket通道,等待服务器返回登录成功信息;APP扫描二维码,获取uuid及登录信息,推送给服务端,处理后的登录信息通过websocket返回给PC端,PC端得到登录信息后保存即登录成功。APP扫描确认登录的信息可以采用ActiveMQ进行推送。

生成二维码部分引入依赖文件

     <dependency>
	    <groupId>com.google.zxing</groupId>
	    <artifactId>core</artifactId>
	    <version>3.1.0</version>
	</dependency>
	<dependency>  
         <groupId>com.google.zxing</groupId>  
         <artifactId>javase</artifactId>  
         <version>3.1.0</version>  
     </dependency> 

二维码登录后台控制层Controller

/**
 * 项目名称:dream_user
 * 项目包名:org.fore.user.controller
 * 创建时间:2017年8月8日下午5:29:41
 * 创建者:Administrator-宋发元
 * 创建地点:杭州
 */
package org.fore.user.controller;

import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.fore.model.user.UserAccount;
import org.fore.model.user.UserModel;
import org.fore.user.qrcode.websocket.WebSocketHandler;
import org.fore.user.service.UserAccountService;
import org.fore.user.service.UserService;
import org.fore.utils.jms.JmsSender;
import org.fore.utils.mvc.TokenUtil;
import org.fore.utils.mvc.annotation.LimitLess;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;


/**
 * 描述:控制层
 * @author songfayuan
 * 2017年8月8日下午5:29:41
 */
@Controller
@RequestMapping("/qrcodelogin")
public class QrCodeLoginController {

	private Logger logger = LoggerFactory.getLogger(QrCodeLoginController.class);
	
	public static int defaultWidthAndHeight=260;
	
	@Autowired
	private WebSocketHandler webSocketHandler;
	@Autowired
	private UserService userService;
	@Autowired
	private UserAccountService userAccountService;
	@Autowired
	@Qualifier(value = "qrCodeLoginSender")
	private JmsSender jmsSender;
	
	/**
	 * 描述:PC获取二维码
	 * @param uuid
	 * @param request
	 * @param response
	 * @throws ServletException
	 * @throws IOException
	 * @author songfayuan
	 * 2017年8月11日上午9:04:43
	 */
	@RequestMapping("/getLoginQrCode")
	@ResponseBody
	@LimitLess
	public void getLoginQrCode(String uuid, HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		//生成参数
		//String uuid = generateUUID();
		String host = request.getHeader("Host");
	    JSONObject data = new JSONObject();
	    data.put("code", 200);
	    data.put("msg", "获取二维码成功");
	    data.put("uuid", uuid);
	    data.put("host", host);
	    logger.info("【二维码内容】:{}",data);
        
        //生成二维码
	    Map<EncodeHintType, Object>  hints=new HashMap<EncodeHintType, Object>();
        // 指定纠错等级  
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);  
        // 指定编码格式  
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");  
        hints.put(EncodeHintType.MARGIN, 1);
        try {
			BitMatrix bitMatrix = new MultiFormatWriter().encode(data.toString(),BarcodeFormat.QR_CODE, defaultWidthAndHeight, defaultWidthAndHeight, hints);
			OutputStream out = response.getOutputStream();
			MatrixToImageWriter.writeToStream(bitMatrix, "png", out);//输出二维码
            out.flush();
            out.close();
            
        } catch (WriterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 描述:app确认请求处理
	 * @param uuid
	 * @param host
	 * @param userid
	 * @author songfayuan
	 * 2017年8月11日上午9:05:56
	 */
	@RequestMapping("/sendCodeLoginInfo")
	@ResponseBody
	@LimitLess
	public void sendCodeLoginInfo(String uuid, String host, Integer userid) {
		// 注册成功后 或 登录,需要同步账户信息,获取用户基本信息
		UserAccount account = userAccountService.findCurrentUserAccount(userid);
		userAccountService.syncAccount(account);

		UserModel userModel = userService.findUserById(userid);
		userModel = changeUserForShow(userModel);
		JSONObject token = TokenUtil.generateTokenByQrCodeLogin(userid, host);
		JSONObject object = new JSONObject();
		object.put("code", 10086);
		object.put("uuid", uuid);
		object.put("userinfo", userModel);
		object.put("token", token);
		object.put("msg", "登录成功");
		//this.webSocketHandler.forwardQrCode(object.toString());
		jmsSender.sendMessage(object.toString()); //采用ActiveMQ进行推送,也可以直接注入websocket进行发送
	}
	//处理用户登录信息
	private UserModel changeUserForShow(UserModel userModel) {
		UserModel user = new UserModel();
		user.setId(userModel.getId());
		user.setUserName(userModel.getUserName());
		user.setUserSex(userModel.getUserSex());
		user.setUserPortrait(userModel.getUserPortrait());
		return user;
	}
	
	/**
	 * 描述:唯一标识符
	 * @return
	 * @author songfayuan
	 * 2017年8月11日上午9:06:12
	 */
	public static String generateUUID() {
		String uuid = UUID.randomUUID().toString();
		uuid = uuid.replace("-", "");
		Long currentTime = System.currentTimeMillis();
		String currentDate = String.valueOf(currentTime);
		return uuid + currentDate;
	}
	
}

websocket实现(本案例采用Spring自带的websocket)

package org.fore.sms.qrcode.websocket;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;

@Configuration
@EnableWebMvc
@EnableWebSocket
public class QrCodeLoginWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {

	@Autowired
	private QrCodeLoginWebSocketEndPoint endPoint;
	@Autowired
	private QrCodeLoginHandshakeInterceptor interceptor;

	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry.addHandler(endPoint, "/qrcodelogin.do").addInterceptors(interceptor).setAllowedOrigins("*");
//		 registry.addHandler(endPoint,
//		 "/sockjs.do").addInterceptors(interceptor).setAllowedOrigins("*")
//		 .withSockJS();
	}

	/**
	 * Each underlying WebSocket engine exposes configuration properties that
	 * control runtime characteristics such as the size of message buffer sizes,
	 * idle timeout, and others.
	 */

	/**
	 * For Tomcat, WildFly, and GlassFish add a
	 * ServletServerContainerFactoryBean to your WebSocket Java config:
	 */
	@Bean
	public ServletServerContainerFactoryBean createWebSocketContainer() {
		ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
		container.setMaxTextMessageBufferSize(8192);
		container.setMaxBinaryMessageBufferSize(8192);
		return container;
	}

	/**
	 * For Jetty, you’ll need to supply a pre-configured Jetty
	 * WebSocketServerFactory and plug that into Spring’s
	 * DefaultHandshakeHandler through your WebSocket Java config:
	 */
	// @Bean
	// public DefaultHandshakeHandler handshakeHandler() {
	//
	// WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
	// policy.setInputBufferSize(8192); /* 设置消息缓冲大小 */
	// policy.setIdleTimeout(600000); /* 10分钟read不到数据的话,则断开该客户端 */
	//
	// return new DefaultHandshakeHandler(new JettyRequestUpgradeStrategy(new
	// WebSocketServerFactory(policy)));
	// }

}

package org.fore.sms.qrcode.websocket;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

@Component
public class QrCodeLoginHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
	private Logger logger = LoggerFactory.getLogger(QrCodeLoginHandshakeInterceptor.class);

	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Map<String, Object> attributes) throws Exception {
		return super.beforeHandshake(request, response, wsHandler, attributes);
	}

	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Exception ex) {
		super.afterHandshake(request, response, wsHandler, ex);
	}
}

package org.fore.sms.qrcode.websocket;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.fore.model.quota.tcp.ReqCode;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

@Component
public class QrCodeLoginWebSocketEndPoint extends TextWebSocketHandler {
	private Logger logger = LoggerFactory.getLogger(QrCodeLoginWebSocketEndPoint.class);

	private static Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();
	private static Map<WebSocketSession,String > sessionMap2 = new ConcurrentHashMap<>();

	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		logger.info("WebSocketHandler:客户端{}上线", session.getRemoteAddress());
		String uuid = generateUUID();
		sessionMap.put(uuid,session);
		sessionMap2.put(session,uuid);
	}

	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		String msg = message.getPayload();
		String ipAddress = session.getRemoteAddress().toString();
		JSONObject requestData = JSON.parseObject(msg);
		Integer code = requestData.getInteger("code");
		JSONObject result = new JSONObject();
		String uuid	= sessionMap2.get(session);
		result.put("code", 200);
		result.put("uuid", uuid);
		switch (code) {
		case ReqCode.REQ_QR_CODE:
			logger.info("WebSocketHandler:客户端{}发送消息{}...", ipAddress, msg);
			if(session.isOpen())
			session.sendMessage(new TextMessage(result.toString()));
			logger.info("WebSocketHandler:客户端{}发送消息{}完成", ipAddress, msg);
			break;
		default:
			break;
		}
	}

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		String ipAddress = session.getRemoteAddress().toString();
		logger.info("WebSocketHandler:客户端{}下线", ipAddress);
		logger.info("WebSocketHandler:删除客户端{}的session...", ipAddress);
		logger.info("WebSocketHandler:删除sessionMap的客户端{}连接...", ipAddress);
		String uuid = sessionMap2.get(session);
		sessionMap.remove(uuid);
		sessionMap2.remove(session);
		logger.info("WebSocketHandler:删除sessionMap的客户端{}连接完成", ipAddress);
		logger.info("WebSocketHandler:删除WebSocket客户端{}连接...", ipAddress);
//		logger.info("{}", sessionMap);
		sessionMap.remove(session);
//		logger.info("{}", sessionMap);
		logger.info("WebSocketHandler:删除WebSocket客户端{}连接完成", ipAddress);
		logger.info("WebSocketHandler:删除客户端{}的session完成", ipAddress);
		if(session.isOpen())
		session.close();
	}

	@Override
	public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
		logger.info("WebSocketHandler:客户端{}异常", session.getRemoteAddress(), exception);
	}
	
	//发送消息
	public void sendMessage(String userInfo) throws Exception {
		JSONObject json = JSONObject.parseObject(userInfo);
		String uuid = json.getString("uuid");
		WebSocketSession session = sessionMap.get(uuid);
		if (session == null) {
			logger.info("app发送给PC的登录信息:{}参数不正确!",userInfo);
		}else {
			logger.info("app发送给PC的登录信息:{}",userInfo);
			session.sendMessage(new TextMessage(userInfo));
		}
	}
	
	//唯一标识符
	public static String generateUUID() {
		String uuid = UUID.randomUUID().toString();
		uuid = uuid.replace("-", "");
		Long currentTime = System.currentTimeMillis();
		String currentDate = String.valueOf(currentTime);
		return uuid + currentDate;
	}
}

JMS实现

package org.fore.sms.qrcode.jms;

import org.fore.utils.jms.Listener;
import org.fore.sms.qrcode.websocket.QrCodeLoginWebSocketEndPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;

@Component
public class QrCodeLoginListener implements Listener {
	private Logger logger = LoggerFactory.getLogger(QrCodeLoginListener.class);
	@Autowired
	private QrCodeLoginWebSocketEndPoint qrCodeLoginWebSocketEndPoint;

	@Override
	public void onMessage(String message) {
		logger.info("app确认登录信息:接收app推送的确定PC登录消息{}", message);
		JSONObject object = JSONObject.parseObject(message);
		try {
			qrCodeLoginWebSocketEndPoint.sendMessage(object.toJSONString());
		} catch (Exception e) {
			logger.info("app确认登录信息:接收app推送的确定PC登录消息异常", e);
		}
	}

}

核心代码就酱......

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宋发元

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值