第三方登录接入-qq,weibo-java

开发之前

需求:网站接入qq,sina微博登录,本文最后付效果图:

说明:大部分网站本身是需要用户概念的,很多操作依附于用户,而qq或微博作为一种登录方式指向用户而已,我参考了一下其他网站的做法,

一般有如下两种做法:

1,强制绑定:用户第一次通过qq登录时必须与该网站账户绑定,也就是用户必须要先有一个此网站账户才能登录成功

2,互相独立,用户第一次通过qq登录时直接重新为用户注册一个账户,如以用户名为qq_123456直接注册一个账户,与其他账户无关;

 

站在用户角度考虑下,可能需要更多的选择性,因此我是如下考虑的:

用户登录后在个人中心中也可设置绑定。

---------------------------------------------------------------------------------------------------

文档说明

现在大部分第三方的登录OAuth2.0为标准,所以开发流程基本都一致,一般都是一下步骤:

1,申请接入,获取appid&appkey(接入后又第三方发放)

2,用户登录第三方下发token,

3,通过token获取用户唯一标示,一般是一个openId

api地址:

qq:http://wiki.connect.qq.com/api列表

sina:http://open.weibo.com/wiki/授权机制

qq&sina也提供了java sdk

https://github.com/sunxiaowei2014/weibo4j-oauth2-beta3.1.1/

http://qzonestyle.gtimg.cn/qzone/vas/opensns/res/doc/qqConnect_Server_SDK_java_v2.0.zip

sina的虽然开源了,但里面很多代码写的有问题,用的之后需要注意

------------------------------------------------------------------------------------------------------------

开发

数据结构方面需要加以一张表用来维护登录方式和用户关联(通过openId以及登录方式确定唯一性)

网站引入,appid,appkey,回调地址的配置有不再赘述了

代码基本上参照sdk中的demo就可以了,

简单贴一下controller(springMVC架构)中的代码吧

AUthController为父类,存放一些第三方登录的通用方法,BindController为用户绑定提供方法,第三方登录的controller基本上就是一个登录跳转方法,一个回调方法,以及一些接口api的调用

public abstract class AuthController extends BaseController {@Resourceprivate JavaMailSender mailSender;@Resourceprivate IBAuthService authService;protected LoginUser getU(BAuth auth){LoginUser loginUser= new LoginUser();loginUser.setAccessToken(auth.getAccessToken());loginUser.setIcon(auth.getIcon() == null ? IPortalConstants.defaultIconUrl: auth.getIcon());loginUser.setId(auth.getUser().getId());loginUser.setLoginType(auth.getType());loginUser.setNickName(auth.getNickName() == null ? auth.getUser().getNickName() : auth.getNickName());loginUser.setOpenId(auth.getOpenId());return loginUser;}protected LoginUser getU(User user){LoginUser loginUser= new LoginUser();loginUser.setIcon(user.getIcon() == null ? IPortalConstants.defaultIconUrl: user.getIcon());loginUser.setId(user.getId());loginUser.setLoginType(AuthType.bresume.getCode());loginUser.setNickName( user.getNickName());return loginUser;}protected boolean setUser2Session(BAuth auth){LoginUser loginuser = this.getU(auth);SessionContextHolder.getSession().setAttribute(IPortalConstants.SESSION_KEY_LOGIN_USER, loginuser);return true;}protected boolean setUser2Session(User user){LoginUser loginuser = this.getU(user);SessionContextHolder.getSession().setAttribute(IPortalConstants.SESSION_KEY_LOGIN_USER, loginuser);return true;}protected void sendRegisterMail(User user,String code) {PropertiesLoader loader = new PropertiesLoader("mail.properties");Map<String, Object> map = new HashMap<String, Object>();Email email = new Email();email.setSender(loader.getProperty("mail.from"));email.setAddress(user.getEmail());email.setSubject(loader.getProperty("mail.register.success.subject"));// 从模板生成HashMap<String, Object> param = new HashMap<String, Object>();param.put("userName", user.getUserName());param.put("userId", user.getId());param.put("code", code);email.setContent(MailUtils.getMailText(param,loader.getProperty("mail.register.success.content")));map.put("email", email);MailUtils.sendMailByAsynchronousMode(map, mailSender);}protected String callBack(Model model,BAuth newAuth){BAuth oldAuth = authService.findOne(newAuth.getOpenId(),newAuth.getType());if (oldAuth != null && oldAuth.getUser() != null) {// 判定有登录记录//刷新accessToken
            oldAuth.setAccessToken(newAuth.getAccessToken());oldAuth.setExpiresIn(newAuth.getExpiresIn());oldAuth.setIcon(newAuth.getIcon());oldAuth.setNickName(newAuth.getNickName());oldAuth.setRefreshAccessTime(new Date());authService.update(oldAuth);this.setUser2Session(oldAuth);return "redirect:/index";} else if(oldAuth==null) {// 判定首次登录,记录oldAuth = new BAuth();oldAuth.setAccessToken(newAuth.getAccessToken());oldAuth.setExpiresIn(newAuth.getExpiresIn());oldAuth.setCreatedTime(new Date());oldAuth.setIcon(newAuth.getIcon());oldAuth.setNickName(newAuth.getNickName());oldAuth.setOpenId(newAuth.getOpenId());oldAuth.setRefreshAccessTime(new Date());oldAuth.setType(newAuth.getType());authService.save(oldAuth);//用户绑定,跳转页面model.addAttribute("openId", newAuth.getOpenId());model.addAttribute("loginFrom", newAuth.getType());return "site/bindAuth.jsp";}else{// 登录过但因某种原因为绑定账户
            oldAuth.setAccessToken(newAuth.getAccessToken());oldAuth.setExpiresIn(newAuth.getExpiresIn());oldAuth.setIcon(newAuth.getIcon());oldAuth.setNickName(newAuth.getNickName());oldAuth.setRefreshAccessTime(new Date());authService.update(oldAuth);//用户绑定,跳转页面model.addAttribute("openId", newAuth.getOpenId());model.addAttribute("loginFrom", newAuth.getType());return "site/bindAuth.jsp";}}}
AuthController
@RequestMapping("/")
@Controller
public class BindController extends AuthController {@Resourceprivate IUserService userService;@Resourceprivate IBAuthService authService;@Resourceprivate IUserVerifiedService verifiedService;@Resourceprivate JavaMailSender mailSender;@RequestMapping("/ingore-bind")public String ingore_bind(@RequestParam(value = "loginFrom", required = true) Integer loginFrom,@RequestParam(value = "openId", required = true) String openId,ModelMap model, HttpServletResponse response) {BAuth auth = authService.findOne(openId, loginFrom);if (auth == null) {return "404";}if (auth.getUser() == null) {User user = new User();/** user.setUserName(userName); user.setPassword(password);*/// user.setEmail(email);
            user.setNickName(auth.getNickName());user.setIcon(auth.getIcon());user.setRegisterType(AuthType.fromCode(loginFrom).getRt().getType());user.setType(UserType.PERSIONAL.getCode());user.setLevel(0);userService.registerFromAuth(user);auth.setUser(user);authService.save(auth);}this.setUser2Session(auth);return "redirect:/index";}@RequestMapping("/login-bind")public @ResponseBody JSONObject bind(@RequestParam(value = "loginFrom", required = true) Integer loginFrom,@RequestParam(value = "openId", required = true) String openId,@RequestParam(value = "email", required = true) String email,@RequestParam(value = "password", required = true) String password,ModelMap model, HttpServletResponse response) {BAuth auth = authService.findOne(openId, loginFrom);if (auth == null) {return this.toJSONResult(false,"404");}if (auth.getUser() == null) {try {// 登陆校验User user = userService.loginCheck(email, password);auth.setUser(user);authService.update(auth);} catch (CoreException e) {if (e.getErrorCode() == PortalErrorCode.USER_PASSWORD_ERROR_TIMES_EXCEED_ERROR) {return this.toJSONResult(false,this.getMessage(e, e.getArgs()));} else {return this.toJSONResult(false, this.getMessage(e));}}}this.setUser2Session(auth);return this.toJSONResult(true);}@RequestMapping("/regist-bind")public @ResponseBody JSONObject registBind(@RequestParam(value = "loginFrom", required = true) Integer loginFrom,@RequestParam(value = "openId", required = true) String openId,@RequestParam(value = "email", required = true) String email,@RequestParam(value = "password", required = true) String password,ModelMap model, HttpServletResponse response) {BAuth auth = authService.findOne(openId, loginFrom);if (auth == null) {return this.toJSONResult(false);}if (auth.getUser() == null) {User user=new User();
//            user.setUserName(userName);
            user.setPassword(password);user.setEmail(email);try {user.setRegisterType(RegisterType.PORTAL_REGISTER.getType());user.setType(UserType.PERSIONAL.getCode());user.setLevel(0);user.setNickName(auth.getNickName());user.setIcon(auth.getIcon());userService.register(user);//生成邮箱验证码UserVerified uv = new UserVerified(user);verifiedService.save(uv);// 发送注册成功的邮件if (CommonUtils.isNotEmpty(user.getEmail())) {sendRegisterMail(user,uv.getCode());}} catch (CoreException e) {return this.toJSONResult(false, this.getMessage(e));}auth.setUser(user);authService.save(auth);}this.setUser2Session(auth);return this.toJSONResult(true);}}
BindController
@RequestMapping("/")
@Controller
public class QQController extends AuthController {@Resourceprivate IBAuthService authService;@Resourceprivate IUserService userService;@RequestMapping("/qqlogin")public void index(HttpServletRequest request, HttpServletResponse response,Model model) throws IOException {response.setContentType("text/html;charset=utf-8");try {response.sendRedirect(new Oauth().getAuthorizeURL(request));LOGGER.info("login by qq");} catch (QQConnectException e) {e.printStackTrace();}}@RequestMapping("/qq_callback")public String callback(HttpServletRequest request,HttpServletResponse response, Model model) {try {AccessToken accessTokenObj = (new Oauth()).getAccessTokenByRequest(request);String accessToken = null, openID = null;long tokenExpireIn = 0L;if (accessTokenObj.getAccessToken().equals("")) {LOGGER.error("QQ Login failed,caused by 没有获取到响应参数");return "404";}accessToken = accessTokenObj.getAccessToken();tokenExpireIn = accessTokenObj.getExpireIn();LOGGER.info("Get accessToken from qq,accessToken:" + accessToken+ ",tokenExpireIn" + tokenExpireIn);// 利用获取到的accessToken 去获取当前用的openidOpenID openIDObj = new OpenID(accessToken);openID = openIDObj.getUserOpenID();LOGGER.info("利用获取到的accessToken:" + accessToken+ ", 去获取到当前用户openid:" + openID + ".");String icon = null, nickName = null;// 去获取用户在Qzone的昵称等信息UserInfo qzoneUserInfo = new UserInfo(accessToken, openID);UserInfoBean userInfoBean = qzoneUserInfo.getUserInfo();if (userInfoBean.getRet() == 0) {nickName = userInfoBean.getNickname();// userInfoBean.getGender();
icon = userInfoBean.getAvatar().getAvatarURL30();// userInfoBean.getAvatar().getAvatarURL50();// userInfoBean.getAvatar().getAvatarURL100();} else {LOGGER.error("很抱歉,我们没能正确获取到您的信息,原因是:" + userInfoBean.getMsg());}BAuth newAuth = new BAuth();newAuth.setAccessToken(accessToken);newAuth.setExpiresIn(tokenExpireIn);newAuth.setIcon(icon);newAuth.setNickName(nickName);newAuth.setOpenId(openID);newAuth.setType(AuthType.QQ.getCode());return this.callBack(model, newAuth);// 通过openid判断首次登录与否/*    BAuth bauth = authService.findOne(openID, AuthType.QQ.getCode());if (bauth != null && bauth.getUser() != null) {// 判定有登录记录//刷新accessTokenbauth.setAccessToken(accessToken);bauth.setExpiresIn(tokenExpireIn);bauth.setIcon(icon);bauth.setNickName(nickName);bauth.setRefreshAccessTime(new Date());authService.update(bauth);this.setUser2Session(bauth);return "redirect:/index";} else if(bauth==null) {// 判定首次登录,记录bauth = new BAuth();bauth.setAccessToken(accessToken);bauth.setCreatedTime(new Date());bauth.setExpiresIn(tokenExpireIn);bauth.setIcon(icon);bauth.setNickName(nickName);bauth.setOpenId(openID);bauth.setRefreshAccessTime(new Date());bauth.setType(AuthType.QQ.getCode());authService.save(bauth);//用户绑定,跳转页面model.addAttribute("openId", openID);model.addAttribute("loginFrom", AuthType.QQ.getCode());return "site/bindAuth.jsp";}else{// 登录过但因某种原因为绑定账户bauth.setAccessToken(accessToken);bauth.setExpiresIn(tokenExpireIn);bauth.setIcon(icon);bauth.setNickName(nickName);bauth.setRefreshAccessTime(new Date());authService.update(bauth);//用户绑定,跳转页面model.addAttribute("openId", openID);model.addAttribute("loginFrom", AuthType.QQ.getCode());return "site/bindAuth.jsp";}*/} catch (QQConnectException e) {e.printStackTrace();}return "redirect:/index";}@RequestMapping("/qqss")public void talk(HttpServletRequest request, HttpServletResponse response,Model model) throws IOException {response.setContentType("text/html;charset=utf-8");request.setCharacterEncoding("utf-8");String con = request.getParameter("con");HttpSession session = request.getSession();String accessToken = (String) session.getAttribute("demo_access_token");String openID = (String) session.getAttribute("demo_openid");System.out.println(accessToken);System.out.println(openID);// 请开发者自行校验获取的con值是否有效if (con != "") {Topic topic = new Topic(accessToken, openID);try {GeneralResultBean grb = topic.addTopic(con);if (grb.getRet() == 0) {response.getWriter().println("<a href=\"http://www.qzone.com\" target=\"_blank\">您的说说已发表成功,请登录Qzone查看</a>");} else {response.getWriter().println("很遗憾的通知您,发表说说失败!原因: " + grb.getMsg());}} catch (QQConnectException e) {System.out.println("抛异常了?");}} else {System.out.println("获取到的值为空?");}}
}
QQController
@RequestMapping("/")
@Controller
public class SinaController extends AuthController {@Resourceprivate IBAuthService authService;@Resourceprivate IUserService userService;@RequestMapping("/sinalogin")public void index(HttpServletRequest request, HttpServletResponse response,Model model) throws IOException {response.setContentType("text/html;charset=utf-8");try {response.sendRedirect(new Oauth().authorize("code"));LOGGER.info("login by weibo");} catch (WeiboException e) {e.printStackTrace();}}@RequestMapping("/weibo_callback")public String callback(HttpServletRequest request,HttpServletResponse response, Model model) throws IOException {try {Oauth oauth = new Oauth();String code = request.getParameter("code");LOGGER.info("code: " + code);AccessToken accessTokenObj = oauth.getAccessTokenByCode(code);if (accessTokenObj == null) {LOGGER.error("AccessToken 获取失败,code:" + code);}String accessToken = accessTokenObj.getAccessToken();String openId = accessTokenObj.getUID();String expireInStr = accessTokenObj.getExpireIn();Users um = new Users(accessToken);User user = um.showUserById(openId);LOGGER.info(user.toString());BAuth newAuth = new BAuth();newAuth.setAccessToken(accessToken);newAuth.setExpiresIn(expireInStr != null ? Long.parseLong(expireInStr) : 3600);newAuth.setIcon(user.getAvatarLarge());newAuth.setNickName(user.getScreenName());newAuth.setOpenId(openId);newAuth.setType(AuthType.SINA.getCode());return this.callBack(model, newAuth);} catch (WeiboException e) {if (401 == e.getStatusCode()) {LOGGER.error("Unable to get the access token.");} else {e.printStackTrace();}}return "redirect:/index";}}
SinaController

 

-------------------------------------------------------------------------------------------------------------

页面效果

注:该流程为用户首次使用第三方登录时流程

1,登录页面放置第三方登录图标

2,点击图标接入第三方接口,可跳转至第三方登录界面

3,第三方登录完成,用户账户绑定

4,用户登录后,可在个人设置中管理第三方登录的绑定

 

转载于:https://www.cnblogs.com/china2k/p/4217108.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/257789.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python替换img的路径为新的路径_以“五智”为核心 南宁电信打造5G时代数字家庭新路径...

来源&#xff1a;通信信息报本报讯(特约记者 许辉坚)近日&#xff0c;中国电信广西南宁分公司在协助广西自治区通信管理局主办的“电信用户委员会体验活动”中&#xff0c;以发挥用户委员会的平台和纽带作用&#xff0c;促进持续改善电信服务质量为主题&#xff0c;以“五智”能…

0118——RTLabel和正则表达式

RTLabel和RegexKitLite都要导入第三方库 使用Regexkitlite库进行正则表达式的解析 1.库是使用MRR&#xff0c;如果在ARC工程里面使用这个类&#xff0c;必须在project->build phases->compile resources里面将regexKitLite.m的编译指令设为&#xff1a;-fno-objc-arc 2.需…

热带雨林绘画软件测试,儿童画教程|色彩练习与思维创想——好热闹的热带雨林!...

课程类型综合创意绘画课程参考课时90分钟工具材料勾线笔、水彩笔、卡纸课程构思小朋友们知道吗&#xff1f;热带雨林是地球上抵抗力和稳定性最高的生态系统&#xff0c;常年气候非常的炎热&#xff0c;雨量充沛&#xff0c;没有明显的季节差异&#xff0c;生物群落演替速度极快…

Linux命令行编辑快捷键

2019独角兽企业重金招聘Python工程师标准>>> ctrl ? 撤消前一次输入 ctrl c 另起一行 ctrl r 输入单词搜索历史命令 ctrl u 删除光标前面所有字符相当于VIM里d shift^ ctrl k 删除光标后面所有字符相当于VIM里d shift$ 删除 ctrl d 删除光标所在位置上的字符…

思科查看服务器启动配置文件,启动配置检查UCS

本文解释如何使用UCS将启动功能和命令迅速执行配置状态检查。UCS将启动功能允许用户迅速执行一粗略检查为了保证刀片适当地配置允许BIOS继续。意志启动配置检查是验证CPU和DIMM配置。此检查迅速帮助用户调试启动问题。思科UCS在意志启动配置检查提供几个工具帮助。这些工具包括…

lnmp下配置虚拟主机

一&#xff1a;首先熟悉几个命令 which php ---> which是通过 PATH环境变量到该路径内查找可执行文件&#xff0c;所以基本的功能是寻找可执行文件 whereis php ----> 将和php文件相关的文件都查找出来 service php-fpm/nginx/mysqld restart 重启服务 二&…

datazen Active Directory AD 配置

今天苦心经营的datazen 链接AD&#xff0c;文档已经无法吐槽了简单的几句话&#xff0c;根本不够用。 先说一下链接AD 的好处吧&#xff0c; 1 首先免去设置密码的麻烦&#xff0c;因为直接用AD账号的密码。 2 更安全&#xff0c;因为客户可不想自己的自己的系统&#xff0c;开…

Android CardView卡片布局 标签: 控件

CardView介绍 CardView是Android 5.0系统引入的控件&#xff0c;相当于FragmentLayout布局控件然后添加圆角及阴影的效果&#xff1b;CardView被包装为一种布局&#xff0c;并且经常在ListView和RecyclerView的Item布局中&#xff0c;作为一种容器使用。CardView应该被使用在显…

橱柜高度与身高对照表_下一套房子装修,橱柜就照这样打,布局尺寸这么详细,不信不好用...

闲在家的时候&#xff0c;动手给自己做一顿色香味俱全的美食&#xff0c;是一个享受的过程。享受的&#xff0c;不仅仅是味蕾的体验&#xff0c;择菜、洗菜、切菜、下锅...每一个环节都是。不过&#xff0c;这取决于你是否有一个好厨房&#xff1b;而厨房最最最重要的家具&…

.NET手记-JS获取Url参数

最近为App做活动专区&#xff0c;其中很多活动都是采用html 5页面来制作的。一方面体量较小&#xff0c;制作快速&#xff0c;更新维护容易&#xff1b;另一方面&#xff0c;嵌入App后适配效果也不会很差。 这里我们采用混编形式来从native app传参给web页面&#xff0c;我们采…

axios打包放到服务器上接口地址404_如何把网页文件放到云虚拟主机

把网页文件放到云虚拟主机上&#xff0c;可以用虚拟主机控制面板、FTP软件等方式。我们在运营网站时&#xff0c;经常需要制作一些特殊网页文件&#xff0c;比如404页面、sitemap页面、robots文件等&#xff0c;以及像站长平台严重文件、统计代码验证文件等&#xff0c;这些页面…

cisco数据中心理论小记-3

port channelSTP-防环,非根非指定口block.PCH-带宽利用率提升&#xff0c;逻辑上单链路 l2/l3都可以使用passive -LACP /active-Lacp/ on-Static无协议-on有协商-LACPneuxs 不支持PACPvPC---纯二层 技术&#xff0c;没有三层功能vitrual Port channel解决的是跨框 &#xff0c;…

-----------最小生成树----------------

最小生成树(Minimum Spanning Tree) 1:是一棵树(是一种特殊的图) 连通的,没有回路 有V 个顶点 一定有 V-1条边 2:生成树 包含了全部的顶点,所有的V-1条边 都在图里 剩下的三个土 都是第一个完全图的生成树 只要是 4个顶点 3条边 没有回路 就是生成树 这3个图 随便的加一…

jQuery Easing 使用方法及其图解

从jQuery API 文档中可以知道&#xff0c;jQuery自定义动画的函数.animate( properties [, duration] [, easing] [, complete] )有四个参数&#xff1a; properties&#xff1a;一组包含作为动画属性和终值的样式属性和及其值的集合duration(可选)&#xff1a;动画执行时间&am…

可以进行单元测试么_前端与单元测试

先来几个专业词汇&#xff0c;这样显得高大上一点&#xff08;不存在的。&#xff09;BDD: Behavior-Driven Development (行为驱动开发)TDD: Test-Driven Development (测试驱动开发)ATDD: Acceptance Test Driven Development(验收测试驱动开发)好&#xff0c;说完了&#xf…

excel 行高 上下留白_拒绝加班,工作中最常用的57个Excel小技巧来了!

今天高顿君分享的 Excel小技巧&#xff0c;全是工作是最常用且简单易操作的&#xff0c;共57个&#xff0c;希望对同学们有所帮助。&#xff08;适合版本 Excel2007及以上&#xff09;一、文件操作1、为excel文件添加打开密码文件 - 信息 - 保护工作簿 - 用密码进行加密。2、为…

经验分享:三步走教你升级企业NAS设备

前几年凡是对于数据存储有需求的企业都已经购买了相关的NAS产品&#xff0c;不过电脑和网络升级换代是比较频繁的&#xff0c;几年过去了中小企业对数据存储的需求也水涨船高&#xff0c;然而面对当初的NAS存储设备该如何处理呢&#xff1f;扔掉可惜使用又不如意的鸡肋问题能够…

获取访客进站关键词_拼多多访客突然下降是为什么?拼多多访客突然暴涨又是怎么回事?...

在当下这个互联网时代&#xff0c;可以说流量就代表这金钱。这一点在做电商的商家那里表现的就更为直观了&#xff0c;如果你做了一个拼多多的店铺&#xff0c;之前店铺的流量一直都比较好&#xff0c;而现在拼多多店铺的流量忽然下降了&#xff0c;那么店铺中的销售额就会受到…

通过Matlab实现离散序列卷积和

前言 年轻人&#xff0c;你对数学一无所知&#xff0c;你只是习惯了而已。—冯诺伊曼 Young man, in mathematics you dont understand things. You just get used to them.—John von Neumann。 一、卷积和是什么&#xff1f; 卷积的本质是描述一个瞬时动作&#xff08;激励…

mac电脑下Tomcat和Apach配置流程(超详细)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 前言 本章介绍在mac 电脑下如何配置Tomcat、Apach等环境 一、Apache介绍及配置 1.XAMPP安装 为了更好的进行各项软件服务的配置&#xff0c;引入快捷脚本工具——XMAPP。…