Skip to content

接入QQ登录

芯笑

本文介绍了如和在开发网址时接入QQ登陆功能,主要介绍后端的Java代码。并且将介绍如何阅读官方的开发文档来自己开发相关的代码。相关链接如下:

腾讯开放平台 OPEN.QQ.COM

准备

主要步骤:

用户授权和登录流程:在您的网站上提供QQ登录按钮,当用户点击时,将重定向到QQ登录页面。用户将在QQ登录页面上输入其QQ账号和密码,并授权您的网站访问其个人信息。一旦用户完成登录和授权,QQ将重定向回您事先设置的回调地址,并携带授权码或访问令牌。

项目说明

我们首先需要申请appid和appkey,然后才能通过id和key来获取access_token和openID进而得到用户信息。

yml中的配置信息如下,id、key和redirect需要根据自己的需要填入:

qq.app.id=
qq.app.key=
qq.url.authorization=https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&state=%s
qq.url.access.token=https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s
qq.url.openid=https://graph.qq.com/oauth2.0/me?access_token=%S
qq.url.user.info=https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s
qq.url.redirect=

获取accessToken

参考开发文档可知获取accessToken的步骤如下:

  1. 获取Authorization Code
  2. 通过Authorization Code获取Access Token

请求https://graph.qq.com/oauth2.0/token地址并带入以下参数:

参数是否必须含义
grant_type必须授权类型,此值固定为 authorization_code
client_id必须申请 QQ 登录成功后,分配给网站的 appid
client_secret必须申请 QQ 登录成功后,分配给网站的 appkey
code必须上一步返回的 authorization code。用户成功登录并授权后,会跳转到指定回调地址,并在 URL 中携带该参数。例如:http://www.qq.com/my.php?code=520DD95263C1CFEA087******。该 code 在 10 分钟内过期
redirect_uri必须必须与上一步请求中传入的 redirect_uri 保持一致

成功后会返回

access_token=FE04CCE2&expires_in=7776000&refresh_token=88E4BE14

处理后便可得到access_token。

/**
	 * 通过Authorization Code获取Access Token
	 * @param code
	 * @return
	 */
	private String getQQAccessToken(String code){
		String accessToken = null;
		String url = null;
		try {
			url = String.format(appConfig.getQqUrlAccessToken(), appConfig.getQqAppId(),appConfig.getQqAppKey(),code,
					URLEncoder.encode(appConfig.getQqUrlRedirect(),"utf-8"));

		}catch (UnsupportedEncodingException e){
			logger.error("获取QQ AccessToken失败",e);
		}
		String tokenResult = OKHttpUtils.getRequest(url);
		if (tokenResult == null || tokenResult.indexOf(Constants.VIEW_OBJ_RESULT_KEY) == -1){
			logger.error("获取QQ AccessToken失败,返回结果:{}",tokenResult);
			throw new BusinessException("获取QQ AccessToken失败");
		}
		//access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14
		String[] tokenResultArr = tokenResult.split("&");
		if (tokenResultArr != null && tokenResultArr.length > 0){
			for (String str : tokenResultArr){
				if (str.indexOf("access_token") != -1){
					accessToken = str.split("=")[1];
					break;
				}
			}
		}
		return accessToken;
	}

获取Openid

使用前期获取到的access_token来得到OpenID,开发文档获取OpenID

使用GET请求https://graph.qq.com/oauth2.0/me地址并带入以下参数:

参数是否必须含义
access_token必须在 Step1 中获取到的 access token

成功后返回的内容格式如下:

callback({"client_id":"YOUR_APPID","openid":"YOUR_OPENID"});
private String getQQOpenId(String accessToken){
		String url = String.format(appConfig.getQqUrlOpenid(),accessToken);
		String openIdResult = OKHttpUtils.getRequest(url);
		String tmpJson = getQQRes(openIdResult);
		if (tmpJson == null){
			logger.error("获取QQ OpenId失败,返回结果:{}",tmpJson);
			throw new BusinessException("获取QQ OpenId失败");
		}
		Map jsondata = JsonUtils.convertJson2Obj(tmpJson,Map.class);
		if (jsondata != null){
			logger.error("获取QQ OpenId失败,返回结果:{}",tmpJson);
			throw new BusinessException("获取QQ OpenId失败");
		}
		return String.valueOf(jsondata.get("openid"));
	}
	//callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
	private  String getQQRes(String result){
		if (StringUtils.isNotBlank(result)){
			int pos = result.indexOf("callback");
			if (pos != -1){
				int startIndex = result.indexOf("(");
				int endIndex = result.lastIndexOf(")");
				String json = result.substring(startIndex+1,endIndex-1);
				return json;
			}
		}
		return null;
	}

处理用户信息

参考get_user_info —用户信息文档中的返回参数说明

以下代码中使用的UserDto,将需要的信息直接转成了用户对象

private QQInfoDto getQQInfo(String accessToken,String qqOpenId) throws BusinessException{
		String url = String.format(appConfig.getQqUrlUserInfo(),accessToken,appConfig.getQqAppId(),qqOpenId);
		String res = OKHttpUtils.getRequest(url);
		if (StringUtils.isNotBlank(res)){
			QQInfoDto qqInfoDto = JsonUtils.convertJson2Obj(res,QQInfoDto.class);
			if (qqInfoDto.getRet() != 0){
				logger.error("获取QQ用户信息失败,返回结果:{}",res);
				throw new BusinessException("获取QQ用户信息失败");
			}
			return qqInfoDto;
		}
		throw new BusinessException("获取QQ用户信息失败");
	}

登录

@Override
	public SessionWebUserDto qqLogin(String code) {
		String accessToken = getQQAccessToken(code);
		String openId = getQQOpenId(accessToken);
		UserInfo userInfo = this.userInfoMapper.selectByQqOpenId(openId);
		String avatar = null;
		if (userInfo == null){
			//注册
			QQInfoDto qqInfoDto = getQQInfo(accessToken,openId);
			UserInfo bean = new UserInfo();
			String nickName = qqInfoDto.getNickname();
			nickName = nickName.length() > 20 ? nickName.substring(0,20) : nickName;
			avatar = StringTools.isEmpty(qqInfoDto.getFigureurl_qq_2()) ? qqInfoDto.getFigureurl_qq_1() : qqInfoDto.getFigureurl_qq_2();
			Date now = new Date();
			bean.setUserId(StringTools.getRandomString(Constants.COUNT_10));
			bean.setQqOpenId(openId);
			bean.setNickName(nickName);
			bean.setQqAvatar(avatar);
			bean.setJoinTime(new Date());
			bean.setLastLoginTime(now);
			bean.setStatus(UserStatusEnum.ENABLE.getStatus());
			bean.setUseSpace(0L);
			SysSetingsDto sysSetingsDto = redisComponent.getSysSetings();
			bean.setTotalSpace(sysSetingsDto.getUserInitUseSpace()*Constants.MB);
			this.userInfoMapper.insert(bean);
			userInfo = userInfoMapper.selectByQqOpenId(openId);
		}else {
			//更新最后登录时间
			UserInfo updateUser = new UserInfo();
			updateUser.setLastLoginTime(new Date());
			avatar = userInfo.getQqAvatar();
			this.userInfoMapper.updateByUserId(updateUser, userInfo.getUserId());
		}
		SessionWebUserDto sessionWebUserDto = new SessionWebUserDto();
		sessionWebUserDto.setNickName(userInfo.getNickName());
		sessionWebUserDto.setUserId(userInfo.getUserId());
		sessionWebUserDto.setAvatar(avatar);
		if (ArrayUtils.contains(appConfig.getAdmin().split(","),userInfo.getEmail()==null?"":userInfo.getEmail())){
			sessionWebUserDto.setAdmin(true);
		}else sessionWebUserDto.setAdmin(false);
		UserSpaceDto userSpaceDto = new UserSpaceDto();
		//TODO 获取用户已经使用的空间
		Long useSpace = fileInfoMapper.selectUserSpace(userInfo.getUserId());
		userSpaceDto.setUseSpace(useSpace);
		userSpaceDto.setTotalSpace(userInfo.getTotalSpace());
		redisComponent.saveUserSpace(userInfo.getUserId(),userSpaceDto);
		return sessionWebUserDto;
	}

ps.本文出现的代码仅为QQ登录时需要的主要代码,仅提供一些主要思路,其余一些次要的代码需要根据自己的需要使用,便不在文内全部展示。

分享
上一篇
代码贡献-PR
下一篇
AOP参数校验