第三方登录接入-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 { @Resource private JavaMailSender mailSender; @Resource private 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"; } } }
@RequestMapping("/")
@Controller
public class BindController extends AuthController {
@Resource
private IUserService userService;
@Resource
private IBAuthService authService;
@Resource
private IUserVerifiedService verifiedService;
@Resource
private 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);
}
}
@RequestMapping("/")
@Controller
public class QQController extends AuthController {
@Resource
private IBAuthService authService;
@Resource
private 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 去获取当前用的openid
OpenID 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) {
// 判定有登录记录
//刷新accessToken
bauth.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("获取到的值为空?");
}
}
}
@RequestMapping("/")
@Controller
public class SinaController extends AuthController {
@Resource
private IBAuthService authService;
@Resource
private 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";
}
}
-------------------------------------------------------------------------------------------------------------
页面效果
注:该流程为用户首次使用第三方登录时流程
1,登录页面放置第三方登录图标

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

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

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


浙公网安备 33010602011771号